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

Three.js中顶点/片元着色器颜色为何受相机位置影响?

问题分析与解决方案:按点到原点距离分配颜色异常问题

问题背景

创建1000个点组成环形,使用ShaderMaterial关联顶点着色器和片元着色器,期望按每个点到原点的距离分配颜色,但实际颜色受相机位置影响;后续尝试通过varying变量传递距离值后,所有点直接变成蓝色。


原Shader代码

原顶点着色器

const vertexShader = `
uniform float uTime;
uniform float uRadius;

varying vec3 vColor;
varying float vDistance;

void main() {

  vDistance = distance(position, vec3(0.0));

  // Do Not Touch
  gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 );
  gl_PointSize = 5.0;
}

`

export default vertexShader

原片元着色器

const fragmentShader = `
uniform float uDistance[1000];
uniform float uResolutionWidth;
uniform float uResolutionHeight;

varying float vDistance;

  void main() {

    vec2 resolution = vec2(uResolutionWidth, uResolutionHeight);

    vec2 st = gl_FragCoord.xy/resolution;
    
    float pct = distance(st, vec2(1.0));

    vec3 color = vec3(mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 0.0, 1.0), pct));

    gl_FragColor = vec4( color, 1.0 );
  }

`

export default fragmentShader

用户修改后的Shader代码

修改后的顶点着色器

varying vec3 vRealPosition;

void main() {

  vDistance = distance(position, vec3(0.0));
  vColor = mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 0.0, 1.0), vDistance);
  vRealPosition = position;

  // Do Not Touch
  gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 );
  gl_PointSize = 5.0;
}

修改后的片元着色器

varying vec3 vRealPosition;

void main() {

    vec2 resolution = vec2(uResolutionWidth, uResolutionHeight);

    vec2 st = gl_FragCoord.xy/resolution;
    
    float pct = distance(vRealPosition, vec3(0.0));

    vec3 color = vec3(mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 0.0, 1.0), pct));

    gl_FragColor = vec4( vColor, 1.0 );
  }

问题原因

  1. 原代码逻辑偏离需求:原片元着色器完全没用到传递的vDistance,反而用gl_FragCoord计算屏幕坐标到右上角的距离,颜色自然受相机位置影响——相机移动会改变点的屏幕坐标,导致pct值变化。
  2. 修改后全蓝的核心问题:mix函数的第三个参数要求是0~1之间的归一化值,但直接使用的vDistance是点到原点的实际距离(比如环形半径为5,该值就是5),远大于1,mix会直接取第二个颜色参数(蓝色),所以所有点都显示蓝色。

正确实现方案

核心思路:在顶点着色器中计算点到原点的距离,归一化到0~1范围后传递给片元着色器,或直接在顶点着色器中计算好颜色传递。

方案一:顶点传递归一化距离,片元计算颜色

顶点着色器

uniform float uRadius; // 环形的最大半径,作为归一化参考值
varying float vNormalizedDistance;

void main() {
  // 计算点到原点的实际距离
  float distanceToOrigin = distance(position, vec3(0.0));
  // 归一化到0~1范围
  vNormalizedDistance = distanceToOrigin / uRadius;

  gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
  gl_PointSize = 5.0;
}

片元着色器

varying float vNormalizedDistance;

void main() {
  // 用归一化距离混合颜色,0对应红色,1对应蓝色
  vec3 color = mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 0.0, 1.0), vNormalizedDistance);
  gl_FragColor = vec4(color, 1.0);
}

方案二:顶点直接计算颜色,片元直接使用

如果不需要在片元做额外处理,可直接在顶点计算好颜色:

顶点着色器

uniform float uRadius;
varying vec3 vColor;

void main() {
  float distanceToOrigin = distance(position, vec3(0.0));
  float normalizedDistance = distanceToOrigin / uRadius;
  vColor = mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 0.0, 1.0), normalizedDistance);

  gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
  gl_PointSize = 5.0;
}

片元着色器

varying vec3 vColor;

void main() {
  gl_FragColor = vec4(vColor, 1.0);
}

关键注意事项

  • 确保uRadius正确传递给着色器,值为环形的最大半径(所有点到原点的距离不会超过此值),保证归一化后的距离在0~1区间内。
  • 避免使用屏幕坐标(gl_FragCoord)计算与原点的距离,这是2D屏幕空间的坐标,和3D空间中点到原点的实际距离无关,会受相机位置影响。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 00:25:18