一、概念
在 GPU 渲染中,颜色的计算分为两个阶段:
-
顶点着色器 (Vertex Shader) :
- 只对每个顶点执行一次。
- 可以为每个顶点输出不同的属性,例如位置。
-
片元着色器 (Fragment Shader) :
- 对三角形覆盖的每个像素执行一次。
- 接收到的是顶点属性经过插值后的结果。
这就是为什么虽然三角形只有三个顶点,却能让三角形内部的每个像素显示出不同的颜色。
const material = new THREE.ShaderMaterial({
glslVersion: THREE.GLSL3, // 必须指定 GLSL3 才能使用 in/out
vertexShader: `
out vec3 vPosition; // 传递给片元 shader
void main() {
vPosition = position;
gl_Position = vec4(position, 1.0);
}
`,
fragmentShader: `
out vec4 FragColor; // GLSL 3.0 输出
in vec3 vPosition; // 来自顶点 shader 的插值
void main() {
FragColor = vec4(vPosition * 0.5 + 0.5, 1.0);
}
`
});
二、原理
在你的代码里:
vPosition = position; // 顶点坐标 (x,y,z)
FragColor = vec4(vPosition * 0.5 + 0.5, 1.0);
-
每个顶点坐标不同
- 三角形有三个顶点
(x,y,z),每个值不一样。 - 经过
*0.5+0.5映射后,每个顶点都对应一个不同的 RGB 颜色。
- 三角形有三个顶点
-
GPU 自动插值
- 光栅化时,GPU 会把三角形内部的像素分配给三个顶点。
- 每个像素的位置不同,它的
(x,y,z)由三个顶点的坐标加权平均得到。 - 因此,每个像素的
vPosition也不同 → 映射到颜色后也不同。
-
结果:渐变色
- 三角形的三个顶点是三种不同颜色。
- 内部像素的颜色是三者插值混合出来的。
- 所以三角形内部会显示为彩色渐变。
三、实例演算(注:在glsl 计算与js中不一样,例 pos = (1,1,0); posT = (pos, 1.0) 能够得到 (1,1,1,1.0) 如是加减乘除是对每一个值 进行操作,例+0.5 为(1.5.1.5.1.5.1.5))
假设三角形三个顶点:
-
A 点:
(-1,-1,0)- →
(0,0,0.5)→ 深蓝色
- →
-
B 点:
(1,-1,0)- →
(1,0,0.5)→ 紫红色
- →
-
C 点:
(0,1,0)- →
(0.5,1,0.5)→ 绿色偏粉
- →
在渲染时:
- 边 AB 上的像素:颜色从蓝色逐渐过渡到紫红色。
- 边 AC 上的像素:颜色从蓝色逐渐过渡到绿色。
- 三角形内部:任意一点的颜色 =
A、B、C 三个颜色加权平均,结果就是彩色渐变。
四、为什么没写入颜色也能变彩色?
关键在于 position 本身就是变化的。
- 你没有显式写死颜色,而是用
(x,y,z)作为颜色来源。 - 不同像素位置 → 不同插值后的
(x,y,z)值。 - 这些值经过
*0.5+0.5转换后直接用作 RGB。
因此,即使你没写 uniform 或 attribute 的颜色,坐标本身就提供了差异,结果自然就是彩色的。
五、总结
- 三个顶点各自有不同的坐标
(x,y,z)。 - 映射后变成三个不同的
(r,g,b)颜色。 - GPU 在片元阶段自动插值 → 内部像素颜色随位置变化。
- 最终三角形呈现渐变彩色,而不是单一颜色。
换句话说:彩色效果来自于“顶点位置的不同 + GPU 的插值机制”。
本文部分内容借助 AI 辅助生成,并由作者整理审核。