threejs合并模型的几何体提高渲染速度

本文介绍了一种针对dae格式三维模型的合并优化方法,通过分类和几何体合并减少渲染计算次数,提高渲染效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

dae 格式的三维模型进行合并,第一个函数对模型类型分类后合并,第二个函数对同一类的模型合并,第三个函数对模型的几何体合并;使用了 threejs 类库和 jQuery 类库的方法。合并后的模型由于数据没有共用,模型体积比合并之前的多个模型体积总和大;合并后的模型在渲染时比合并之前的多个模型减少了渲染的计算和处理次数,渲染速度比较快。下面是参考的代码:


// 模型操作类
var ObjectHelper = function () {

    this.objectTag = 'ObjectHelper';

}

// 合并模型
ObjectHelper.prototype.mergeObjectByType = function ( obj, refer ) {

    if ( ! obj || ! refer || ! refer.mergeObjects || ! refer.mergeBufferGeometry ) return null;
    console.time( '合并模型 ' + obj.name );

    var objAll, objCopy, matrixTemp;
    objAll = {
        'Mesh': [],
        'SkinnedMesh': [],
        'Line': [],
        'LineSegments': [],
        'other': []
    };

    obj.updateMatrixWorld( true );
    obj.traverse( function ( child ) {

        objCopy = child.clone( false );
        matrixTemp = objCopy.matrix.clone();
        matrixTemp.getInverse( matrixTemp.clone() );
        objCopy.applyMatrix( matrixTemp.clone() );
        objCopy.applyMatrix( child.matrixWorld.clone() );

        if ( child.type === 'Mesh' ) {

            objCopy.name = '[已合并Mesh] ' + objCopy.name;
            objAll[ child.type ].push( objCopy );

        } else if ( child.type === 'SkinnedMesh' ) {

            objCopy.name = '[已合并SkinnedMesh] ' + objCopy.name;
            objAll[ child.type ].push( objCopy );

        } else if ( child.type === 'Line' ) {

            objCopy.name = '[已合并Line] ' + objCopy.name;
            objAll[ child.type ].push( objCopy );

        } else if ( child.type === 'LineSegments' ) {

            objCopy.name = '[已合并LineSegments] ' + objCopy.name;
            objAll[ child.type ].push( objCopy );

        } else if ( child.type !== 'Group' ) {

            objCopy.name = '[未合并] ' + objCopy.name;
            objAll[ 'other' ].push( objCopy );

        }

    } );

    var group = new THREE.Group();

    for ( var type in objAll ) {

        if ( objAll[ type ].length < 1 ) continue;

        if ( type === 'other' ) {

            for ( var i = 0, l = objAll[ type ].length; i < l; i++ ) {
                group.add( objAll[ type ][ i ] );
            }

        } else {

            objCopy = refer.mergeObjects( objAll[ type ], refer );
            objCopy.name = objAll[ type ][ 0 ][ 'name' ];
            $.extend( objCopy.userData, objAll[ type ][ 0 ][ 'userData' ] );
            group.add( objCopy );

        }

    }

    group.name = obj.name;
    $.extend( group.userData, obj.userData );
    console.timeEnd( '合并模型 ' + obj.name );

    return group;

}

// 合并某个类型的所有模型
ObjectHelper.prototype.mergeObjects = function ( objs, refer ) {

    if ( ! objs || objs.length < 1 || ! refer || ! refer.mergeBufferGeometry ) return null;

    var type = objs[ 0 ].type, geometries = [], materials = {}, materialsArr = [],
        groups, materialTemp, geometryTemp, count = 0, countTemp = 0, startTemp,
        uvAttr = false, vertices, uvs, geometry = new THREE.BufferGeometry(),
        geometry2, midNow;
    
    if ( type === 'Mesh' || type === 'SkinnedMesh' ) 
    materialsArr.push( new THREE.MeshBasicMaterial( { side: THREE.BackSide, name: '默认背面材质' } ) );

    for ( var i = 0, l = objs.length; i < l; i++ ) {

        materialTemp = objs[ i ].material;
        geometryTemp = objs[ i ].geometry.clone();
        geometryTemp.applyMatrix( objs[ i ].matrix.clone() );
        objs[ i ].geometry.dispose();

        if ( geometryTemp.vertices ) {

            countTemp = geometryTemp.vertices.length;
            if ( objs[i].geometry.faceVertexUvs ) uvAttr = true;

        } else {

            countTemp = geometryTemp.attributes.position.count;
            if ( objs[i].geometry.attributes.uv ) uvAttr = true;

        }

        if ( geometryTemp.groups.length === 0 ) {

            geometries.push( {
                geo: geometryTemp,
                start: 0,
                count: countTemp,
                mid: materialTemp.id
            } );

            count += countTemp;

        } else {

            groups = geometryTemp.groups;
            for ( var j = 0, jl = groups.length; j < jl; j++ ) {

                geometries.push( {
                    geo: geometryTemp,
                    start: groups[ j ].start,
                    count: groups[ j ].count,
                    mid: materialTemp.id ? materialTemp.id : materialTemp[ groups[ j ].materialIndex ].id
                } );

                count += groups[ j ].count;

            }

        }

        if ( materialTemp.id ) {

            materials[ materialTemp.id ] = materialTemp;

            if ( materialTemp.transparent ) {

                materialTemp.side = THREE.DoubleSide;
                if ( materialTemp.opacity === 1 ) materialTemp.opacity = 0.6;
                
            }

        } else {

            for ( var j = 0, jl = materialTemp.length; j < jl; j++ ) {

                materials[ materialTemp[ j ].id ] = materialTemp[ j ];

                if ( materialTemp[ j ].transparent ) {

                    materialTemp[ j ].side = THREE.DoubleSide;
                    if ( materialTemp[ j ].opacity === 1 ) materialTemp[ j ].opacity = 0.6;
                    
                }
                
            }

        }

    }

    vertices = new Float32Array( count * 3 );
    uvs = new Float32Array( count * 2 );
    geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
    if ( uvAttr ) geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
    geometry.computeVertexNormals();
    geometry2 = geometry.clone();
    count = 0;

    while ( geometries.length > 0 ) {

        midNow = geometries[ 0 ].mid;
        startTemp = count;
        countTemp = 0;

        for ( var i = 0, l = geometries.length; i < l; i++ ) {

            if ( geometries[ i ].mid === midNow ) {

                if ( geometries[ i ].geo.isBufferGeometry ) {
                    refer.mergeBufferGeometry( geometry, count, geometries[ i ].geo, geometries[ i ].start, geometries[ i ].count );
                } else {

                    geometry2.fromGeometry( geometries[ i ].geo );
                    refer.mergeBufferGeometry( geometry, count, geometry2, geometries[ i ].start, geometries[ i ].count );

                }

                count += geometries[ i ].count;
                countTemp += geometries[ i ].count;

                geometries[ i ].geo.dispose();
                geometries.splice( i, 1 );
                i--;
                l--;

            }

        }
        
        if ( type === 'Mesh' || type === 'SkinnedMesh' ) {

            if ( ! materials[ midNow ].transparent )
            geometry.addGroup(
                startTemp,
                countTemp,
                0
            );
            
        }

        console.log( '合并模型:', materials[ midNow ].name );
        materialsArr.push( materials[ midNow ] );
        materials[ midNow ] = materialsArr.length - 1;

        geometry.addGroup(
            startTemp,
            countTemp,
            materials[ midNow ]
        );

    }

    var obj;

    if ( materialsArr.length === 1 ) {
        obj = new THREE[ type ]( geometry, materialsArr[ 0 ] );
    } else {
        obj = new THREE[ type ]( geometry, materialsArr );
    }

    return obj;

}

// 合并几何体
ObjectHelper.prototype.mergeBufferGeometry = function ( geometry1, offset, geometry2, start, count ) {

    if (
        ! geometry1 || ! geometry1.isBufferGeometry || ! geometry2 || ! geometry2.isBufferGeometry
        || offset < 0 || start < 0 || count < 0
    ) return null;

    var attributes1 = geometry1.attributes, attributes2 = geometry2.attributes,
        attribute1, attributeArray1, attribute2, attributeArray2, attributeOffset, length,
        attributeStart, attributeCount;

    for ( var key in attributes1 ) {

        if ( attributes2[ key ] === undefined ) continue;

        attribute1 = attributes1[ key ];
        attributeArray1 = attribute1.array;
        attribute2 = attributes2[ key ];
        attributeArray2 = attribute2.array;

        attributeOffset = attribute1.itemSize * offset;
        attributeStart = attribute2.itemSize * start;
        attributeCount = attribute2.itemSize * count;

        length = Math.min( attributeCount, attributeArray1.length - attributeOffset );

        for ( var i = attributeStart, j = attributeOffset, z = 0; z < length; i ++, j ++, z ++ ) {
            attributeArray1[ j ] = attributeArray2[ i ];
        }

    }

    return geometry1;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

codehuicn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值