html三角形怎么画(webgl 系列 —— 三角形)
三角形
有人说三维模型的基本单元是三角形 。比如复杂的游戏角色 ,也只是用许多三角形画出来的 。
不管上述说法是否属实 ,本篇先把三角形画出来 。
如何绘制一个三角形
鼠标点击绘点示例我们写了这样的代码:
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 < 0) { console.log(Failed to get the storage location of a_Position); return -1; } // 将整个缓冲区对象分配给 atttibute(a_Position) 变量 gl.vertexAttribPointer(a_Position, count, gl.FLOAT, false, 0, 0); // 激活 attribute 变量 ,使缓冲区对 attribute 变量分配生效 gl.enableVertexAttribArray(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 bindBuffergl.bindBuffer(target, buffer) - 绑定缓冲区 。例如示例中的: gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
为什么要将绑定缓冲区对象?不能直接向缓冲区写入数据 ,只能通过目标写入数据 。就像这样:
目标表示缓冲区的用途 。例如这里的 gl.ARRAY_BUFFER 指包含顶点属性(例如顶点坐标 、纹理坐标数据或顶点颜色数据)的缓冲区 。
bufferDatagl.bufferData(target, size, usage) - 将数据写入缓冲区 。例如示例中的:gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
gl.STATIC_DRAW 是 usage 中的一种 ,指缓冲区的内容可能经常使用 ,而不会经常更改 。内容被写入缓冲区 ,但不被读取 。
此方法执行后 ,webgl 系统内部状态如下:
vertexAttribPointerg.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,效果却是:
enableVertexAttribArraygl.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)
创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!