Three.js ShaderMaterial 仅适用于某些 GPU

Three.js ShaderMaterial only working on some GPUs

我为 3d 飞机跟踪器添加了发光效果:

效果由 THREE.ShaderMaterial 驱动。该效果仅适用于某些 PC。在其他 PC 上,效果不可见,但没有显示任何错误,其他一切正常。我在多台 PC 和操作系统上试过,似乎是 hardware-related 问题,而不是 OS 问题。

glow 效果的代码如下所示:

private _createGlow(radius: number, segments: number) {

    let material = new THREE.ShaderMaterial({
        uniforms: {
            "c":   { type: "f", value: 0.01 },
            "p":   { type: "f", value: 6 },
            glowColor: { type: "c", value: new THREE.Color(0x002296) },
            viewVector: { type: "v3", value: this._camera.position }
        },
        vertexShader: `
        uniform vec3 viewVector;
        uniform float c;
        uniform float p;
        varying float intensity;
        void main() 
        {
            vec3 vNormal = normalize( normalMatrix * normal );
            vec3 vNormel = normalize( normalMatrix * viewVector );
            intensity = pow( c - dot(vNormal, vNormel), p );

            gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
        }`,
        fragmentShader: `
        uniform vec3 glowColor;
        varying float intensity;
        void main() 
        {
            vec3 glow = glowColor * intensity;
            gl_FragColor = vec4( glow, 1.0 );
        }`,
        side: THREE.FrontSide,
        blending: THREE.AdditiveBlending,
        transparent: true
    });

    let geometry = new THREE.SphereGeometry(radius, segments, segments);
    let mesh = new THREE.Mesh(geometry, material);
    mesh.scale.multiplyScalar(1.2);
    return mesh;
}

创建场景的代码如下:

this._scene = new THREE.Scene();
this._scene.add(this._universe);
this._scene.add(this._glow);
this._scene.add(this._atmosphere1);
this._scene.add(this._atmosphere2);
this._scene.add(this._earth);
this._scene.add(this._light);
this._scene.add(new THREE.AmbientLight(0x333333, 0.9));

这是 GPU 驱动程序问题还是类似问题?我该如何解决?谢谢!

你的问题有很多未知数。由于我们无法访问您测试过代码的所有系统,因此我们不能只拿走您的代码,"fix it" 并返回一个工作片段。

然而,您可以做的是在进行 3D 编程时无处不在的有用的东西,即微分调试 - 分而治之。

着色器使用的功能至少是最近 10 年制造的硬件所支持的,所以我会更广泛地寻找问题。乍一看我怀疑有几个罪魁祸首:

  • 剔除 - 您的网格可能在渲染过程中被错误地剔除
  • 深度失败 - 你的网格可能没有通过深度测试(从你的代码中看不出为什么会这样)
  • 环境制服(normalMatrix 等)可能未正确初始化。

在这种情况下,基本过程是从最基本的基本着色器开始,逐步进行,直到找到导致错误行为的单个语句或表达式。

首先制作一个只输出白色(或glowColor)的着色器并禁用剔除并测试它 - 你看到你的球体了吗?

  • 是 - 深度检查有效;
  • 否 - 深度检查有问题。

启用剔除 - 你看到球体了吗?

  • 是的 - 剔除很好(可能 - 你可以看到模型的背面);
  • 否 - 您的面孔被错误剔除。

接下来调试法线向量 - 将法线传递给像素着色器并将它们输出为片段颜色。您可以使用 (n * 0.5) + 0.5 对它们进行编码以获得对象的 world-space "normal map"。看起来不错吗?

  • 是 - 环境制服可能是正确的;
  • 否 - 这可能是 three.js 中的错误。

最后添加强度计算。它有效吗?嗯,它不应该,因为此时我们已经重建了整个发光效果,我们知道它并不总是有效。那么为什么它不起作用?

最后一个问题 - 如果 GPU 除以 0 会怎样?

  • 首先 - 你不会收到错误。
  • 其次 - 不应允许这种情况发生,因为到处都是 NaN 并最终导致荒谬的 GPU-dependent 结果。

我猜你的代码为什么不起作用是因为 pow( c - dot(vNormal, vNormel), p ) returns NaN 只要基数是负数。不同的 GPU 可能有不同的处理 NaN 的方式,有些比其他的更不明显。

简而言之:您应该通读整篇文章。方法比结果有用多了