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

112 阅读2分钟

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

初识WebGL

Why WebGL / Why GPU ?

  • WebGL是什么:GPU != WebGL != 3D,它是一种3D绘图协议,属于OpenGL的子集,这种绘图技术标准允许把JavaScript和OpenGL ES 2.0结合在一起,通过增加OpenGL ES 2.0的一个JavaScript绑定,WebGL可以为HTML5 Canvas提供硬件3D加速渲染。
  • GPU是什么:GPU有由大量的小运算单元组成,每个运算单元只负责处理很简单的计算,每个运算单元彼此独立,因此所有计算可以并行处理。

WebGL使用

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

如何利用WebGL绘制一个点

要使用WebGL进行绘图就必须使用着色器,在代码中,着色器是一字符串的形式‘嵌入’在JavaScript文件中的,在程序运行前它就已经设置好了。

顶点着色器:用来描述顶点的特性(如位置、颜色等)的程序。顶点是指二维或三维中的一个点。
片元着色器:进行逐片元处理过程的程序。片元是一个webgl中的术语,你可以理解为像素(图片的单元)。

3D矩阵

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

  • 投影矩阵
  • 模型矩阵
  • 视图矩阵
  • 法向量矩阵

WebGL绘制三角形

// 顶点着色器代码
const vertex_source = `
    attribute vec3 aPos;
    
    void main() {
        gl_Position = vec4(aPos, 1);
    }
`;

// 片段着色器代码
const fragment_source = `
    precision mediump float;
    uniform vec3 uColor;
    void main() {
        gl_FragColor = vec4(uColor, 1.0);
    }
`;

// 三角形的顶点数据
const vertexs: number[] = [
    0, 0.5, 0,
    -0.5, 0, 0,
    0.5, 0, 0
];

const draw = (gl: WebGLRenderingContext) => {
    // 1.准备工作
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); // 告诉webgl窗口大小
    gl.clearColor(0, 0, 0, 0); // 设定clearColor的颜色缓冲
    gl.clear(gl.COLOR_BUFFER_BIT); // 用clearColor的颜色,清屏

    // 2.创建着色器程序
    // 创建顶点着色器
    var vertexShader = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(vertexShader, vertex_source); // 绑定着色器代码
    gl.compileShader(vertexShader); // 编译着色器
    var success = gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS);
    if (!success) {
        throw "colud not compile vertex shader:" + gl.getShaderInfoLog(vertexShader);
    }
    // 创建片段着色器
    var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(fragmentShader, fragment_source); // 绑定着色器代码
    gl.compileShader(fragmentShader); // 编译着色器
    success = gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS);
    if (!success) {
        throw "colud not compile vertex shader:" + gl.getShaderInfoLog(fragmentShader);
    }
    // 链接着色器程序
    var program = gl.createProgram();
    gl.attachShader(program, vertexShader); // 添加着色器
    gl.attachShader(program, fragmentShader); 
    gl.linkProgram(program);
    success = gl.getProgramParameter(program, gl.LINK_STATUS);
    if (!success) {
        throw "colud not link shader: " + gl.getProgramInfoLog(program);
    }
    // 已经链接成程序,可以删除着色器
    gl.deleteShader(vertexShader);
    gl.deleteShader(fragmentShader);

    gl.useProgram(program);

    // 3. 传递数据
    // 传递attribute数据
    const aPosAttLocation = gl.getAttribLocation(program, "aPos");
    const aPosAttBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, aPosAttBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexs), gl.STATIC_DRAW);

    gl.vertexAttribPointer(aPosAttLocation, 3, gl.FLOAT, false, 0, 0); // 告诉gl如何解析数据
    gl.enableVertexAttribArray(aPosAttLocation);

    // 传递全局变量数据
    const uColorLocation = gl.getUniformLocation(program, "uColor");
    gl.uniform3f(uColorLocation, 0.36, 0.42, 0.60);

    // 4. 绘制
    gl.drawArrays(gl.TRIANGLES, 0, 3);

总结

一些简单的图形我们可以直接通过canvas2d直接绘制,但是当遇到一些复杂且像素多的图的时候,canvas2d的效率就偏低且难以实现。这时我们就可以通过WebGL来实现,同时,如果我们要实现复杂的粒子效果,那么WebGL就是不二首选。