# 工作原理
GLSL
attribute
uniform
varying
vec2
mat4
WebGL 在 GPU 上的工作基本上分为两部分
- 第一部分是将顶点转换到裁剪空间坐标
- 第二部分是基于第一部分的结果绘制像素点。
当你调用
export function draw(gl) {
const primitiveType = gl.TRIANGLES;
const offset = 0;
const count = 9;
gl.drawArrays(primitiveType, offset, count);
}
这里的9表示“处理9个顶点”,所以将会有9个顶点被转换。
左侧是你提供的数据。顶点着色器(Vertex Shader)是你写进GLSL 中的一个方法,每个顶点调用一次,在这个方法中做一些数学运算后设置了一个特殊的gl_Position变量, 这个变量就是该顶点转换到裁剪空间中的坐标值,GPU接收该值并将其保存起来。
# 三角形
// 定义一个三角形填充到缓冲里
function setGeometry(gl) {
const WIDTH = gl.canvas.width
const HEIGHT = gl.canvas.height
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([
WIDTH * 0.5,
HEIGHT * (1 - 0.75),
WIDTH * 0.06,
HEIGHT * (1 - 0.17),
WIDTH * 0.88,
HEIGHT * (1 - 0.09)
]),
gl.STATIC_DRAW
);
}
然后在我们的顶点着色器中定义一个 varying(可变量)用来给片断着色器传值。
// 一个属性变量,将会从缓冲中获取数据
attribute vec2 a_position;
uniform mat3 u_matrix;
varying vec4 v_color;
void main() {
// 将位置和矩阵相乘
gl_Position = vec4((u_matrix * vec3(a_position, 1)).xy, 0, 1);
// 从裁减空间转换到颜色空间
// 裁减空间范围 -1.0 到 +1.0
// 颜色空间范围 0.0 到 1.0
v_color = gl_Position * 0.5 + 0.5;
}
在片断着色器中定义同名 varying 变量。
precision mediump float; // 这决定了 GPU 在计算浮点数时使用的精度。
// highp 高精度
// mediump 中精度
// lowp 低精度
varying vec4 v_color; // 同名变量
void main() {
gl_FragColor = v_color;
}
# 渲染结果
# 着色原理
WebGL先获得顶点着色器中计算的三个颜色值,在光栅化三角形时将会根据这三个值进行插值。 每一个像素在调用片断着色器时,可变量的值是与之对应的插值。
我们的给顶点着色器施加了一个包含平移,旋转和缩放的的矩阵,并将结果转换到裁剪空间。
gl_Position = vec4((u_matrix * vec3(a_position, 1)).xy, 0, 1);
同时将这些值转换到颜色空间中赋给我们定义的可变量v_color。
v_color = gl_Position * 0.5 + 0.5;
利用这三个值进行插值后传进每个像素运行的片断着色器中。
varying vec4 v_color; // 同名变量
void main() {
gl_FragColor = v_color;
}
# 缓冲操作
缓冲操作是在GPU上获取顶点和其他顶点数据的一种方式。
- gl.createBuffer创建一个缓冲
- gl.bindBuffer是设置缓冲为当前使用缓冲
- gl.bufferData将数据拷贝到缓冲,这个操作一般在初始化完成
// 创建对应缓冲
const positionBuffer = gl.createBuffer();
// 绑定位置信息缓冲
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// 通过绑定点向缓冲中存放数据
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([0, 0, 0, 0.5, 0.7, 0]),
gl.STATIC_DRAW
);
一旦数据存到缓冲中,还需要告诉WebGL怎么从缓冲中提取数据传给顶点着色器的属性。
// 开启属性
gl.enableVertexAttribArray(positionLocation);
// 告诉属性怎么从 ARRAY_BUFFER 中读取数据
const size = 2; // 每次迭代运行提取两个单位数据
const type = gl.FLOAT; // 每个单位的数据类型是32位浮点型
const normalize = false; // 不需要归一化数据
const stride = 0; // 每次迭代运行运动多少内存到下一个数据开始点
// 0 = 移动单位数量 * 每个单位占用内存(sizeof(type))
const offset = 0; // 从缓冲起始位置开始读取
// 告诉如何使用传入的数据
gl.vertexAttribPointer(
positionLocation,
size,
type,
normalize,
stride,
offset
);