Search code examples
three.jsfragment-shadervertex-shaderuv-mappingmaterials

UV mapping texture on a THREE JS cube


Im trying to create a cube with some texture using web GLSL shaders for the first time. But the entire box is the top left pixel of the image and i cant quite figure out how to fix this. Help pls :))

SandTexture

THREE JS SCENE

entire code:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>My first three.js app</title>
        <style>
            body { margin: 0; }
        </style>
    </head>
    <body>
        <script type="module">
            import * as THREE from 'https://unpkg.com/three/build/three.module.js';

            const scene = new THREE.Scene();
            const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

            const renderer = new THREE.WebGLRenderer();
            renderer.setSize( window.innerWidth, window.innerHeight );
            document.body.appendChild( renderer.domElement );



            const geometry = new THREE.BoxGeometry(1, 1, 1);
            const material = new THREE.RawShaderMaterial({
              vertexShader: ` 
              attribute vec3 position;

              uniform mat4 projectionMatrix;
              uniform mat4 modelViewMatrix;

              uniform mat4 modelMatrix;
              uniform mat4 viewMatrix;

              uniform float uTime;
              varying vec2 vUv;

                void main() {
                    
                    gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
                }
              `,
              fragmentShader: `
                precision mediump float;

                uniform sampler2D uTexture;
                uniform float uTime;

                varying vec2 vUv;

              void main() {
                
                vec4 color = texture2D(uTexture, vUv);

                gl_FragColor = vec4(color.xyz, 1);
                }
              `
            });

            material.uniforms.uTime = {value: 0};
            material.uniforms.uTexture = {value: new THREE.TextureLoader().load("Sandblock.jpg")}

            console.log(material.uniforms);


            const mesh = new THREE.Mesh(geometry, material);
            scene.add(mesh);




            camera.position.z = 2;
            function animate() {
                requestAnimationFrame( animate );
                renderer.render( scene, camera );

            }

            animate();

            document.addEventListener('keydown', (event) => {
              if (event.code == "KeyD") {
                camera.position.x += 1;
              }
              if (event.code == "KeyA") {
                camera.position.x -= 1;
              }
              if (event.code == "KeyS") {
                camera.position.z += 1;
              }
              if (event.code == "KeyW") {
                camera.position.z -= 1;
              }
            });
        </script>
    </body>
</html>

have been messing around the GLSL for some time now, and tried changing

uniform sampler2D uTexture; -> uniform sampler3D uTexture;

but nothing is changed so i just left it :)


Solution

  • You need to add the uv attribute to your vertex shader, then pass it as a varying to your fragment shader:

    attribute vec uv;
    varying vec2 vUv;
    
    void main() {
        gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
    
        // We need to assign a value to the varying
        vUv = uv;
    }
    

    This way, when you do a texture lookup in the fragment shader with texture2D(uTexture, vUv);, the values will be available. Otherwise, I think they'll either be 0.0, or just give you an error.