我的3D和Three.js学习入门(3)

举报
yd_289006361 发表于 2026/09/21 13:09:24 2026/09/21
【摘要】 3D 零基础入门 · 第 3 章:材质、灯光、相机——模型是怎么"被看见"的前两章我们有了三角形的形状(Mesh)和摆放位置(TRS)。但你把一个纯白模型丢进场景,往往是——黑的。这一章回答:物体为什么有颜色?为什么"没光就看不见"?相机又是怎么看世界的? 3.1 三个角色的分工一次 3D 渲染,就像一次拍照片:角色3D 里的对应类比被拍的物体模型(Mesh)模特打在物体上的光灯光(Lig...

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 一次),浏览器做这件事:

  1. 告诉显卡:我要画哪些三角形(Mesh 的几何)
  2. 每个三角形,算它表面颜色 = 材质 × 灯光(光从哪来、表面怎么反射)
  3. 从相机位置看过去,把可见的三角形画到屏幕上(看不见的被"裁剪"掉)

类比:一排排打灯的工作室里,摄影师每按 60 次快门就拼成 1 秒电影。


3.6 动手做:把三个旋钮玩一圈

[!demo] demos/demo03-lighting.html | 材质 · 灯光 · 相机

切换材质类型、拖动粗糙度 / 金属度 / 主灯角度 / 亮度四个旋钮,亲眼验证"没光=全黑"、粗糙度 0 像镜子、金属度 1 变铁。

页面上有一个旋转的球,右侧控制面板可以调:

  1. 材质类型:切换 基础材质 / 标准材质
  2. 粗糙度:从 0(镜面反射亮斑)拖到 1(哑光磨砂)——观察反光变化
  3. 金属度:从 0 到 1——观察球慢慢变"铁"的质感
  4. 旋转主灯:拖动滑块让方向光绕圈——观察明暗面跟着动,立体感随之变化
  5. 灯光亮度:拉到 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:187 matFor()(9 种材质)、viz3d.js:698-710(相机 + 灯光)。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。