这个算属于 WebGL 进阶吧, 因为这个属于优化, 运行效率, 如果你不了解 WebGL 的基本知识,你可以到这里先学习基础知识.
我会在下面放上几个比较好的学习链接.

涉及到的知识点如下

  1. 编程里面的位运算 中的(&)这个符号的运算法则
  2. WebGL vertexAttribPointer 这个接口中的 normalized 到底是怎么做的归一化
  3. 给 WebGL 传递一个值 为什么我们能拿到四个值(用于优化每次传递数据的尺寸)

如果先了解写这篇文章的意图话可以直接跳转到意图开始看. 啊哈

意图

在看一些引擎源码的时候总是在给 GUP 传递颜色的时候会看类似下面这样的代码. 下面我写一个伪代码.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// 着色器伪代码
let vs = `
attribute vec2 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_tint; 😄😄😄

varying vec2 v_texCoord;
varying vec4 v_tint;

void main() {
gl_Position = vec4(a_position, 1.0, 1.0);
v_tint = a_tint;
v_texCoord = a_texCoord;
}
`;

let fs = `
precision mediump float;
uniform sampler2D u_texture;

varying vec2 v_TexCoord;
varying vec4 v_tint;

void main() {
gl_FragColor = texture2D(u_texture, v_TexCoord) * v_tint;
}
`;


let arrayBuffer = new ArrayBuffer(5*4);
let float32Array = new Float32Array(arrayBuffer);
let uint32Array = new Uint32Array(arrayBuffer);

// 传递顶点数据
float32Buffer[0] = 1;
float32Buffer[1] = 0;
// 传递uv坐标
float32Buffer[3] = 0;
float32Buffer[4] = 0;
// 传递颜色
let a = {
tint: 0x00ff00,
alpha: 1,
};

let b = (a.tint >> 16) + (a.tint & 0xff00) + ((a.tint & 0xff) << 16) + (a.alpha * 255 << 24); 😄😄😄
uint32Array[5] = b; 😄😄😄
// ... 可能还会有其他的数据传递这个都得看他们是怎么处理了,

// 指定顶点属性 和 UV 属性我就不写了.
[...]

// 指定 颜色 属性
gl.enableVertexAttribArray(2);
// void gl.vertexAttribPointer(index, size, type, normalized, stride, offset);
gl.vertexAttribPointer(2, 4, gl.UNSIGNED_BYTE, true, 5*4, 4*4); 😄😄😄

我只会解释 在代码后面 带 😄😄😄 这个标识的代码.我相信其他的代码,聪明的你自己看懂这个看随意😄.

总会出现,在传递的颜色的时候,使用 Uint32Array 这种类型,其他的依旧是 Float32Array, 而且乍一看,还带了一堆位运算.

普通人表示看不懂. 有点强,这个三行代码.

于是乎这篇文章诞生了.

解决疑惑

首先你要做的就是,自己变成电脑,去执行代码.

那一堆位运算到底做了啥

& 这个运算符 就是

1
2
3
4
5
1 & 1 => 1

1 & 0 => 0

0 & 0 => 0

(a.tint >> 16) + (a.tint & 0xff00) + ((a.tint & 0xff) << 16) + (a.alpha * 255 << 24)

它既然是位运算,那我们就把他转成二进制看,毕竟自己依然是电脑了,这个才是你的母语.

好经过上面的推算得出最终的结果了. 就是 11111111 00000000 11111111 00000000

有Uint32Array 处理一下我的数据

因为的数据可能是负数 这个是不行的.

1
uint32Array[5] = b; 😄😄😄

一个负数就变成正数了.

至于他是怎么变的, 你可以找度娘 问她 编程里的大小端是什么东西.

指定这些数据怎么用

1
2
// void gl.vertexAttribPointer(index, size, type, normalized, stride, offset);
gl.vertexAttribPointer(2, 5, gl.UNSIGNED_BYTE, true, 5*4, 4*4); 😄😄😄

解释上面的句子就是 ==> 我是以 无符号字节(unsigned byte) 传递 并给我传递这些数据归一化, 而且我的尺寸是4 那就说明你要给我传递的这个数字给我分割成 4个数字.

好我们就执行这行代码.
在传递给 GUP 的时候他到底做了啥, 我这个做一个 伪代码 模拟一下.

1
2
3
4
5
6
7
8
9
10
11
传递给 GPU 11111111 00000000 11111111 00000000 ===> 对应的十进制是 255 0 255 0

我开始分割 因为你的尺寸是 4, 从低位开始切割

输出 ===> [00000000, 11111111, 00000000, 11111111] ===> [0, 255, 0, 255];

但是这个数据的返回 不是着色器要 的 范围, 归一化

因为你的数据类型是 无符号字节(unsigned byte) 它的范围是 0 ~ 255, Nice.

[0, 255, 0, 255] / 255 ===> [0,1,0,1]

传递给着色器

1
attribute vec4 a_tint; 😄😄😄

这个里 上一步输出的 数据类型简直就是

学习链接

这个里我给 能有幸看到这片文章的人 推送几个比较有价值的 学习网站吧. 这些都写的不错.

WebGL(MDN)中文版

如果你感觉上面那个([WebGL(MDN)中文版)比较的简单,你可以浏览这个教程网站写的也很不错.

WebGL入门教程
WebGL2教程

但是要注意这个都是入门教程,这个东西不足以支撑你写高效率的框架.

实践出真知,只有多磨多练才是硬道理.

有问题 QQ 讨论.

ヾ( ̄▽ ̄)Bye~Bye~