WebGL 基础

CPU 与 GPU

都是计算机的处理单元,区别是 CPU 处理单个任务的功能强大,而 GPU 没有 CPU 的功能强大,但由于它是由大量的小型处理单元构成,可以同时处理大量的简单任务,所以 GPU 更适合用来处理图形问题(因为图形任务大多是重复的大批量的任务),总之,CPU 基于低延时的设计,GPU是基于大的吞吐量设计。

WebGL (web 图形库)是一个 JavaScript API ,将 JavaScriptOpenGL ES 结合在一起,达到可以使用 GPU 为 <canvas> 绘图提供硬件加速的目的(该 API 可以在 <cancas> 元素中使用)。


WebGL 渲染流程

  1. 数据准备

    提供顶点坐标、索引(三角形绘制顺序)、uv(决定贴图坐标)、法线(决定光照效果)、各种矩阵(投影矩阵等)。

    其中顶点数据存储在缓存区(因为数量较多),以修饰符 attribute 传递给顶点着色器。

    矩阵以修饰符 uniform 传递给顶点着色器。

  2. 生成顶点着色器

    编译由 JavaScript 定义的顶点着色器程序(字符串),并传递给 GPU

    获取顶点坐标(获取图形的顶点的三维坐标)

  3. 图元装配

    GPU根据顶点数量,逐个执行顶点着色器程序,生成顶点的最终坐标,完成坐标转换。

    这里的坐标转换是指将三维的坐标转换为二维的绘图形式,将这些二维的坐标连起来,可以使图形看起来像是立体的一样。

  4. 生成片元着色器

    处理模型的颜色、质地、光照效果、阴影等

  5. 光栅化

    通过片元着色器确定每个像素点,以及根据深度缓存区判断哪些像素点被挡住了,不需要渲染,最终将像素点信息存在颜色缓存区,完成整个渲染。

注意

  • 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 使用的缩放空间坐标系,每个轴的坐标范围从 -11,且不考虑纵横比、实际尺寸和其他因素。

顶点着色器 需要对顶点坐标进行转化,通过将其保存在由 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个无符号整数的向量
  • 声明一个向量类型的变量v2vec2 v2
  • 向量在着色器代码中很有用,可用方便的存储和操作颜色、位置、纹理、坐标等不仅包含一个组成部分的量,也能但能访问向量中的某个分量,基本语法是 向量名.分量名

矩阵

3D场景中的移位、旋转、缩放等变换都是由矩阵的运算来实现的,故着色语言提供了对矩阵类型的支持。

矩阵类型说明
mat22×2 的浮点数矩阵
mat33×3 的浮点数矩阵
mat44×4 的浮点数矩阵
mat3×23×2 的浮点数矩阵
mat3×33×3 的浮点数矩阵
mat3×43×4 的浮点数矩阵
mat2×22×2 的浮点数矩阵
mat2×32×3 的浮点数矩阵
mat2×42×4 的浮点数矩阵
mat4×24×2 的浮点数矩阵
mat4×34×3 的浮点数矩阵
mat4×44×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();