You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}, [])

问题成因
  1. 法线未同步模型变换
    顶点着色器中直接传递原始normal给vNormal,但球体在JSX中设置的旋转(rotation.y = 30)是通过模型矩阵生效的,原始法线没有经过**法线矩阵(normalMatrix)**变换,导致片元着色器中的vNormal还是局部空间的方向,和世界空间的相机位置计算点积时出现方向错误。当法线与视线夹角大于90度时,点积结果为负,mix函数的权重变成负数,最终让白色(vec3(1.))的混合占比超过100%,呈现纯白色。

  2. 菲涅尔权重未做范围限制
    菲涅尔点积的原生范围是[-1, 1],你直接乘以1.2后范围扩展到[-1.2, 1.2],负数权重会彻底打乱mix的混合逻辑,进一步放大颜色异常。

  3. 旋转逻辑错误(间接影响)
    useFrame中直接给rotation.y赋值30(弧度),球体只会固定在该角度而非动态旋转,虽然不是变白的直接原因,但会让问题表现更突兀,正确的动态旋转应该用增量更新(如rotation.y += 0.01)。


内容的提问来源于stack exchange,提问作者Dronius

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 14:19:54