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 的方式,有些比其他的更不明显。
简而言之:您应该通读整篇文章。方法比结果有用多了
我为 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 的方式,有些比其他的更不明显。
简而言之:您应该通读整篇文章。方法比结果有用多了