【第二届青训营-寒假前端场】- 「WebGL基础」笔记

将自己在掘金上发的笔记搬了过来:WebGL基础个人博客

Why WebGL / Why GPU?

  • WebGL是什么?
    • GPU ≠ WebGL ≠ 3D
  • WebGL为什么不像其他前端技术那么简单?

现代的图像系统

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

【第二届青训营-寒假前端场】- 「WebGL基础」笔记

  • 如上图,现代图像的渲染如图过程
  1. 轮廓提取/ meshing
  2. 光栅化
  3. 帧缓存
  4. 渲染

The Pipeline

【第二届青训营-寒假前端场】- 「WebGL基础」笔记

GPU

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

WebGL & OpenGL关系

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

【第二届青训营-寒假前端场】- 「WebGL基础」笔记

WebGL绘图步骤

步骤

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

【第二届青训营-寒假前端场】- 「WebGL基础」笔记

如图,针对几个单词进行解释:

  • 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(The Shaders)

  1. Vertex Shader(顶点着色器)

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

    attribute vec2 position;// vec2 二维向量
    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);//对应rgba(255,0,0,1.0),红色
    }
    

其具体步骤如下:

  1. 创建顶点着色器和片元着色器代码:

    // 顶点着色器程序代码
    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. 使用 createShader() 创建着色器对象

  3. 使用 shaderSource() 设置着色器的程序代码

  4. 使用 compileShader() 编译一个着色器

    // 顶点着色器
    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);
    
  5. 使用**createProgram()** 创建 WebGLProgram 对象

  6. 使用 attachShader()WebGLProgram 添加一个片段或者顶点着色器。

  7. 使用 **linkProgram() **链接给定的WebGLProgram,从而完成为程序的片元和顶点着色器准备GPU代码的过程。

  8. 使用 useProgram() 将定义好的WebGLProgram 对象添加到当前的渲染状态

    // 创建着色器程序并链接
    const program = gl.createProgram();
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);
    
    gl.useProgram(program);
    

将数据存到缓冲区中(Data to Frame Buffer)

  • 坐标轴:webGL的坐标系统是归一化的,浏览器和canvas2D的坐标系统是以左上角为坐标原点,y轴向下,x轴向右,坐标值相对于原点。而webGL的坐标系是以绘制画布的中心点为原点正常的笛卡尔坐标系

通过一个顶点数组表示其顶点,使用 createBuffer() 创建并初始化一个用于储存顶点数据或着色数据的WebGLBuffer对象并返回bufferId,然后使用 bindBuffer() 将给定的 bufferId 绑定到目标并返回,最后使用**bufferData()**,将数据绑定至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)

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

输出结果(Output)

Output

drawArrays() 从向量数组中绘制图元

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

【第二届青训营-寒假前端场】- 「WebGL基础」笔记

WebGL太复杂?其他方式

canvas 2D

看看人家canvas2D,绘制同样的三角形:

// canvas 简单粗暴,都封装好了
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

mesh-js/mesh.js: A graphics system born for visualization

上一篇:Taro 中使用 canvas 绘制分享海报保存到本地


下一篇:BigIP Cookie 解码获取真实IP (python2)