• webgl 系列 —— 三角形


    其他章节请看:

    webgl 系列

    三角形

    有人说三维模型的基本单元是三角形。比如复杂的游戏角色,也只是用许多三角形画出来的。

    不管上述说法是否属实,本篇先把三角形画出来。

    如何绘制一个三角形

    鼠标点击绘点示例我们写了这样的代码:

    points.forEach(item => {
        gl.vertexAttrib3f(a_Position, item.x, item.y, 0.0);
        gl.drawArrays(gl.POINTS, 0, 1);
    })
    

    这种方法一次只能绘制一个点。

    比如需要绘制一个三角形,应该是一个连贯的动作。比如在顶点着色器中一次性画三个点,然后用线连接;而不是绘制一个点,在绘制一个点,在绘制一个点...,不应该是零散的

    Tip:提前透露 - 只要一次性将三个点绘制出来,其实三角形也就画出来了。

    一次性绘制多个顶点可以使用缓冲区对象

    缓冲区对象

    可以使用 webgl 中的缓冲区对象(buffer object) 一次性的向着色器传入多个顶点数据。

    使用缓冲区对象向顶点着色器传入多个顶点数据,需要如下5个步骤:

    1. 创建缓冲区对象
    2. 绑定缓冲区对象
    3. 写入数据到缓冲区对象
    4. 分配缓冲区对象给一个 attribute 变量
    5. 开启 attribute 变量

    一次绘制三个点

    效果

    实现

    完整代码如下:

    // 一次绘制三个点
    
    const VSHADER_SOURCE = `
    attribute vec4 a_Position;
    void main() {
      gl_Position = a_Position;
      gl_PointSize = 10.0;               
    }
    `
    
    const FSHADER_SOURCE = `
    void main() {
      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }
    `
    
    function main() {
        const canvas = document.getElementById('webgl');
        const gl = canvas.getContext("webgl");
        if (!gl) {
            console.log('Failed to get the rendering context for WebGL');
            return;
        }
    
        if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
            console.log('Failed to intialize shaders.');
            return;
        }
    
        gl.clearColor(0, 0, 0, 1);
        gl.clear(gl.COLOR_BUFFER_BIT);
    
        // 顶点数据
        const vertices = {
            // 顶点数据。Float32Array 当做普通数组,对应 C 语言的 float
            data: new Float32Array([
                0.0, 0.5,
                -0.5, -0.5,
                0.5, -0.5
            ]),
            // 顶点数
            vertexNumber: 3,
            // 每个顶点分量数,例如第一个点(0.0, 0.5)
            count: 2,
        }
    
        // 将顶点的位置写入顶点着色器
        initVertexBuffers(gl, vertices)
    
        // gl.drawArrays(mode, first, count) 
        // 还是画点(gl.POINTS),从缓冲区的第一个位置(0)开始画,绘制3(vertices.vertexNumber)个点
        gl.drawArrays(gl.POINTS, 0, vertices.vertexNumber);
    }
    
    // 将三个顶点通过缓冲对象一次写入着色器
    function initVertexBuffers(gl, {data, count}) {
        // 1. 创建缓冲区对象
        const vertexBuffer = gl.createBuffer();
        if (!vertexBuffer) {
            console.log('创建缓冲区对象失败');
            return -1;
        }
    
        // 绑定缓冲区对象绑定到`目标`。只能通过目标向缓冲区写数据
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    
        // 将数据写入缓冲区
        gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
    
        const a_Position = gl.getAttribLocation(gl.program, 'a_Position');
        if (a_Position < 0) {
            console.log('Failed to get the storage location of a_Position');
            return -1;
        }
    
        // 将整个缓冲区对象分配给 atttibute(a_Position) 变量
        gl.vertexAttribPointer(a_Position, count, gl.FLOAT, false, 0, 0);
    
        // 激活 attribute 变量,使缓冲区对 attribute 变量分配生效
        gl.enableVertexAttribArray(a_Position);
    }
    

    通过 initVertexBuffers() 将3个顶点分配给 attribute 变量,执行 gl.drawArrays(gl.POINTS, 0, vertices.vertexNumber) 时,顶点着色器执行了3次。顶点着色器执行过程和缓冲区数据传入过程如下:

    绘制出所有点后,颜色缓冲区的内容就会自动显示在浏览器中。

    initVertexBuffers() 里面涉及了许多概念和api,请往下阅读。

    Float32Array

    绘制三维图形 webgl 需要处理大量相同的数据,例如顶点坐标。为了优化性能,就引入了一种特殊的数组(类型化数组),浏览器事先知道数组中的数据类型,处理起来就更有效率。

    Float32Array 就是其中一种类型化数组,通常用于存储顶点坐标或颜色。

    Tip: webgl 中很多操作都需要用到类型化数组

    webgl 中有如下类型化数组:

    数组类型 每个元素所占字节数 对应C语言的数据类型
    Int8Array 1 8 位有符号整数
    Uint8Array 1 8 位无符号整型
    Int16Array 2 16 位有符号整数
    Uint16Array 2 16 位无符号整型
    Int32Array 4 32 位有符号的整型
    Uint32Array 4 32 位无符号的整型
    Float32Array 4 32 位的浮点数 float
    Float64Array 8 64 位的浮点数 double

    bindBuffer

    gl.bindBuffer(target, buffer) - 绑定缓冲区。例如示例中的: gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);

    为什么要将绑定缓冲区对象?不能直接向缓冲区写入数据,只能通过目标写入数据。就像这样:

    目标表示缓冲区的用途。例如这里的 gl.ARRAY_BUFFER 指包含顶点属性(例如顶点坐标、纹理坐标数据或顶点颜色数据)的缓冲区。

    bufferData

    gl.bufferData(target, size, usage) - 将数据写入缓冲区。例如示例中的:gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);

    gl.STATIC_DRAW 是 usage 中的一种,指缓冲区的内容可能经常使用,而不会经常更改。内容被写入缓冲区,但不被读取。

    此方法执行后,webgl 系统内部状态如下:

    vertexAttribPointer

    g.vertexAttribPointer(index, size, type, normalized, stride, offset) - 将整个缓冲区对象分配给 atttibute 变量。

    例如示例中的:

    // count - 指定缓冲区每个顶点的分量个数,缺少则按照 vertexAttrib3f 的补全方式
    // gl.FLOAT 与上文的 Float32Array 对应
    // normalized - 对于类型gl.FLOAT和gl.HALF_FLOAT,此参数无效
    // stride - 指定相邻两个顶点间的字节数。默认为0
    // offset - 指定缓冲区对象中的偏移量。即attribute 从缓冲区何处开始存储
    gl.vertexAttribPointer(a_Position, count, gl.FLOAT, false, 0, 0);
    

    此方法执行后,webgl 系统内部状态如下:

    疑惑: 说vertexAttribPointer最后一个参数(offset)必须是类型的字节长度的倍数。尝试将 0 改成 4,效果却是:

    enableVertexAttribArray

    gl.enableVertexAttribArray(index) - 激活 attribute 变量,使缓冲区对 attribute 变量分配生效。

    此方法执行后,webgl 系统内部状态如下:

    drawArrays

    gl.drawArrays(mode, first, count) - 执行顶点着色器,按照 mode 指定的参数绘制图形。first 指定从哪个点开始绘制,count 指绘制需要几个点。

    能绘制的图形有:


    Tip: drawArrays 更多细节请看这里

    三角形

    只要一次性将三个点绘制出来,其实三角形也就画出来了。

    修改上面示例一行代码就好(gl.POINTS -> gl.LINE_LOOP):

    // 前
    gl.drawArrays(gl.POINTS, 0, vertices.vertexNumber);
    
    // 后
    gl.drawArrays(gl.LINE_LOOP, 0, vertices.vertexNumber);
    

    效果如下:

    矩形

    效果如下:

    核心代码如下:

        // 定义四个点
        const vertices = {
            data: new Float32Array([
                -0.5, 0.5,
                -0.5, -0.5,
                0.5, 0.5,
                0.5, -0.5,
            ]),
            // 顶点数
            vertexNumber: 4,
            count: 2,
        }
    
        initVertexBuffers(gl, vertices)
        // TRIANGLE_STRIP
        gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertices.vertexNumber);
    

    Tip:四个点的顺序有要求

    三角扇

    效果如下:

    在矩形的基础上,改为 gl.TRIANGLE_FAN 即可。

    Tip:GL_TRIANGLE_FAN - 绘制各三角形形成一个扇形序列,以 v0 为起点:(v0, v1, v2)、(v0, v2, v3)、(v0, v3, v4)

    其他章节请看:

    webgl 系列

  • 相关阅读:
    带你吃透Servlet技术
    springboot+vue+nodejs+java卤菜品销售商城系统
    计算机网络 | 网络安全 | HTTP & HTTPS | URI解析全过程
    IntelliJ IDEA 2022.2来了:已完全支持 Spring 6 和 Spring Boot 3
    C++ 数学与算法系列之认识格雷码
    单元测试与Mockito
    今日睡眠质量记录80分
    01 # 手写 new 的原理
    BIM → CIM,捷码赋能智慧城市多行业应用开发
    (221)Verilog HDL: Fsm3onehot
  • 原文地址:https://www.cnblogs.com/pengjiali/p/17183771.html