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 数据。

相关推荐
Mh4 小时前
别再只会 `find` 了:Map 在前端业务里的真实用法
前端·javascript
陈随易5 小时前
FFmpeg 9.0 发布,代号 Lei,音视频处理再升级
前端·后端·程序员
爱丶狸5 小时前
Grafana_Zabbix_ImageRenderer_部署与前端操作手册
linux·前端·zabbix·grafana·kylin
用户059540174468 小时前
把AI对话记忆存储测试从手工改成Playwright+pytest,覆盖率从20%提到96%,回归时间缩短90%
前端·css
kyriewen8 小时前
别再这样写TypeScript了——Code Review中最常见的8个反模式
前端·javascript·typescript
名字还没想好☜8 小时前
Next.js ‘use client‘ 到底加在哪:Server/Client Components 边界与常见报错
开发语言·前端·javascript·react·next.js
午安~婉9 小时前
GitHub Token/ GitHub Stats统计显示图异常
前端·github·vercel·github token
码云之上9 小时前
AI Agent 工程化总览篇:从 Prompt 到 Harness
前端·人工智能
2501_926978339 小时前
以说明书 DNA 为模板——完整 AGI 的结构图景
前端·人工智能·经验分享·笔记·ai写作
IT_陈寒9 小时前
Vite静态资源引用这个坑我踩得有点疼
前端·人工智能·后端