前言#
首先,构成三维模型的基本单位是三角形,那意味着一张图会会充斥着许许多多的顶点。
在上一篇张我们只能绘制一个点,对于 WebGL 提供一种 缓冲区机制(buffer object) 来向着色器一次性设置多个顶点,达到多点绘制的能力。
缓冲区的使用过程#
我们通过 示例:一次性绘制多个点 例子来认识缓冲区的使用:
效果图如下:
- 分别定义顶点着色去位置和大小,及片元着色器颜色:
// Vertex shader program
var VSHADER_SOURCE = `
attribute vec4 a_Position;
void main() {
gl_Position = a_Position;
gl_PointSize = 10.0;
}`;
// Fragment shader program
var FSHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}`;
- 初始化着色器程序,以及缓存区
function main() {
// 初始化 gl
var canvas = document.getElementById("webgl");
var gl = getWebGLContext(canvas);
// 初始化着色器程序
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
}
// 初始化缓存区
var n = initVertexBuffers(gl);
// 绘制
}
- 缓冲区的逻辑(创建缓存区,绑定缓存区,写入数据,分配变量,开启变量):
function initVertexBuffers(gl) {
var vertices = new Float32Array([0.0, 0.5, -0.5, -0.5, 0.5, -0.5]);
var n = 3; // The number of vertices
/**
* 1. 创建缓存区
* 如果返回 null,表示创建缓存区失败
* gl.deleteBuffer(buffer) 方法用于删除缓存区对象。
*/
var vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log("Failed to create the buffer object");
return -1;
}
/**
* 2. 将缓存区绑定到目标
* gl.bindBuffer(target, buffer) 方法接受两个参数:
* target:绑定目标,可以是 gl.ARRAY_BUFFER 或 gl.ELEMENT_ARRAY_BUFFER。
* gl.ARRAY_BUFFER:顶点属性数据缓存区(本例子)。
* gl.ELEMENT_ARRAY_BUFFER:顶点索引缓存区。
* buffer:缓存区对象。
*/
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
/**
* 3. 向缓存区写入数据
* gl.bufferData(target, data, usage) 方法接受三个参数:
* target:缓存区目标,可以是 gl.ARRAY_BUFFER 或 gl.ELEMENT_ARRAY_BUFFER。
* data:数据
* usage:使用方法,可以是 gl.STATIC_DRAW、gl.DYNAMIC_DRAW 或 gl.STREAM_DRAW。
* gl.STATIC_DRAW:只会向缓冲区写入一次,但需要绘制多次(本例子)。
* gl.DYNAMIC_DRAW:只会向缓冲区写入一次,但需要绘制若干次。
* gl.STREAM_DRAW:会向缓存区多次写入数据,并绘制多次。
*/
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
var a_Position = gl.getAttribLocation(gl.program, "a_Position");
/**
* 4. 将缓存区数据分配给 a_Position 变量
* gl.vertexAttribPointer(location, size, type, normalized, stride, offset) 方法接受多个参数:
* location:attribute 变量的存储位置,即 gl.getAttribLocation 返回的变量。
* size:顶点属性的大小。(本例为2,表示每个顶点属性有 2 个值 x,y。)
* type:数据类型。
* gl.FLOAT:32 位浮点数。
* gl.BYTE:8 位有符号整数,范围是 [-128, 127]。
* gl.UNSIGNED_BYTE:8 位无符号整数,范围是 [0, 255]。
* gl.SHORT:16 位有符号整数,范围是 [-32768, 32767]。
* gl.UNSIGNED_SHORT:16 位无符号整数,范围是 [0, 65535]。
* gl.INT:32 位有符号整数,范围是 [-2147483648, 2147483647]。
* gl.UNSIGNED_INT:32 位无符号整数,范围是 [0, 4294967295]。
* normalized:是否归一化,WebGL 默认是 false。
* stride:相邻顶点属性之间的字节数。
* offset:顶点属性的起始位置(偏移量)。
*/
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
/**
* 5. 链接 a_Position 变量和缓存区
* gl.enableVertexAttribArray(index) 方法接受一个参数:
* index:顶点属性索引,即 gl.getAttribLocation 返回的变量。
*/
gl.enableVertexAttribArray(a_Position);
return n;
}
- 绘制图形
var n = initVertexBuffers(gl);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, n);
WebGL 的基本图形#
我们在绘制方法 gl.drawArrays
中,我们将 gl.POINTS 改为 gl.TRIANGLES:
gl.drawArrays(gl.POINTS, 0, n);
可以看到这样的效果:
WebGL 的基本图形有这些:
mode | 描述 |
---|---|
gl.POINTS | 绘制点 |
gl.LINES | 绘制线段,按 (v0,v1), (v2,v3),(v4,v5) 顺序绘制 |
gl.LINE_STRIP | 绘制线段,并连接所有点。按 (v0,v1), (v1,v2), (v2,v3) 顺序绘制 |
gl.LINE_LOOP | 绘制线段,并连接第一个和最后一个点。按 (v0,v1), (v1,v2), (v2,v0) 顺序绘制 |
gl.TRIANGLES | 绘制(多个独立的)三角形。按 (v0,v1,v2), (v3,v4,v5) 顺序绘制 |
gl.TRIANGLE_STRIP | 绘制三角形,并连接所有点(三角形共用一条边)。按 (v0,v1,v2), (v1,v2,v3), (v2,v3,v4) 顺序绘制 |
gl.TRIANGLE_FAN | 绘制三角形,共享起点。按 (v0,v1,v2), (v0,v2,v3), (v0,v3,v4) 顺序绘制 |
效果如下:
gl.LINES(线段)
var vertices = new Float32Array([0, 0.5, -0.5, -0.5, 0.2, -0.5, 0.7, 0.5]);
var n = 4; // The number of vertices
gl.LINE_STRIP(线条)
var vertices = new Float32Array([0, 0.5, -0.5, -0.5, 0.5, -0.5, 1, 0.5]);
var n = 4; // The number of vertices
gl.LINE_LOOP(回路)
var vertices = new Float32Array([-0.5, 0.5, -1, -0.5, 0, -0.5, 0.5, 0.5, -0.5, 0.5, -1, -0.5]);
var n = 6; // The number of vertices
gl.TRIANGLE_STRIP
var vertices = new Float32Array([-0.5, 0.5, -1, -0.5, 0, -0.5, 0.5, 0]);
var n = 4; // The number of vertices
gl.TRIANGLE_FAN(三角扇)
var vertices = new Float32Array([-0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, -0.5]);
var n = 4; // The number of vertices
变化(移动,旋转,缩放)#
平移#
现在展示尝试将 gl.TRIANGLE 例子中的三角形如何移动到屏幕的右上角。这里将修改三角形的顶点数据,这样的操作成为逐顶点操作(per-vertex operation)。
- 修改顶点着色器,添加平移变量 u_Translation
var VSHADER_SOURCE = `attribute vec4 a_Position;
uniform vec4 u_Translation;
void main() {
gl_Position = a_Position + u_Translation;
}`;
- 获取平移变量存储位置
var n = initVertexBuffers(gl);
var u_Translation = gl.getUniformLocation(gl.program, "u_Translation");
- 给平移变量赋值(赋予其次坐标值)
var Tx = 0.5,
Ty = 0.5,
Tz = 0.0;
gl.uniform4f(u_Translation, Tx, Ty, Tz, 0.0);
// 最终我们新三角形的位置将是 gl_Position = a_Position + u_Translation;
旋转#
现在示意旋转效果图:
- 通过 三角函数 换算,设置顶点位置坐标:
var VSHADER_SOURCE = `attribute vec4 a_Position;
uniform float u_CosB, u_SinB;
void main() {
gl_Position.x = a_Position.x * u_CosB - a_Position.y * u_SinB;
gl_Position.y = a_Position.x * u_SinB + a_Position.y * u_CosB;
gl_Position.z = a_Position.z;
gl_Position.w = 1.0;
}`;
- 通过 弧度制 计算三角函数值
var n = initVertexBuffers(gl);
// Pass the data required to rotate the shape to the vertex shader
var radian = (Math.PI * ANGLE) / 180.0; // Convert to radians
var cosB = Math.cos(radian);
var sinB = Math.sin(radian);
- 赋值给将三角函数值传递给顶点着色器:
var u_CosB = gl.getUniformLocation(gl.program, "u_CosB");
var u_SinB = gl.getUniformLocation(gl.program, "u_SinB");
gl.uniform1f(u_CosB, cosB);
gl.uniform1f(u_SinB, sinB);
// draw...
矩阵旋转#
- 引用矩阵工具函数:
<script src="../lib/cuon-matrix.js"></script>
- 在顶点着色器中,使用矩阵旋转来替换三角函数:
var VSHADER_SOURCE = `
attribute vec4 a_Position;
uniform mat4 u_xformMatrix;
void main() {
gl_Position = u_xformMatrix * a_Position;
}`;
- 定义变换矩阵:
var n = initVertexBuffers(gl);
var radian = (Math.PI * ANGLE) / 180.0; // Convert to radians
var cosB = Math.cos(radian),
sinB = Math.sin(radian);
var xformMatrix = new Float32Array([cosB, sinB, 0.0, 0.0, -sinB, cosB, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]);
- 在绘制之前,将变换矩阵传递给顶点着色器:
var u_xformMatrix = gl.getUniformLocation(gl.program, "u_xformMatrix");\
/**
* gl.uniformMatrix4fv(location, transpose, value)
* location: uniform变量的位置
* transpose: 是否转置矩阵,在 webgl 中为 false
* value: 4x4矩阵,即 xformMatrix 的值
*/
gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix);
矩阵平移#
类似,我们可以通过矩阵平移来改变三角形的位置:
var Tx = 0.5,
Ty = 0.5,
Tz = 0.0;
var xformMatrix = new Float32Array([
1.0, 0.0, 0.0, 0.0, // x轴缩放
0.0, 1.0, 0.0, 0.0, // y轴缩放
0.0, 0.0, 1.0, 0.0, // z轴缩放
Tx, Ty, Tz, 1.0// 平移
]);
矩阵缩放#
类似,我们可以通过矩阵缩放来改变三角形的大小:
var Sx = 1.0,
Sy = 1.5,
Sz = 1.0;
var xformMatrix = new Float32Array([
Sx, 0.0, 0.0, 0.0, // x轴缩放
0.0, Sy, 0.0, 0.0, // y轴缩放
0.0, 0.0, Sz, 0.0, // z轴缩放
0.0, 0.0, 0.0, 1.0// 平移
]);