WebGL 与动画实现 | 青训营笔记
这是我参与「第四届青训营 」笔记创作活动的的第 21 天
学习资料
初识 WebGL
Modern Graphics System
现代图像系统
- 光栅(Raster):几乎所有的现代图形系统都是基于光栅来绘制图形的,光栅就是指构成图像的像素阵列。
- 像素(Pixel):一个像素对应图像上的一个点,它通常保存图像上的某个具体位置的颜色等信息。
- 帧缓存(Frame Buffer):在绘图过程中,像素信息被存放于帧缓存中,帧缓存是一块内存地址。
- CPU(Central Processing Unit):中央处理单元,负责逻辑计算。
- GPU(Graphics Processing Unit):图形处理单元,负责图形计算。
- 轮廓提取 / meshing
- 光栅化
- 帧缓存
- 渲染
The Pipline
渲染管线
CPU vs GPU
CPU 的工作能力,与管道本身的处理速度(频率)合管道的数量(内核数)有关系,频率越高,那么运算处理单一任务的速度就越快,内核数越多,那么能同时并行处理的任务数就越多。只能串行处理。
GPU 由大量的小运算单元构成,每个运算单元只负责处理很简单的计算,每个运算单元彼此独立,因此所有计算可以并行处理。
WebGL & OpenGL
WebGL 是 OpenGL 的子集,是其在 Web 上的实现。
OpenGL, OpenGL ES, WebGL, GLSL, GLSL ES API Tables
WebGL Startup
启动 WebGL
- 创建 WebGL 上下文
- 创建 WebGL Program
- 将数据存入缓冲区
- 将缓冲区数据读取到 GPU
- GPU 执行 WebGL 程序,输出结果
Create WebGL Context
创建 WebGL 上下文
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
function create3DContext(canvas, options) {
const names = ['webgl', 'experimental-webgl', 'webkit-3d', 'moz-webgl'];
if (options.webgl2) names.unshift('webgl2');
let context = null;
for (let ii = 0; ii < names.length; ++ii) {
try {
context = canvas.getContext(names[ii], options);
} catch (e) {
// no-empty
}
if (context) {
break;
}
}
return context;
}
The Shaders
着色器
- Vertex Shader
顶点着色器
attribute vec2 position;
void main() {
gl_PointSize = 1.0;
gl_Position = vec4(position, 1.0, 1.0);
}
- Fragment Shader
片段着色器
percision mediump float;
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
Create Program
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertex);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragment);
gl.compileShader(fragmentShader);
const program = gl.createProgram();
gl.attachShader(program, vertextShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
Data to Frame Buffer
Axes
Typed Array
const points = new Float32Array([
-1, -1,
0, 1,
1, -1,
]);
const bufferId = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
gl.bufferData(gl.ARRAT_BUFFER, points, gl.STATIC_DRAW);
Frame Buffer to GPU
const vPosition = gl.getAttribLocation(program, 'position'); //获取顶点着色器中position变量的地址
gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0); //给变量设置长度和类型
gl.enableVertexAttribArray(vPosition); //激活这个变量
'position' :
attribute vec2 position;
void main() {
gl_PointSize = 1.0;
gl_Position = vec4(position, 1.0, 1.0);
}
Output
l.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, points.length / 2);
Mesh.js
2D:
const canvas = document.querySelector('canvas');
cosnt ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(250, 0);
ctx.lineTo(500, 500);
ctx.lineTo(0, 500);
ctx.fillStyle = 'red';
ctx.fill();
mesh.js:
const {Renderer, Figure2D, Mesh2D} = mesh.js;
const canvas = document.querySelector('canvas');
const renderer = new Renderer(canvas);
const figure = new Figure2D();
figure.beginPath();
figure.moveTo(250, 0);
figure.lineTo(500, 500);
figure.lineTo(0, 500);
const mesh = new Mesh2D(figure, canvas);
mesh.setFill({
color: [1, 0, 0, 1],
});
renderer.drawMeshes([mesh]);
Polygons
多边形
Draw Polygon with 2D Triangulations
用三角剖分绘制多边形
使用 Earcut 进行三角剖分
const vertices = [
[-0.7, 0.5],
[-0.4, 0.3],
[-0.25, 0.71],
[-0.1, 0.56],
[-0.1, 0.13],
[0.4, 0.21],
[0, -0.6],
[-0.3, -0.3],
[-0.6, -0.3],
[-0.45, 0.0],
];
const points = vertices.flat();
const triangles = earcut(points);
Transform
平移
旋转
缩放
旋转 + 缩放是线性变换
线性变换 + 平移
Apply Transforms:
3D Matrix
3D 标准模型的四个齐次矩阵(mat4)
- 投影矩阵 Projection Matrix
- 模型矩阵 Model Matrix
- 视图矩阵 View Matrix
- 法向量矩阵 Normal Matrix