首页
/ three.js ColladaLoader 深度解析:加载 .dae 模型、提取动画与机器人运动学控制的完整指南

three.js ColladaLoader 深度解析:加载 .dae 模型、提取动画与机器人运动学控制的完整指南

2026-09-06 14:35:28作者:秋阔奎Evelyn

Collada(.dae)是一种由 Khronos 组织制定的通用 3D 场景交换格式,广泛存在于旧项目资产库、机器人仿真与游戏工作流中。three.js 通过 addons 中的 ColladaLoader 提供了对这一格式的加载能力,但它只支持官方规范定义的一个子集,并且会针对 Z-UP 坐标系的资产做简单的旋转换算——这些边界条件直接决定了你在实际项目中的资产处理策略。读完本文,你将掌握 ColladaLoader 的完整 API(load / parse)、返回结果对象的结构(scene / animations / kinematics / library),并能从 ColladaLoader.js 的源码层面理解它的解析流水线、坐标系与单位换算逻辑,以及如何用加载结果驱动骨骼动画与关节运动学。

一、ColladaLoader 是什么:定位与继承关系

API 文档ColladaLoader 的定义非常简洁:

A loader for the Collada format.

The Collada format is very complex so this loader only supports a subset of what is defined in the official specification.

两点关键信息:

  1. 它是 Loader 的子类(文档标注 Inheritance: Loader →),因此自动继承 three.js 加载器基类的通用能力:manager(LoadingManager)、pathcrossOriginrequestHeaderwithCredentials 等属性都可以直接配置;
  2. 只支持规范子集。Collada 1.5 规范涉及几何、特效、控制器、动画、运动学、物理等多个 <library> 域,ColladaLoader 覆盖了其中常用部分,但并非全功能实现。遇到加载失败或表现异常时,应首先怀疑资产是否使用了未支持的规范特性。

坐标系与单位换算(文档明示的行为)

文档还特别说明了坐标系统一策略:

Assets with a Z-UP coordinate system are transformed into Y-UP by a simple rotation. The vertex data are not converted.

这不是文档的客套话,而是可以直接在源码中验证的行为。ColladaLoader.parse() 在组装完成场景后执行:

// Handle coordinate system conversion
if ( asset.upAxis === 'Z_UP' ) {

    console.warn( 'THREE.ColladaLoader: You are loading an asset with a Z-UP coordinate system. The loader just rotates the asset to transform it into Y-UP. The vertex data are not converted, see #24289.' );
    scene.rotation.set( - Math.PI / 2, 0, 0 );

}

// Apply unit scale
scene.scale.multiplyScalar( asset.unit );
  • Z-UP 资产:整个场景根节点被绕 X 轴旋转 -90°(scene.rotation.set( - Math.PI / 2, 0, 0 ))来对齐 three.js 的 Y-UP 约定,顶点数据本身不做变换。控制台会打印警告提示这一近似处理;
  • 单位换算asset.unit 取自 Collada 的 <asset><unit> 声明,最终对整个 scene 做等比缩放(scene.scale.multiplyScalar( asset.unit ))。例如以毫米为单位的工业模型会被缩小到米制尺度,无需手动缩放。

这个实现细节意味着:旋转是作用在 scene 节点上的,而不是写入几何体——如果你后续需要把模型烘焙到某个父节点下,记得先 scene.updateMatrixWorld() 或考虑 Object3D.applyMatrix4 之类的烘焙手段。

二、引入方式:addons 显式导入

ColladaLoader 不属于 three.js 核心构建产物,而是 addon,必须显式导入(文档对应 Installation#Addons):

import { ColladaLoader } from 'three/addons/loaders/ColladaLoader.js';

实现位于 examples/jsm/loaders/ColladaLoader.js,其内部依赖两个同目录子模块:

此外它还依赖核心的 FileLoaderLoaderUtilsTextureLoader,并额外引入了 TGALoader 以支持 Collada 中常见的 TGA 纹理。

三、核心用法:loadAsync 与回调式加载

文档标准示例

API 文档给出的最小可用示例:

const loader = new ColladaLoader();
const result = await loader.loadAsync( './models/collada/elf/elf.dae' );
scene.add( result.scene );

result.scene 是一个 Group,即 Collada 视觉场景(visual scene)映射出的场景图,直接加入 Scene 即可渲染。

回调式加载 + LoadingManager

官方示例 webgl_loader_collada.html 展示了生产环境更常见的回调写法,并借助 LoadingManager 在模型、纹理等所有子资源全部就绪后再入场景:

import { ColladaLoader } from 'three/addons/loaders/ColladaLoader.js';

const loadingManager = new THREE.LoadingManager( function () {

    scene.add( elf );        // 所有资源加载完毕后执行

} );

const loader = new ColladaLoader( loadingManager );
loader.load( './models/collada/elf/elf.dae', function ( collada ) {

    elf = collada.scene;

} );

该示例加载的正是仓库中真实存在的资产 elf.dae(配套纹理同目录存放)。

load() 源码可以看到其内部机制:

load( url, onLoad, onProgress, onError ) {

    const scope = this;

    const path = ( scope.path === '' ) ? LoaderUtils.extractUrlBase( url ) : scope.path;

    const loader = new FileLoader( scope.manager );
    loader.setPath( scope.path );
    loader.setRequestHeader( scope.requestHeader );
    loader.setWithCredentials( scope.withCredentials );
    loader.load( url, function ( text ) {

        try {

            onLoad( scope.parse( text, path ) );

        } catch ( e ) {
            // 回调 onError 或 console.error,并上报 manager.itemError( url )
        }

    }, onProgress, onError );

}

几个要点:

  • path 推导:若未显式设置 loader.path,则从 url 提取目录部分(LoaderUtils.extractUrlBase),作为 parsepath 参数——这决定了内嵌资源(如独立纹理文件)的相对寻址基准;
  • 错误语义parse 抛出的异常会被捕获并路由到 onError,同时触发 manager.itemError( url ),即 LoadingManager.onError 也会被调用;
  • data URI 支持:文档明确 url 可以是 data URI,因此也适合把小型 .dae 内联进前端工程。

四、API 详解

4.1 构造函数

new ColladaLoader( manager = new LoadingManager() )

继承自 Loader,可传入 LoadingManager 统一管理并发加载(上节示例已演示)。

4.2 load( url, onLoad, onProgress, onError )

文档参数说明如下,这里结合源码补充行为细节:

参数 说明
url 文件路径/URL,也接受 data URI。相对路径会结合 loader.path 解析
onLoad 加载完成后执行,参数是 parse() 的结果对象({scene, animations, kinematics, library}
onProgress 加载过程中执行,透传给底层 FileLoader
onError 出错时执行;若不传,异常会走 console.error 且仍会触发 manager.itemError

该方法重写了基类的 Loader#load 签名,但回调约定一致。

4.3 parse( text, path ) : Object

文档描述:

Parses the given Collada data and returns a result object holding the parsed scene, an array of animation clips and kinematics.

  • text:原始 Collada 数据字符串;
  • path:资源路径,用于寻址外部纹理等依赖资源;
  • 返回值:解析后的资产对象。

parse() 源码的完整流程值得拆解:

parse( text, path ) {

    if ( text.length === 0 ) {

        return { scene: new Scene() };

    }

    // Parse XML to library data
    const parser = new ColladaParser();
    const parseResult = parser.parse( text );

    if ( parseResult === null ) {

        return null;

    }

    const { library, asset, collada } = parseResult;

    // Setup texture loaders
    const textureLoader = new TextureLoader( this.manager );
    textureLoader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin );

    let tgaLoader;
    if ( TGALoader ) {
        tgaLoader = new TGALoader( this.manager );
        tgaLoader.setPath( this.resourcePath || path );
    }

    // Compose Three.js objects from library data
    const composer = new ColladaComposer( library, collada, textureLoader, tgaLoader );
    const { scene, animations, kinematics } = composer.compose();

    scene.animations = animations;
    // … Z-UP 旋转与单位缩放(见第一节)…

    return {
        get animations() {
            console.warn( 'THREE.ColladaLoader: Please access animations over scene.animations now.' );
            return animations;
        },
        kinematics: kinematics,
        library: library,
        scene: scene
    };

}

由此得到几个可验证的事实:

  1. 空输入返回 { scene: new Scene() } 而非抛错;解析失败parser.parse 返回 null)则返回 null——调用方需自行判空;
  2. 纹理寻址基准this.resourcePath || path,即设置了 resourcePath 时优先于 parse 的 path 参数;
  3. 动画挂载位置animations 数组同时写入 scene.animations。直接访问返回值上的 result.animations 会触发弃用警告(getter 中 console.warn),新代码应统一使用 result.scene.animations
  4. 返回对象包含 4 个成员
成员 类型 说明
scene Group Collada visual scene 对应的场景图,含 scene.animations
animations Array<AnimationClip> 动画剪辑(访问返回值上的该属性已弃用,请用 scene.animations
kinematics Object 运动学模型,含 jointsgetJointValuesetJointValue(见第五节)
library Object 原始解析出的 Collada library 数据,供高级用户做二次提取

五、解析流水线:ColladaParser 与 ColladaComposer 的分工

parse() 把工作量委托给了两级结构:

5.1 ColladaParser:XML → library 数据

ColladaParser.js 负责把 XML 文本解析为纯数据结构,产出 { library, asset, collada } 三元组。library 对应 Collada 中的各 <library_*> 域。

5.2 ColladaComposer:library 数据 → three.js 对象

ColladaComposer.compose() 展示了完整的构建顺序:

compose() {

    const library = this.library;

    this.buildLibrary( library.animations, this.buildAnimation.bind( this ) );
    this.buildLibrary( library.clips, this.buildAnimationClip.bind( this ) );
    this.buildLibrary( library.controllers, this.buildController.bind( this ) );
    this.buildLibrary( library.images, this.buildImage.bind( this ) );
    this.buildLibrary( library.effects, this.buildEffect.bind( this ) );
    this.buildLibrary( library.materials, this.buildMaterial.bind( this ) );
    this.buildLibrary( library.cameras, this.buildCamera.bind( this ) );
    this.buildLibrary( library.lights, this.buildLight.bind( this ) );
    this.buildLibrary( library.geometries, this.buildGeometry.bind( this ) );
    this.buildLibrary( library.visualScenes, this.buildVisualScene.bind( this ) );

    this.setupAnimations();
    this.setupKinematics();

    const scene = this.parseScene( getElementsByTagName( this.collada, 'scene' )[ 0 ] );
    scene.animations = this.animations;

    return { scene, animations: this.animations, kinematics: this.kinematics };

}

从源码结构看,Composer 依次构建了以下能力域:

  • 动画animations / clips):构建 AnimationClip 与关键帧轨道,从 Composer 的 three 核心导入中可以看到它使用 VectorKeyframeTrackQuaternionKeyframeTrack 以及 InterpolateBezierInterpolateDiscrete 等插值模式,对应 Collada <channel>interpolation 属性;
  • 控制器controllers):这是 Collada 实现蒙皮(skin)与形变(morph)的核心——Composer 内含 buildSkeletonbuildBoneHierarchy 等方法,将 skin.joints 映射为 Skeleton / Bone / SkinnedMesh,并按顶点权重降序截断以控制每顶点影响骨骼数;
  • 图像与特效images / effects / materials):由 TextureLoaderTGALoader 协同加载纹理,buildMaterial 将特效映射到 three.js 内置材质——从 Composer 顶部导入 可见材质目标为 MeshBasicMaterialMeshLambertMaterialMeshPhongMaterial 三类;
  • 相机与灯光cameras / lights):可还原为 PerspectiveCamera / OrthographicCameraAmbientLight / DirectionalLight / PointLight / SpotLight
  • 几何与视觉场景geometries / visualScenes):生成 BufferGeometryMeshLine / LineSegments 并组装节点层级,最终由 <collada><scene> 元素指定的 visual_scene 作为入口生成顶层 Group
  • 运动学setupKinematics):解析 Collada 物理运动学域,产出可交互的关节 API。

六、实战一:加载带骨骼动画的模型

仓库提供了三个 Collada 示例页,其中 webgl_loader_collada_skinning.html 演示了蒙皮模型加载,对应的测试资产是 skin_and_morph.dae。结合 parse 的返回结构,标准用法为:

const loader = new ColladaLoader();
loader.load( './models/collada/skin_and_morph.dae', function ( collada ) {

    scene.add( collada.scene );

    // 动画剪辑挂在 scene.animations 上
    const mixer = new THREE.AnimationMixer( collada.scene );
    collada.scene.animations.forEach( function ( clip ) {

        mixer.clipAction( clip ).play();

    } );

    clock = new THREE.Clock();
    renderer.setAnimationLoop( function () {

        mixer.update( clock.getDelta() );
        renderer.render( scene, camera );

    } );

} );

配合 three.js 的 AnimationMixerscene.animations 中的剪辑即可直接驱动骨骼/形变动画。

七、实战二:机器人关节运动学控制(kinematics)

这是 ColladaLoader 区别于大多数加载器的独特能力。官方示例 webgl_loader_collada_kinematics.html 加载 ABB 工业机械臂模型 abb_irb52_7_120.dae,用 TWEEN 在每个关节的限位范围内随机取目标值,持续驱动机械臂做"随机运动":

const loader = new ColladaLoader();
loader.load( './models/collada/abb_irb52_7_120.dae', function ( collada ) {

    dae = collada.scene;
    dae.scale.setScalar( 10.0 );
    dae.updateMatrix();

    kinematics = collada.kinematics;

    init();

} );

核心驱动逻辑:

function setupTween() {

    const duration = THREE.MathUtils.randInt( 1000, 5000 );
    const target = {};

    for ( const prop in kinematics.joints ) {

        if ( ! kinematics.joints[ prop ].static ) {

            const joint = kinematics.joints[ prop ];

            // 起始值取上次的值或零位
            tweenParameters[ prop ] = tweenParameters[ prop ] || joint.zeroPosition;
            // 在限位内随机取目标值
            target[ prop ] = THREE.MathUtils.randInt( joint.limits.min, joint.limits.max );

        }

    }

    kinematicsTween = new TWEEN.Tween( tweenParameters ).to( target, duration )
        .onUpdate( function ( object ) {

            for ( const prop in kinematics.joints ) {

                if ( ! kinematics.joints[ prop ].static ) {

                    kinematics.setJointValue( prop, object[ prop ] );

                }

            }

        } );

    kinematicsTween.start();
    setTimeout( setupTween, duration );

}

setupKinematics 的实现 可以确认 kinematics 对象的完整 API 契约:

成员 说明
kinematics.joints 关节数组(必须是数组以保留关节顺序),每个关节含 type(如 axis / planar)、limits: { min, max }zeroPositionstaticaxis 等字段
kinematics.getJointValue( jointIndex ) 读取关节当前角度/位移;索引不存在时返回并警告
kinematics.setJointValue( jointIndex, value ) 设置关节值;源码会校验越界(超出 limits 打印警告)与 static 关节(不可动则警告),随后沿节点层级把变换应用到与 sid 关联的 Object3D

setJointValue 的内部机制是把关节值转换为旋转(axis 类型绕关节轴旋转,planar 类型走另一分支,未知类型会警告 Unknown joint type),并写入 jointMap[jointIndex].position 缓存,保证 getJointValue 与视觉状态一致。这一 API 与 ROS 生态的 collada 机器人模型(示例页注明模型来自 collada robots 项目)的关节编号天然对应,是把数字孪生/远程遥操作模型接入 Web 端的重要桥梁。

八、仓库中的可用测试资产

写示例或验证行为时,可直接使用 examples/models/collada/ 下已入库的模型:

资产 对应示例 用途
elf/elf.dae webgl_loader_collada.html 基础静态模型加载与渲染
skin_and_morph.dae webgl_loader_collada_skinning.html 骨骼蒙皮 + 形变(morph)控制器
abb_irb52_7_120.dae webgl_loader_collada_kinematics.html 多轴关节运动学控制
Cobra_s350.dae 复杂静态模型

此外该目录还包含 pumpstormtroopertest 等子目录资产,可用于回归加载行为。

九、小结与适用边界

  • 能力边界ColladaLoader 支持规范子集——覆盖几何、蒙皮/形变控制器、基础/朗伯/ Phong 三类材质、动画剪辑、相机、灯光与运动学关节;资产若依赖未实现的高级特效特性,加载结果可能与源工具不符;
  • 坐标与单位:Z-UP 资产通过 scene.rotation 整体旋转到 Y-UP(顶点不转换),asset.unit 决定整体缩放——两者都发生在 parse 阶段,调用 parse 拿到手时场景已就绪;
  • API 演进提示:动画请从 result.scene.animations 读取,result.animations 属性访问会触发弃用警告;
  • 选型参考:对新项目而言,GLTF 是 more 主流的选择(仓库内同样提供 GLTFLoader 及更完整的示例矩阵);而 ColladaLoader 的价值集中在既有 .dae 资产库、机器人/工业仿真(kinematics)与教学演示场景。

examples/index.html 的示例索引找到上述 Collada 示例页即可在本地仓库环境中运行验证(通常通过仓库提供的开发服务器浏览 examples/ 目录)。核心源码文件汇总:ColladaLoader.js(入口与 parse 流程)、ColladaParser.js(XML 解析)、ColladaComposer.js(对象组装与运动学),API 文档见 ColladaLoader.html.md

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.74 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.81 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
595
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
920
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.63 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
518
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
389