Search code examples
three.jsglslshader

Not getting expect result from THREE.js/GLSL code


Apologies for the vague question but I wasn't sure how to phrase this. I'm trying to write some THREE.js/GLSL code that produces a circular gradient (for some SDF stuff). With the code below I would expect to see the gradient on the plane, but the plane remains white and nothing else renders on it. Can anyone tell me where I'm going wrong?

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>My first three.js app</title>
        <style>
            body { margin: 0; }
            canvas { width: 100%; height: 100% }
        </style>
    </head>
    <body>
        <script type="x-shader/x-vertex" id="sdfVS">
            varying vec2 vUv; // pass the uv coordinates of each pixel to the frag shader

            void main() {
              vUv = uv;
              gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
            }
        </script>
        <script type="x-shader/x-fragment" id="sdfFS">
            precision mediump float;
            uniform vec2 u_resolution;
            varying vec2 vUv;

            float circle(vec2 pos, float radius)
            {
                return distance(pos, vec2(radius));
            }

            void main()
            {
                vec2 pos = (gl_FragCoord.xy / vUv) * 2.0 - 1.0;
                float circle1 = circle(pos, 0.5);
                vec3 color = vec3(circle1);
                gl_FragColor = vec4(color, 1.0);
            }
        </script>
        <script src="../../js/three.min.js"></script>
        <script>
            var scene, camera, renderer, aspect, geometry, material, plane;
            var container;
            var frustumSize = 2;

            init();
            animate();

            function init() {
                container = document.createElement( 'div' );
                document.body.appendChild( container );

                scene = new THREE.Scene();
                scene.background = new THREE.Color(0x0000ff);

                aspect = window.innerWidth / window.innerHeight;
                camera = new THREE.OrthographicCamera( 0.5 * frustumSize * aspect / - 2, 0.5 * frustumSize * aspect / 2, frustumSize / 2, frustumSize / - 2, 0.1, 1 );
                cameraOrthoHelper = new THREE.CameraHelper( camera );
                scene.add( cameraOrthoHelper );

                var width = 1;
                var height = 1;
                geometry = new THREE.PlaneGeometry(width, height);

                material = new THREE.ShaderMaterial( {
                    vertexShader: document.getElementById('sdfVS').textContent,
                    fragmentShader: document.getElementById('sdfFS').textContent,
                    side: THREE.DoubleSide
                } );

                plane = new THREE.Mesh( geometry, material );
                plane.rotation.x = 0;
                plane.rotation.y = THREE.Math.degToRad( -90 );
                plane.rotation.z = 0;
                scene.add( plane )

                renderer = new THREE.WebGLRenderer();
                renderer.setPixelRatio( window.devicePixelRatio );
                renderer.setSize( window.innerWidth, window.innerHeight );
                container.appendChild( renderer.domElement );

                window.addEventListener( 'resize', onWindowResize, false );
            }

            function onWindowResize() {
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
                renderer.setSize( window.innerWidth, window.innerHeight );
            }

            function animate() {
                requestAnimationFrame( animate );
                render();
            }

            function render() {
                camera.position.x = -1;
                camera.position.y = 0;
                camera.position.z = 0;
                camera.lookAt( scene.position );
                camera.updateMatrixWorld();
                renderer.render( scene, camera );
            }
        </script>
    </body>
</html>

Solution

  • Can't get this line:

    vec2 pos = (gl_FragCoord.xy / vUv) * 2.0 - 1.0;

    Seems like you want to calculate uv coordinates, but if so, then you already have uvs (passed in vUv).

    You can do the thing this way, as an option:

    var scene, camera, renderer, aspect, geometry, material, plane;
    var container;
    var clock = new THREE.Clock();
    var frustumSize = 2;
    
    init();
    animate();
    
    function init() {
      container = document.createElement('div');
      document.body.appendChild(container);
    
      scene = new THREE.Scene();
      scene.background = new THREE.Color(0x0000ff);
    
      aspect = window.innerWidth / window.innerHeight;
      camera = new THREE.OrthographicCamera(0.5 * frustumSize * aspect / -2, 0.5 * frustumSize * aspect / 2, frustumSize / 2, frustumSize / -2, 0.1, 1);
      cameraOrthoHelper = new THREE.CameraHelper(camera);
      scene.add(cameraOrthoHelper);
    
      var width = 1;
      var height = 1;
      geometry = new THREE.PlaneGeometry(width, height);
    
      material = new THREE.ShaderMaterial({
      	uniforms: {time: {value: 0}},
        vertexShader: document.getElementById('sdfVS').textContent,
        fragmentShader: document.getElementById('sdfFS').textContent,
        side: THREE.DoubleSide
      });
    
      plane = new THREE.Mesh(geometry, material);
      plane.rotation.x = 0;
      plane.rotation.y = THREE.Math.degToRad(-90);
      plane.rotation.z = 0;
      scene.add(plane)
    
      renderer = new THREE.WebGLRenderer();
      renderer.setPixelRatio(window.devicePixelRatio);
      renderer.setSize(window.innerWidth, window.innerHeight);
      container.appendChild(renderer.domElement);
    
      window.addEventListener('resize', onWindowResize, false);
    }
    
    function onWindowResize() {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    }
    
    function animate() {
      requestAnimationFrame(animate);
      render();
    }
    
    function render() {
      camera.position.x = -1;
      camera.position.y = 0;
      camera.position.z = 0;
      camera.lookAt(scene.position);
      camera.updateMatrixWorld();
      var t = clock.getElapsedTime();
      material.uniforms.time.value = t;
      renderer.render(scene, camera);
    }
    body {
      overflow: hidden;
      margin: 0;
    }
    <script src="https://threejs.org/build/three.min.js"></script>
    <script type="x-shader/x-vertex" id="sdfVS">
      varying vec2 vUv; // pass the uv coordinates of each pixel to the frag shader
    
      void main() {
        vUv = uv;
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
      }
    </script>
    <script type="x-shader/x-fragment" id="sdfFS">
      precision mediump float;
    
      uniform float time;
      uniform vec2 u_resolution;
      varying vec2 vUv;
    
      float circle(vec2 uv, vec2 pos, float radius) {
        return smoothstep(radius, 0., length(uv - pos));
      }
    
      void main()
      {
        vec2 uv = vUv * 2. - 1.;
        vec2 pos = vec2(cos(time), sin(time));
        float circle1 = circle(uv, pos, 1.0);
        vec3 color = vec3(circle1);
        gl_FragColor = vec4(color, 1.0);
      }
    </script>