三角形
有人说三维模型的基本单元
是三角形。比如复杂的游戏角色,也只是用许多三角形画出来的。
不管上述说法是否属实,本篇先把三角形画出来。
如何绘制一个三角形
鼠标点击绘点示例我们写了这样的代码:
points.forEach(item => {
gl.vertexAttrib3f(a_Position, item.x, item.y, 0.0);
gl.drawArrays(gl.POINTS, 0, 1);
})
这种方法一次只能绘制一个点。
比如需要绘制一个三角形,应该是一个连贯的动作。比如在顶点着色器中一次性画三个点,然后用线连接;而不是绘制一个点,在绘制一个点,在绘制一个点…,不应该是零散的
Tip:提前透露 – 只要一次性将三个点绘制出来,其实三角形也就画出来了。
一次性绘制多个顶点可以使用缓冲区对象
。
缓冲区对象
可以使用 webgl 中的缓冲区对象
(buffer object) 一次性的向着色器传入多个顶点
数据。
使用缓冲区对象向顶点着色器传入多个顶点数据,需要如下5个步骤:
-
创建
缓冲区对象 -
绑定
缓冲区对象 -
写入
数据到缓冲区对象 -
分配
缓冲区对象给一个 attribute 变量 -
开启
attribute 变量
一次绘制三个点
效果
实现
完整代码如下:
// 一次绘制三个点
const VSHADER_SOURCE = `
attribute vec4 a_Position;
void main() {
gl_Position = a_Position;
gl_PointSize = 10.0;
}
`
const FSHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
function main() {
const canvas = document.getElementById('webgl');
const gl = canvas.getContext("webgl");
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// 顶点数据
const vertices = {
// 顶点数据。Float32Array 当做普通数组,对应 C 语言的 float
data: new Float32Array([
0.0, 0.5,
-0.5, -0.5,
0.5, -0.5
]),
// 顶点数
vertexNumber: 3,
// 每个顶点分量数,例如第一个点(0.0, 0.5)
count: 2,
}
// 将顶点的位置写入顶点着色器
initVertexBuffers(gl, vertices)
// gl.drawArrays(mode, first, count)
// 还是画点(gl.POINTS),从缓冲区的第一个位置(0)开始画,绘制3(vertices.vertexNumber)个点
gl.drawArrays(gl.POINTS, 0, vertices.vertexNumber);
}
// 将三个顶点通过缓冲对象一次写入着色器
function initVertexBuffers(gl, {data, count}) {
// 1. 创建缓冲区对象
const vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log('创建缓冲区对象失败');
return -1;
}
// 绑定缓冲区对象绑定到`目标`。只能通过目标向缓冲区写数据
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 将数据写入缓冲区
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
const a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position
通过 initVertexBuffers()
将3个顶点分配给 attribute 变量,执行 gl.drawArrays(gl.POINTS, 0, vertices.vertexNumber)
时,顶点着色器执行了3次。顶点着色器执行过程和缓冲区数据传入过程如下:
绘制出所有点后,颜色缓冲区的内容就会自动显示在浏览器中。
initVertexBuffers()
里面涉及了许多概念和api,请往下阅读。
Float32Array
绘制三维图形 webgl 需要处理大量相同的数据,例如顶点坐标。为了优化性能
,就引入了一种特殊的数组(类型化数组),浏览器事先知道数组中的数据类型,处理起来就更有效率。
Float32Array 就是其中一种类型化数组,通常用于存储顶点坐标或颜色。
Tip: webgl 中很多操作都需要用到类型化数组
。
webgl 中有如下类型化数组:
数组类型 |
每个元素所占字节数 |
对应C语言的数据类型 |
Int8Array |
1 |
8 位有符号整数 |
Uint8Array |
1 |
8 位无符号整型 |
Int16Array |
2 |
16 位有符号整数 |
Uint16Array |
2 |
16 位无符号整型 |
Int32Array |
4 |
32 位有符号的整型 |
Uint32Array |
4 |
32 位无符号的整型 |
Float32Array |
4 |
32 位的浮点数 float |
Float64Array |
8 |
64 位的浮点数 double |
bindBuffer
gl.bindBuffer(target, buffer) – 绑定
缓冲区。例如示例中的: gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
为什么要将绑定缓冲区对象
?不能直接向缓冲区写入数据,只能通过目标
写入数据。就像这样:
目标
表示缓冲区的用途。例如这里的 gl.ARRAY_BUFFER
指包含顶点属性(例如顶点坐标、纹理坐标数据或顶点颜色数据)的缓冲区。
bufferData
gl.bufferData(target, size, usage) – 将数据写入
缓冲区。例如示例中的:gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
gl.STATIC_DRAW
是 usage 中的一种,指缓冲区的内容可能经常使用,而不会经常更改。内容被写入缓冲区,但不被读取。
此方法执行后,webgl 系统内部状态如下:
vertexAttribPointer
g.vertexAttribPointer(index, size, type, normalized, stride, offset) – 将整个缓冲区对象分配
给 atttibute 变量。
例如示例中的:
// count - 指定缓冲区每个顶点的分量个数,缺少则按照 vertexAttrib3f 的补全方式
// gl.FLOAT 与上文的 Float32Array 对应
// normalized - 对于类型gl.FLOAT和gl.HALF_FLOAT,此参数无效
// stride - 指定相邻两个顶点间的字节数。默认为0
// offset - 指定缓冲区对象中的偏移量。即attribute 从缓冲区何处开始存储
gl.vertexAttribPointer(a_Position, count, gl.FLOAT, false, 0, 0);
此方法执行后,webgl 系统内部状态如下:
疑惑
: 说vertexAttribPointer最后一个参数(offset)必须是类型的字节长度的倍数。尝试将 0 改成 4,效果却是:
enableVertexAttribArray
gl.enableVertexAttribArray(index) – 激活
attribute 变量,使缓冲区对 attribute 变量分配生效。
此方法执行后,webgl 系统内部状态如下:
drawArrays
gl.drawArrays(mode, first, count) – 执行顶点着色器,按照 mode 指定的参数绘制图形。first 指定从哪个点开始绘制,count 指绘制需要几个点。
能绘制的图形有:
Tip: drawArrays 更多细节请看这里
三角形
只要一次性将三个点绘制出来,其实三角形也就画出来了。
修改上面示例一行代码就好(gl.POINTS
-> gl.LINE_LOOP
):
// 前
gl.drawArrays(gl.POINTS, 0, vertices.vertexNumber);
// 后
gl.drawArrays(gl.LINE_LOOP, 0, vertices.vertexNumber);
效果如下:
矩形
效果如下:
核心代码如下:
// 定义四个点
const vertices = {
data: new Float32Array([
-0.5, 0.5,
-0.5, -0.5,
0.5, 0.5,
0.5, -0.5,
]),
// 顶点数
vertexNumber: 4,
count: 2,
}
initVertexBuffers(gl, vertices)
// TRIANGLE_STRIP
gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertices.vertexNumber);
Tip:四个点的顺序有要求
三角扇
效果如下:
在矩形的基础上,改为 gl.TRIANGLE_FAN
即可。
Tip:GL_TRIANGLE_FAN – 绘制各三角形形成一个扇形序列,以 v0 为起点:(v0, v1, v2)、(v0, v2, v3)、(v0, v3, v4)
其他章节请看:
webgl 系列
作者:彭加李
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.e1idc.net