我无法在使用 WebGL1 或 2 的着色器中使用导数 (dFdx)

I Can’t get derivatives (dFdx) working in my shader with WebGL1 or 2

我希望有人能帮我解决这个问题。我已经筋疲力尽了,已经完成了我发现的所有讨论和示例,但仍然无法让 dFdx 工作,无论是对于 WebGl1 还是 WebGL2。部分代码如下。

感谢您的帮助。

片段着色器使用:

#extension GL_OES_standard_derivatives : enable
precision highp float;

varying vec3 vNormal;
varying vec2 vUv;
varying vec3 vViewPosition;

uniform vec3 color;
uniform float animateRadius;
uniform float animateStrength;

vec3 faceNormal(vec3 pos) {
   vec3 fdx = vec3(dFdx(pos.x), dFdx(pos.y), dFdx(pos.z));  
   vec3 fdy = vec3(dFdy(pos.x), dFdy(pos.y), dFdy(pos.z));
   //vec3 fdx = dFdx(pos);
  //vec3 fdy = dFdy(pos);
  return normalize(cross(fdx, fdy));
}

控制台显示如下:

THREE.WebGLProgram: shader error:  0 35715 false gl.getProgramInfoLog Must have an compiled fragment shader attached. <empty string> THREE.WebGLShader: gl.getShaderInfoLog() fragment
WARNING: 0:2: 'GL_OES_standard_derivatives' : extension is not supported
ERROR: 0:14: 'GL_OES_standard_derivatives' : extension is not supported
ERROR: 0:14: 'GL_OES_standard_derivatives' : extension is not supported
ERROR: 0:14: 'GL_OES_standard_derivatives' : extension is not supported
ERROR: 0:15: 'GL_OES_standard_derivatives' : extension is not supported
ERROR: 0:15: 'GL_OES_standard_derivatives' : extension is not supported
ERROR: 0:15: 'GL_OES_standard_derivatives' : extension is not supported1: #define lengthSegments 300.0
2: #extension GL_OES_standard_derivatives : enable
3: precision highp float;
4: 
5: varying vec3 vNormal;
6: varying vec2 vUv;
7: varying vec3 vViewPosition;

这里也是Javascript的一部分:

module.exports = function (app) {
    const totalMeshes = isMobile ? 30 : 40;
    const isSquare = false;
    const subdivisions = isMobile ? 200 : 300;
    
    const numSides = isSquare ? 4 : 8;
    const openEnded = false;
    const geometry = createTubeGeometry(numSides, subdivisions, openEnded);
    // add to a parent container
    const container = new THREE.Object3D();
    const lines = [];
    //lines.length = 0;   
  
    ShaderLoader("scripts/Grp3D/Phys4646A2/tube.vert", "scripts/Grp3D/Phys4646A2/tube.frag", function (vertex, fragment) {
    const baseMaterial = new THREE.RawShaderMaterial({
        vertexShader: vertex,
        fragmentShader: fragment,
        side: THREE.FrontSide,
        extensions: {
        derivatives: true
        },
        defines: {
        lengthSegments: subdivisions.toFixed(1),
        ROBUST: false,
        ROBUST_NORMALS: false, 
        FLAT_SHADED: isSquare
        },
        uniforms: {
        thickness: { type: 'f', value: 1 },
        time: { type: 'f', value: 0 },
        color: { type: 'c', value: new THREE.Color('#303030') },
        animateRadius: { type: 'f', value: 0 },
        animateStrength: { type: 'f', value: 0 },
        index: { type: 'f', value: 0 },
        totalMeshes: { type: 'f', value: totalMeshes },
        radialSegments: { type: 'f', value: numSides },
        wavelength: { type: 'f', value: 2.0 }
        }
    });

    for( var i = 0; i < totalMeshes; i++){
        
        var t = totalMeshes <= 1 ? 0 : i / (totalMeshes - 1);
        
        var material = baseMaterial.clone();
        material.uniforms = THREE.UniformsUtils.clone(material.uniforms);
        material.uniforms.index.value = t;
        material.uniforms.thickness.value = randomFloat(0.005, 0.0075);
        material.uniforms.wavelength.value = 2.0;
        
        var mesh = new THREE.Mesh(geometry, material);
        mesh.frustumCulled = false; 
        lines.push(mesh);
        container.add(mesh);
    }   
    });
    
    return {
    object3d: container,
    update,
    setPalette
    };
        
    function update (dt,wvl) {
    dt = dt / 1000;
    lines.forEach(mesh => {
        //console.log(dt);
        mesh.material.uniforms.time.value += dt;
        mesh.material.uniforms.wavelength.value = wvl;
    });
    }
}; 

...

在具有 300 版 es 着色器的 WebGL2 中,默认支持它们,无需扩展。

<canvas></canvas>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r122/build/three.module.js';

const canvas = document.querySelector('canvas');
const renderer = new THREE.WebGLRenderer({canvas});

const material = new THREE.RawShaderMaterial({
  vertexShader: `#version 300 es
  in vec4 position;
  out vec4 vPosition;
  uniform mat4 projectionMatrix;
  uniform mat4 modelViewMatrix;
  void main() {
    gl_Position = projectionMatrix * modelViewMatrix * position;
    vPosition = gl_Position;
  }
  `,
  fragmentShader: `#version 300 es
  precision mediump float;
  in vec4 vPosition;
  out vec4 outColor;
  void main() {
    outColor = vec4(normalize(vec3(dFdx(vPosition.x), dFdy(vPosition.y), 0)) * 0.5 + 0.5, 1);
  }
  `,
});

const geo = new THREE.BoxBufferGeometry();
const mesh = new THREE.Mesh(geo, material);
const scene = new THREE.Scene();
scene.add(mesh);
mesh.rotation.set(0.4, 0.4, 0);

const camera = new THREE.PerspectiveCamera(45, 2, 0.1, 10);
camera.position.z = 3;

renderer.render(scene, camera);

</script>

注:

const fs = `#version 300 es
...
`;

第一行有 #version 300 es

const fs = `
#version 300 es
...
`;

第 2 行有 #version 300 es(错误)

对于 WebGL1,您的代码应该可以工作,但 three.js 如果 WebGL2 存在,则会自动选择它。为了测试 WebGL1,我们可以通过自己创建 WebGL 上下文来强制它使用 WebGL1,

const canvas = document.querySelector(selectorForCanvas);
const context = canvas.getContext('webgl');
const renderer = new THREE.WebGLRenderer({canvas, context});

或者,我们可以使用a helper script有效地禁用webgl2

<script src="https://greggman.github.io/webgl-helpers/webgl2-disable.js"></script>
<canvas></canvas>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r122/build/three.module.js';

const canvas = document.querySelector('canvas');
const renderer = new THREE.WebGLRenderer({canvas});

const material = new THREE.RawShaderMaterial({
  vertexShader: `
  attribute vec4 position;
  varying vec4 vPosition;
  uniform mat4 projectionMatrix;
  uniform mat4 modelViewMatrix;
  void main() {
    gl_Position = projectionMatrix * modelViewMatrix * position;
    vPosition = gl_Position;
  }
  `,
  fragmentShader: `
  #extension GL_OES_standard_derivatives : enable
  precision mediump float;
  varying vec4 vPosition;
  void main() {
    gl_FragColor = vec4(normalize(vec3(dFdx(vPosition.x), dFdy(vPosition.y), 0)) * 0.5 + 0.5, 1);
  }
  `,
});

const geo = new THREE.BoxBufferGeometry();
const mesh = new THREE.Mesh(geo, material);
const scene = new THREE.Scene();
scene.add(mesh);
mesh.rotation.set(0.4, 0.4, 0);

const camera = new THREE.PerspectiveCamera(45, 2, 0.1, 10);
camera.position.z = 3;

renderer.render(scene, camera);

</script>

否则您可以查看 renderer.capabilities.isWebGL2 以了解 three.js 是否选择 WebGL2 并适当调整着色器。

至于为什么你的代码以前能用,现在不能用,有两个想法:

  1. 你的机器以前不支持 WebGL2,现在支持了
  2. 您已将 three.js 升级到自动选择 WebGL2 的版本,而您使用的先前版本没有。