R3F中GLSL菲涅尔效果导致球体半边纯白的问题排查
问题:R3F球体旋转时菲涅尔效果半边变白
在基于React Three Fiber(R3F)的应用中,给球体顶点着色器添加菲涅尔效果后,出现球体旋转时半边呈现纯白色的问题,效果如下:
相关代码
顶点着色器(Vertex.glsl)
(cnoise函数取自第三方噪声实现)
uniform float uTime; varying vec3 vPosition; varying vec3 vNormal; varying vec2 vUv; varying float vDisplacement; // cnoise function... void main() { vPosition = position; vNormal = normal; vUv = uv; float customSpeed = uTime; customSpeed = customSpeed * 1.15; vec3 animatedNormal = normal; animatedNormal = animatedNormal * 2.; animatedNormal.x += customSpeed / 2.5; animatedNormal.y += customSpeed / 3.; animatedNormal.z += customSpeed / 4.; vDisplacement = cnoise2(vec4(animatedNormal, 1.), vec4(10.)) / 7.; vec3 newPosition = position + normal * vDisplacement; vec4 modelViewMatrixPosition = modelViewMatrix * vec4(newPosition, 1.0); vec4 projectedPosition = projectionMatrix * modelViewMatrixPosition; gl_Position = projectedPosition; }
片元着色器(Fragment.glsl)
uniform sampler2D uSky; varying float vDisplacement; varying highp vec3 vPosition; varying highp vec3 vNormal; #define PI 3.14159265359 void main() { float phi = acos(vNormal.y); float angle = atan(vNormal.x, vNormal.z); vec2 fakeUv = vec2((angle + PI) / (2. * PI), phi / PI); float fresnel = dot(normalize(cameraPosition - vPosition), normalize(vNormal)); vec4 texture = texture2D(uSky, fakeUv / 3. + vDisplacement); gl_FragColor = vec4(mix(vec3(1.), texture.rgb, fresnel * 1.2), 1.0); // gl_FragColor = texture; // if we only use this it works fine }
Sphere.tsx代码
// 球体JSX部分 <mesh ref={ref}> <icosahedronGeometry args={[2, 200]} /> <shaderMaterial ref={shaderMaterialRef} vertexShader={vertex} fragmentShader={fragment} /> </mesh> // 旋转逻辑 useFrame(() => { ref.current.rotation.y = 30; }, [])
问题成因
法线未同步模型变换
顶点着色器中直接传递原始normal给vNormal,但球体在JSX中设置的旋转(rotation.y = 30)是通过模型矩阵生效的,原始法线没有经过**法线矩阵(normalMatrix)**变换,导致片元着色器中的vNormal还是局部空间的方向,和世界空间的相机位置计算点积时出现方向错误。当法线与视线夹角大于90度时,点积结果为负,mix函数的权重变成负数,最终让白色(vec3(1.))的混合占比超过100%,呈现纯白色。菲涅尔权重未做范围限制
菲涅尔点积的原生范围是[-1, 1],你直接乘以1.2后范围扩展到[-1.2, 1.2],负数权重会彻底打乱mix的混合逻辑,进一步放大颜色异常。旋转逻辑错误(间接影响)
useFrame中直接给rotation.y赋值30(弧度),球体只会固定在该角度而非动态旋转,虽然不是变白的直接原因,但会让问题表现更突兀,正确的动态旋转应该用增量更新(如rotation.y += 0.01)。
内容的提问来源于stack exchange,提问作者Dronius
相关产品推荐
相关产品推荐

