引子
在上篇文章中,我们可以看到,如果想更改多个绘制对象中的某一个对象的参数时,我们直接重新申请的gl中的buffer,然后重新把所有的顶点数据传入到buffer中,进而绘制。
这种办法主要是针对顶点数据不得不改的时候,比如说,你本来画的是一个格子形状的东西,但是后面需要变成球形的东西。这种时候直接替换新的顶点数据是可以的。
但是,有的时候,我们并不是想要把格子-->球形,仅仅是想换一下格子的颜色。就为了这个,整个替换顶点数据当然不好。
那怎么办呢?使用 shader 中的 uniform 变量,即可。
在这里,回顾一下,program 和 uniform 和 buffer 的关系。
当我们绘制三角形的时候,简单这么说:当你调用绘制api(例如gl.drawArrays)之前,必须要选定一个 program,然后调用绘制api,这个时候,program 就开始跑。
怎么跑呢?
答:迭代当前buffer中的数据,例如坐标啦,颜色啦,啥的,然后在屏幕上绘制一个点,每三个点,就用插值法,绘制中间的区域,也就是绘制一个三角形,这样就将一个buffer中的数据都绘制完成。在这个过程之前,你可以设置一下uniform变量,那么在这个绘制过程中,这个变量就会保持不变。当某一个buffer的数据绘制结束之后,你可以随时更改uniform变量,然后接着绘制。
你发现了吗?也就是说不同buffer的数据,可以共用一个 program,但是在切换buffer的时候,我们可以对uniform变量进行修改,从而得到我们的目的,那就是,不同绘制对象的某些参数,可以自由独立的控制,例如可以控制颜色。
你又发现了吗?这样写,不用重新生成buffer数据。真是个好办法!
首先第一步要在 fragment_shader里加一个uniform变量,按照习惯,我们命名为:u_color。
由于颜色是RGB三个数组成的,这个 u_color应该是一个vec3类型:
uniform vec3 u_color;
复制代码然后需要在 js 代码里,获取这个uniform变量引用:
u_color = gl.getUniformLocation(program, "u_color");
复制代码这块可以留意一下,获取一个变量的引用,是需要传program的,这也恰恰说明了,uniform是属于program的。
如何给这个变量传值呢?如下:
gl.uniform3f(u_color, this.color.R, this.color.G, this.color.B);
复制代码这里注意下,由于 u_color 变量是 vec3 类型,所以传值的时候,需要使用 uniform3f 这个api,后面需要传三个参数,分别代表 vec3 的第一个元素,第二个元素,第三个元素。
这里三个值分别代表 颜色 RGB。
好了,我们要想这个 u_color 生效,还需要真正的设置一下颜色:
gl_FragColor = vec4(u_color, 1.0);
复制代码看一下上面的代码,我们完全用这个uniform变量来设置颜色,而忽略buffer中的 color 部分了。所以,我们在构造buffer的时候,不用把颜色传递进去了。
这里说一点,buffer中到底应不应该带颜色,取决于你的需求,大部分时候,buffer中是应该带上颜色的。
我们可以使用uniform这种动态的变量,和buffer中的静态颜色,进行叠加计算,从而得出好玩的效果。
这点后面再说。
复制代码在上篇文章,我们定义了一个 GridObject 的class,来描述一个格子的行为。
其中 render 方法,根据本文提出的新做法,需要修改如下:
render(gl, program) {
gl.bindBuffer(gl.ARRAY_BUFFER, this.glbuffer);
if (this.modelUpdated) {
this.modelUpdated = false;
if (this.a_PointVertex == null) {
this.a_PointVertex = gl.getAttribLocation(program, 'a_PointVertex');
}
}
//////////////////////////
gl.vertexAttribPointer(this.a_PointVertex, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(this.a_PointVertex);
gl.uniform3f(u_color, this.color.R, this.color.G, this.color.B); // 设置颜色uniform
gl.drawArrays(gl.TRIANGLES, 0, this.pointCount); // 绘制当前格子的buffer
}
复制代码我们可以看到,主要就是在绘制之前,要修改一下 u_color。
如果有两个 GridObject 对象,前后分别调用 render 的话,那么设置uniform和绘制的顺序如下:
如果有更多的格子,只要按照这个顺序,去写逻辑,那么每个格子之间是不会乱的,而自始至终,u_color 就是一个变量,被所有的格子所共用。
<p>
<b>第一个格子的颜色R:</b>
<input id="gridcolorR" type="range" min="0" max="1" value="0" step="0.01" oninput="gl_draw()" />
<b id="gridcolorRvalue">0</b>
</p>
<p>
<b>第一个格子的颜色G:</b>
<input id="gridcolorG" type="range" min="0" max="1" value="0" step="0.01" oninput="gl_draw()" />
<b id="gridcolorGvalue">0</b>
</p>
<p>
<b>第一个格子的颜色B:</b>
<input id="gridcolorB" type="range" min="0" max="1" value="0" step="0.01" oninput="gl_draw()" />
<b id="gridcolorBvalue">0</b>
</p>
复制代码在 gl_draw 这个回调函数里,设置一下第一个格子的颜色, 然后绘制两个格子:
gridOne.color.R = gridColorRDom.value;
gridOne.color.G = gridColorGDom.value;
gridOne.color.B = gridColorBDom.value;
gridOne.render(gl, program);
gridTwo.render(gl, program);
复制代码效果如下:

正文结束,下面是答疑
复制代码本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。