WebGL 基础
CPU 与 GPU
都是计算机的处理单元,区别是 CPU 处理单个任务的功能强大,而 GPU 没有 CPU 的功能强大,但由于它是由大量的小型处理单元构成,可以同时处理大量的简单任务,所以 GPU 更适合用来处理图形问题(因为图形任务大多是重复的大批量的任务),总之,CPU 基于低延时的设计,GPU是基于大的吞吐量设计。
WebGL
(web 图形库)是一个 JavaScript API
,将 JavaScript
与 OpenGL ES
结合在一起,达到可以使用 GPU 为 <canvas>
绘图提供硬件加速的目的(该 API
可以在 <cancas>
元素中使用)。
WebGL 渲染流程
-
数据准备
提供顶点坐标、索引(三角形绘制顺序)、uv(决定贴图坐标)、法线(决定光照效果)、各种矩阵(投影矩阵等)。
其中顶点数据存储在缓存区(因为数量较多),以修饰符
attribute
传递给顶点着色器。矩阵以修饰符
uniform
传递给顶点着色器。 -
生成顶点着色器
编译由
JavaScript
定义的顶点着色器程序(字符串),并传递给 GPU获取顶点坐标(获取图形的顶点的三维坐标)
-
图元装配
GPU根据顶点数量,逐个执行顶点着色器程序,生成顶点的最终坐标,完成坐标转换。
这里的坐标转换是指将三维的坐标转换为二维的绘图形式,将这些二维的坐标连起来,可以使图形看起来像是立体的一样。
-
生成片元着色器
处理模型的颜色、质地、光照效果、阴影等
-
光栅化
通过片元着色器确定每个像素点,以及根据深度缓存区判断哪些像素点被挡住了,不需要渲染,最终将像素点信息存在颜色缓存区,完成整个渲染。
注意
- WebGL 只能绘制 三角形、线、点。
- 图形的顶点的三维坐标,一般使用三维软件做这个事情
WebGL 上下文
在 <canvas>
中使用 WebGL
,并创建 WebGL
上下文。
<cancas id='ex'></canvas>
function init() {
const canvas = document.getElementById('ex')
const gl = canvas.getContext('webgl') // 检测浏览器是否支持 WebGL,不支持返回 null, 支持返回 WebGL 上下文
gl.clearColor(0.0, 0.0, 0.0, 1.0) // 用黑色清除上下文内的所有元素
gl.clear(gl.COLOR_BUFFER_BIT) // 用上面指定的颜色清除缓冲区
}
着色器
着色器是使用 OpenGL ES 着色语言(GLSL)
编写的程序,它负责记录像素点的位置和颜色。
绘制 WebGL
有两种不同的着色器,顶点着色器
和 片元着色器
。通过用 GLSL
编写着色器,并将代码文本传递给 WebGL
,使其在 GPU 执行时编译,顶点着色漆
和 片元着色器
的集合称之为 着色器程序
。
顶点着色器
每渲染一个形状时,顶点着色器
会在形状中的每个顶点运行,它的作用是将输入顶点从原始坐标系转换到 WebGL
使用的缩放空间坐标系,每个轴的坐标范围从 -1
到 1
,且不考虑纵横比、实际尺寸和其他因素。
顶点着色器
需要对顶点坐标进行转化,通过将其保存在由 GLSL
提供的特殊变量中来返回转换后的顶点。
注:使用 顶点着色器
可以将一个图形按照一定的比例进行缩放。
示例:略
片元着色器
片元着色器
会在 顶点着色器
处理完图形后,对图形的每个像素点调用一次,它的作用是确定像素的颜色,通过指定应用到像素的纹理元素,获取纹理元素的颜色,然后将适当的光照应用于颜色。之后颜色存储在特殊变量gl_FragColor
中,返回到 WebGL
层。该颜色将最终绘制到屏幕上图形对应像素的对应位置。
注:使用 片元着色器
为图形设置颜色和纹理等外观。
GLSL着色器语言
GLSL 是一种高级图像编程语言,基于 C/C++ 语法即流程控制。
空类型
void
,用来声明不返回任何值的函数(在顶点着色器以及片元着色器中存在的 main 函数就是一个返回值为空类型的函数)。
void main(){}
向量
向量类型 | 说明 |
---|---|
vec2 | 包含2个浮点数的向量 |
vec3 | 包含3个浮点数的向量 |
vec4 | 包含4个浮点数的向量 |
ivec2 | 包含2个整数的向量 |
ivec3 | 包含3个整数的向量 |
ivec4 | 包含4个整数的向量 |
bvec2 | 包含2个布尔类型的向量 |
bvec3 | 包含3个布尔类型的向量 |
bvec4 | 包含4个布尔类型的向量 |
uvec2 | 包含2个无符号整数的向量 |
uvec3 | 包含3个无符号整数的向量 |
uvec4 | 包含4个无符号整数的向量 |
- 声明一个向量类型的变量
v2
:vec2 v2
。 - 向量在着色器代码中很有用,可用方便的存储和操作颜色、位置、纹理、坐标等不仅包含一个组成部分的量,也能但能访问向量中的某个分量,基本语法是
向量名.分量名
。
矩阵
3D场景中的移位、旋转、缩放等变换都是由矩阵的运算来实现的,故着色语言提供了对矩阵类型的支持。
矩阵类型 | 说明 |
---|---|
mat2 | 2×2 的浮点数矩阵 |
mat3 | 3×3 的浮点数矩阵 |
mat4 | 4×4 的浮点数矩阵 |
mat3×2 | 3×2 的浮点数矩阵 |
mat3×3 | 3×3 的浮点数矩阵 |
mat3×4 | 3×4 的浮点数矩阵 |
mat2×2 | 2×2 的浮点数矩阵 |
mat2×3 | 2×3 的浮点数矩阵 |
mat2×4 | 2×4 的浮点数矩阵 |
mat4×2 | 4×2 的浮点数矩阵 |
mat4×3 | 4×3 的浮点数矩阵 |
mat4×4 | 4×4 的浮点数矩阵 |
- 是否可以使用
matX×Y
来自定义维度的矩阵?
采样器
用来处理纹理采样相关操作的数据类型,一般一个采样器变量代表一副纹理贴图。
采样器类型 | 说明 |
---|---|
sampler2D | 用于访问浮点型的二维纹理 |
sampler3D | 用于访问浮点型的三维纹理 |
samplerCube | 用于访问浮点型的立方贴图纹理 |
samplerCubeShadow | 用于访问浮点型的立方阴影纹理 |
sampler2DShadow | 用于访问浮点型的二维阴影纹理 |
sampler2DArray | 用于访问浮点型的二维纹理数组 |
sampler2DArrayShadow | 用于访问浮点型的二维阴影纹理数组 |
issampler2D | 用于访问整型的二维纹理 |
issampler3D | 用于访问整型的三维纹理 |
issamplerCube | 用于访问整型的立方贴图纹理 |
issampler2DArray | 用于访问整型的二维纹理数组 |
usampler2D | 用于访问无符号整型的二维纹理 |
usampler3D | 用于访问无符号整型的三维纹理 |
usamplerCube | 用于访问无符号整型的立方贴图纹理 |
usampler2DArray | 用于访问无符号整型的二维纹理数组 |
three.js示例
const scene = new THREE.Scene(); // 场景
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); // 相机
const renderer = new THREE.WebGLRenderer(); // 渲染器
renderer.setSize( window.innerWidth, window.innerHeight ); // 渲染器像素?
document.body.appendChild( renderer.domElement ); // 将渲染器加入 DOM, 是一个 canvas 元素
const geometry = new THREE.BoxGeometry(); // 立方体
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } ); // 材质
const cube = new THREE.Mesh( geometry, material ); // 网格, 包含立方体和作用在立方体上的材质
scene.add( cube ); // 将网格加入场景
camera.position.z = 5; // 摄像机与网格分开一些距离, 因为默认它们都在 (0, 0, 0) 坐标点
function animate() {
requestAnimationFrame( animate ); // 循环这个动画
cube.rotation.x += 0.01; // 设置旋转
cube.rotation.y += 0.01; // 设置旋转
renderer.render( scene, camera ); // 渲染场景 和 相机
};
animate();