WebGL与动画实现|青训营笔记

106 阅读5分钟

这是我参与「第四届青训营 」笔记创作活动的的第19天

WebGL是什么?

GPU 不等于WebGL 不等于 3D

Modern Graphics System

  • 光栅:几乎所有的现代图形系统都是基于光栅来绘制图像的,光栅就是指构成图像的像素阵列。
  • 像素:一个像素对应图像上的一个点,它通常保存图像上的某个具体位置的颜色等信息。
  • 帧缓存:在绘图过程中,像素信息被存放于缓存中,帧缓存是一块内存地址。
  • CPU:中央处理单元,负责逻辑计算。
  • GPU:图形处理单元,负责图形计算。

image.png

图形绘制过程

  1. 轮廓提取
  2. 光栅化
  3. 帧缓存
  4. 渲染

image.png

GPU

GPU由大量的小运算单元构成

每个运算单元只负责处理很简单的计算

每个运算单元彼此独立

因此所有计算可以并行处理

WebGl Startup

  1. 创建WebGL上下文
  2. 创建WebGl Program
  3. 将数据存入缓冲区
  4. 将缓冲区数据读取到GPU
  5. 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

image.png

4.Data to Frame Buffer

image.png

Draw Polygon with 2D Triangulations

使用Earcut 进行三角剖分

image.png

3D标准模型的四个齐次矩阵

  1. 投影矩阵Projection Matrix
  2. 模型矩阵 Model Matrix
  3. 视图矩阵 View Matrix
  4. 法向量矩阵 Normal Matrix

现代的图像系统

  • 光栅(Raster):几乎所有的现代图形系统都是基于光栅来绘制图形的,光栅就是指构成图像的像素阵列。
  • 像素(Pixel):一个像素对应图像上的一个点,它通常保存图像上的某个具体位置的颜色等信息。
  • 帧缓存(Frame Buffer):在绘图过程中,像素信息被存放于帧缓存中,帧缓存是一块内存地址。
  • CPU (Central Processing Unit):中央处理单元,负责逻辑计算。
  • GPU (Graphics Processing Unit):图形处理单元,负责图形计算。

现代图像的渲染如图过程

image.jpeg

  1. 轮廓提取/ meshing
  2. 光栅化
  3. 帧缓存
  4. 渲染

GPU

  • GPU由大量的小运算单元构成
  • 每个运算单元只负责处理很简单的计算
  • 每个运算单元彼此独立
  • 因此所有计算可以并行处理

WebGL & OpenGL关系

OpenGL, OpenGL ES, WebGL, GLSL, GLSL ES API Tables (umich.edu)

image (1).jpeg

WebGL绘图步骤

步骤

  1. 创建WebGL上下文
  2. 创建WebGL Program
  3. 将数据存入缓冲区
  4. 将缓冲区数据读取到GPU
  5. GPU执行WebGL程序,输出结果

image (2).jpeg

创建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)

  1. Vertex Shader(顶点着色器)

通过类型数组position,并行处理每个顶点的位置

attribute vec2 position;// vec2 二维向量
void main() {
    gl_PointSize = 1.0;
    gl_Position = vec4(position, 1.0, 1.0);
}
复制代码
  1. 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)

平移

image (3).jpeg

旋转

image (4).jpeg

image (5).jpeg

缩放

image (6).jpeg

image (7).jpeg

线性变换(旋转+缩放)

image (8).jpeg

image (9).jpeg

从线性变换到齐次矩阵

image (10).jpeg

3D Matrix

3D标准模型的四个齐次矩阵(mat4)

  1. 投影矩阵Projection Matrix(正交投影和透视投影)
  2. 模型矩阵Model Matrix (对顶点进行变换Transform)
  3. 视图矩阵View Matrix(3D的视角,想象成一个相机,在相机的视口下)
  4. 法向量矩阵Normal Matrix(垂直于物体表面的法向量,通常用于计算物体光照)

Read more

  1. The Book of Shaders (介绍片元着色器,非常好玩的)
  2. Mesh.js (底层库,欸嘿)
  3. Glsl Doodle (片元着色器的一个轻量库,有很多小demo)
  4. SpriteJS (月影老师写的开源库orz)
  5. Three.js(很多有意思的游戏项目)
  6. Shadertoy BETA(很多有意思的项目)