在WebGL中,
varying
和attribute
是着色器程序中的两个重要概念,分别用于在顶点着色器(Vertex Shader)和片段着色器(Fragment Shader)之间传递数据。它们在图形渲染流程中扮演着关键角色,理解它们的区别和使用方式对于开发者正确编写着色器程序至关重要。
1. attribute
的使用
attribute
是顶点着色器中的一种变量,用于存储每个顶点的输入数据。attribute
通常是与顶点相关的,例如顶点的位置、颜色、法线、纹理坐标等。
在WebGL中,attribute
数据一般通过buffer
传递给GPU。这些数据是每个顶点的属性,每次绘制调用时,都会传入一个不同的值。
示例:顶点着色器使用attribute
// 顶点着色器
attribute vec4 a_position; // 顶点位置
attribute vec4 a_color; // 顶点颜色
varying vec4 v_color; // 将颜色传递给片段着色器
void main() {
gl_Position = a_position; // 将位置传递给内建变量gl_Position
v_color = a_color; // 将颜色传递给片段着色器
}
在这个示例中:
a_position
和a_color
是attribute
变量,分别代表每个顶点的位置信息和颜色信息。v_color
是varying
变量,用于传递数据到片段着色器。
2. varying
的使用
varying
是顶点着色器与片段着色器之间传递数据的桥梁。它允许从顶点着色器将数据传递到片段着色器。每个varying
变量在顶点着色器和片段着色器中都有声明,它的值会在顶点着色器执行时被计算,并在片段着色器中使用。
示例:片段着色器使用varying
// 片段着色器
precision mediump float;
varying vec4 v_color; // 从顶点着色器接收到的颜色
void main() {
gl_FragColor = v_color; // 设置片段的颜色
}
在这个示例中:
v_color
是从顶点着色器传递过来的数据,包含了每个顶点的颜色。- 片段着色器使用
v_color
来为每个片段(像素)设置颜色。
3. attribute
和varying
的区别
特性 | attribute | varying |
---|---|---|
作用 | 顶点着色器的输入,代表每个顶点的属性 | 顶点着色器和片段着色器之间的数据传递 |
数据来源 | 由CPU通过buffer 传递给GPU | 从顶点着色器传递到片段着色器 |
是否每个顶点不同 | 每个顶点有不同的值 | 每个片段(像素)会接收不同的值,但相同的片段来自相同的插值 |
类型 | 由顶点数据定义,如vec3 , vec4 等 | 与attribute 类型一致 |
4. 典型的使用场景
-
attribute
的常见使用场景:- 顶点位置:每个顶点的位置坐标(
a_position
)。 - 法线:用于计算光照效果,顶点的法向量(
a_normal
)。 - 纹理坐标:定义顶点处的纹理坐标,用于纹理映射(
a_texCoord
)。 - 颜色:顶点的颜色(
a_color
)。
- 顶点位置:每个顶点的位置坐标(
-
varying
的常见使用场景:- 插值计算:当需要进行插值计算时,使用
varying
变量传递数据,比如平滑的颜色渐变。 - 光照计算:在顶点着色器中计算法线信息,然后通过
varying
传递到片段着色器进行光照计算。 - 纹理映射:顶点着色器传递纹理坐标到片段着色器进行纹理采样。
- 插值计算:当需要进行插值计算时,使用
5. 完整代码示例:简单的彩色三角形渲染
下面是一个简单的WebGL渲染代码,其中包含了如何使用attribute
和varying
来渲染一个带有颜色的三角形。
HTML部分:
<canvas id="webglCanvas" width="500" height="500"></canvas>
JavaScript部分:
// 获取WebGL上下文
const canvas = document.getElementById("webglCanvas");
const gl = canvas.getContext("webgl");
if (!gl) {
console.log("WebGL未能初始化");
}
// 顶点着色器代码
const vsSource = `
attribute vec4 a_position; // 顶点位置
attribute vec4 a_color; // 顶点颜色
varying vec4 v_color; // 传递颜色到片段着色器
void main() {
gl_Position = a_position;
v_color = a_color;
}
`;
// 片段着色器代码
const fsSource = `
precision mediump float;
varying vec4 v_color; // 从顶点着色器接收到的颜色
void main() {
gl_FragColor = v_color; // 设置片段的颜色
}
`;
// 创建着色器程序
function createShaderProgram(gl, vsSource, fsSource) {
const vs = createShader(gl, gl.VERTEX_SHADER, vsSource);
const fs = createShader(gl, gl.FRAGMENT_SHADER, fsSource);
const program = gl.createProgram();
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error("无法链接着色器程序:", gl.getProgramInfoLog(program));
return null;
}
gl.useProgram(program);
return program;
}
function createShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error("着色器编译失败:", gl.getShaderInfoLog(shader));
return null;
}
return shader;
}
// 顶点数据
const vertices = new Float32Array([
0.0, 0.5, 0.0, 1.0, 0.0, 0.0, // 顶点1:位置 + 颜色(红色)
-0.5, -0.5, 0.0, 0.0, 1.0, 0.0, // 顶点2:位置 + 颜色(绿色)
0.5, -0.5, 0.0, 0.0, 0.0, 1.0 // 顶点3:位置 + 颜色(蓝色)
]);
// 创建和绑定缓冲区
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// 获取着色器程序中的属性位置
const program = createShaderProgram(gl, vsSource, fsSource);
const positionLocation = gl.getAttribLocation(program, "a_position");
const colorLocation = gl.getAttribLocation(program, "a_color");
// 启用属性
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 6 * Float32Array.BYTES_PER_ELEMENT, 0);
gl.vertexAttribPointer(colorLocation, 3, gl.FLOAT, false, 6 * Float32Array.BYTES_PER_ELEMENT, 3 * Float32Array.BYTES_PER_ELEMENT);
gl.enableVertexAttribArray(positionLocation);
gl.enableVertexAttribArray(colorLocation);
// 清除屏幕并绘制三角形
gl.clear(gl.COLOR_BUFFER_BIT);
gl.clearColor(0.0, 0.0, 0.0, 1.0); // 背景色为黑色
gl.drawArrays(gl.TRIANGLES, 0, 3);
解释:
- 顶点着色器:
a_position
表示顶点的坐标,a_color
表示顶点的颜色。varying v_color
将颜色传递给片段着色器。 - 片段着色器:从顶点着色器接收到
v_color
,并将其设置为片段的最终颜色。 - JavaScript部分:创建WebGL上下文,编译着色器,绑定顶点数据并设置属性指针,最后
绘制一个带有颜色渐变的三角形。
总结
attribute
用于存储每个顶点的输入数据,如位置、法线、颜色等。varying
用于在顶点着色器和片段着色器之间传递数据,通常用于颜色插值、光照计算等场景。attribute
只在顶点着色器中使用,varying
则在顶点着色器和片段着色器之间共享。