这是我参与「第四届青训营 」笔记创作活动的的第19天
WebGL是什么?
GPU 不等于WebGL 不等于 3D
Modern Graphics System
- 光栅:几乎所有的现代图形系统都是基于光栅来绘制图像的,光栅就是指构成图像的像素阵列。
- 像素:一个像素对应图像上的一个点,它通常保存图像上的某个具体位置的颜色等信息。
- 帧缓存:在绘图过程中,像素信息被存放于缓存中,帧缓存是一块内存地址。
- CPU:中央处理单元,负责逻辑计算。
- GPU:图形处理单元,负责图形计算。
图形绘制过程
- 轮廓提取
- 光栅化
- 帧缓存
- 渲染
GPU
GPU由大量的小运算单元构成
每个运算单元只负责处理很简单的计算
每个运算单元彼此独立
因此所有计算可以并行处理
WebGl Startup
- 创建WebGL上下文
- 创建WebGl Program
- 将数据存入缓冲区
- 将缓冲区数据读取到GPU
- GPU执行WebGL程序,输出结果 WebGL context
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 < name.length; ++ii) {
try {
context = canvas.getContext(names[ii], options)
}catch(e) {
}
if(context) {
break
}
}
return context
}
复制代码
The Shaders
1.Vertex Shader
attribute vec2 position
void main() {
gl_PointSize = 1.0
gl_Position = vec4(position, 1.0, 1.0)
}
复制代码
2.Fragment Shader
precision mediump float
void main() {
gl_FragColor = vec4(1.0 , 0.0, 0.0, 1.0)
}
复制代码
3.Create Program
4.Data to Frame Buffer
Draw Polygon with 2D Triangulations
使用Earcut 进行三角剖分
3D标准模型的四个齐次矩阵
- 投影矩阵Projection Matrix
- 模型矩阵 Model Matrix
- 视图矩阵 View Matrix
- 法向量矩阵 Normal Matrix
现代的图像系统
- 光栅(Raster):几乎所有的现代图形系统都是基于光栅来绘制图形的,光栅就是指构成图像的像素阵列。
- 像素(Pixel):一个像素对应图像上的一个点,它通常保存图像上的某个具体位置的颜色等信息。
- 帧缓存(Frame Buffer):在绘图过程中,像素信息被存放于帧缓存中,帧缓存是一块内存地址。
- CPU (Central Processing Unit):中央处理单元,负责逻辑计算。
- GPU (Graphics Processing Unit):图形处理单元,负责图形计算。
现代图像的渲染如图过程
- 轮廓提取/ meshing
- 光栅化
- 帧缓存
- 渲染
GPU
- GPU由大量的小运算单元构成
- 每个运算单元只负责处理很简单的计算
- 每个运算单元彼此独立
- 因此所有计算可以并行处理
WebGL & OpenGL关系
OpenGL, OpenGL ES, WebGL, GLSL, GLSL ES API Tables (umich.edu)
WebGL绘图步骤
步骤
- 创建WebGL上下文
- 创建WebGL Program
- 将数据存入缓冲区
- 将缓冲区数据读取到GPU
- GPU执行WebGL程序,输出结果
创建WebGL上下文(Creat WebGL Context)
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;
}
复制代码
创建WebGL Program(The Shaders)
- Vertex Shader(顶点着色器)
通过类型数组position,并行处理每个顶点的位置
attribute vec2 position;// vec2 二维向量
void main() {
gl_PointSize = 1.0;
gl_Position = vec4(position, 1.0, 1.0);
}
复制代码
- Fragment Shader(片元着色器)
为顶点轮廓包围的区域内所有像素进行着色
precision mediump float;
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);//对应rgba(255,0,0,1.0),红色
}
复制代码
// 顶点着色器
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, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
复制代码
将数据存到缓冲区中(Data to Frame Buffer)
// 顶点数据
const points = new Float32Array([
-1, -1,
0, 1,
1, -1,
]);
// 创建缓冲区
const bufferId = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
复制代码
读取缓冲区数据到GPU(Frame Buffer to GPU)
- getAttribLocation() 返回了给定WebGLProgram对象中某属性的下标指向位置。
- vertexAttribPointer() 告诉显卡从当前绑定的缓冲区(bindBuffer()指定的缓冲区)中读取顶点数据。
- enableVertexAttribArray() 可以打开属性数组列表中指定索引处的通用顶点属性数组。
const vPosition = gl.getAttribLocation(program, 'position'); // 获取顶点着色器中的position变量的地址
gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0); // 给变量设置长度和类型
gl.enableVertexAttribArray(vPosition); // 激活这个变量
复制代码
输出结果(Output)
// output
gl.clear(gl.COLOR_BUFFER_BIT); //清除缓冲的数据
gl.drawArrays(gl.TRIANGLES, 0, points.length / 2);
复制代码
WebGL太复杂?其他方式
canvas 2D
const canvas = document.querySelector('canvas');
const 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} = meshjs;
const canvas = document.querySelector ('canvas');
const renderer = new Renderer(canvas);
const figure = new Figure2D();
figurie.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]);
复制代码
Earcut
使用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)
复制代码
3D Meshing
由设计师导出给我们,再提取
SpriteJS/next - The next generation of spritejs.
图形变换(Transforms)
平移
旋转
缩放
线性变换(旋转+缩放)
从线性变换到齐次矩阵
3D Matrix
3D标准模型的四个齐次矩阵(mat4)
- 投影矩阵Projection Matrix(正交投影和透视投影)
- 模型矩阵Model Matrix (对顶点进行变换Transform)
- 视图矩阵View Matrix(3D的视角,想象成一个相机,在相机的视口下)
- 法向量矩阵Normal Matrix(垂直于物体表面的法向量,通常用于计算物体光照)
Read more
- The Book of Shaders (介绍片元着色器,非常好玩的)
- Mesh.js (底层库,欸嘿)
- Glsl Doodle (片元着色器的一个轻量库,有很多小demo)
- SpriteJS (月影老师写的开源库orz)
- Three.js(很多有意思的游戏项目)
- Shadertoy BETA(很多有意思的项目)