I am trying to add multiple textures on the same object in Three.js. I have a 3d T-shirt and the following elements as .png for the T-shirt textures:
The front, back and sleeves of the T-shirt:
The neck for the T-shirt:
And the normal map that will give the cloth folds:
I'm using this code to add the first two images as textures to my object, so that I apply the front, back and sleeves image and the neck image to my material.
const loader = new THREE.TextureLoader();
function initObject() {
var texture1 = loader.load( 'img/ak-jersey-no-neck.png' );
var texture2 = loader.load( 'img/ak-only-neck.png' );
var pitchMaterialParams = {
uniforms: THREE.UniformsUtils.merge([{
texture1: null,
texture2: null,
}]),
vertexShader:
`
varying vec2 vUv;
void main()
{
vUv = uv;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
}
`,
fragmentShader:
`
precision mediump float;
uniform sampler2D texture1;
uniform sampler2D texture2;
varying vec2 vUv;
void main() {
vec4 t1 = texture2D( texture1, vUv );
vec4 t2 = texture2D( texture2, vUv );
gl_FragColor = vec4(mix(t1.rgb, t2.rgb, t2.a), 1.0);
}
`
};
var material = new THREE.ShaderMaterial(pitchMaterialParams);
material.uniforms.texture1.value = texture1;
material.uniforms.texture2.value = texture2;
const objLoader = new OBJLoader();
objLoader.load('object.obj', (object) => {
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.material = material;
}
} );
scene.add(object)
// console.log(object);
},
// called when loading is in progresses
function ( xhr ) {
console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
},
(error) => {
console.log('An error happened');
console.log(error);
}
);
}
initObject();
With this code, the two textures are correctly applied to my object.
I tried and added a third one with the normal map, but in doesn't blend correctly:
const loader = new THREE.TextureLoader();
const normalTexture = new THREE.TextureLoader().load('img/228180-NormalMap.png')
function initObject() {
var texture1 = loader.load( 'img/ak-jersey-no-neck.png' );
var texture2 = loader.load( 'img/ak-only-neck.png' );
var pitchMaterialParams = {
uniforms: THREE.UniformsUtils.merge([{
texture1: null,
texture2: null,
normalMap: null,
}]),
vertexShader:
`
varying vec2 vUv;
void main()
{
vUv = uv;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
}
`,
fragmentShader:
`
precision mediump float;
uniform sampler2D texture1;
uniform sampler2D texture2;
uniform sampler2D normalMap;
varying vec2 vUv;
void main() {
vec4 t1 = texture2D( texture1, vUv );
vec4 t2 = texture2D( texture2, vUv );
vec4 t3 = texture2D( normalMap, vUv );
gl_FragColor = vec4(mix(t1.rgb, t2.rgb, t3.rgb), 1.0);
}
`
};
var material = new THREE.ShaderMaterial(pitchMaterialParams);
material.uniforms.texture1.value = texture1;
material.uniforms.texture2.value = texture2;
material.uniforms.normalMap.value = normalTexture;
// material.normalMap = normalTexture;
const objLoader = new OBJLoader();
objLoader.load('object.obj', (object) => {
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.material = material;
}
} );
scene.add(object)
// console.log(object);
},
// called when loading is in progresses
function ( xhr ) {
console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
},
(error) => {
console.log('An error happened');
console.log(error);
}
);
}
initObject();
The result is just a combining of the 3 textures:
I would like to apply the normalMap as it was applied to the MeshStandardMaterial, so I obtain a result like this, where the fold are visible on the T-shirt:
Is there a way to combine the normalMap image with the other textures in my ShaderMaterial so that it blends like a normalMap should?