WEBGL入门---绘制渐变三角形


绘制渐变三角形:深入理解缓冲区

上节带领大家学习了基本三角形图元的绘制过程,以及如何使用缓冲区向着色器传递多个数据,但上节只演示了往着色器传递坐标这一种数据,本节通过绘制渐变三角形,讲解一下如何通过缓冲区向着色器传递多种数据。

目标

本节通过一个鼠标每点击三次便会绘制一个渐变三角形的示例,带大家深入理解缓冲区的用法,最终效果如下图所示:

通过本节学习,你将会掌握如下内容:

  • 顶点数据在 buffer 中的排布方式。
  • 切换 buffer 时,bindBuffer 的重要性。
  • 使用多个 buffer 读取多种顶点数据。
  • 使用单个 buffer 读取多种顶点数据。
  • 如何实现渐变效果。

渐变三角形

上节我们实现的是单色三角形,通过在片元着色器中定义一个 uniform 变量,接收 JavaScript 传递过去的颜色值来实现。那渐变三角形的处理与单色三角形有何不同呢?

渐变三角形颜色不单一,在顶点与顶点之间进行颜色的渐变过渡,这就要求我们的顶点信息除了包含坐标,还要包含颜色。这样在顶点着色器之后,GPU 根据每个顶点的颜色对顶点与顶点之间的颜色进行插值,自动填补顶点之间像素的颜色,于是形成了渐变三角形。
那既然我们需要为每个顶点传递坐标信息和颜色信息,因此需要在顶点着色器中额外增加一个 attribute 变量a_Color,用来接收顶点的颜色,同时还需要在顶点着色器和片元着色器中定义一个 varying 类型的变量v_Color,用来传递顶点颜色信息。

着色器

  • 顶点着色器

  • 片元着色器
    片元着色器新增一个 varying 变量 v_Color,用来接收插值后的颜色。
  

按照正常思路,我们可以创建两个 buffer,其中一个 buffer 传递坐标,另外一个 buffer 传递颜色。创建两个 buffer,将 a_Position 和 positionBuffer 绑定,a_Color 和 colorBuffer 绑定,然后设置各自读取 buffer 的方式。

请谨记:程序中如果有多个 buffer 的时候,在切换 buffer 进行操作时,一定要通过调用 gl.bindBuffer 将要操作的 buffer 绑定到 gl.ARRAY_BUFFER 上,这样才能正> > 确地操作 buffer 。您可以将 bindBuffer 理解为一个状态机,bindBuffer 之后的对 buffer 的一些操作,都是基于最近一次绑定的 buffer 来进行的。

以下 buffer 的操作需要在绑定 buffer 之后进行:

  • gl.bufferData:传递数据。
  • gl.vertexAttribPointer:设置属性读取 buffer 的方式。

JavaScript 部分


    

假如我们顶点坐标数组中有四个顶点 8 个元素【30, 30, 30, 40, 40, 30, 20, 0】,顶点着色器中的 a_Position 属性在读取顶点坐标信息时,以 2 个元素为一组进行读取:

又假如我们顶点颜色数组中有两个顶点 8 个元素 【244, 230, 100, 1, 125, 30, 206, 1】,那么顶点着色器中的 a_Color 属性在读取顶点颜色信息时,以 4 个元素(r, g, b, a)为一组进行读取,如下图所示。


接下来我们为 canvas 添加点击事件:

  canvas.addEventListener('click', e => {
    var x = e.pageX;
    var y = e.pageY;
    positions.push(x, y);
    //随机一种颜色
    var color = randomColor();
    //将随机颜色的 rgba 值添加到顶点的颜色数组中。
    colors.push(color.r, color.g, color.b, color.a);
    //顶点的数量是 3 的整数倍时,执行绘制操作。
    if (positions.length % 6 == 0) {
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.DYNAMIC_DRAW);
        gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.DYNAMIC_DRAW);
        render(gl);
    }
})
// 顺便绘制图像
function render(gl) {
      //用设置的清空画布颜色清空画布。
      gl.clear(gl.COLOR_BUFFER_BIT);
      if (positions.length <= 0) {
        return;
      }
      //绘制图元设置为三角形。
      var primitiveType = gl.TRIANGLES;
      //因为我们要绘制三个点,所以执行三次顶点绘制操作。
      gl.drawArrays(primitiveType, 0, positions.length / 2);
    }

相关