Cheers,
I'm using a SketchUp 3D model in a Mapbox custom layer (with threebox/THREE.js) and I'm having a problem getting it to look less than awful.
The model looks great in SketchUp but when I add it to Mapbox, the lights are all messed up. The triangles seem to have individual directional light sources that point all over the place.
And this is what it looks like after exported (as .OBJ + .MTL) and imported:
The onAdd looks like this:
onAdd: function(map, gl) {
this.tb = new Threebox(map, gl, { defaultLights: true });
const baseName = "hanse388_006";
var manager = new THREE.LoadingManager();
new THREE.MTLLoader(manager).load(
baseName + ".mtl",
function(materials) {
materials.preload();
new THREE.OBJLoader(manager).setMaterials(materials).load(
baseName + ".obj",
function(object) {
this.boat = this.tb.Object3D({ obj: object, units: "meters" });
this.boat.setCoords([DEFAULT_LNG, DEFAULT_LAT, 0]);
this.tb.add(this.boat);
}.bind(this)
);
}.bind(this)
);
The default lights from threebox (slightly changed by me) look like this:
this.scene.add( new THREE.AmbientLight( 0xffffff, 0.6 ) );
var sunlight = new THREE.DirectionalLight( 0xffffff, 0.7 );
sunlight.position.set(0,80000000,100000000);
sunlight.matrixWorldNeedsUpdate = true;
this.world.add(sunlight);
As a workaround suggested by #Mugen87, I tried the export/import in a different format.
SketchUp doesn't support glTF but when I used Collada (.dae) it worked nicely!
Related
I try to import a Ready Player Me GLB avatar into a Matterport scene (in ThreeJS).
It works but the avatar is very flat (left picture) compared to the rendering into RPM (right picture) or basic GLTF Viewer. I don't understand what I missed ? Pseudo code :
const gltfLoader = new THREE.GLTFLoader();
gltfLoader.load('https://api.readyplayer.me/v1/avatars/63580183b445da7aa57b9ce3.glb',
(gltf) => {
this.mesh = gltf.scene;
const light = new THREE.AmbientLight( 0xFFFFFF, 1 );
light.castShadow = true
this.mesh.add( light );
...
}, undefined);
On the WebGL part I have access to Matterport renderer :
(renderer, THREE, effectComposer) => {
renderer.outputEncoding = THREE.sRGBEncoding
renderer.toneMapping = THREE.ACESFilmicToneMapping
}
Is it related to Antialias ? Or a paramter or lighting that flatten hair and hands ? Or is it related to texture ?
Thanks !
EDIT 11/12/2022:
Thanks to #Mugen87, I find how to use the EquirectangularShader from GLTFLoader sample. This code works with Matterport context.
let pmremGenerator = new THREE.PMREMGenerator( this.context.renderer );
pmremGenerator.compileEquirectangularShader();
let envMap = pmremGenerator.fromScene( new THREE.RoomEnvironment() ).texture;
this.context.scene.environment = envMap;
Instead of using a single instance of AmbientLight, apply a more advanced type of environmental lighting to your scene. Especially if you already enabled tone mapping, consider to use a HDR environment map. The basic GLTFLoader example of three.js is a good code template. The most important part is:
new RGBELoader()
.load( 'textures/equirectangular/royal_esplanade_1k.hdr', function ( texture ) {
texture.mapping = THREE.EquirectangularReflectionMapping;
scene.environment = texture;
// more scene setup
} );
Applying an environment map to Scene.environment ensures all PBR materials in the scene are going to use it.
I am trying to change the color of my 3D model "behind" the png texture I set (which includes transparency).
I have done a lot of researches, and i finally found an example with a cube which actually works, but I can't understand how to make that with my gltf 3D model (not a BoxGeometry).
METHOD :
Define an array of two materials,
first one is my png texture with transparency = true;
second one is a basic material with its plain color (the color i will be able to change later...)
var materialBack = new THREE.MeshBasicMaterial({color: 0xfadce6});
var materialTxt = new THREE.MeshBasicMaterial({map: mytexture,transparent: true});
var materials = [materialBack, materialTxt];
It works perfect with a cube :
var geometry = new THREE.BoxBufferGeometry();
geometry.clearGroups();
geometry.addGroup( 0, Infinity, 0 );
geometry.addGroup( 0, Infinity, 1 );
var cube = new THREE.Mesh( geometry, materials );
Problem : I can't figure out how to do the same when my model is actually an imported GLTF, and not a "BoxBufferGeometry". It looks like we can't assign an array to o.material :
var loader = new THREE.GLTFLoader();
loader.load(mymodel.glb, function(gltf) {
gltf.scene.traverse((o) => {
if (o.isMesh) {
o.material = materials;
}
scene.add(gltf.scene);
});
I also tried to extract geometry from gltf, then create a new mesh, but without success :
var loader = new THREE.GLTFLoader();
loader.load(mymodel.glb, function(gltf) {
var geometry = gltf.scene.getObjectByName("name").geometry;
mymesh = new THREE.Mesh(geometry,materials);
scene.add(mymesh);
});
Can someone please help ?
I'm actually trying to add some materials to my Gltf objects in three js r113, but I don't know how to use correctly material parameters and the light as I see my object in three js viewer. This is What I get in Firefox
and this is what I'm dreaming about
.
I guess this is the value I need to apply to my code
This is how I add my gltf and how I try to add materials :
// Load a glTF resource of FENCE
gltfLoader.load( 'Fence.gltf', function ( gltf ) {
fenceModel = gltf.scene;
// fenceModel.traverse(function (child) {
// if (child.isMesh) {
// child.material = new THREE.MeshLambertMaterial({
// color: 0xc07341, //light Brown
// reflectivity: 0,
// dithering: true
// });
// }
// });
});
var ambientlight = new THREE.AmbientLight(0xffffff, 0.5 );
scene.add( ambientlight );
Actually my floor is only a gltf file with no materials.
Maybe I need to add some shadow porperties to my floor and then I could see the fence shadow ?
I need some help to understand how to do a better light effect on object.
Thank you, sorry for my english I'm using translator to help me.
Ps: My gltf object contain texture in it.
I'm pretty new to three.js and I tried for hours to create a skybox/skydome for a better visual feeling to my world (in this case space). Googled, checked tutorials, asked here on StackOverflow. And nothing worked, or I got a silly and dumb answer here on SO. Question is simple: how to make a skybox/dome?
This is how you do a skydome in threejs.
var skyGeo = new THREE.SphereGeometry(100000, 25, 25);
First the geometry. I wanted it big and made it big
var loader = new THREE.TextureLoader(),
texture = loader.load( "images/space.jpg" );
Loads the texture of your background space. One thing here is that you need it to run through a server to be able to load the texture. I use wamp or brackets preview.
Create the material for the skybox here
var material = new THREE.MeshPhongMaterial({
map: texture,
});
Set everything together and add it to the scene here.
var sky = new THREE.Mesh(skyGeo, material);
sky.material.side = THREE.BackSide;
scene.add(sky);
This might not be the best solution for this, but it´s easy specially for a beginner in threejs. Easy to understand and create.
This is how you can load image as texture and apply that on innerside of a sphere geometry to emulate skydome.
Complete solution with error callback for future reference
//SKY
var loader = new THREE.TextureLoader();
loader.load(
"./assets/universe.png",
this.onLoad,
this.onProgress,
this.onError
);
onLoad = texture => {
var objGeometry = new THREE.SphereBufferGeometry(30, 60, 60);
var objMaterial = new THREE.MeshPhongMaterial({
map: texture,
shading: THREE.FlatShading
});
objMaterial.side = THREE.BackSide;
this.earthMesh = new THREE.Mesh(objGeometry, objMaterial);
scene.add(this.earthMesh);
//start animation
this.start();
};
onProgress = xhr => {
console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
};
// Function called when download errors
onError = error => {
console.log("An error happened" + error);
};
I have a house model in my game, and I have some materials for the house geometry. There is a material for the wall of the house, and I have a texture-map-image to show the bricks.
var mat = new THREE.MeshPhongMaterial( {
ambient: 0x969696,
map: THREE.ImageUtils.loadTexture( 'textures/G/G0.jpg' ),
overdraw: true,combine: THREE.MultiplyOperation
} );
In this way above, the texture map appears like GL_CLAMP I want it to show like GL_REPEAT.
What should I do?
If you can not see the images check this.
I have posted a full working example at:
http://stemkoski.github.com/Three.js/Texture-Repeat.html
The relevant part of the code example is:
// for example, texture repeated twice in each direction
var lavaTexture = THREE.ImageUtils.loadTexture( 'images/lava.jpg' );
lavaTexture.wrapS = lavaTexture.wrapT = THREE.RepeatWrapping;
lavaTexture.repeat.set( 2, 2 );
var lavaMaterial = new THREE.MeshBasicMaterial( { map: lavaTexture } );
var lavaBall = new THREE.Mesh( THREE.GeometryUtils.clone(sphereGeom), lavaMaterial );
scene.add( lavaBall );
It's called THREE.RepeatWrapping there. The loadTexture defaults to THREE.ClampToEdgeWrapping (see ctor function from previous link). Don't know if you can use the callback (because this in JS is a bit weird (looks like it points to the created Image, not the created Texture)). Signature:
loadTexture: function ( path, mapping, callback ) {
Better you just name the texture locally and set the wrap modes manually:
var t = THREE.ImageUtils.loadTexture( 'textures/G/G0.jpg' );
t.wrapS = t.wrapT = THREE.RepeatWrapping;
Looks like you're not going far with threejs without looking at the actual code...
Image must be 8x8, 16x16, 32x32, 128x128, 256x256, 512x512 etc.
And all be working.
=)