大家好,我是西瓜,你现在看到的是 2021 iThome 铁人赛『如何在网页中绘制 3D 场景?从 WebGL 的基础开始说起』系列文章的第 3 篇文章。本系列文章从 WebGL 之基础开始介绍,最後建构出绘制 3D、光影效果之网页。本章节讲述的是 WebGL 基本的运作机制以及如何使用其提供的功能
在上一篇 WebGL 的绘制流程,同时也建立了 shader 并链结成 program,如果有需要可以回到上一篇复习:Day 2: 画一个三角形(上),而接下来要告诉 GPU 『画什麽』,精确来说,就是提供上一篇 vertex shader 中 a_position
所需的资料
这个有点指标的感觉,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
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
首先在 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
vec4
且 size 只喂 2 进去也是可以的,剩下的空间 WebGL 会有预设值填上,预设值的部份与之後 3D 相关,之後再来讨论type
与第四个参数 normalized
: 设定原始资料与 attribute 的转换,type
指的是原始资料的型别,此范例直接一点传入 gl.FLOAT
,而 normalize
在整数型别时可以把资料除以该型别的最大值使 attribute 变成介於 -1 ~ +1 之间的浮点数,此范例不使用此功能传 false
进去即可
type: gl.UNSIGNED_BYTE
搭配 normalize: true
使用,在 shader 中 attribute 就会直接是符合 gl_FragColor
的颜色资料stride
与第六个参数 offset
: 控制读取 buffer 时的位置,stride
表示这次与下次 vertex shader 执行时 attribute 读取的起始位置的距离,设定为 0
表示每份资料是紧密排列的,offset
则是第一份资料距离开始位置的距离,这两个参数的单位皆为 byte笔者画了一份示意图表示这个范例呼叫 gl.vertexAttribPointer()
後 buffer 与 attribute 的运作关系
本范例没有使用到 stride
与 offset
,既然都有上图了那就举个使用 stride
与 offset
的状况:
在上面已经使用 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
至 +1
才会落在画布中-1
, 右为 +1
、y 轴方向,上为 +1
,下为 -1
假设三角形顶点分别依序为 A, B, C,示意图如下:
gl.useProgram(program);
gl.drawArrays(gl.TRIANGLES, 0, 3);
设定使用上篇建立好的 program,接着 gl.drawArrays()
就是『画』这个动作,其参数功能:
mode
: 透过这个参数可以请 WebGL 画 点、线,而面的部份就是三角形 gl.TRIANGLES
了first
: 类似上面的 offset,精确来说是『略过多少个顶点』count
: 有多少个顶点,我们画一个三角形,共三个顶点三角形画出来了:
到最後一刻才呼叫
gl.useProgram()
,整个资料设定的过程还是透过 attribute 的『位置』设定的,这表示资料在一定程度上可以与 shader 脱钩,打个比方,同一个 3D 物件可以根据情况使用不同的 shader 来渲染达成不同的效果,但是在记忆体中这个 3D 物件只需要一份就好
三角形的颜色是写死在 fragment shader 内,读者们可以试着调颜色、顶点位置玩玩看,本篇的完整程序码可以在这边找到:
花了这麽多力气,就只是一个纯色的三角形,而且定位还得先用画布的 -1 ~ 1
来算,接下来继续介绍更多 shader 接收资料、参数的方式来使绘制更加灵活
import pandas as pd pd.set_option('max_rows', 5) i...
#odoo #开源系统 #数位赋能 #E化自主 在我们简单认识了odoo的ERP核心应用模组後,我们...
今天一早要出门,所以就大概讲一下常用的一些小撇步以及昨天有讲到的commands. 大家怎麽去抓取D...
嘿~~ 各位好,我是菜市场阿龙! Youtube 频道:https://www.youtube.co...
今天介绍两个粒子效果应用范例,一个是透过按键控制粒子效果生成以及删除,第二个是制作炸弹炸物体产生的效...