three.jsglslwebglfragment-shadervertex-shader

Error compiling fragment shader in Three.js: 'texture' not found


I'm working with Three.js and am facing an issue when compiling a fragment shader. Here's my code: https://jsbin.com/cigadibeya/3/edit?html,js,output. I tried to implement animation like here: https://www.shadertoy.com/view/wlyXRD

THREE.WebGLProgram: shader error: 0 gl.VALIDATE_STATUS false gl.getProgramInfoLog Fragment shader is not compiled.
ERROR: 0:241: 'texture' : no matching overloaded function found
ERROR: 0:241: 'xyz' : field selection requires structure or vector on left hand sid

Solution

  • The problem occurs due to the differences between THREE WebGL GLSL versions. You are using version r82, which uses GLSL 1.0, which means you have to use texture2D, which is deprecated in GLSL 3.0, which ShaderToy uses.

    https://registry.khronos.org/OpenGL-Refpages/gl4/index.php

    You can check the current GLSL version for a given THREE version.

    From r119 the GLSL version is added automatically.

    https://github.com/mrdoob/three.js/wiki/Migration-Guide#r119--r120

    const renderer = new THREE.WebGLRenderer();
    const gl = renderer.getContext(); 
    const glslVersion = gl.getParameter(gl.SHADING_LANGUAGE_VERSION); console.log('GLSL Version:', glslVersion);
    

    You also need to replace the name of the main function on main, because ShaderToy accepts a mainImage / fragColor => gl_FragColor etc. ShaderToy provides its own environment and abstractions.

    Image shaders implement the mainImage() function in order to generate the procedural images by computing a color for each pixel.

    But even if you change everything I wrote, you will still see a black screen... because your camera is set too close.

    Try set:

    camera.position.z = 10;
    
    //...
    
    return (lightIntensity * (k_d * dotLN + k_s * pow(dotRV, alpha)) * 0.5 + 0.5 * texture2D(iChannel0, ref.xy).xyz);
    
    //...
    
    void main() {
        vec2 fragCoord = gl_FragCoord.xy;
        vec3 dir = rayDirection(45.0, iResolution.xy, fragCoord);
        vec3 eye = vec3(0.0, 0.0, 5.0);
        vec2 sdf = shortestDistanceToSurface(eye, dir, MIN_DIST, MAX_DIST);
        float dist = sdf.x;
        
        if (dist > MAX_DIST - EPSILON) {
            gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
            return;
        }
        
        vec3 p = eye + dist * dir;
        vec3 N = estimateNormal(p);
        
        float l1 = length(cross(vec3(0., 0.0, 1.), N));
        l1 = smoothstep(0.65, 0.6, l1) - smoothstep(l1, 0.65, 0.6);
        float l2 = length(cross(vec3(0., 0.01, 1.05), N));
        l2 = smoothstep(0.65, 0.6, l2) - smoothstep(l2, 0.65, 0.6);
        float l3 = length(cross(vec3(0.02, 0.0, 1.1), N));
        l3 = smoothstep(0.65, 0.59, l3) - smoothstep(l3, 0.65, 0.59);
        vec3 color = vec3(l1, l2, l3);
        
        gl_FragColor = vec4(color, 1.0);
    }
    

    Full changes

    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r82/three.min.js"></script>
    
    <canvas class="webgl"></canvas>
    <script id="vertexShader" type="x-shader/x-vertex">
    varying vec2 vertexUV;
    
    void main() {
        vertexUV = uv;
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
    </script>
    
    <script id="fragmentShader" type="x-shader/x-fragment">
    precision highp float;
    
    uniform vec3 iResolution;
    uniform float iTime;
    uniform sampler2D iChannel0;
    
    #define M_NONE -1.0
    #define M_NOISE 1.0
    
    float hash(float h) {
        return fract(sin(h) * 43758.5453123);
    }
    
    float noise(vec3 x) {
        vec3 p = floor(x);
        vec3 f = fract(x);
        f = f * f * (3.0 - 2.0 * f);
    
        float n = p.x + p.y * 157.0 + 113.0 * p.z;
        return mix(
                mix(mix(hash(n + 0.0), hash(n + 1.0), f.x),
                        mix(hash(n + 157.0), hash(n + 158.0), f.x), f.y),
                mix(mix(hash(n + 113.0), hash(n + 114.0), f.x),
                        mix(hash(n + 270.0), hash(n + 271.0), f.x), f.y), f.z);
    }
    
    #define OCTAVES 4
    float fbm(vec3 x) {
        float v = 0.0;
        float a = 0.5;
        vec3 shift = vec3(100);
        for (int i = 0; i < OCTAVES; ++i) {
            v += a * noise(x);
            x = x * 2.0 + shift;
            a *= 0.5;
        }
        return v;
    }
    
    const int MAX_MARCHING_STEPS = 200;
    const float MIN_DIST = 0.0;
    const float MAX_DIST = 100.0;
    const float EPSILON = 0.002;
    
    float sdBox(vec3 p, vec3 b) {
        vec3 q = abs(p) - b;
        return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0) - 2.0;
    }
    
    float opSmI(float d1, float d2, float k) {
        float h = clamp(0.5 - 0.5 * (d2 - d1) / k, 0.0, 1.0);
        return mix(d2, d1, h) + k * h * (1.0 - h);
    }
    
    mat3 rotateY(float t) {
        float c = cos(t);
        float s = sin(t);
        return mat3(vec3(c, 0, s),
                    vec3(0, 1, 0),
                    vec3(-s, 0, c));
    }
    
    mat3 rotateX(float t) {
        float c = cos(t);
        float s = sin(t);
        return mat3(vec3(1, 0, 0),
                    vec3(0, c, -s),
                    vec3(0, s, c));
    }
    
    mat3 rotateZ(float t) {
        float c = cos(t);
        float s = sin(t);
        return mat3(vec3(c, -s, 0),
                    vec3(s, c, 0),
                    vec3(0, 0, 1));
    }
    
    float sdCapsule(vec3 p, vec3 a, vec3 b, float r) {
        vec3 pa = p - a;
        vec3 ba = b - a;
        float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
        return length(pa - ba * h) - r;
    }
    
    float sceneSDF(vec3 sP) {
        float f = sdCapsule(sP, vec3(-1.7, 0., 0.), vec3(1.7, 0., 0.), 0.3);
        float d = 0.02 * sin(sP.x * 5.0) * sin((sin(sP.x * 2.0 + iTime) + 1.0) * sP.x * 20.0) + noise(vec3(sP.z * 2.5 - iTime * 1.0)) - 1.2;
        return f + 0.5 * d;
    }
    
    vec2 shortestDistanceToSurface(vec3 eye, vec3 marchingDirection, float start, float end) {
        float depth = start;
        for (int i = 0; i < MAX_MARCHING_STEPS; i++) {
            float dist = sceneSDF(eye + depth * marchingDirection);
            if (dist < EPSILON) {
                return vec2(depth, float(i + 1));
            }
            depth += dist;
            if (depth >= end) {
                return vec2(end, 0.0);
            }
        }
        return vec2(end, 0.0);
    }
    
    vec3 rayDirection(float fieldOfView, vec2 size, vec2 fragCoord) {
        vec2 xy = fragCoord - size / 2.0;
        float z = size.y / tan(radians(fieldOfView) / 2.0);
        return normalize(vec3(xy, -z));
    }
    
    vec3 estimateNormal(vec3 p) {
        return normalize(vec3(
            sceneSDF(vec3(p.x + EPSILON, p.y, p.z)) - sceneSDF(vec3(p.x - EPSILON, p.y, p.z)),
            sceneSDF(vec3(p.x, p.y + EPSILON, p.z)) - sceneSDF(vec3(p.x, p.y - EPSILON, p.z)),
            sceneSDF(vec3(p.x, p.y, p.z + EPSILON)) - sceneSDF(vec3(p.x, p.y, p.z - EPSILON))
        ));
    }
    
    vec3 phongContribForLight(vec3 k_d, vec3 k_s, float alpha, vec3 p, vec3 eye,
                              vec3 lightPos, vec3 lightIntensity) {
        vec3 N = estimateNormal(p);
        vec3 L = normalize(lightPos - p);
        vec3 V = normalize(eye - p);
        vec3 R = normalize(reflect(-L, N));
        
        vec3 ref = reflect(p - eye, N);
        
        float dotLN = dot(L, N);
        float dotRV = dot(R, V);
        
        if (dotLN < 0.0) {
            return vec3(0.0, 0.0, 0.0);
        }
        
        if (dotRV < 0.0) {
            return lightIntensity * (k_d * dotLN);
        }
        return (lightIntensity * (k_d * dotLN + k_s * pow(dotRV, alpha)) * 0.5 + 0.5 * texture2D(iChannel0, ref.xy).xyz);
    }
    
    void main() {
        vec2 fragCoord = gl_FragCoord.xy;
        vec3 dir = rayDirection(45.0, iResolution.xy, fragCoord);
        vec3 eye = vec3(0.0, 0.0, 5.0);
        vec2 sdf = shortestDistanceToSurface(eye, dir, MIN_DIST, MAX_DIST);
        float dist = sdf.x;
        
        if (dist > MAX_DIST - EPSILON) {
            gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
            return;
        }
        
        vec3 p = eye + dist * dir;
        vec3 N = estimateNormal(p);
        
        float l1 = length(cross(vec3(0., 0.0, 1.), N));
        l1 = smoothstep(0.65, 0.6, l1) - smoothstep(l1, 0.65, 0.6);
        float l2 = length(cross(vec3(0., 0.01, 1.05), N));
        l2 = smoothstep(0.65, 0.6, l2) - smoothstep(l2, 0.65, 0.6);
        float l3 = length(cross(vec3(0.02, 0.0, 1.1), N));
        l3 = smoothstep(0.65, 0.59, l3) - smoothstep(l3, 0.65, 0.59);
        vec3 color = vec3(l1, l2, l3);
        
        gl_FragColor = vec4(color, 1.0);
    }
    </script>
    <script>
      // Canvas
      const canvas = document.querySelector('canvas.webgl')
    
      // Scene
      const scene = new THREE.Scene();
    
      // Objects
      const geometry = new THREE.SphereGeometry(5, 50, 50);
    
      // Materials
      const loader = new THREE.TextureLoader();
    
      const material = new THREE.ShaderMaterial({
        vertexShader: document.getElementById('vertexShader').textContent,
        fragmentShader: document.getElementById('fragmentShader').textContent,
        uniforms: {
          iResolution: { value: new THREE.Vector3(window.innerWidth, window.innerHeight, 1) },
          iTime: { value: 0 },
          iChannel0: { value: loader.load('https://i.ibb.co/CsjHTSQ/test.png') }
        }
      });
    
      // Mesh
      const mesh = new THREE.Mesh(geometry, material);
      scene.add(mesh);
    
      // Camera
      const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
      camera.position.z = 10;
    
      // Renderer
    const renderer = new THREE.WebGLRenderer({ canvas });
      renderer.setSize(window.innerWidth, window.innerHeight);
    const gl = renderer.getContext();
    const glslVersion = gl.getParameter(gl.SHADING_LANGUAGE_VERSION);
    console.log('GLSL Version:', glslVersion);
    
      // Animation loop
      function animate() {
        requestAnimationFrame(animate);
    
        // Update uniforms
        material.uniforms.iTime.value += 0.05;
    
        renderer.render(scene, camera);
      }
    
      animate();
    </script>