首页/ 学科题库/ webgl/ WebGL 片元着色器与 attribute、uniform 数据传参
webgl 实战教程

WebGL 片元着色器与 attribute、uniform 数据传参

2026-09-13 约 9 分钟 juanwangdev

WebGL 片元着色器与 attribute、uniform 数据传参

一句话结论:attribute 负责逐顶点数据,uniform 负责整批共享数据,两者在顶点着色器里汇合,再通过 varying 插值送到片元着色器决定每个像素的颜色。

片元着色器与颜色输出

是什么

片元着色器是 WebGL 管线中决定每个像素颜色的可编程阶段,通过给内置变量 gl_FragColor 赋值输出最终颜色。它接收的是光栅化后每个像素对应的一组插值数据,包括颜色、UV、法线等。

解决什么问题

它解决的是“像素级效果如何实现”的问题。渐变、纹理采样、光照计算、雾效、描边等与像素密度相关的效果都必须在这一阶段完成,因为只有这里才能拿到当前像素的坐标与插值属性。

为什么这样做

颜色写入放在光栅化之后,是因为像素数量远多于顶点,逐像素计算才能表现细节。GPU 为每个像素独立执行同一份着色器,插值由固定硬件完成,因此代码只需按单个像素的视角写逻辑,不必关心三角形覆盖了哪些像素。

怎么用

glsl
// 顶点着色器:把颜色作为 varying 传出
attribute vec3 aPosition;
attribute vec3 aColor;
varying vec3 vColor;

void main() {
  vColor = aColor;
  gl_Position = vec4(aPosition, 1.0);
}
glsl
// 片元着色器:接收插值颜色并输出
precision mediump float;
varying vec3 vColor;

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

常见坑

  • 片元着色器不写精度限定符 → 原因:GLSL ES 要求浮点默认精度 → 处理:首行声明 precision mediump float;
  • 输出颜色出现色带 → 原因:mediump 精度不足 → 处理:颜色计算改用 highp
  • 以为 varying 会做线性插值 → 原因:插值按透视校正执行 → 处理:需要屏幕空间线性插值时改用 noperspective 或手动除 w
  • 变量名在两类着色器中不一致 → 原因:varying 名称必须完全一致 → 处理:统一命名并集中管理

attribute 与 uniform 的区别

是什么

attribute 与 uniform 是 JavaScript 向着色器传值的两种通道。attribute 描述逐顶点属性,每个顶点可以取不同值;uniform 描述整次绘制共享的常量,同一批顶点与像素读取同一份数据。

解决什么问题

两者分离解决了“数据更新频率不同”的矛盾。顶点位置、颜色、法线逐顶点变化,必须逐个传入;而视图矩阵、光源位置、时间这类整批一致的数据如果也逐顶点传,会造成大量冗余传输与状态设置开销。

为什么这样做

GPU 在执行着色器时不区分“变量来源”,但驱动程序需要预知数据的组织方式才能高效批量传输。attribute 按顶点顺序打包成缓冲,可以一次性上传并驻留显存;uniform 则存放在常量区,在绘制调用前设置一次即可被所有执行单元读取。

怎么用

对比项attributeuniform
数据粒度逐顶点每次绘制调用一份
可用阶段仅顶点着色器顶点与片元着色器
典型内容位置、法线、UV、顶点色矩阵、光照参数、时间、纹理采样器
更新方式绑定缓冲并设置 vertexAttribPointer绘制前调用 uniformMatrix4fv 等
数量上限受 MAX_VERTEX_ATTRIBS 限制受 MAX_VERTEX_UNIFORM_VECTORS 限制
JavaScript
// attribute:绑定顶点位置
const loc = gl.getAttribLocation(program, 'aPosition');
gl.enableVertexAttribArray(loc);
gl.vertexAttribPointer(loc, 3, gl.FLOAT, false, 0, 0);

// uniform:每帧设置一次 MVP 矩阵
const uLoc = gl.getUniformLocation(program, 'uMvpMatrix');
gl.uniformMatrix4fv(uLoc, false, mvpMatrix);

常见坑

  • 用 uniform 传逐顶点数据 → 原因:需求混淆 → 处理:颜色渐变、每顶点偏移一律改用 attribute 加 varying
  • 忘记 enableVertexAttribArray → 原因:顶点属性默认关闭 → 处理:绑定后立即启用,程序切换后重新获取位置
  • uniform 位置为 null 仍继续赋值 → 原因:变量被编译器优化掉 → 处理:判断返回值,未使用的 uniform 直接跳过
  • 在片元着色器声明 attribute → 原因:GLSL ES 1.00 不允许 → 处理:attribute 只在顶点阶段声明,跨阶段数据用 varying

要点总结

  • 片元着色器必须给 gl_FragColor 赋值,并声明浮点精度限定符
  • varying 由顶点着色器写入、片元着色器读取,中间由硬件插值
  • attribute 是逐顶点数据,只能在顶点着色器使用
  • uniform 是整批共享数据,两类着色器都能读取,绘制前设置一次
  • 顶点属性用完要启用,uniform 位置可能被优化为空,使用前需判空

常见问题

attribute 和 uniform 的区别是什么?

attribute 是逐顶点数据,每个顶点可以取不同值,只能在顶点着色器中使用;uniform 是一次绘制内所有顶点与像素共享的全局常量,两类着色器中都能读取。

片元着色器怎么拿到顶点阶段的数据?

通过 varying 变量传递。顶点着色器写入 varying,GPU 在光栅化时按重心坐标插值,片元着色器读到的是当前像素对应的插值结果。

为什么 uniform 不能每个顶点取不同值?

因为 uniform 只在每次绘制调用时设置一次,整批顶点与像素共用同一份数据,需要逐顶点差异时必须使用 attribute 或 varying。

gl_FragColor 不赋值会怎样?

输出颜色是未定义值,画面可能全黑或出现随机杂色,且不会报编译错误,因此在片元着色器主函数中必须显式赋值。

想在手机上练这篇的配套题目?
使用微信 卷王开发者小程序/App,首页顶部扫一扫识别下方二维码,即可解锁本讲对应真题与错题本。
了解产品 App
← 上一篇 WebGL 渲染管线流程与顶点着色器最小实现 下一篇 → WebGL 着色器编译、链接与错误排查