WebGL第三十课:多个绘制对象的参数调节-颜色
本文标题:WebGL第三十课:多个绘制对象的参数调节-颜色 复制代码
友情提示
这篇文章是WebGL课程专栏的第30篇,强烈建议从前面开始看起。因为花了大量的工夫来讲解向量的概念和矩阵运算。这些基础知识会影响你的思维。
本课代码直接跳转获取:三十课代码
引子
在上篇文章中,我们可以看到,如果想更改多个绘制对象中的某一个对象的参数时,我们直接重新申请的gl中的buffer,然后重新把所有的顶点数据传入到buffer中,进而绘制。
这种办法主要是针对顶点数据不得不改的时候,比如说,你本来画的是一个格子形状的东西,但是后面需要变成球形的东西。这种时候直接替换新的顶点数据是可以的。
但是,有的时候,我们并不是想要把格子
-->球形
,仅仅是想换一下格子的颜色。就为了这个,整个替换顶点数据当然不好。
那怎么办呢?使用 shader 中的 uniform 变量,即可。
program uniform buffer 厘清关系
在这里,回顾一下,program 和 uniform 和 buffer 的关系。
当我们绘制三角形的时候,简单这么说:当你调用绘制api(例如gl.drawArrays
)之前,必须要选定一个 program,然后调用绘制api,这个时候,program 就开始跑。
怎么跑呢?
答:迭代当前buffer中的数据,例如坐标啦,颜色啦,啥的,然后在屏幕上绘制一个点,每三个点,就用插值法,绘制中间的区域,也就是绘制一个三角形,这样就将一个buffer中的数据都绘制完成。在这个过程之前,你可以设置一下uniform变量,那么在这个绘制过程中,这个变量就会保持不变。当某一个buffer的数据绘制结束之后,你可以随时更改uniform变量,然后接着绘制。
你发现了吗?也就是说不同buffer的数据,可以共用一个 program,但是在切换buffer的时候,我们可以对uniform变量进行修改,从而得到我们的目的,那就是,不同绘制对象的某些参数,可以自由独立的控制,例如可以控制颜色。
你又发现了吗?这样写,不用重新生成buffer数据。真是个好办法!
利用 uniform 来设置颜色
首先第一步要在 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和绘制的顺序如下:
第一个格子 设置 program 中的 u_color
第一个格子 进行绘制 第一个格子 自己的buffer
第二个格子 设置 program 中的 u_color
第二个格子 进行绘制 第二个格子 自己的buffer
如果有更多的格子,只要按照这个顺序,去写逻辑,那么每个格子之间是不会乱的,而自始至终,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); 复制代码
效果如下:
作者:庖丁解牛
链接:https://juejin.cn/post/7026244216531451935