09-mini版WebGLAttributes

源码:github.com/buglas/thre...

1-WebGLAttributesMini 的代码

src\renderers\webgl\WebGLAttributesMini.js

arduino 复制代码
/**
 * 管理 BufferAttribute 与 WebGL buffer 的映射。
 * 首次上传用 bufferData,后续仅在 attribute.version 变化时用 bufferSubData 增量更新。
 */
function WebGLAttributesMini( gl ) {
    const buffers = new WeakMap();
    /** 首次创建 GPU 端 ARRAY_BUFFER 并上传完整数据 */
    function createBuffer( attribute, bufferType ) {
        const array = attribute.array;
        const usage = attribute.usage;
        const size = array.byteLength;
        const buffer = gl.createBuffer();
        gl.bindBuffer( bufferType, buffer );
        gl.bufferData( bufferType, array, usage );
        let type;
        if ( array instanceof Float32Array ) {
            type = gl.FLOAT;
        }
        return {
            buffer: buffer,
            type: type,
            bytesPerElement: array.BYTES_PER_ELEMENT,
            version: attribute.version,
            size: size
        };
    }
    /** 顶点数据变更时,原地更新已有 buffer(不支持改变 array 字节长度) */
    function updateBuffer( buffer, attribute, bufferType ) {
        gl.bindBuffer( bufferType, buffer );
        // TODO: 支持 updateRanges
        gl.bufferSubData( bufferType, 0, attribute.array );
    }
​
    /** 获取 attribute 对应的 GPU buffer */
    function get( attribute ) {
        return buffers.get( attribute );
    }
​
    /** 删除 attribute 对应的 GPU buffer */
    function remove( attribute ) {
        const data = buffers.get( attribute );
        if ( data ) {
            gl.deleteBuffer( data.buffer );
            buffers.delete( attribute );
        }
    }
​
    /** 更新 attribute 对应的 GPU buffer 
     * attribute: BufferAttribute
    */
    function update( attribute, bufferType ) {
        const data = buffers.get( attribute );
        console.log('attribute',attribute);
        if ( data === undefined ) {
            buffers.set( attribute, createBuffer( attribute, bufferType ) );
        } else if ( data.version < attribute.version ) {
            if ( data.size !== attribute.array.byteLength ) {
                throw new Error( 'THREE.WebGLAttributesMini: The size of the buffer attribute's array buffer does not match the original size. Resizing buffer attributes is not supported.' );
            }
            updateBuffer( data.buffer, attribute, bufferType );
            data.version = attribute.version;
        }
    }
    return {
        get: get,
        remove: remove,
        update: update
    };
}
​
export { WebGLAttributesMini };

2-WebGLAttributesMini 的功能定位

WebGLAttributes 负责将CPU里的attribute 数据上传到GPU,也就是创建VBO。

WebGLAttributesMini 亦是如此,只是具体功能做了删减。

其对应的webgl 内容是:

ini 复制代码
const buffer = gl.createBuffer();
gl.bindBuffer( bufferType, buffer );
gl.bufferData( bufferType, array, usage );

上面的array 就是attribute 数据。

webgl在将CPU里的attribute 数据上传到GPU时,是把attribute 数据深拷贝一份上传的。

所以在gl.bufferData() 后,array 的变化并不会影响GPU 里的数据。

3-WebGLAttributesMini 中的方法详解

构造函数

javascript 复制代码
function WebGLAttributesMini( gl ) {
    const buffers = new WeakMap();
  • 工厂函数,接收 WebGL2 上下文 gl
  • WeakMap 缓存:BufferAttribute → GPU 元数据
  • 对外暴露三个方法:getremoveupdate

Buffer 缓存对象结构:

arduino 复制代码
return {
    buffer: buffer,
    type: type,
    bytesPerElement: array.BYTES_PER_ELEMENT,
    version: attribute.version,
    size: size
};
字段 含义
buffer gl.createBuffer() 得到的 WebGL buffer
type WebGL 数据类型(Mini 版仅 gl.FLOAT
bytesPerElement 每个元素字节数(Float32 为 4)
version 上次同步时的 attribute.version
size 首次上传时的 array.byteLength

createBuffer --- 首次上传

ini 复制代码
function createBuffer( attribute, bufferType ) {
    const array = attribute.array;
    const usage = attribute.usage;
    const size = array.byteLength;
    const buffer = gl.createBuffer();
    gl.bindBuffer( bufferType, buffer );
    gl.bufferData( bufferType, array, usage );
    let type;
    if ( array instanceof Float32Array ) {
        type = gl.FLOAT;
    }
    return { ... };
}
  1. gl.createBuffer() 创建 GPU buffer
  2. gl.bufferData(...) 整包上传 TypedArray
  3. 仅当 arrayFloat32Array 时设置 type = gl.FLOAT。完整版还有许多其它类型的type ,此处做了简化。

在纯色三角形的绘制中,VBO 主要是用于存储三角形的position 的,如下所示:

ini 复制代码
const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array( [
    - 1.0, - 1.0, 0.0,
    1.0, - 1.0, 0.0,
    0.0, 1.0, 0.0
] );
geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );

updateBuffer --- 数据变更时更新

scss 复制代码
function updateBuffer( buffer, attribute, bufferType ) {
    gl.bindBuffer( bufferType, buffer );
    gl.bufferSubData( bufferType, 0, attribute.array );
}

与完整版不同:

  • 总是从 offset 0 全量 bufferSubData
  • 不支持 attribute.updateRanges 局部更新
  • 没有 onUploadCallback() 回调

适合静态或偶尔整包更新的几何体,不适合大量局部改动的场景。

update --- 将CPU中的attribute 数据更新到GPU

kotlin 复制代码
function update( attribute, bufferType ) {
    const data = buffers.get( attribute );
    if ( data === undefined ) {
        buffers.set( attribute, createBuffer( attribute, bufferType ) );
    } else if ( data.version < attribute.version ) {
        if ( data.size !== attribute.array.byteLength ) {
            throw new Error( 'THREE.WebGLAttributesMini: ...' );
        }
        updateBuffer( data.buffer, attribute, bufferType );
        data.version = attribute.version;
    }
}

逻辑:

情况 行为
首次见到 attribute createBuffer,写入 WeakMap
attribute.version 变大 updateBuffer,同步 version
version 未变 什么都不做(避免重复上传)
array 字节长度变化 抛错,不支持 resize

versionBufferAttribute.needsUpdate = true 触发递增,与完整版 Three.js 一致。

Mini 版不处理:

  • InterleavedBufferAttribute(不会解包到 attribute.data
  • GLBufferAttribute(外部已有 buffer 的情况)
  • ELEMENT_ARRAY_BUFFER 索引(Mini 用 drawArrays,不用 index buffer)

get / remove

get(attribute) --- 供 WebGLBindingStatesMini 绑定 VAO

src\renderers\webgl\WebGLBindingStatesMini.js

ini 复制代码
const attribute = attributes.get( geometryAttribute );
const buffer = attribute.buffer;
const type = attribute.type;
const bytesPerElement = attribute.bytesPerElement;
gl.enableVertexAttribArray( 0 );
gl.bindBuffer( gl.ARRAY_BUFFER, buffer );
gl.vertexAttribPointer(
    0,
    size,
    type,
    normalized,
    size * bytesPerElement,
    0
);

remove(attribute) --- 几何体 dispose 时释放 GPU 资源:

src\renderers\webgl\WebGLGeometriesMini.js

ini 复制代码
function onGeometryDispose( event ) {
    const geometry = event.target;
    attributes.remove( geometry.index );
    for ( const name in geometry.attributes ) {
        attributes.remove( geometry.attributes[ name ] );
    }
    geometry.removeEventListener( 'dispose', onGeometryDispose );
    delete geometries[ geometry.id ];
}

4-WebGLAttributesMini 的典型运行时流程

  1. 场景遍历到 Meshobjects.update(object)geometries.update(geometry)
  2. 对每个 attribute 调用 attributes.update(..., ARRAY_BUFFER)
  3. needsUpdate = true,CPU 数据通过 bufferSubData 同步到 GPU
  4. 绘制前 bindingStates.setup(program, geometry)attributes.get() 取 buffer → 设置 vertexAttribPointer
  5. bufferRenderer.render(0, position.count) 执行 drawArrays

总结

WebGLAttributes 会在GPU中创建buffer,然后向buffer 中存储attribute 数据。

相关推荐
李伟_Li慢慢1 小时前
11-mini版WebGLGeometries
前端·three.js
李伟_Li慢慢1 小时前
18-mini版WebGLRenderList
前端·three.js
李伟_Li慢慢1 小时前
15-mini版WebGLMaterials
前端·three.js
ji_shuke1 小时前
Vue3 前端批量打印 PDF/图片踩坑记:跨域、合并打印、对话框闪退与按钮一直 loading
前端·pdf·状态模式·pdf打印
李伟_Li慢慢1 小时前
06-mini版WebGLState
前端·three.js
李伟_Li慢慢1 小时前
04-three.js的渲染流程概览
前端·three.js
李伟_Li慢慢1 小时前
07-WebGLProperties
前端·three.js
爱勇宝1 小时前
AI 都能写代码了,还要学计算机吗?
前端·后端·程序员
石像鬼₧魂石1 小时前
【Y2Ksoft】四季贵州水世界乐园管理系统 —— 玻璃拟态 · 单文件HTML · 开箱即用
前端·html