WebGL第二十三课:开始画三角形| 8月更文挑战

396 阅读4分钟

这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战

本文标题:WebGL第二十三课:开始画三角形| 8月更文挑战

本文的最后代码,用一篇单独的文章给出,需要的小伙伴直接跳:二十三课代码

引子

前面的课程用了很多的篇幅来讲一些基础的知识。对于这些基础的知识,我们都是通过WebGL画点的方式来做演示的,因为画点最好理解。

但是画点至少有下面两个不好的地方:

  • 点数太多,画一个正方形,可能用几百个点
  • 点数少了,不好看

那怎么办呢?画三角

准备坐标点

我们知道,WebGL至少需要三个点,才能画一个三角形,那么我们就先准备这三个点。

随便给出三个点:

A: (0.5,0)(-0.5,0)
B: (0,0.5)(0, 0.5)
C: (0.5,0)(0.5, 0)

如图:

image.png

用代码给出的话,简单如下:

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需要这种格式的数据,这里以前也提过,就不再赘述。

好,既然已经准备好了,那么接下来只需要做两步:

    1. 传入WebGL
    1. 三角形模式画出来

传入 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); 先留个心眼,后面要讲。

刷新一下页面,如下图所示:

image.png

结果我们看不见任何东西。

gl.CULL_FACE 是干嘛的

我们把

gl.enable(gl.CULL_FACE);

这一句删掉,之后,刷新页面,发现,哎!三角形出来了。

那为什么加这一句扫兴的代码呢?

先不管他吧。后面讲3D的时候,自然就会搞明白。

加上这一句之后,画不出来,是因为下面的图:

271878617EB521CBFA520237765EEF41.png

就是因为我们传入的点的顺序,是顺时针的,所以就画不出来。

知道这回事,就简单了,我们将A和C对调:

var A = [0.5, 0];
var B = [0, 0.5];
var C = [-0.5, 0];

其余代码不变,刷新页面看一看:

image.png

哦~太好了,终于画出三角形了。

比起前面的画正方形啥的,我们这里只准备了三个点,就能涂满整个三角区域。舒服多了。

最好玩的就是,我们前面搞的 拉伸 旋转 位移 的代码,一点也不用改,完全适配现在的代码。

23-4.gif

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。

刷新一下页面:

image.png

看出来了吧,最上面的一个点,颜色是白的。

自上而下,有渐变的效果了。这就是插值的作用。




  正文结束,下面是答疑

小瓜瓜说:这次课还挺简单的,只要记住插值就行了

  • 答:小瓜瓜抓住了这次课的重点,我们祝贺他!