我的3D和Three.js学习入门(3)
3D 零基础入门 · 第 3 章:材质、灯光、相机——模型是怎么"被看见"的
前两章我们有了三角形的形状(Mesh)和摆放位置(TRS)。
但你把一个纯白模型丢进场景,往往是——黑的。
这一章回答:物体为什么有颜色?为什么"没光就看不见"?相机又是怎么看世界的?
3.1 三个角色的分工
一次 3D 渲染,就像一次拍照片:
| 角色 | 3D 里的对应 | 类比 |
|---|---|---|
| 被拍的物体 | 模型(Mesh) | 模特 |
| 打在物体上的光 | 灯光(Light) | 摄影棚的灯 |
| 记录画面的机器 | 相机(Camera) | 摄像机 |
在 Three.js 里,没有灯 = 全黑;没有相机 = 渲染不出画面。这三样缺一不可。
3.2 材质(Material):表面看起来像什么
材质决定了模型的表面怎么反射光。同一束光照在皮肤、金属、布上,效果完全不一样。
最常用的两种
| 材质 | 特点 | 适合 |
|---|---|---|
MeshBasicMaterial |
不理会光照,永远显示自己颜色(像自发光贴纸) | 调试、UI 元素 |
MeshStandardMaterial |
真的会"吃光",有粗糙度 roughness 和金属度 metalness 两个旋钮 | 绝大多数真实物体 |
粗糙度 = 表面打磨得多糙:
0像镜子,1像磨砂玻璃。
金属度 = 看起来像不像金属:0是塑料/皮肤,1是抛光钢。
再高级一点:贴图(Texture)
颜色不一定是一个色值,可以是一张图片"贴"在模型表面——像给白色模型贴彩纸。这张图片叫贴图(map / texture)。
我们项目的核心机密之一:一张大贴图(atlas)上面画了所有的衣服碎片,每件装备从里面"剪"一块自己用。 这叫图集(atlas),第 6 章和第 15 章会展开。
看一下 viz3d.js 第 187 行的 matFor——它的 9 种材质分支,就是游戏里的 9 种"材质类型":
case "skin": // 皮肤:贴 skin 图集,颜色可由用户调
return new THREE.MeshStandardMaterial({ map: loadTexture(ATLAS.skin), color: new THREE.Color(skinColor), roughness: 0.88 });
case "hair": // 头发:纯色 + 略糙
return new THREE.MeshStandardMaterial({ color: new THREE.Color(hairColor), roughness: 0.5 });
case "fantasy-dyable": // 可染色衣服:最讲究的一个
return new THREE.MeshPhysicalMaterial({ map: null, color: new THREE.Color(dye || 0xffffff), roughness: 0.55, sheen: 0.5, ... });
注意
skin和hair的颜色是个变量——这就是"换肤/换发色不重载模型、直接改颜色"的原理(第 6 章演示)。
3.3 灯光(Light):没光=看不见
Three.js 里最常用的三种灯:
| 灯光 | 效果 | 类比 |
|---|---|---|
AmbientLight 环境光 |
均匀地把整个场景照亮一点点,没有方向感 | 阴天的天光 |
DirectionalLight 方向光 |
从某个方向打来平行光(模拟太阳),有明暗面,最能看出立体感 | 晴天太阳 |
PointLight 点光源 |
一个点向四周发光(灯泡),有衰减 | 台灯 |
渲染立体感的最大功臣就是方向光——它让模型的每个面亮度不一样,立体感(3D 感)就出来了。全用环境光,模型会显得"平"。
看我们项目的灯光配置(viz3d.js:708-710),就是"天光 + 两盏太阳"的经典组合:
scene.add(new THREE.HemisphereLight(0xdbeafe, 0x1b1f30, 1.8)); // 环境天光
const key = new THREE.DirectionalLight(0xffffff, 2.4); // 主光(正面偏右上方)
key.position.set(2.5, 4, 3.5);
const fill = new THREE.DirectionalLight(0xb6c7ff, 1.0); // 补光(背面偏左)
fill.position.set(-3, 2, -2);
一个"场景灯光工程师"的思路:主光负责造型,补光负责别让阴影死黑,环境光垫底。
3.4 相机(Camera):从哪个角度看世界
相机也是一件"物体"——它有自己的 position 和 rotation。Three.js 默认的是透视相机 PerspectiveCamera:近大远小,和人眼一样。
标准写法:
const camera = new THREE.PerspectiveCamera( 视场角, 宽/高, 近裁面, 远裁面 );
camera.position.set(0, 1, 3.6); // 相机站哪
| 参数 | 意思 | 类比 |
|---|---|---|
| 视场角 FOV | 能看到多宽(度数) | 广角 vs 长焦 |
| 宽高比 | 宽÷高,否则画面变形 | 横屏 vs 竖屏 |
| 近/远裁面 | 只渲染这个范围内的物体 | 眼睛对焦距离范围 |
我们项目相机是
PerspectiveCamera(28, 1, 0.05, 100),28° 是比较"长焦"的视角,人物看起来不那么变形(viz3d.js:702)。
配套的 OrbitControls(轨道控制)让你用鼠标拖拽=绕人物转圈看,缩放=走近走远——相机一直在动,人物不动。
3.5 三者合起来:渲染一帧发生了什么
每一帧(每秒 60 帧,约 16ms 一次),浏览器做这件事:
- 告诉显卡:我要画哪些三角形(Mesh 的几何)
- 每个三角形,算它表面颜色 = 材质 × 灯光(光从哪来、表面怎么反射)
- 从相机位置看过去,把可见的三角形画到屏幕上(看不见的被"裁剪"掉)
类比:一排排打灯的工作室里,摄影师每按 60 次快门就拼成 1 秒电影。
3.6 动手做:把三个旋钮玩一圈
[!demo] demos/demo03-lighting.html | 材质 · 灯光 · 相机
切换材质类型、拖动粗糙度 / 金属度 / 主灯角度 / 亮度四个旋钮,亲眼验证"没光=全黑"、粗糙度 0 像镜子、金属度 1 变铁。
页面上有一个旋转的球,右侧控制面板可以调:
- 材质类型:切换
基础材质 / 标准材质 - 粗糙度:从 0(镜面反射亮斑)拖到 1(哑光磨砂)——观察反光变化
- 金属度:从 0 到 1——观察球慢慢变"铁"的质感
- 旋转主灯:拖动滑块让方向光绕圈——观察明暗面跟着动,立体感随之变化
- 灯光亮度:拉到 0 试试——见证"没光=全黑"
把这几个旋钮玩一圈,你就能看懂任何 Three.js 场景(包括我们项目的)里"为什么这样打光、为什么皮肤是这个质感"了。
3.7 本章小结
- 渲染三件套:模型 + 灯光 + 相机,缺一不可
- 材质决定表面反射光的方式:
MeshStandardMaterial有粗糙度/金属度两个核心旋钮;贴图 = 图片包在模型上 - 灯光:环境光垫底 + 方向光造立体感(明暗面 = 3D 感)
- 相机:透视相机近大远小,配 OrbitControls 用鼠标转
- 我们项目的材质 9 种 kind(
viz3d.js:192)和灯光配置(viz3d.js:708)你现在能看懂了
下一章 → 我们把"内存里的模型"落成"磁盘上的文件":GLTF / GLB 结构。
配套 demo:
demos/demo03-lighting.html
相关源码位置:viz3d.js:187matFor()(9 种材质)、viz3d.js:698-710(相机 + 灯光)。
- 点赞
- 收藏
- 关注作者
评论(0)