WebGL第二十九课:针对多个绘制对象的代码重构
引子
前面的文章中,我们大多数都是在绘制一个对象,也就是说一个画面里,只调用了一次 drawArrays
这个api来绘制。
那么如果,我们有多个独立的东西需要绘制的话,我们肯定就需要多次单独调用drawArrays
这个api。
本次文章的目的就是用一个尽量舒服的代码结构,来完成多个绘制目标的初始化,进而渲染。
单个绘制目标
我们这次课用来做例子的单个目标,选定为一个小格子(矩形)。也就是说,我们要画出多个小格子,并且可以自由控制其中的任何一个格子,例如,大小,位置等等,而不影响其他格子的状态。
那么单个格子的模型,根据我们的管理,使用6个点,两个三角形来表示。
同时这个格子,具有一些可以调节的参数:
宽 width
高 height
x坐标
y坐标
颜色
上面五个参数都是一个float类型,你可能会问颜色不是三个float吗?
好吧,为了简化操作,我们RGB三个数值选取一样的,所以就用一个float完事。。。。。。
下面的代码,使用了 js 里的 class 语法,这样写起来清晰一点:
class GridObject { // 宽 高 x坐标 y坐标 // 左下角为基准点 constructor(width, height, posx, posy, color) { this.width = width; this.height = height; this.posx = posx; this.posy = posy; this.color = color; } } 复制代码
那么这些数据最终是要传送给gl的,我们肯定会在gl里申请一个buffer,来存储这部分数据,问题就来了:
单个格子是否使用单独的buffer?
这个答案不唯一。我们这次就用单个的buffer来存储单个的格子,而不是所有格子都用一个buffer
。
所以在constructor里,添加相应的变量:
constructor(......) { ...... this.glbuffer = null; // 存储数据的buffer this.a_PointVertex = null; // vertex_shader中 坐标 数据的引用 this.a_Color = null; // vertex_shader中 颜色 数据的引用 } 复制代码
那么数据既然已经准备好了,我们就想办法,让他变成gl所需要的格式,这里不再反复提及,平坦化数据如下:
this.data = [ // 第一个三角形 this.posx, this.posy, this.color, // 左下角点 this.posx + this.width, this.posy, this.color, // 右下角点 this.posx + this.width, this.posy + this.height, this.color, // 右上角点 // 第二个三角形 this.posx + this.width, this.posy + this.height, 1 - this.color, // 右上角点 this.posx, this.posy + this.height, 1 - this.color, // 左上角点 this.posx, this.posy, 1 - this.color, // 左下角点 ]; this.dataArr = new Float32Array(this.data); this.pointCount = 6; // 一个格子固定六个点 两个三角形 复制代码
上面颜色部分值得一提的是,两个三角形的颜色互补,这是为了更好的观察他们。
生成数据既然好了,那么我们就将数据传入到gl中:
this.glbuffer = gl.createBuffer(); // 创建新的buffer gl.bindBuffer(gl.ARRAY_BUFFER, this.glbuffer); gl.bufferData(gl.ARRAY_BUFFER, this.dataArr, gl.STATIC_DRAW); 复制代码
以上是数据生成和传入部分,那么渲染部分怎么来搞呢。
对于单个格子来说,只需要管理自己的绘制逻辑即可
第一点:将当前gl中的上下文切换到当前对象相应的变量。
第二点:vertex_shader 中attribute的设置。
第三点:利用
drawArrays
api,进行绘制。
第一点其实就一句话:
gl.bindBuffer(gl.ARRAY_BUFFER, this.glbuffer); // 告诉下面的代码,我现在操作或者引用的buffer,是this.glbuffer,不是别的格子的buffer 复制代码
第二点, vertex_shader 里,a_PointVertex用来接收数据元的前两个数据, a_Color 用来接收数据元的第三个数据:
this.a_PointVertex = gl.getAttribLocation(program, 'a_PointVertex'); gl.vertexAttribPointer(this.a_PointVertex, 2, gl.FLOAT, false, 12, 0); gl.enableVertexAttribArray(this.a_PointVertex); this.a_Color = gl.getAttribLocation(program, 'a_Color'); gl.vertexAttribPointer(this.a_Color, 1, gl.FLOAT, false, 12, 8); gl.enableVertexAttribArray(this.a_Color); 复制代码
最后就是绘制:
gl.drawArrays(gl.TRIANGLES, 0, this.pointCount); 复制代码
注意上面,我们绘制的点的个数,就是 this.pointCount , 就是6个。
到了这里,我们已经把 GridObject 的大概职能搞定。
多个绘制目标
我们可以在程序初始化的时候,实例化多个 GridObject 的对象,然后分别对这些对象进行设置参数,生成数据,渲染即可。
为了观察方便,我们不妨设置两个下面的对象:
gridOne = new GridObject(0.5, 0.5, 0, 0, 0.3); gridOne.genData(gl); gridTwo = new GridObject(0.5, 0.5, -0.5, -0.5, 0.3); gridTwo.genData(gl); 复制代码
上面第一个格子,是(0,0)处;
第二个格子,是(-0.5, -0.5)处;
绘制之后的效果如下:
这和我们的预期是一致的。
利用滑竿来修改参数
我们绘制多个单独的对象的目的就是为了:可以单独的修改其中一个,而不影响另一个。
我们设置一个滑竿,来修改第一个格子的颜色。
html部分:
<p> <b>第一个格子的颜色:</b> <input id="gridcolor" type="range" min="0" max="1" value="0" step="0.05" oninput="gl_draw()" /> <b id="gridcolorvalue">0</b> </p> 复制代码
js 部分:
gridColorDom = document.getElementById("gridcolor"); // 获取dom gridOne.color = gridColorDom.value; // 将滑竿的值设置到第一个格子中的color中去 复制代码
问题就来了,重新设置颜色之后,需要清除当前的buffer,然后根据现有的各个参数,重新攒一个buffer出来, 所以GridObject
应该包含这么一个方法:
genData(gl) { this.data = [ // 第一个三角形 this.posx, this.posy, this.color, // 左下角点 this.posx + this.width, this.posy, this.color, // 右下角点 this.posx + this.width, this.posy + this.height, this.color, // 右上角点 // 第二个三角形 this.posx + this.width, this.posy + this.height, 1 - this.color, // 右上角点 this.posx, this.posy + this.height, 1 - this.color, // 左上角点 this.posx, this.posy, 1 - this.color, // 左下角点 ]; this.dataArr = new Float32Array(this.data); this.pointCount = 6; // 一个格子固定六个点 两个三角形 if (this.glbuffer != null) { // 先删掉原来的buffer gl.deleteBuffer(this.glbuffer); this.a_PointVertex = null; this.a_Color = null; } this.glbuffer = gl.createBuffer(); // 创建新的buffer gl.bindBuffer(gl.ARRAY_BUFFER, this.glbuffer); gl.bufferData(gl.ARRAY_BUFFER, this.dataArr, gl.STATIC_DRAW); this.modelUpdated = true; } 复制代码
从上述代码可以看出,每次GridObject对象有参数修改之后,都应该调用这个方法,重新生成buffer。
所以在每次滑竿修改之后,应该这样:
gridOne.color = gridColorDom.value; // 设置颜色值 gridOne.genData(gl); // 重新生成buffer 复制代码
放出效果如下:
如图所见,我们的滑竿控制了第一个格子的颜色,第二个格子岿然不动。成功~
作者:庖丁解牛
链接:https://juejin.cn/post/7025531238957973541