I am trying to apply a texture to my 3D model with Three JS.
I tried a lot of ways from the Three JS Examples but for any reasons nothing is working.
Here is how I apply the texture to my model:
//LOADING TEXTURE
var textureLoader = new THREE.TextureLoader();
var diffuse = textureLoader.load( "models/Carbon.png" );
diffuse.encoding = THREE.sRGBEncoding;
diffuse.wrapS = THREE.RepeatWrapping;
diffuse.wrapT = THREE.RepeatWrapping;
diffuse.repeat.x = 1;
diffuse.repeat.y = 1;
var normalMap = textureLoader.load( "models/Carbon_Normal.png" );
normalMap.wrapS = THREE.RepeatWrapping;
normalMap.wrapT = THREE.RepeatWrapping;
var material = new THREE.MeshPhysicalMaterial({
roughness: 0.5,
clearcoat: 1.0,
clearcoatRoughness: 0.1,
map: diffuse,
normalMap: normalMap
});
// LOADING MODEL
var loader = new THREE.GLTFLoader();
loader.load("models/stairs.gltf", function(gltf){
model = gltf.scene
model.traverse((newModel) => {
if (newModel.isMesh){
newModel.material = material;
newModel.castShadow = true;
newModel.receiveShadow = true;
}
});
scene.add(model);
});
I appreciate any help!
When loading a texture for a glTF model, you have to set Texture.flipY to false to satisfy the uv convention of glTF.
diffuse.flipY = false;
normalMap.flipY = false;
Besides, you always have to ensure that your model has texture coordinates. You can do this by checking if the geometries of your meshes have an uv attribute. If this attribute is missing, I suggest you generate texture coordinates in a DCC tool like Blender.
three.js R112
Related
I'm trying to map an Image onto my GLB import, however the GLB is just showing up as black. Not really sure where I'm going wrong here.
let textureLoader= new THREE.TextureLoader();
let texture = textureLoader.load("images/pexels-luis-quintero-2471234.jpeg");
texture.flipY = true;
gltfLoader.load( file.path, (gltf) => {
gltf.scene.traverse( (child, key) => {
if(child.isMesh){
child.material = new THREE.MeshBasicMaterial();
let material = new THREE.SpriteMaterial( { map: texture } );
let sprite = new THREE.Sprite( material );
child.material.map = sprite;
}
});
** Added the texture to :-
child.material.map = texture
However the image does not fit to the dimensions of the GLB
An instance of THREE.Sprite is a 3D object similar to THREE.Mesh. You can't assign a sprite to the map property of a material. Do this:
child.material = new THREE.MeshBasicMaterial();
child.material.map = texture;
Besides, if you add a manually loaded color texture to a glTF asset, make sure to add:
texture.encoding = THREE.sRGBEncoding;
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 am trying to recreate Sketchfab's "ambient environment" feature in threejs.
see sketchfab background settings
I want my model in threejs to show a traditional environment map (360 picture of a location), but I want the scene to show a blurred, ambient image as the environement.
I tried loading two cubetextures and applying one to the envMap of the model, and the other to the scene.background:
var loader = new THREE.TextureLoader();
loader.load( "../../image/download/img1.jpg", function ( texture ) {
texture.encoding = THREE.sRGBEncoding;
texture.anisotropy = maxAnisotropy;
var cubemapGenerator = new THREE.EquirectangularToCubeGenerator( texture, { resolution: 512 } );
var cubeMapTexture = cubemapGenerator.update( renderer );
var pmremGenerator = new THREE.PMREMGenerator( cubeMapTexture );
pmremGenerator.update( renderer );
var pmremCubeUVPacker = new THREE.PMREMCubeUVPacker( pmremGenerator.cubeLods );
pmremCubeUVPacker.update( renderer );
envMapCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
var envMap = envMapCubeRenderTarget ? envMapCubeRenderTarget.texture : null;
texture.dispose();
pmremGenerator.dispose();
pmremCubeUVPacker.dispose();
object.material.envMap = envMap
var loader2 = new THREE.TextureLoader();
loader2.load( "../../image/download/img2.jpg", function ( texture ) {
texture.encoding = THREE.sRGBEncoding;
texture.anisotropy = maxAnisotropy;
var cubemapGenerator = new THREE.EquirectangularToCubeGenerator( texture, { resolution: 512 } );
envMapBackground = cubemapGenerator.renderTarget;
scene.background = envMapBackground; //old = texture
scene.background.anisotropy = maxAnisotropy;
});
});
When I try to use two cubemaps, my scene just renders a blank background...
SEE THE SOLUTION BELOW
I am really confused. The target i am trying to achieve is;
Use multiple textures on a loaded mesh.
I have searched multiple times and still i can see the similar questions but nothing helped me.
What i'v tried is (yet);
Created a new mesh with the target mesh's geometry and pushed to target object3d element. (Like a photoshop layer.)
var texture = new THREE.Texture(mapCanvas);
texture.minFilter = THREE.LinearFilter;
texture.needsUpdate = true;
var material = new THREE.MeshPhongMaterial({map: texture, transparent: true});
var targetMesh = book.children[0].children[1],
newMesh = new THREE.Mesh(targetMesh.geometry, material);
book.children[0].children.push(newMesh);
Result, wrong geometry attributes, or am i missing something?.
But i think it could be a easier solution like using multiple textures at the same time with a correct order.
Full code:
sampleImage.onload = function() {
var mapCanvas = document.createElement('canvas');
mapCanvas.width = sampleImage.width;
mapCanvas.height = sampleImage.height;
var ctx = mapCanvas.getContext('2d');
ctx.translate(sampleImage.width / 2, sampleImage.height / 2);
ctx.rotate(Math.PI);
ctx.translate(-sampleImage.width / 2, -sampleImage.height / 2);
ctx.drawImage(sampleImage, 0, 0, sampleImage.width, sampleImage.height);
var texture = new THREE.Texture(mapCanvas);
texture.minFilter = THREE.LinearFilter;
texture.needsUpdate = true;
var material = new THREE.MeshPhongMaterial({map: texture, transparent: true});
var targetMesh = book.children[0].children[1],
newMesh = new THREE.Mesh(targetMesh.geometry, material);
book.children[0].children.push(newMesh);
};
I found a solution with cloning.
Do not push directly to the mesh group using javascript array push.
use .add method.
book.children[0].add(newMesh);
I'm trying to offset a texture that's being used as an environment map, but not having much luck.
The texture is loaded with the loadTextureCube() method, which gets applied to my mesh just fine, but the offsets don't seem to have any effect.
The texture is just a big gray circle to give a little bit of gloss.
Any thoughts on what I'm doing wrong?
var urls = [
'pos-x.png',
'neg-x.png',
'pos-y.png',
'neg-y.png',
'pos-z.png',
'neg-z.png'
];
var cubemap = THREE.ImageUtils.loadTextureCube(urls);
cubemap.offset.y = -.5;
cubemap.offset.x = -.5;
cubemap.needsUpdate=true;
I'm assuming based on loadTextureCube that your utilizing the cube shader approach to skyboxes. So far everything with your code is fine. The problem your seeing is that while your texture has offset properties, the material (or more specifically the cube shader program therein) does not have uniforms to pass this along to the fragmentation shader. This of course is assuming your doing something like this:
Eg. cube shader material
var skyboxGeo = new THREE.CubeGeometry( 5000, 5000, 5000 );
var cubeShader = THREE.ShaderUtils.lib[ "cube" ];
cubeShader.uniforms[ "tCube" ].value = cubemap;
var skyboxMat = new THREE.ShaderMaterial( {
fragmentShader: cubeShader.fragmentShader,
vertexShader: cubeShader.vertexShader,
uniforms: cubeShader.uniforms,
side: THREE.BackSide
});
var skybox = new THREE.Mesh( skyboxGeo, skyboxMat );
scene.add( skybox );
There's likely a number of work arounds, but you could alway try something like a MeshFaceMaterial on a standard cube to achieve the desired result:
Eg. standard material
var skyboxGeo = new THREE.CubeGeometry( 5000, 5000, 5000 );
var materialArray = [];
for (var i = 0; i < 6; i++) {
var cubeTex = THREE.ImageUtils.loadTexture( urls[i] );
cubeTex.offset.x = -.5;
cubeTex.offset.y = -.5;
materialArray.push( new THREE.MeshBasicMaterial({
map: cubeTex,
side: THREE.BackSide
}));
}
var skyboxMat = new THREE.MeshFaceMaterial( materialArray );
var skyBox = new THREE.Mesh( skyboxGeo, skyboxMat );
Hope that helps
~D