Add normalMap to ShaderMaterial

650 Views Asked by At

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:

enter image description here

The neck for the T-shirt:

enter image description here

And the normal map that will give the cloth folds:

enter image description here

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.

enter image description here

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:

enter image description here

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:

enter image description here

Is there a way to combine the normalMap image with the other textures in my ShaderMaterial so that it blends like a normalMap should?

0

There are 0 best solutions below