attribute 与 uniform 本质区别(WebGL)
都是给着色器传数据,核心差异:数据给谁用、多久变一次、谁负责迭代、内存模型
1. 核心本质
| 特性 | attribute | uniform |
|---|---|---|
| 作用对象 | 顶点着色器专用 | 顶点着色器 + 片元着色器都能用 |
| 数据频率 | 逐顶点变化 | 一次绘制调用内全局不变 |
| 数据源 | 顶点缓冲区 VBO(GPU 显存数组) | CPU 单次上传的常量值 |
| 迭代方式 | GPU 自动遍历每个顶点,每个顶点拿到自己的 attribute | 一次 drawCall,所有顶点 / 片元共用同一个值 |
| 典型数据 | 顶点坐标、法线、UV、顶点颜色 | 变换矩阵、光照颜色、纹理单元、全局参数 |
attribute
- 存放在顶点缓冲区对象 VBO,是一组数组,数组长度 = 顶点数量。
gl.drawArrays/drawElements的时候,GPU 会自动循环:每处理 1 个顶点,从 VBO 读出当前顶点对应那一组 attribute 数据送入顶点着色器。
例:画三角形 3 个顶点,position 属性数组有 3 组数据,顶点 0 拿第 0 组,顶点 1 拿第 1 组,顶点 2 拿第 2 组。
- 只能在顶点着色器声明 ,不能直接在片元着色器用;想给片元,需要通过
varying/in输出传递。
uniform
- drawCall 整个绘制批次里,全部顶点、全部片元拿到完全相同的值。
- 由 CPU 调用
gl.uniformXXX()把数值传给 GPU,不是从 VBO 读取。 - 顶点、片元着色器都可以直接声明使用。
- 一次 draw 调用内部值固定;想要变,CPU 重新调用 uniform 接口修改,再执行下一次 draw。
2. 形象类比
画一个三角形(3 个顶点)
- attribute:每个人的个人信息。3 个人(顶点),每人有自己的坐标、uv,每个人拿到属于自己那一份。
- uniform:全局公共设置。比如灯光颜色、投影矩阵,三个人全部共用同一套参数。
3. 关键易错点
-
❌ uniform 不能传数组当顶点数据流。uniform 数组只是 "一堆全局常量",不会随顶点自动迭代。大量顶点数据必须走 attribute+VBO。
-
attribute 数量有限(WebGL1 一般最多 8 个 attribute 槽位);uniform 数量上限更高。
-
attribute 来自显存 VBO,CPU 只上传一次,之后多次 draw 复用;uniform 每次修改都要 CPU‑GPU 拷贝。
-
数据流动:
CPU内存 → gl.bufferData → VBO显存 → attribute → 顶点着色器
CPU内存 → gl.uniform* → GPU寄存器 → uniform → 顶点/片元着色器
4. 极简示例
顶点着色器
// 逐顶点,每个顶点不一样
attribute vec3 aPos;
attribute vec2 aUv;
// 全局统一,所有顶点一样
uniform mat4 uMvpMatrix;
// 传给片元
varying vec2 vUv;
void main(){
gl_Position = uMvpMatrix * vec4(aPos,1.0);
vUv = aUv;
}
片元着色器
precision mediump float;
varying vec2 vUv;
// uniform片元也能用
uniform vec3 uLightColor;
uniform sampler2D uTexture;
void main(){
gl_FragColor = texture2D(uTexture,vUv)*vec4(uLightColor,1.0);
}
5. 什么时候选哪个
- ✅ 每个顶点不一样 → attribute(位置、uv、法线、顶点色)
- ✅ 整个物体 / 整个绘制批次全部共用同一个值 → uniform(矩阵、光源、材质颜色、纹理)
补充 WebGL2
WebGL2 语法把attribute改成in(顶点输入),varying改成out/in,逻辑本质完全不变 ;uniform关键字不变。