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

85 阅读4分钟

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

本文是针对青训营《WebGL与动画实现》这节课所学的课后总结,通过月影老师各种小案例的讲解,让我了解了许多关于WebGL绘图及相关库的知识,让我觉得这个领域非常有趣。

简介

WebGL是什么?

  • GPU ≠ WebGL ≠ 3D

现代的图像系统

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

image.png

现代图像渲染过程:

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

image.png

The Pipeline

image.png

CPU vs GPU

CPU:

  • 执行计算机和操作系统所需的命令和流程 GPU:
  • GPU由大量的小运算单元构成
  • 每个运算单元只负责处理很简单的计算
  • 每个运算单元彼此独立
  • 因此所有计算可以并行处理

WebGL & OpenGL

image.png 深入了解:OpenGL, OpenGL ES, WebGL, GLSL, GLSL ES API Tables (umich.edu)

WebGL绘图步骤

步骤:

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

image.png (Raw Vertices & Primitives 原始顶点&原语; Vertex Processor 顶点着色器;Fragment Processor 片元着色器)

创建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;
}

创建WebGL Program

  1. 创建Vertex Shader(顶点着色器)和Fragment Shader(片元着色器)
// 顶点着色器
const vertexShaderCode = `
attribute vec2 position;
void main() {
    gl_PointSize = 1.0;
    gl_Position = vec4(position, 1.0, 1.0);
}
`;
// 片元着色器
const fragmentShaderCode = `
precision mediump float;
void main() {
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;

2.创建着色对象、并导入着色目标、编译着色器

// 顶点着色器
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);

3.创建WebGLProgram实例、添加着色器、链接准备

const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);

gl.useProgram(program);

将数据存入缓冲区

首先明确坐标系:WebGL是正常的笛卡尔坐标系(浏览器和canvas2D的坐标系统是以*左上角为坐标原点,y轴向下,x轴向右)

创建顶点数据,创建存储对象并进行绑定,最后将数据绑定至bufferData中:

// 顶点数据
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

const vPosition = gl.getAttribLocation(program, 'position'); // 获取顶点着色器中的position变量的地址
gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0); // 给变量设置长度和类型
gl.enableVertexAttribArray(vPosition); // 激活这个变量

GPU执行WebGL程序,输出结果

gl.clear(gl.COLOR_BUFFER_BIT); //清除缓冲的数据 gl.drawArrays(gl.TRIANGLES, 0, points.length / 2);

image.png

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

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)

图形的变换

平移

image.png

旋转

image.png

缩放

image.png

旋转+缩放

image.png

image.png

image.png

老师在课上也给了一个demo:

attribute vec2 position;
uniform mat3 modelMatrix;
void main() {
  gl_PointSize = 1.0;
  vec3 pos = modelMatrix * vec3(position, 1.0);
  gl_Position = vec4(pos, 1.0);
}
let transform = gl.getUniformLocation(program, 'modelMatrix');
gl.uniformMatrix3fv(transform, false, 
  [0.5, 0, 0, 
   0, 0.5, 0,
   0.1, -0.1, 1]); 

3D Matrix

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

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

Read More

The Book of Shaders Mesh.js Glsl Doodle SpriteJS (月影老师写的开源库) Three.js Shadertoy BETA

总结

通过这节课的学习,让我初步了解了WebGL以及现代图像系统的构成,通过老师的小案例,让我初步了解了WebGL的实现过程,后续月影老师还给我们讲了一些图像的变换的相关知识,我受益匪浅。