webgl扩展系列之二--------------------ANGLE_instanced_arrays(实例化绘制)

一.介绍

ANGLE_instanced_arrays 扩展是一种用于实现WebGL中实例化渲染的扩展。实例化渲染允许您在单个绘制调用中多次使用相同的几何体,并对每个实例应用不同的属性(如颜色、位置、大小等)。这对于在图形渲染中实现重复元素、粒子系统、草地等效果非常有用,因为它可以显著提高渲染性能。

通常,在非实例化渲染中,您需要为每个几何体的每个顶点指定属性(如颜色、位置等)。然而,在实例化渲染中,您只需指定实例之间的属性,并且相同的几何体可以多次重用,以绘制多个实例。

ANGLE_instanced_arrays 扩展引入了以下两个函数:

  1. drawArraysInstancedANGLE(mode, first, count, primcount): 在单个绘制调用中绘制多个实例。它类似于 drawArrays 函数,但允许您指定要绘制的实例数量。

  2. vertexAttribDivisorANGLE(index, divisor): 用于设置通用顶点属性的分频器(divisor)。分频器确定属性在多个实例之间的更新频率。

  3. drawElementsInstancedANGLE(mode,type,count,offset,primcount):与 drawArraysInstancedANGLE 类似,这个函数允许您在单个绘制调用中绘制多个实例,但是它使用的是索引缓冲来定义绘制的图元。

    • mode: 表示要绘制的基本图元类型的枚举值,例如 gl.TRIANGLES 表示绘制三角形。其他可能的值包括 gl.POINTSgl.LINES 等。
    • count: 表示要绘制的索引数量。
    • type: 表示索引数组的数据类型,例如 gl.UNSIGNED_BYTEgl.UNSIGNED_SHORT 等。
    • offset: 表示索引数组中的偏移量。
    • primcount: 表示要绘制的实例数量。

以下是一个使用 ANGLE_instanced_arrays 扩展的示例:

javascript 复制代码
// 获取扩展
var ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext) {
    alert("ANGLE_instanced_arrays extension not supported.");
}

// 设置分频器
ext.vertexAttribDivisorANGLE(positionAttributeLocation, 0); // 非实例化属性
ext.vertexAttribDivisorANGLE(colorAttributeLocation, 1); // 实例化属性

// 绘制多个实例
ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, vertexCount, instanceCount);

请注意,使用 ANGLE_instanced_arrays 扩展需要确保您的浏览器和设备支持该扩展。它在许多现代的 WebGL 实现中都有良好的支持,但在使用之前最好检查目标平台的兼容性信息。

二.绘制多个四边形

2.1 编写shader代码

js 复制代码
 var vShader =
    "attribute vec3 a_position;" +
    "uniform mat4 u_modelMatrix;" +
    "void main(){" +
    "gl_Position = u_modelMatrix*vec4(a_position, 1.0);" +
    "v_color=a_color;" +
    "}"

    var fShader =
    "precision mediump float;" +
    "uniform vec3 u_color;"+
    "void main() {" +
    "   gl_FragColor =vec4(u_color,1.0);" +
    " }"

2.2 初始化缓冲区

js 复制代码
function initVertexBuffer(gl) {
    //创建位置缓冲区
    var positions = new Float32Array( [
        1, 0, 0, 
        0, 1, -1,
        -1, 0, -1,   
        0, -1, 0,
    ])
    const fSize=positions.BYTES_PER_ELEMENT
    var positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
    gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW)



    //创建索引缓冲区
    const typedindex=new Uint8Array([
            0,1,2,
            2,3,0
        ])

    const indexBuffer=gl.createBuffer()
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer)
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,typedindex,gl.STATIC_DRAW)
    return {
        positionBuffer,
        // colorBuffer,
        indexBuffer,
        fSize
    }

}
const objBuffer=initVertexBuffer(gl)
const colorList= [
    [0, 0, 1],
    [1, 0, 0],
    [0, 1, 0],
]

2.3 绘制并更新颜色和模型矩阵

js 复制代码
//绑定一个对象的顶点和索引缓冲区
function bindObjBuffer(gl,objBuffer){
    const fSize=objBuffer.fSize
    gl.bindBuffer(gl.ARRAY_BUFFER,objBuffer.positionBuffer)     
    var a_position = gl.getAttribLocation(gl.program, 'a_position');     
    gl.vertexAttribPointer(a_position, 3, gl.FLOAT, false,0, 0)
    gl.enableVertexAttribArray(a_position)

    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,objBuffer.indexBuffer)
}


var modelMatrix = new Matrix4()
function drawAsUsually(gl,objBuffer,colorList,currentAngle,ext){
    var u_modelMatrixLocation = gl.getUniformLocation(gl.program, 'u_modelMatrix')
    var u_colorLocation = gl.getUniformLocation(gl.program, 'u_color')
    bindObjBuffer(gl,objBuffer)
    colorList.forEach((colorItem,index)=>{
        //设置矩阵
        modelMatrix.setRotate(currentAngle+index*30, 0, 0, 1)
        gl.uniformMatrix4fv(u_modelMatrixLocation, false, modelMatrix.elements)
        //设置颜色
        gl.uniform3fv(u_colorLocation, colorList[index])
        gl.drawElements(gl.TRIANGLES,6,gl.UNSIGNED_BYTE,0)
    })

}

三. 使用扩展绘制

3.1 修改shader,将u_color 使用a_color代替,u_modelMatrix使用a_modelMatrix代替

js 复制代码
var vShader =
    "attribute vec3 a_position;" +
    "attribute vec3 a_color;" +
    "attribute mat4 a_modelMatrix;" +
    "varying vec3 v_color;" +
    "void main(){" +
    "gl_Position = a_modelMatrix*vec4(a_position, 1.0);" +
    "v_color=a_color;" +
    "}"

var fShader =
    "precision mediump float;" +
    "varying vec3 v_color;" +
    "void main() {" +
    "   gl_FragColor =vec4(v_color,1.0);" +
    " }"

3.2 创建颜色缓冲区并在绘制时绑定

js 复制代码
//为颜色创建Buffer
const colorBuffer=gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER,colorBuffer)
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(colorList.flat()),gl.STATIC_DRAW)


function drawAsInstance(gl,objBuffer,colorBuffer,matrixBuffer,matrixData,matrices,currentAngle,ext){
    //绑定颜色缓冲区
    var a_color = gl.getAttribLocation(gl.program, 'a_color');
    gl.bindBuffer(gl.ARRAY_BUFFER,colorBuffer)     
    gl.vertexAttribPointer(a_color, 3, gl.FLOAT, false,0, 0)
    gl.enableVertexAttribArray(a_color)
    //设置颜色偏移
    ext.vertexAttribDivisorANGLE(a_color,1);


    //绑定位置和索引缓冲区
    bindObjBuffer(gl,objBuffer,ext)

    ext.drawElementsInstancedANGLE(
        gl.TRIANGLES,
        6,
        gl.UNSIGNED_BYTE,
        0,
        3,
    );
}

3.3 为矩阵创建缓冲区

js 复制代码
//为矩阵创建buffer
const numInstances=colorList.length
const matrixData=new Float32Array(numInstances * 16)

const matrixBuffer=gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER,matrixBuffer)
gl.bufferData(gl.ARRAY_BUFFER,matrixData.byteLength, gl.DYNAMIC_DRAW)

const matrices =[]
for(let i=0;i<numInstances;i++){
    const byteOffsetToMatrix = i * 16 * 4;
    const numFloatsForView = 16;
    const matrix=new Matrix4()
    matrix.elements=new Float32Array(matrixData.buffer,byteOffsetToMatrix,numFloatsForView)
    matrices.push(matrix)
}

这儿有三点要注意

  • gl.bufferData(gl.ARRAY_BUFFER,matrixData.byteLength, gl.DYNAMIC_DRAW) 由于矩阵要在每一帧动态更新,所以这儿先创建了一个 空的定型数组,第二个参数只给了长度,第三个参数是gl.DYNAMIC_DRAW,而不是gl.STATIC_DRAW
  • 创建矩阵时,让矩阵的元素指向matrixData的buffer,这样后续矩阵更新时matrixData即可更新

3.4 在绘制时动态更新矩阵缓冲区

js 复制代码
function drawAsInstance(gl,objBuffer,colorBuffer,matrixBuffer,matrixData,matrices,currentAngle,ext){
    //绑定颜色缓冲区
    var a_color = gl.getAttribLocation(gl.program, 'a_color');
    gl.bindBuffer(gl.ARRAY_BUFFER,colorBuffer)     
    gl.vertexAttribPointer(a_color, 3, gl.FLOAT, false,0, 0)
    gl.enableVertexAttribArray(a_color)
    //设置颜色偏移
    ext.vertexAttribDivisorANGLE(a_color,1);


    //绑定位置和索引缓冲区
    bindObjBuffer(gl,objBuffer,ext)


    const matrixLoc=gl.getAttribLocation(gl.program, 'a_modelMatrix');

    //更新矩阵
    matrices.forEach((matrix,index)=>{
        matrix.setRotate(currentAngle+index*30, 0, 0, 1)
    })

    updateMatrixBuffer(gl,matrixBuffer,matrixData,matrixLoc,ext)

    ext.drawElementsInstancedANGLE(
        gl.TRIANGLES,
        6,
        gl.UNSIGNED_BYTE,
        0,
        3,
    );
}


function updateMatrixBuffer(gl,matrixBuffer,matrixData,matrixLoc,ext){
    //更新矩阵缓冲区
    gl.bindBuffer(gl.ARRAY_BUFFER, matrixBuffer);
    //填充数据
    gl.bufferSubData(gl.ARRAY_BUFFER, 0, matrixData);

    const bytesPerMatrix = 4 * 16;
    //更新矩阵每一列
    for (let i = 0; i < 4; ++i) {
        const loc = matrixLoc + i;
        gl.enableVertexAttribArray(loc);
        // 注意stride和offset
        const offset = i * 16;  // 一行有4个单精度浮点数,1个就占用4字节
        gl.vertexAttribPointer(
            loc,              // location
            4,                // size (num values to pull from buffer per iteration)
            gl.FLOAT,         // type of data in buffer
            false,            // normalize
            bytesPerMatrix,   // stride, num bytes to advance to get to next set of values
            offset,           // offset in buffer
        );
        // 这行说的是attribute只对下一个实例才进行迭代
        ext.vertexAttribDivisorANGLE(loc, 1);
    }
}

四.webgl2.0中实例化

webgl2.0标准实现了实例化

js 复制代码
gl.vertexAttribDivisor(index, divisor)
js 复制代码
gl.drawArraysInstanced(mode, first, count, instanceCount);
gl.drawElementsInstanced(mode, count, type, offset, instanceCount);
相关推荐
5:0019 分钟前
Qt:(创建项目)
java·前端·qt
green_pine_21 分钟前
CSS学习笔记12——CSS3新增特性
前端·css·笔记·学习
Wenhao.33 分钟前
Go-web开发之帖子功能
开发语言·前端·golang
蓝婷儿41 分钟前
前端面试每日三题 - Day 22
前端·面试·职场和发展
java1234_小锋1 小时前
如何配置NGINX作为反向代理服务器来缓存后端服务的响应?
前端·nginx·缓存
A_aspectJ1 小时前
【Bootstrap V4系列】学习入门教程之 表格(Tables)和画像(Figure)
前端·bootstrap·dreamweaver
同聘云1 小时前
腾讯云web服务器配置步骤是什么?web服务器有什么用途?
服务器·前端·腾讯云
巴巴_羊1 小时前
CSS八股 3
前端·css
前后端杂货铺1 小时前
uniapp利用生命周期函数实现后台常驻示例
android·前端·ios·微信小程序·uni-app
大道归简2 小时前
案例:自动化获取Web页面小说(没钱修什么仙)——selenium
前端·selenium·自动化