# 着色器
JS
getAttribLocation
enableVertexAttribArray
vertexAttribPointer
getUniformLocation
uniform1i
uniform2f
uniform1fv
uniform2fv
uniform3fv
uniform4fv
createBuffer
bindBuffer
bufferData
createTexture
bindTexture
texImage2D
texParameteri
activeTexture
GLSL
attribute
uniform
varying
vec4
vec2
texture2D
在工作原理中我们提到,WebGL每次绘制需要两个着色器, 一个顶点着色器和一个片断着色器,每一个着色器都是一个方法。 一个顶点着色器和一个片断着色器链接在一起放入一个着色程序中(或者只叫程序)。
一个典型的WebGL应用会有多个着色程序。
# 顶点着色器
一个顶点着色器的工作是生成裁剪空间坐标值,通常是以下的形式
void main() {
gl_Position = doMathToMakeClipspaceCoordinates
}
每个顶点调用一次(顶点)着色器,每次调用都需要设置一个特殊的全局变量gl_Position, 该变量的值就是裁减空间坐标值。
顶点着色器需要的数据,可以通过以下三种方式获得。
- Attributes 属性 (从缓冲中获取的数据)
- Uniforms 全局变量 (在一次绘制中对所有顶点保持一致值)
- Textures 纹理 (从像素或纹理元素中获取的数据)
# Attributes
attribute vec4 a_position;
void main() {
gl_Position = a_position;
}
const buf = gl.createBuffer();
const someData = []
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, someData, gl.STATIC_DRAW);
const positionLoc = gl.getAttribLocation(someShaderProgram, "a_position");
// 开启从缓冲中获取数据
gl.enableVertexAttribArray(positionLoc);
var numComponents = 3; // (x, y, z)
var type = gl.FLOAT; // 32位浮点数据
var normalize = false; // 不标准化
var offset = 0; // 从缓冲起始位置开始获取
var stride = 0; // 到下一个数据跳多少位内存
// 0 = 使用当前的单位个数和单位长度 ( 3 * Float32Array.BYTES_PER_ELEMENT )
gl.vertexAttribPointer(positionLoc, numComponents, type, false, stride, offset);
# Uniforms
attribute vec4 a_position;
uniform vec4 u_offset;
void main() {
gl_Position = a_position + u_offset;
}
// 首先在初始化时找到全局变量的地址
const offsetLoc = gl.getUniformLocation(someProgram, "u_offset");
// 然后在绘制前设置全局变量
gl.uniform4fv(offsetLoc, [1, 0, 0, 0]); // 向右偏移一半屏幕宽度
一个数组可以一次设置所有的全局变量,例如
// 着色器里
uniform vec2 u_someVec2[3];
// JavaScript 初始化时
const someVec2Loc = gl.getUniformLocation(someProgram, "u_someVec2");
// 渲染的时候
gl.uniform2fv(someVec2Loc, [1, 2, 3, 4, 5, 6]); // 设置数组 u_someVec2
单独设置数组中的某个值
const someVec2Element0Loc = gl.getUniformLocation(someProgram, "u_someVec2[0]");
单独设置数组中的某个值
struct SomeStruct {
bool active;
vec2 someVec2;
};
uniform SomeStruct u_someThing;
const someThingActiveLoc = gl.getUniformLocation(someProgram, "u_someThing.active");
# Textures
见下文--片段着色器--Textures
# 片段着色器
一个片断着色器的工作是为当前光栅化的像素提供颜色值,通常是以下的形式
precision mediump float;
void main() {
gl_FragColor = doMathToMakeAColor;
}
每个像素都将调用一次片断着色器,每次调用需要从你设置的特殊全局变量 gl_FragColor 中获取颜色信息。
片断着色器所需的数据,可以通过以下三种方式获取
- Varyings 可变量 (从顶点着色器传递并插值的数据)
- Uniforms 全局变量 (对于单个绘图调用的每个像素保持相同的值)
- Textures 纹理 (从像素或纹理元素中获取的数据)
# Varyings
在工作原理提到过,可变量是一种顶点着色器给片断着色器传值的方式。
为了使用可变量,要在两个着色器中定义同名的可变量。 给顶点着色器中可变量设置的值,会作为参考值进行内插,在绘制像素时传给片断着色器的可变量。
attribute vec4 a_position;
uniform vec4 u_offset;
varying vec4 v_positionWithOffset;
void main() {
gl_Position = a_position + u_offset;
v_positionWithOffset = a_position + u_offset; // set
}
precision mediump float;
varying vec4 v_positionWithOffset; // get
void main() {
// 从裁剪空间 (-1 <-> +1) 转换到颜色空间 (0 -> 1).
vec4 color = v_positionWithOffset * 0.5 + 0.5
gl_FragColor = color;
}
# Uniforms
同上文--顶点着色器--Uniforms
# Textures
在着色器中获取纹理信息,可以先创建一个sampler2D类型全局变量,然后用GLSL方法texture2D 从纹理中提取信息。
precision mediump float;
uniform sampler2D u_texture; // 纹理
void main() {
vec2 texcoord = vec2(0.5, 0.5) // 获取纹理中心的值
gl_FragColor = texture2D(u_texture, texcoord);
}
从纹理中获取的数据取决于很多设置。 至少要创建并给纹理填充数据,例如
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
const level = 0;
const width = 2;
const height = 1;
const data = new Uint8Array([
255, 0, 0, 255, // 一个红色的像素
0, 255, 0, 255, // 一个绿色的像素
]);
gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
在初始化时找到全局变量的地址
const someSamplerLoc = gl.getUniformLocation(someProgram, "u_texture");
在渲染的时候WebGL要求纹理必须绑定到一个纹理单元上
const unit = 5; // 挑选一个纹理单元
gl.activeTexture(gl.TEXTURE0 + unit);
gl.bindTexture(gl.TEXTURE_2D, tex);
然后告诉着色器你要使用的纹理在那个纹理单元
gl.uniform1i(someSamplerLoc, unit);