这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战
本文标题:WebGL第二十四课:画多边形| 8月更文挑战
本文的最后代码,用一篇单独的文章给出,需要的小伙伴直接跳:二十四课代码
引子
上一次课,我们用三个点,画出了一个三角形。并且中间填充了黑色(其实是WebGL自行插值的)。
那么这一次课,我们扩展一下,画一个N边形,并且这个N可以通过页面上的一个滑竿来控制,即时生效。
最终的效果如下:
我们通过上面的动图可以看出,当N边形的边数越来越多,我们画出的图形就越趋向于一个圆。
这正是WebGL如何模拟一些复杂的曲线、曲面的,就是不断的逼近。(要考虑效果和性能,不能把数据点搞太多)
准备坐标点
我们用四边形,也就是正方形,来作为例子。
我们先观察一下正方形:
正方形有四个顶点,我们通过划分区域,将这个正方形划分成四个小三角形。
而每一个三角形有三个顶点,也就是说,我们需要事先准备 12 个坐标点。
我们先人工的把这十二个点描述出来,如下:
第一个三角形的三个点:OAB
第二个三角形的三个点:OBC
第三个三角形的三个点:OCD
第四个三角形的三个点:ODA
注意,我上面严格遵守了,一定要逆时针这个原则。
后面写代码的时候,也是按照这个顺序来写。
编写生成N边形坐标点的函数
我们先写出函数头:
function GetPolyN(center, R, N) {
}
center :中心
R : 中心到顶点的距离
N : 多边形的边数
我们的思维模式就是:
-
- 将一个圆周 N 等分,找出这N等分的坐标点。
-
- 再将这N等分的坐标点,两两一组,与中心点组合,正好就是三个点一组。
-
- 将上面的所有坐标点,平坦化
根据上面的思路,得出下面的代码:
// GetTri 函数在上次课已经讲解过了
function GetTri(A, B, C) {
return [A[0], A[1], B[0], B[1], C[0], C[1]];
}
// 获得N边形
function GetPolyN(center, R, N) {
// 1. 先在圆周上,均匀获取 N 个点
let idx = 0;
let x = 0;
let y = 0;
let rad = 0;
let pointArr = [];
for (; idx != N; idx++) {
rad = ((2 * Math.PI) / N) * idx;
x = R * Math.cos(rad) + center[0]; // 考虑 中心到顶点的长度 中心
y = R * Math.sin(rad) + center[1]; // 考虑 中心到顶点的长度 中心
pointArr.push([x, y]);
}
// 2. 将这N个点,每两个一组,与中心,正好是三个点,组成N个三角形
let res = []; // 平坦化数组
for (idx = 0; idx != N; idx++) {
res.push(...GetTri(pointArr[idx], pointArr[(idx + 1) % N], center)); // 注意这里是逆时针排布的
}
return res;
}
页面上加一个滑竿来控制N
这个属于html的知识,我们只写列出代码如下:
<p>
<b>N边形:</b>
<input id="N" type="range" min="3" max="100" value="3" step="1" oninput="updatefunc()" />
<b id="Nvalue">0</b>
</p>
值得注意的是,我们设置了 min 是3,也就是最小画的就是三角形。最大是100,我们最后会发现100,足以模拟一个圆了。
buffer 的删除与新建
这一次课有一点不一样的过程, 那就是当N变化的时候,我们需要重新生成数据点,然后重新在WebGL里生成一个buffer,为了不造成内存泄露,我们还需要将前面的buffer删掉。
也就是说,我们需要将生成buffer,传入数据的代码,挪到updatefunc函数里。
最终的updatefunc如下:
function updatefunc() {
// 这里判断一下,如果以前有buffer,就删掉
if (buffer_id) {
gl.deleteBuffer(buffer_id);
}
// 生成N边形
data = GetPolyN([0, 0], 0.5, NDom.value);
dataArr = new Float32Array(data);
pointCount = data.length / 2;
// 重新创建WebGL的buffer,并且将多边形的点传入
buffer_id = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer_id);
gl.bufferData(gl.ARRAY_BUFFER, dataArr, gl.STATIC_DRAW);
// 指定 data 的格式
gl.vertexAttribPointer(a_PointVertex, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(a_PointVertex);
/*
...
...
没有变化的代码
...
...
*/
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, pointCount);
}
整点花活
上面确实可以画出多边形了,但是我们还是不确认,是不是由一个一个小三角形组成的。
那不妨这样。我们把小三角形的颜色区分开,一个一个的,用不同的颜色。那就一目了然了。
思路:
既然要不同的点,用不同的颜色。那么就必须要把颜色也传入到WebGL的buffer里去了。
这里有两种途径:
-
- 利用老的buffer,如下
[数据点1坐标][数据点1颜色][数据点2坐标][数据点2颜色][数据点3坐标][数据点3颜色]………………
-
- 新开一个buffer
buffer_point(老的buffer):
[数据点1坐标][数据点2坐标][数据点3坐标]………………
buffer_color:
[数据点1颜色][数据点2颜色][数据点3颜色]………………
上面两种办法都行啊,这里我选择第二种办法,也就是不动原有的buffer,而新建一个buffer_color。
我们知道一个数据点,就要对应一个颜色,而一个颜色由RGB三个字段组成。
所以我们在shader里接收的时候,要使用vec3。
这里对比坐标,坐标是xy,只需要vec2。
vertex_shader 的改动
在 vertex_shader 中要加一个 attribute 变量 a_PointColor:
attribute vec3 a_PointColor;
为了将这个 a_PointColor 传给 fragment_shader ,我们必须再加一个 varying 变量:
attribute vec3 a_PointColor;
varying vec3 color;
那么最终的vertex_shader
<script id="vertex_shader" type="myshader">
// Vertex Shader
precision mediump int;
precision mediump float;
uniform mat3 u_all;
attribute vec2 a_PointVertex;
attribute vec3 a_PointColor;
varying vec3 color;
void main() {
vec3 coord = u_all * vec3(a_PointVertex, 1.0);
gl_Position = vec4(coord.x, coord.y, 0.0, 1.0);
color = a_PointColor;
}
</script>
fragment_shader 的改动
这个就很简单了,加一个 varying 同名变量 , 然后用这个变量当做颜色就行了:
<script id="fragment_shader" type="myshader">
// Fragment shader
precision mediump int;
precision mediump float;
varying vec3 color;
void main() {
gl_FragColor = vec4(color, 1.0);
}
</script>
针对坐标点数组,生成一个颜色数组
我们只需要记住,每个坐标点,对应一个颜色,而一个颜色有三个分量RGB就行了:
function GetRandomColor(pointCount) {
let res = [];
let idx = 0;
for (; idx != pointCount; idx++) {
res.push(Math.random());//R
res.push(Math.random());//G
res.push(Math.random());//B
}
return res;
}
需要注意的是,我们上面的颜色是随机生成的,这样看起来更 花活 一点。
最终效果
这里不给出最终的代码,从文章开头的链接,可以直接拿到最终代码。
其实只要将上面所说的,稍微攒一攒,就行了。
最后的花活效果如下:
正文结束,下面是答疑
小丫丫说:我终于看见五颜六色的东西了!
- 答:花里胡哨的东西!