I've been using the Add a 3D Model Mapbox GL example as a template to add 3D objects to my Mapbox map. It works but sometimes has issues with textures.
I'm loading an OBJ file and adding it to the scene. Here's what the model looks like in a plain THREE.js scene:
Here's a distilled version of the code to render that scene (see gist for full details):
async function addOBJ(path, mtlPath) {
const loader = new OBJLoader2();
const matLoader = new MTLLoader();
matLoader.load(mtlPath, mtlParseResult => {
const materials = MtlObjBridge.addMaterialsFromMtlLoader(mtlParseResult);
loader.addMaterials(materials);
loader.load(path, group => {
scene.add(group);
});
});
}
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.001, 1000 );
camera.position.set(86, 100, -5);
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
addOBJ('silo_p2.obj', 'silo_p2.mtl');
}
When I try to add the same model in my Mapbox map, the texture comes out oddly:
What's going on in my THREE.js + Mapbox scene? It looks like the texture has turned to noise.
Full source available here, but this is the gist:
function getSpriteMatrix(coord, sceneCenter) {
const rotationX = new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(1, 0, 0), 90 * Math.PI / 180);
const s = sceneCenter.meterInMercatorCoordinateUnits();
return new THREE.Matrix4()
.makeTranslation(coord.x - sceneCenter.x, coord.y - sceneCenter.y, coord.z - sceneCenter.z)
.scale(new THREE.Vector3(s, -s, s))
.multiply(rotationX);
}
class SpriteCustomLayer {
type = 'custom';
renderingMode = '3d';
constructor(id) {
this.id = id;
this.model = loadObj('silo_p2.obj', 'silo_p2.mtl');
}
async onAdd(map, gl) {
this.camera = new THREE.Camera();
const centerLngLat = map.getCenter();
this.center = MercatorCoordinate.fromLngLat(centerLngLat, 0);
const {x, y, z} = this.center;
this.cameraTransform = new THREE.Matrix4().makeTranslation(x, y, z);
this.map = map;
this.scene = this.makeScene();
// use the Mapbox GL JS map canvas for three.js
this.renderer = new THREE.WebGLRenderer({
canvas: map.getCanvas(),
context: gl,
antialias: true,
});
// From https://threejs.org/docs/#examples/en/loaders/GLTFLoader
this.renderer.gammaOutput = true;
this.renderer.gammaFactor = 2.2;
this.renderer.autoClear = false;
this.addModel();
}
makeScene() {
const scene = new THREE.Scene();
scene.add(new THREE.AmbientLight( 0xffffff, 0.25 ));
for (const y of [-70, 70]) {
const directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(0, y, 100).normalize();
scene.add(directionalLight);
}
return scene;
}
async addModel() {
const model = await this.model;
flipSides(model);
const scene = model.clone();
const matrix = getSpriteMatrix(
MercatorCoordinate.fromLngLat([-74.0445, 40.6892], 0),
this.center,
);
scene.applyMatrix(matrix);
this.scene.add(scene);
}
render(gl, matrix) {
this.camera.projectionMatrix = new THREE.Matrix4()
.fromArray(matrix)
.multiply(this.cameraTransform);
this.renderer.state.reset();
this.renderer.render(this.scene, this.camera);
this.map.triggerRepaint();
}
}
map.on('load', () => {
const layer = new SpriteCustomLayer('statue');
map.addLayer(layer);
});
How can I get the texture to look right on this Object in the Mapbox view? I'm using THREE.js r109 and Mapbox GL JS v1.4.0. Because of where these models are coming from, using a different format like GLTF isn't an option for me.