JavaScript|JavaScript WebGL 绘制一个面

引子 JavaScript WebGL 基础疑惑点之后进行了一些优化,然后尝试绘制常见二维的面。
【JavaScript|JavaScript WebGL 绘制一个面】WebGL 中几何体最终都是由三角形构成,由三角形切入比较合适。

  • Origin
  • My GitHub
绘制三角形 这是示例,基于绘制一条直线主要的变化有:
  • 顶点
  • 绘制图元
顶点
三角形有三个顶点,在基础疑惑点中知道坐标系是右手坐标系,个人习惯描述顶点的顺序以图形中心为原点,从第一象限到第四象限。
let vertices = [ 0.5, 0.5, 0.0, // 第一象限 -0.5, 0.5, 0.0, // 第二象限 -0.5, -0.5, 0.0, // 第三象限 ]; // 三角形

绘制图元
这次绘制的是一个面,drawArrays 中绘制模式变为 gl.TRIANGLES 。顺便看看图元的几种模式。
  • gl.POINTS : 绘制一系列点。
  • gl.LINES :绘制一系列单独线段,每两个点作为端点,线段之间不连接。例如有顶点 A、B、C、D、E、F,就会得到了三条线段。
JavaScript|JavaScript WebGL 绘制一个面
文章图片

  • gl.LINE_STRIP : 绘制一系列线段,上一点连接下一点。
JavaScript|JavaScript WebGL 绘制一个面
文章图片

  • gl.LINE_LOOP : 绘制一系列线段,上一点连接下一点,并且最后一点与第一个点相连。
JavaScript|JavaScript WebGL 绘制一个面
文章图片

  • gl.TRIANGLES : 绘制一系列三角形,每三个点作为顶点。例如有 6 个顶点 A、B、C、D、E、F,就会绘制 2 个三角形: ABC 和 DEF。
JavaScript|JavaScript WebGL 绘制一个面
文章图片

  • gl.TRIANGLE_STRIP : 用来绘制有共享边的三角形。从第二个三角形开始,每次读取一个顶点,并利用前面的末尾两个顶点构成一个三角形,以此类推。例如有 6 个顶点 A、B、C、D、E、F,就会绘制 4 个三角形: ABC 和 BCD 和 CDE 和 DEF。
JavaScript|JavaScript WebGL 绘制一个面
文章图片

  • gl.TRIANGLE_FAN : 绘制有共享边的三角形。从第二个三角形开始,每次读取一个顶点,并利用首个顶点和之前最后一个顶点来构成一个三角形,以此类推。例如有 6 个顶点 A、B、C、D、E、F,就会绘制 4 个三角形: ABC 和 ACD 和 ADE 和 AEF。
JavaScript|JavaScript WebGL 绘制一个面
文章图片

执行过程 这里有一个绘制三角形执行过程可视化,结合看看有助于加深理解。
高清处理 上面的示例,在高清屏幕中会出现明显的模糊和锯齿,但跟处理 2d 上下文的模糊又有些不一样。最主要的一个区别是 WebGL 中需要用 viewport 方法指定从标准设备到窗口坐标的映射变换。详细可以见这篇文章里面的解释。
这是高清示例。
function WebGLHD(w = 300, h = 150) { const ratio = window.devicePixelRatio || 1; const canvas = document.createElement("canvas"); const context = canvas.getContext("webgl"); // 高清屏幕模糊问题处理 canvas.width = w * ratio; // 实际渲染像素 canvas.height = h * ratio; // 实际渲染像素 canvas.style.width = `${w}px`; // 控制显示大小 canvas.style.height = `${h}px`; // 控制显示大小 context.viewport(0, 0, context.canvas.width, context.canvas.height); }

绘制矩形 前面有说 WebGL 中几何体最终都是由三角形构成,在绘制多边形的时候需要分解为多个三角形。
这是示例,一个矩形可以分为两个三角形:
let vertices = [ 0.5, 0.5, 0.0, -0.5, 0.5, 0.0, -0.5, -0.5, 0.0, // 第一个三角形 -0.5, -0.5, 0.0, 0.5, -0.5, 0.0, 0.5, 0.5, 0.0, // 第二个三角形 ]; // 矩形

可以发现有一条边是公共,这个时候可以索引缓冲区对象减少冗余的数据。
索引缓冲对象 索引缓冲对象全称是 Index Buffer Object(IBO),通过索引的方式复用已有的数据。
基于上面正方形的示例,主要的变化有以下几方面:
  • 数据
  • 缓冲
  • 绘制
数据
顶点位置数据只需要 4 个就足够了,公共数据使用索引代替。
const vertices = [ 0.5, 0.5, 0.0, // 第 1 个顶点 -0.5, 0.5, 0.0, // 第 2 个顶点 -0.5, -0.5, 0.0, // 第 3 个顶点 0.5, -0.5, 0.0, // 第 4 个顶点 ]; // 矩形

索引数据跟上面提示到的图元绘制模式有关。
绘制模式为 gl.TRIANGLES 时,两个三角形是独立的,索引数据如下:
const indexData = https://www.it610.com/article/[ 0, 1, 2, // 对应顶点位置数据中 1、2、3 顶点的索引 0, 2, 3, // 对应顶点位置数据中 1、3、4 顶点的索引 ]

绘制模式为 gl.TRIANGLE_STRIP 时,利用前一个三角形末尾的两个顶点构建三角形:
const indexData = https://www.it610.com/article/[ 1, 0, 2, 3 // 绘制时,先取索引 1、0、2 的位置数据绘制第一个三角形,然后再取索引 0、2、3 的位置数据 绘制第二个三角形 ]

绘制模式为 gl.TRIANGLE_FAN 时,利用第一个顶点,和前一个三角形末尾的一个顶点,加上新读取的顶点构建三角形:
const indexData = https://www.it610.com/article/[ 0, 1, 2, 3 // 绘制时,先取索引 0、1、2 的位置数据绘制第一个三角形,然后再取索引 0、2、3 的位置数据 绘制第二个三角形 ]

缓冲
索引的数据需要缓冲到对应的变量才能使用。
/** * 缓冲索引数据 * @param {*} gl WebGL 上下文 * @param {*} data 索引数据 */ function setIndexBuffers(gl, data) { // 创建空白的缓冲对象 const buffer = gl.createBuffer(); // 绑定目标 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); // WebGL 不支持直接使用 JavaScript 原始数组类型,需要转换 const dataFormat = new Uint16Array(data); // 初始化数据存储 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, dataFormat, gl.STATIC_DRAW); }

绘制
使用了索引缓冲对象,需要用 drawElements 方法来替代 drawArrays。该方法多了一个 type 参数,指的是索引缓冲数据的类型,有下面的值可取:
  • gl.UNSIGNED_BYTE
  • gl.UNSIGNED_SHORT
前面缓冲索引数据类型转换为了 Uint16Array ,这里应该使用 gl.UNSIGNED_SHORT
三种方式示例如下:
  • TRIANGLES 示例
  • TRIANGLE_STRIP 示例
  • TRIANGLE_FAN 示例
参考资料
  • WebGL基础绘制之二:绘制三角形
  • WebGL lessons
  • 你好,三角形
  • WebGL 1.0 官方索引卡片

    推荐阅读