这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战
本文标题:WebGL第二十三课:开始画三角形| 8月更文挑战
本文的最后代码,用一篇单独的文章给出,需要的小伙伴直接跳:二十三课代码
引子
前面的课程用了很多的篇幅来讲一些基础的知识。对于这些基础的知识,我们都是通过WebGL画点
的方式来做演示的,因为画点
最好理解。
但是画点
至少有下面两个不好的地方:
- 点数太多,画一个正方形,可能用几百个点
- 点数少了,不好看
那怎么办呢?画三角
。
准备坐标点
我们知道,WebGL
至少需要三个点,才能画一个三角形,那么我们就先准备这三个点。
随便给出三个点:
A:
B:
C:
如图:
用代码给出的话,简单如下:
var A = [-0.5, 0];
var B = [0, 0.5];
var C = [0.5, 0];
function GetTri(A, B, C) {
return [A[0], A[1], B[0], B[1], C[0], C[1]];
}
var data = GetTri(A, B, C);
var dataArr = new Float32Array(data);
var pointCount = 3; // 因为是三个点
上面代码的 GetTri
函数,其实就是把ABC三个点,揉到一块,变成一个平坦的数组。之所以这么做,是因为WebGL
需要这种格式的数据,这里以前也提过,就不再赘述。
好,既然点
已经准备好了,那么接下来只需要做两步:
-
- 传入
WebGL
- 传入
-
- 用
三角形模式
画出来
- 用
将点
传入 WebGL
其实怎么将数据传入,也在前面的课程讲过了,比如说: 第四课。
这里其实完全没有变化,直接去第四课就行。这里把核心代码放在这里:
var buffer_id;
buffer_id = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer_id);
gl.bufferData(gl.ARRAY_BUFFER, dataArr, gl.STATIC_DRAW);
用三角形模式
将上面三个点画出来
在前面画点的时候,核心代码其实就一句:
gl.drawArrays(gl.POINTS, 0, pointCount);
这个函数的第一个参数,可以 换成
gl.TRIANGLES
, 就是可以画三角形了。
那么就把这一句换掉:
gl.drawArrays(gl.TRIANGLES, 0, pointCount);
所以最后绘制的代码:
gl.enable(gl.CULL_FACE);
gl.enable(gl.DEPTH_TEST);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, pointCount);
注意上面代码的第一句:
gl.enable(gl.CULL_FACE);
先留个心眼,后面要讲。
刷新一下页面,如下图所示:
结果我们看不见任何东西。
gl.CULL_FACE 是干嘛的
我们把
gl.enable(gl.CULL_FACE);
这一句删掉,之后,刷新页面,发现,哎!三角形出来了。
那为什么加这一句扫兴的代码呢?
先不管他吧。后面讲3D的时候,自然就会搞明白。
加上这一句之后,画不出来,是因为下面的图:
就是因为我们传入的点的顺序,是顺时针的,所以就画不出来。
知道这回事,就简单了,我们将A和C对调:
var A = [0.5, 0];
var B = [0, 0.5];
var C = [-0.5, 0];
其余代码不变,刷新页面看一看:
哦~太好了,终于画出三角形了。
比起前面的画正方形啥的,我们这里只准备了三个点,就能涂满整个三角区域。舒服多了。
最好玩的就是,我们前面搞的 拉伸 旋转 位移
的代码,一点也不用改,完全适配现在的代码。
WebGL 是如何画三角形的
我们准备了三个点,然后传入WebGL
,那么自然就能定三个点
,这三个点
,由于我们的 fragment_shader
给出的颜色是黑色
,那么自然,这三个点的颜色就是黑色。
到这里都没问题,那么WebGL
为什么可以把中间区域全部填充,并且也是黑色呢。
这就是 gl.TRIANGLES
这个模式的另一个作用:
插值
。
插值的意思就是,三个点我画成黑色,那么三个点之间的任何一个点,我都用这三个点的颜色进行融合,其实就是算一个平均值。
刚好,这三个点,都是黑色,那么平均值自然也是黑色,所以自然而然,中间的部分全部填充成了黑色。
如果不信,我把上面的点,也就是(0, 0.5)的颜色搞成白色试试,我们改写一下fragment_shader
:
<script id="fragment_shader" type="myshader">
// Fragment shader
precision mediump int;
precision mediump float;
varying float color;
void main() {
gl_FragColor = vec4(color, color, color, 1.0);
}
</script>
注意,加了一个 float
类型的变量 color
。
自然这个变量,是 vertex_shader
传给 fragment_shader
的。所以vertex_shader
也要改写:
<script id="vertex_shader" type="myshader">
// Vertex Shader
precision mediump int;
precision mediump float;
uniform mat3 u_all;
varying float color;
attribute vec2 a_PointVertex;
void main() {
vec3 coord = u_all * vec3(a_PointVertex, 1.0);
if (a_PointVertex.y > 0.0)
{
color = 1.0;
}
else
{
color = 0.0;
}
gl_Position = vec4(coord.x, coord.y, 0.0, 1.0);
}
</script>
注意代码,我们将y坐标大于0的,color 置 1, 否则 color 置 0。
很明显,我们只有中间一个点的y坐标大于0,所以只有中间一个点的 color 是1。
刷新一下页面:
看出来了吧,最上面的一个点,颜色是白的。
自上而下,有渐变的效果了。这就是插值
的作用。
正文结束,下面是答疑
小瓜瓜说:这次课还挺简单的,只要记住插值
就行了
- 答:小瓜瓜抓住了这次课的重点,我们祝贺他!