十、将非坐标数据传入顶点着色器

163 阅读2分钟

一、介绍

前面的章节,我们通过创建缓冲区对象,在其中存储顶点的坐标数据,然后将缓冲区对象传入顶点着色器。然而三维图形除了顶点坐标信息之外,还有其他信息,如顶点的大小。 本节将非坐标数据传入顶点着色器,步骤同传入顶点坐标到着色器.
步骤:
(1)创建缓冲区
(2)绑定
(3)写入数据
(4)分配到attribute变量
(5)开启

二、实现方式

有两种方式可以同时传入顶点的坐标和尺寸信息,分别为多个缓存区以及利用gl.vertexAttribPointer()的步进和偏移参数

1、多个缓冲区

var VSHADER_SOURCE =
    'attribute vec4 a_Position;\n' +
    'attribute float a_PointSize;\n' +
    'void main() {\n' +
    '  gl_Position = a_Position;\n' +
    '  gl_PointSize = a_PointSize;\n' +
    '}\n';

var FSHADER_SOURCE =
    'void main() {\n' +
    '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' +
    '}\n';

function main() {
    ···
    var n = initVertexBuffers(gl);
    
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.POINTS, 0, n);
}

function initVertexBuffers(gl) {
    #三个顶点的坐标信息
    var vertices = new Float32Array([
        0.0, 0.5,   -0.5, -0.5,   0.5, -0.5
    ]);
    var n = 3;
    #三个顶点的尺寸信息
    var sizes = new Float32Array([
        10.0, 20.0, 30.0  
    ]);

    #步骤(1)
    var vertexBuffer = gl.createBuffer();  
    #步骤(1)
    var sizeBuffer = gl.createBuffer();
    
    #步骤(2)
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    #步骤(3)
    gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
    
    var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
    #步骤(4)
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
    #步骤(5)
    gl.enableVertexAttribArray(a_Position);

    #步骤(2)
    gl.bindBuffer(gl.ARRAY_BUFFER, sizeBuffer);
    #步骤(3)
    gl.bufferData(gl.ARRAY_BUFFER, sizes, gl.STATIC_DRAW);
    var a_PointSize = gl.getAttribLocation(gl.program, 'a_PointSize');
    #步骤(4)
    gl.vertexAttribPointer(a_PointSize, 1, gl.FLOAT, false, 0, 0);
    #步骤(5)
    gl.enableVertexAttribArray(a_PointSize);
    
    #解绑缓冲区对象
    gl.bindBuffer(gl.ARRAY_BUFFER, null);
    return n;
}

注:

两个不同的缓冲区被分配给两个不同的attribute变量
就可以向顶点着色器传递多份逐顶点的数据信息

2、利用gl.vertexAttribPointer()的步进和偏移参数

使用多缓冲区对象向着色器传递多种数据,不适用于数据量大的情况
WebGL允许我们把顶点的坐标和尺寸数据打包到同一个缓冲区中,并通过某种机制分别访问不同种类数据。

function initVertexBuffers(gl) {
    var verticesSizes = new Float32Array([
        #顶点和尺寸信息
        0.0,  0.5,  10.0,  #第一个点
        -0.5, -0.5,  20.0,  #第二个点
        0.5, -0.5,  30.0   #第三个点
    ]);
    var n = 3; #点的数量

    #步骤(1)
    var vertexSizeBuffer = gl.createBuffer();  
    #步骤(2)
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexSizeBuffer);
    #步骤(3)
    gl.bufferData(gl.ARRAY_BUFFER, verticesSizes, gl.STATIC_DRAW);
    
    #类型化数组静态属性,获取数组中每个元素所占字节数
    var FSIZE = verticesSizes.BYTES_PER_ELEMENT;
    #获取a_Position变量的内存地址
    var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
    #步骤(4)
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 3, 0);
    #步骤(5)
    gl.enableVertexAttribArray(a_Position); 

    #获取a_PointSize变量的内存地址
    var a_PointSize = gl.getAttribLocation(gl.program, 'a_PointSize');
    #步骤(4)
    #设置了相邻顶点间字节数以及缓冲区对象偏移量
    gl.vertexAttribPointer(a_PointSize, 1, gl.FLOAT, false, FSIZE * 3, FSIZE * 2);
    #步骤(5)
    gl.enableVertexAttribArray(a_PointSize);  // Enable buffer allocation

    gl.bindBuffer(gl.ARRAY_BUFFER, null);

    return n;
}

注:

该方法相较于方法一,创建缓冲区对象、将缓冲区绑定到目标、向缓冲区写入数据这三个步骤只进行了一次,将缓冲区分配给变量和连接变量与缓冲区对象这两个步骤进行了两次,且主要区别在于gl.vertexAttribPointer()中偏移量的设置,该接口的语法的入参在第五节中有详述,可参考。