画一个三角形(下)

大家好,我是西瓜,你现在看到的是 2021 iThome 铁人赛『如何在网页中绘制 3D 场景?从 WebGL 的基础开始说起』系列文章的第 3 篇文章。本系列文章从 WebGL 之基础开始介绍,最後建构出绘制 3D、光影效果之网页。本章节讲述的是 WebGL 基本的运作机制以及如何使用其提供的功能

在上一篇 WebGL 的绘制流程,同时也建立了 shader 并链结成 program,如果有需要可以回到上一篇复习:Day 2: 画一个三角形(上),而接下来要告诉 GPU 『画什麽』,精确来说,就是提供上一篇 vertex shader 中 a_position 所需的资料

取得 Attribute 位置

这个有点指标的感觉,gl.getAttribLocation 可以取得 attribute 在 program 中的位置,同时也把取得的值印出来看看:

const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
console.log({ positionAttributeLocation })
// => {positionAttributeLocation: 0}

就是单纯的数字,待会这个数字会用来跟 buffer 绑定

找不到的时候这个数字会是 -1,如果 GLSL 里面写了一些没有被使用到的 attribute 变数,那在 GPU 编译的过程中会消失,所以就算 GLSL 原始码有宣告 attribute,有可能因为被判定没有使用到变数导致 gl.getAttribLocation 拿到 -1

建立并使用 Buffer

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

使用 gl.createBuffer() 便可建立 buffer,然後使用 gl.bindBuffer() 『设定目前使用中的 array buffer』;在 WebGL API 中,有许多内部的『对准的目标』(binding point),而看到 bind 的字眼时,他们的功能往往是去设定这些『对准的目标』,设定完成後,接下来呼叫的其他 WebGL API 就会对着设定好的目标做事

除此之外,gl.bindBuffer() 第一个参数传入了 gl.ARRAY_BUFFER,在 mdn 上这个参数叫做 target,表示 buffer 不只有一种,而 gl.ARRAY_BUFFER 这种 buffer 才能与 vertex shader 的 attribute 连结,描述这层连结关系的功能叫做 vertex attribute array

Vertex Attribute Array

首先在 attribute a_position 位置上启用这个功能:

gl.enableVertexAttribArray(positionAttributeLocation);

启用之後,设定 attribute 拿资料的方法:

gl.vertexAttribPointer(
  positionAttributeLocation,
  2, // size
  gl.FLOAT, // type
  false, // normalize
  0, // stride
  0, // offset
);

虽然看似没有提到任何 buffer 的东西,但是经过笔者测试这行执行下去的时候会让 attribute 设定成与目前『对准的 ARRAY_BUFFER 目标』关联,同时来看一下各个参数

  • 第一个参数 index: 看到传入 positionAttributeLocation 应该可以猜到,就是要设定的 attribute 位置
  • 第二个参数 size: 笔者认为这是这个 API 最重要的参数,设定了每次 vertex shader 执行时该 attribute 要从 buffer 中拿出多少个数值,依序填入 vecX 的各个元素,这边使用 2 刚好填满 shader 中的 attribute vec2 a_position
    • 事实上,就算 attribute 是 vec4 且 size 只喂 2 进去也是可以的,剩下的空间 WebGL 会有预设值填上,预设值的部份与之後 3D 相关,之後再来讨论
  • 第三个参数 type 与第四个参数 normalized: 设定原始资料与 attribute 的转换,type 指的是原始资料的型别,此范例直接一点传入 gl.FLOAT ,而 normalize 在整数型别时可以把资料除以该型别的最大值使 attribute 变成介於 -1 ~ +1 之间的浮点数,此范例不使用此功能传 false 进去即可
    • 假设今天原始资料是 0~255 整数表示的 RGB,那麽就可以用 type: gl.UNSIGNED_BYTE 搭配 normalize: true 使用,在 shader 中 attribute 就会直接是符合 gl_FragColor 的颜色资料
  • 第五个参数 stride 与第六个参数 offset: 控制读取 buffer 时的位置,stride 表示这次与下次 vertex shader 执行时 attribute 读取的起始位置的距离,设定为 0 表示每份资料是紧密排列的,offset 则是第一份资料距离开始位置的距离,这两个参数的单位皆为 byte

笔者画了一份示意图表示这个范例呼叫 gl.vertexAttribPointer() 後 buffer 与 attribute 的运作关系

01-vertex-attrib-pointer

本范例没有使用到 strideoffset,既然都有上图了那就举个使用 strideoffset 的状况:

02-vertex-attrib-pointer

传入资料到 buffer,设定三角形的位置

在上面已经使用 gl.bindBuffer() 设定好『对准的 ARRAY_BUFFER 目标』,接下来呼叫 gl.bufferData() 对 buffer 输入资料

gl.bufferData(
  gl.ARRAY_BUFFER,
  new Float32Array([
    0, 0.2,
    0.2, -0.1,
    -0.2, -0.1,
  ]),
  gl.STATIC_DRAW,
);

第二个参数即为 buffer 的资料,也就是三角形顶点的位置,注意要传入与 gl.vertexAttribPointer() type 符合的 TypedArray,关於这些数值:

  1. 在上篇有提到:x, y, z 必须介於 -1+1 才会落在画布中
  2. 在 x 轴方向,左为 -1, 右为 +1、y 轴方向,上为 +1,下为 -1

假设三角形顶点分别依序为 A, B, C,示意图如下:

day3-03-triangle-vertice

终於,『画』这个动作

gl.useProgram(program);
gl.drawArrays(gl.TRIANGLES, 0, 3);

设定使用上篇建立好的 program,接着 gl.drawArrays() 就是『画』这个动作,其参数功能:

  • 第一个参数 mode: 透过这个参数可以请 WebGL 画 点、线,而面的部份就是三角形 gl.TRIANGLES
  • 第二个参数 first: 类似上面的 offset,精确来说是『略过多少个顶点』
  • 第三个参数 count: 有多少个顶点,我们画一个三角形,共三个顶点

三角形画出来了:

result

到最後一刻才呼叫 gl.useProgram(),整个资料设定的过程还是透过 attribute 的『位置』设定的,这表示资料在一定程度上可以与 shader 脱钩,打个比方,同一个 3D 物件可以根据情况使用不同的 shader 来渲染达成不同的效果,但是在记忆体中这个 3D 物件只需要一份就好

三角形的颜色是写死在 fragment shader 内,读者们可以试着调颜色、顶点位置玩玩看,本篇的完整程序码可以在这边找到:

花了这麽多力气,就只是一个纯色的三角形,而且定位还得先用画布的 -1 ~ 1 来算,接下来继续介绍更多 shader 接收资料、参数的方式来使绘制更加灵活


<<:  Day03-搞懂传址、传值? 电脑如何储存资料?

>>:  C++语言和你 SAY HELLO!!

Day 15 [Python ML、Pandas] 统整资料和Maps

import pandas as pd pd.set_option('max_rows', 5) i...

【Day11】特殊性营运流程篇-CRM

#odoo #开源系统 #数位赋能 #E化自主 在我们简单认识了odoo的ERP核心应用模组後,我们...

Day 26: 出门前的Cypress 杂记

今天一早要出门,所以就大概讲一下常用的一些小撇步以及昨天有讲到的commands. 大家怎麽去抓取D...

为你自己学 Laravel - Day 29 前端生态圈

嘿~~ 各位好,我是菜市场阿龙! Youtube 频道:https://www.youtube.co...

Unity与Photon的新手相遇旅途 | Day6-粒子效果应用范例

今天介绍两个粒子效果应用范例,一个是透过按键控制粒子效果生成以及删除,第二个是制作炸弹炸物体产生的效...