1

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

1 Answer 1

0

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>

Sign up to request clarification or add additional context in comments.

1 Comment

@user26951052 Generally, unless you have the intention of using such a low version of Three, it is best to use current releases. The guys are very busy there and the library is constantly evolving, if you're not stuck here you'll be stuck somewhere else in a different context when you use old releases...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.