首页
/ three.js OBJLoader 完全指南:加载与解析 Wavefront OBJ 模型

three.js OBJLoader 完全指南:加载与解析 Wavefront OBJ 模型

2026-09-07 22:07:06作者:滑思眉Philip

OBJLoader 是 three.js 官方提供的 Wavefront OBJ 格式加载器(以 addon 形式发布,位于 three/addons/loaders/OBJLoader.js)。OBJ 是一种以纯文本描述 3D 几何的数据格式,本文将以 官方文档 为骨架,结合仓库内 OBJLoader 源码 与真实示例,系统讲解其 API、底层解析流程、与 MTLLoader 的搭配使用以及各种 OBJ 语法特性的支持情况,帮助你独立完成 OBJ 模型的加载、材质绑定与场景接入。

OBJ 格式简介与加载器的定位

Wavefront OBJ 是一种"人类可读"的简单数据格式(由 Wavefront Technologies 为 Advanced Visualizer 设计),它以纯文本逐行记录几何信息:

  • 每个顶点的位置坐标v x y z);
  • 每个纹理坐标顶点的 UV 坐标vt u v);
  • 顶点法线vn x y z);
  • 构成多边形的——面被定义为顶点、纹理坐标顶点的索引列表(f 行)。

OBJLoader 的作用正是读取这种文本格式,将其转换为 three.js 的场景对象树(一个 Group),其中每个命名的 object/group 对应一个 Mesh 或点/线图元,并尽可能还原材质、平滑组与顶点色等信息。

注意:OBJLoader 只处理 .obj 中的几何数据,不负责加载纹理贴图。OBJ 文件通过 mtllib xxx.mtl 声明的外部材质库需要搭配 MTLLoader 加载(详见下文"与 MTLLoader 搭配"小节)。

快速上手:两行代码载入模型

按官方文档中的最小示例,使用 Promise 风格的 loadAsync() 即可异步加载 OBJ:

const loader = new OBJLoader();
const object = await loader.loadAsync( 'models/monster.obj' );
scene.add( object );

显式导入 addon

OBJLoader 属于 three.js 的 addon(附加模块),不会打包进核心库,使用前必须显式导入。仓库内所有官方示例都通过 importmap 的 three/addons/ 别名引用,例如 webgl_loader_obj.html 中即:

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

若以裸模块方式开发,也可直接按相对路径引用仓库文件 examples/jsm/loaders/OBJLoader.js

使用仓库自带的真实模型

文档示例中的 models/monster.obj 仅为示意。本仓库 examples/models/obj 目录提供了可直接试用的 OBJ 资源,例如:

male02.obj 为例,文件开头如下(# 为注释行):

# Blender v2.54 (sub 0) OBJ File: ''
mtllib male02.mtl
o mesh1.002_mesh1-geometry
v 4.649472 159.854965 5.793066
...

构造函数与属性

new OBJLoader( manager : LoadingManager )

构造一个新的 OBJ 加载器。manager 为加载管理器,用于跟踪加载进度、处理错误与缓存;可省略——从源码 Loader.js 可见,基类构造函数在 manager 未传入时会默认使用全局的 DefaultLoadingManager

const loader = new OBJLoader();                     // 使用默认 DefaultLoadingManager
const loader = new OBJLoader( myLoadingManager );   // 使用自定义 LoadingManager

.materials : MaterialCreator

材质创建器的引用,默认值为 null(对应源码 OBJLoader.js)。

该属性记录了通过 setMaterials() 注入的 MTLLoader 产物。当它为 null 时,加载器会为几何数据回退生成内置材质(详见下文"默认材质与降级策略")。OBJ 中 usemtl 引用的材质名就是通过它从 MTL 材质库里按名取出的。

三个核心方法详解

OBJLoader 重写了基类 Loader 的三个抽象方法,并额外提供 setMaterials()

.load( url, onLoad, onProgress, onError )

从指定 URL 开始加载,并把解析好的 OBJ 资产传给 onLoad() 回调。参数说明见源码 OBJLoader.js

参数 类型 说明
url string 要加载的文件路径/URL,也支持 data URI
onLoad function 加载完成回调,接收解析产物(Group
onProgress onProgressCallback 加载过程中的进度回调
onError onErrorCallback 出错时的回调

从源码可以看到底层实现细节:

const loader = new FileLoader( this.manager );
loader.setPath( this.path );
loader.setRequestHeader( this.requestHeader );
loader.setWithCredentials( this.withCredentials );
loader.load( url, function ( text ) {
    try {
        onLoad( scope.parse( text ) );
    } catch ( e ) {
        if ( onError ) { onError( e ); }
        else { console.error( e ); }
        scope.manager.itemError( url );
    }
}, onProgress, onError );

即:load() 内部委托 FileLoader 以文本方式拉取资源,并把基类 Loader 上设置的 pathrequestHeaderwithCredentials 等配置透传给 FileLoader;拿到文本后调用 parse(text),若解析抛错会调用 onError,同时通过 manager.itemError(url) 通知 LoadingManager 记录一次失败。因此路径相关配置(如 setPath)必须在 load() 之前设置才生效。

.parse( text : string ) : Group

直接解析给定的 OBJ 文本字符串并返回一个 Group。它非常适合在你自己已拿到 OBJ 文本的场景(如从 IndexedDB、自定义网络层、粘贴板读取)跳过 HTTP 请求直接使用。源码实现位于 OBJLoader.js

返回值 Group 上的说明:

  • 每个 o/g 命名的子对象会被构建为 Group 下独立的 Mesh(或 LineSegments/Points),其 mesh.name 取 OBJ 中的对象/组名;
  • 若 OBJ 声明了 mtllib,返回的 container.materialLibraries 数组(Group.materialLibraries)会记录这些材质库文件名,便于调用方据此去加载对应的 MTL(见源码 OBJLoader.js)。
const group = loader.parse( objTextContent );
scene.add( group );
console.log( group.children );        // 每个 o/g 一个 Mesh/LineSegments/Points
console.log( group.materialLibraries ); // ['male02.mtl', ...]

.setMaterials( materials : MaterialCreator ) : OBJLoader

设置 OBJ 使用的材质创建器(通常由 MTLLoader 加载 .mtl 后得到),并返回 loader 自身以支持链式调用。它只是将参数存入 .materials 属性(见源码 OBJLoader.js):

loader.setMaterials( materials ); // materials 来自 MTLLoader 的 loadAsync

该方法在解析阶段起到关键作用:源码 OBJLoader.js 中,每个 usemtl 引用的材质名都会通过 this.materials.create( sourceMaterial.name ) 从 MTL 材质库中实例化真正的材质对象。

继承自 Loader 的链式配置方法

OBJLoader 继承 Loader,因此以下常用配置方法(定义见 Loader.js)同样可用,且全部返回 this 便于链式调用:

方法 作用
setPath( path ) 设置资源的基础路径,load() 时拼接在 url 前
setResourcePath( resourcePath ) 设置依赖资源(如纹理)的基础路径
setCrossOrigin( crossOrigin ) 跨域加载策略,默认 'anonymous'
setWithCredentials( value ) HTTP 请求是否携带凭证,默认 false
setRequestHeader( requestHeader ) 为 HTTP 请求附加请求头
loadAsync( url, onProgress ) Promise 风格的异步加载(基类统一实现,见 Loader.js
const loader = new OBJLoader()
    .setPath( 'models/obj/male02/' )   // 关键:load 之前设置
    .setMaterials( materials );
const obj = await loader.loadAsync( 'male02.obj' );

源码级解析:ParserState 与支持的 OBJ 语法

parse() 的核心是一个自建的状态机 ParserState(见源码 OBJLoader.js)。它逐行读取文本,先统一 \r\n\n、合并 \\\n 续行符,再按首字符分发解析:

OBJ 关键字 含义 OBJLoader 处理方式
v x y z 顶点位置 收集进 state.vertices
v x y z r g b 带顶点色的顶点 额外解析 RGB 并存入 state.colors,颜色按 SRGBColorSpace 处理(见 OBJLoader.js);无颜色时补占位符保证索引对齐
vt u v 纹理坐标 收集进 state.uvs
vn x y z 顶点法线 收集进 state.normals
f ... 多边形面 支持 vv/vtv/vt/vnv//vn 四种索引写法,并将多边形三角化为 v0-vj-vj+1 的三角形扇
l ... 折线 构建为 LineSegments 几何(geometry.type = 'Line'
p ... 构建为 Points 几何(geometry.type = 'Points'
o name / g name 对象/分组声明 调用 state.startObject(name),为后续几何生成独立节点与材质分组边界
usemtl name 切换当前材质 记录材质名,并据此划分几何的 material group 区间
mtllib file.mtl 引用外部材质库 仅记录到 materialLibraries 数组
s 0/off/正整数 平滑组 s 0s off 关闭平滑(产生 flat 着色),其余开启
usemap name 旧式贴图引用 不解析,输出警告提示纹理必须在 MTL 中定义(见 OBJLoader.js
# 注释 跳过
其他行 非空且非 \0 时输出 Unexpected line 警告

几个值得注意的解析细节

  1. 缺失法线时自动计算:若 f 行未提供 vn 索引,addFaceNormal()OBJLoader.js)会用三条边的叉积估算面法线;缺失 UV 时则用 (0,0) 占位(addDefaultUV),保证几何属性索引一致。
  2. 负索引支持:OBJ 规范允许以负整数相对引用"最近已声明的数据",parseVertexIndex 等函数(OBJLoader.js)对负值做了 index + len/3 的换算。
  3. 平滑组决定 flatShading:无 MTL 时生成默认材质会读取 s 声明的平滑状态——material.flatShading = sourceMaterial.smooth ? false : trueOBJLoader.js)。
  4. 多材质网格的 group 划分:当一个 o/g 内先后出现多个 usemtl,解析器记录每个材质段的 groupStart/groupEnd/groupCountstartMaterial_finalize,见 OBJLoader.js),最后在生成 Mesh 时通过 buffergeometry.addGroup( start, count, materialIndex ) 创建多材质子网格(OBJLoader.js)。
  5. 点云判定:如果文件中只有 v 顶点而没有 o/g/f 等图元声明,解析器会把整个文件解释为点云,产出 PointsOBJLoader.js)。
  6. 材质继承:OBJ 规范要求 usemtl 声明的材质对之后所有对象生效直至再次声明。startObject 会克隆"上一个对象"的当前材质作为继承材质(标记 inherited: true),既保证材质延续,又允许后续 usemtl 正确覆盖(见注释 OBJLoader.js)。

默认材质与降级策略

.materials === null(即未调用 setMaterials)或 MTL 中找不到对应材质名时,加载器按图元类型回退创建内置材质(OBJLoader.js):

图元类型 回退材质
普通网格(f) MeshPhongMaterial
折线(l) LineBasicMaterial
点(p)/点云 PointsMaterial({ size, sizeAttenuation: false })

如果注入了 MTL 材质但图元是线/点,加载器也会尝试把 MTL 材质属性"拷贝"到 LineBasicMaterialPointsMaterial 上以保证颜色正确(OBJLoader.js)。

实战:OBJ + MTL 完整加载管线

纯 OBJLoader 只能得到无贴图的几何体。仓库官方示例 webgl_loader_obj.html 演示了标准的 MTL + OBJ 两步加载流程,将其结构提炼如下:

import * as THREE from 'three';
import { MTLLoader } from 'three/addons/loaders/MTLLoader.js';
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';

// 1. 先加载 MTL 材质库并 preload
const mtlLoader = new MTLLoader().setPath( 'models/obj/male02/' );
const materials = await mtlLoader.loadAsync( 'male02.mtl' );
materials.preload();

// 2. 创建 OBJLoader,绑定材质,加载 OBJ
const objLoader = new OBJLoader().setPath( 'models/obj/male02/' );
objLoader.setMaterials( materials );   // 无 MTL 时也可省略,将回退到默认材质

const object = await objLoader.loadAsync( 'male02.obj' );
scene.add( object );

配套说明:

  • setPath 保持一致:OBJ 内的 mtllib male02.mtl 只给出文件名,实际路径靠 loader 的 path 解析,因此两个 loader 的路径前缀应一致指向同一模型目录;
  • materials.preload():MTLLoader 返回的 MaterialCreator 支持 preload(),用于提前加载材质所引用的纹理图片,避免模型出现后纹理"边用边加载"的闪烁;
  • 坐标系与单位:OBJ 文件不带单位与坐标系语义,加载后可能需要自行 position/scale/rotation 调整(示例中即做了 object.scale.setScalar( 0.01 ),因为该模型导出自 iClone,单位为厘米);
  • 顶点色变体:若 OBJ 顶点行带 r g b(如 female02_vertex_colors.obj),解析出的几何会自动带 color attribute,对应材质会开启 vertexColors = true(见 OBJLoader.js),可直接用 MeshBasicMaterial/MeshPhongMaterial 渲染出顶点色效果。

运行完整示例可以直接在浏览器打开 examples/webgl_loader_obj.html(需通过本地 HTTP 服务器访问,以保证模块加载与文件请求正常)。

常见问题与注意事项

  • 路径相对仓库根目录:文档示例中的 models/monster.obj 需配合 setPath 修正,仓库内模型统一位于 examples/models/obj 下。
  • 必须显式导入:OBJLoader 是 addon,不会随 three 核心自动导出,务必按 three/addons/loaders/OBJLoader.js 导入;若沿用旧版直接依赖 three/examples/jsm 路径也可正常工作。
  • load()loadAsync() 二选一:前者用回调,后者返回 Promise(内部即回调封装,见 Loader.js),不要混用同一 loader 的两种风格;onProgress 只反映文本文件本身的下载进度,不代表 MTL/纹理的进度。
  • 材质名大小写敏感usemtl NameA 与 MTL 中的 newmtl NameA 必须完全一致,setMaterials 按名精确查找。
  • 面索引三种写法均可f vf v/vtf v/vt/vnf v//vn 均被支持;只有 v/vt(缺法线)时加载器会自动补面法线,视觉上可能出现面片感,建议优先使用含法线的导出设置。
  • 兼容性提示:OBJ 是 1980 年代的文本格式,不支持骨骼动画、PBR 材质参数完整语义等现代特性,若目标资源为 glTF,可优先考虑 GLTFLoader;OBJLoader 适合需要轻量、可读、生态兼容性极广的几何数据的场景。

相关资源索引

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

项目优选

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