2、资源类型与分类:几何体、纹理、音频、模型文件(glTF/OBJ)、着色器、字体等
做3D网页开发,说白了就是在跟各种资源打交道。你想想看,一个3D场景里,有模型、有贴图、有声音、有特效代码……这些东西怎么管?怎么让它们乖乖听话?
我个人习惯,先把资源分门别类。就像收拾工具箱,螺丝刀放一起,扳手放一起。这样用起来才顺手。今天咱们就聊聊,3D网页里常见的资源类型,以及它们各自的特点。
几何体(Geometry)
几何体是3D世界的「骨架」。一个立方体、一个球体、一个复杂的角色模型,底层都是几何体数据。
在Three.js里,几何体本质上是顶点坐标、索引、法线、UV这些数据的集合。我刚开始学的时候,以为几何体就是「形状」,后来才明白,它其实就是一堆数字。
核心要点:几何体只描述「形状」,不描述「外观」。外观是材质和纹理的事。
常见的几何体类型:
- 基础几何体:BoxGeometry、SphereGeometry、CylinderGeometry。这些是引擎内置的,直接new就行。
- 自定义几何体:BufferGeometry。你可以手动塞顶点数据进去,做任何你想要的形状。
- 复杂几何体:从模型文件里加载进来的。比如一个角色模型,它的几何体可能包含几万个顶点。
我的经验:做移动端项目时,几何体的顶点数要严格控制。我曾经把一个高精度的角色模型直接丢到手机上,结果帧率掉到个位数。后来学乖了,能用BoxGeometry绝不用SphereGeometry,能用低模绝不用高模。
纹理(Texture)
纹理就是贴图。它给几何体穿上「衣服」。没有纹理的3D场景,就像没上色的石膏像,灰蒙蒙的。
纹理的类型其实挺多的:
- 颜色贴图(Map):最常用的,定义物体表面颜色。
- 法线贴图(Normal Map):模拟凹凸细节,不增加顶点数。我特别喜欢用这个,性能开销小,效果却很明显。
- 粗糙度/金属度贴图(Roughness/Metalness):PBR材质必备,控制表面质感。
- 环境贴图(Environment Map):做反射效果用的,比如金属球反射周围环境。
- 透明度贴图(Alpha Map):控制哪些区域透明,比如树叶、栅栏。
纹理的尺寸有讲究。我建议用2的幂次方尺寸:256、512、1024、2048。为什么?因为GPU处理这些尺寸效率最高。我曾经用过一个1024x768的纹理,结果渲染时被GPU强行拉伸成1024x1024,画面糊得一塌糊涂。
避坑指南:纹理文件不要太大。一张4K纹理可能占十几MB,加载慢不说,还容易把显存撑爆。我一般控制在2K以内,除非是展示型项目。
音频(Audio)
音频在3D网页里经常被忽略,但它对沉浸感的影响非常大。你想想,一个3D游戏里没有脚步声、没有环境音,那得多假。
音频资源主要分两类:
- 背景音乐:通常是长音频,循环播放。格式推荐MP3或AAC,压缩率高。
- 音效:短音频,触发式播放。比如点击按钮、碰撞、拾取物品。格式推荐OGG或WAV。
在Three.js里,音频需要配合AudioListener和PositionalAudio来做3D音效。说白了就是声音有位置、有距离衰减。你靠近声源,声音变大;远离,声音变小。
我的习惯:音频文件一定要预加载。我曾经在游戏里放了一个「开门」音效,结果玩家走到门前才加载,延迟了半秒才响。体验非常糟糕。后来我改成在加载页面就把所有音效提前解码好。
模型文件(glTF / OBJ)
这是3D开发里最核心的资源类型。模型文件里包含了几何体、材质、纹理、动画、骨骼……几乎一切。
目前最主流的格式是glTF。为什么?因为它是为Web端设计的,天生就适合在浏览器里跑。glTF文件通常很小,加载快,而且支持PBR材质和骨骼动画。
OBJ格式比较老,但依然常见。它只存几何体和UV,材质需要额外的MTL文件。我一般只在处理老旧资源时才用OBJ。
| 格式 | 优点 | 缺点 | 我的推荐 |
|---|---|---|---|
| glTF | 体积小、加载快、支持动画和PBR | 工具链相对较新 | ★★★★★ |
| OBJ | 兼容性好、几乎所有3D软件都支持 | 文件大、不支持动画、材质分离 | ★★★☆☆ |
| FBX | 功能全面、支持动画和骨骼 | 文件巨大、Web端支持差 | ★★☆☆☆ |
| Collada | 开源标准 | 文件冗余多、解析慢 | ★☆☆☆☆ |
核心建议:能用glTF就别用别的。如果设计师给你的是FBX或OBJ,让他导出成glTF再给你。省下来的加载时间,用户会感谢你的。
着色器(Shader)
着色器是运行在GPU上的小程序。它控制着每个顶点和每个像素怎么渲染。说白了,你看到的颜色、光影、特效,都是着色器算出来的。
着色器分两种:
- 顶点着色器(Vertex Shader):处理每个顶点的位置、法线。比如做波浪效果,就是在顶点着色器里修改顶点的Y坐标。
- 片元着色器(Fragment Shader):处理每个像素的颜色。比如做渐变、发光、马赛克效果。
着色器代码通常用GLSL(OpenGL Shading Language)写。在Three.js里,你可以用ShaderMaterial来自定义着色器。
// 一个最简单的自定义着色器示例
const material = new THREE.ShaderMaterial({
vertexShader: `
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 纯红色
}
`
});
避坑指南:着色器代码一旦写错,整个场景可能都渲染不出来。我曾经在片元着色器里写了个死循环,结果浏览器直接崩溃。调试着色器时,建议先用简单的颜色输出,确认没问题了再加复杂逻辑。
字体(Font)
3D场景里也需要文字。比如游戏里的得分、UI上的按钮文字、3D标题。
字体资源在3D里比较特殊。它不能直接用系统字体,因为3D渲染需要把文字转换成几何体或纹理。
常见的做法有两种:
- TextGeometry:把文字转换成3D几何体。需要加载字体文件(通常是JSON格式的字体数据)。优点是文字可以旋转、缩放、打光。缺点是字体文件比较大,而且中文支持不好。
- Canvas纹理:用Canvas把文字画出来,然后作为纹理贴到平面上。优点是灵活,支持任何字体和语言。缺点是不能做3D立体效果。
我个人更推荐Canvas纹理的方式,尤其是做中文项目时。因为TextGeometry对中文的支持实在太差了,很多字体文件里根本没有中文字形。
我的经验:如果非要用3D文字,可以考虑用图片代替。把文字在Photoshop里做成PNG,然后贴到平面上。虽然不够「3D」,但胜在稳定、兼容性好。
资源管理的核心思路
说了这么多类型,其实核心就一句话:资源要统一管理,统一加载。
我习惯用一个资源管理器(Resource Manager)来统筹所有资源。它负责:
- 记录每个资源的加载状态(未加载、加载中、已加载、加载失败)
- 控制加载顺序(先加载关键资源,后加载次要资源)
- 提供进度回调(让加载页面显示百分比)
- 缓存已加载的资源(避免重复加载)
Three.js自带了LoadingManager,但说实话,功能比较基础。我一般会自己封装一个,加上重试机制、超时处理、资源依赖管理这些功能。
总结一下:几何体是骨架,纹理是皮肤,音频是声音,模型文件是打包好的「角色包」,着色器是特效师,字体是文字工具。把它们分清楚,管理好,你的3D项目就成功了一半。
下一章,咱们聊聊具体的加载策略和进度条实现。嗯,那才是真正考验耐心的地方。