Three.js 光影魔法:如何单独点亮你的3D模型
在 Three.js 的世界里,光与材质的交相互动创造了我们所见的视觉效果。一个常见的场景是:你已经精心布置了全局的环境光和方向光,整个场景看起来很和谐,但你发现其中某个特定的模型显得有些暗淡。你希望单独把它调亮,又不想影响场景中的其他物体。
这可能是一个需要强调的主角模型、一个交互式 UI 元素,或者一个需要模拟发光效果的物体。
这篇文章将带你深入了解如何实现这一目标,从最直接的方法到其背后的核心渲染原理,让你彻底掌握控制物体亮度的“魔法”。
我们的基础场景
在开始之前,让我们先搭建一个简单的基础场景作为参照。这个场景包含两个完全相同的立方体和两种基本光源:环境光(AmbientLight
)和方向光(DirectionalLight
)。
import * as THREE from 'three';
// 场景、相机、渲染器等基础设置...
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111111);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 2, 5);
const renderer = new THREE.WebGLRenderer({ antialias: true });
// ...
// 1. 全局光照
const ambientLight = new THREE.AmbientLight(0x404040, 1.0); // 提供基础环境亮度
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1.2); // 模拟太阳光
directionalLight.position.set(3, 5, 4);
scene.add(directionalLight);
// 2. 两个对比物体
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 物体A:标准蓝色立方体
const standardMaterial = new THREE.MeshStandardMaterial({ color: 0x0077ff });
const standardCube = new THREE.Mesh(geometry, standardMaterial);
standardCube.position.x = -1.5;
scene.add(standardCube);
// 物体B:我们希望调亮的目标
const targetMaterial = new THREE.MeshStandardMaterial({ color: 0x0077ff });
const targetCube = new THREE.Mesh(geometry, targetMaterial);
targetCube.position.x = 1.5;
scene.add(targetCube);
// 渲染循环...
在这个场景中,两个蓝色立方体看起来一模一样,它们的亮度完全由全局光照决定。现在,我们的任务是让右侧的 targetCube
单独变亮。
核心武器:material.emissive
自发光属性
要单独控制一个物体的亮度,最直接、最高效的方法就是利用材质的 自发光(Emissive) 属性。
你可以把它想象成给物体内部装了一个灯泡。这个“灯泡”发出的光会直接叠加到物体最终的颜色上,并且它不受场景中任何光源的影响。
MeshStandardMaterial
和 MeshBasicMaterial
等多种材质都支持以下两个关键属性:
-
material.emissive
: 一个THREE.Color
对象,定义了物体自身发出的光的颜色。 -
material.emissiveIntensity
: 一个浮点数,定义了自发光的强度,默认为1.0
。
实践:点亮我们的目标
现在,我们来修改 targetMaterial
:
// ...接上文...
// 物体B:我们希望调亮的目标
const targetMaterial = new THREE.MeshStandardMaterial({
color: 0x0077ff, // 物体本身的基础颜色依然是蓝色
emissive: 0xffffff, // 关键!设置一个白色的自发光
emissiveIntensity: 0.6 // 控制自发光的强度,可以随意调整
});
const targetCube = new THREE.Mesh(geometry, targetMaterial);
targetCube.position.x = 1.5;
scene.add(targetCube);
效果立竿见 victimes! 右侧的立方体在保持其蓝色色调的同时,整体亮度得到了显著提升。即使你关闭所有场景光源,它依然会显示为一个有亮度的形状。
深入剖析:自发光颜色如何选择?
一个常见的问题是:emissive
的颜色应该怎么设置?设置成红色会怎么样?
1. 白色自发光 (0xffffff
):纯粹的亮度提升
当你只想让物体变亮而不改变其原有色调时,应该使用白色自发光。
-
原理:白色 (
rgb(1,1,1)
) 包含了所有颜色通道。当它与物体的基础色叠加时,会等比例地提升基础色的R、G、B分量,效果就是纯粹的亮度增加。 - 比喻:就像用一个白色手电筒去照一个有色的物体,物体只会变亮,不会变色。
2. 彩色自发光 (如 0xff0000
):创造发光体与色彩混合
当你希望物体本身发出某种特定颜色的光(如霓虹灯、岩浆),或者想创造特殊的艺术效果时,可以使用彩色自发光。
- 原理:彩色自发光会与物体的基础色发生颜色混合。
- 比喻:用一个红色手电筒去照一个蓝色物体,物体最终会呈现出紫色的色调,因为蓝色表面反射的光与手电筒发出的红光混合了。
原理揭秘:为何光照是乘法,自发光是加法?
你可能会好奇,最终的颜色是如何计算出来的。一个简化的模型可以帮助我们理解:
最终颜色 = (基础颜色 * 场景光照) + (自发光颜色 * 自发光强度)
这里的 乘法 和 加法 是关键,它们分别代表了两种完全不同的物理过程。
光照的本质:调制(Modulation),因此是乘法
一个不发光的物体,它的颜色来自于它对入射光线的反射和吸收。材质的 color
属性定义了它对不同颜色光的“反射率”。
-
过程:入射光颜色
*
材质反射率 = 反射光颜色 -
示例:
- 白光
(1,1,1)
照射到红色材质(1,0,0)
上。 - 反射光 =
(1,1,1) * (1,0,0)
=(1*1, 1*0, 1*0)
=(1,0,0)
,结果是红色。 - 材质的颜色像一个滤光片,调制了入射光,所以这是一个乘法过程。
- 白光
自发光的本质:叠加(Addition),因此是加法
自发光是物体自身产生的能量,它与外部光线无关。它的贡献是直接叠加在物体反射的光之上的。
- 过程:物体反射的光 + 物体自身发出的光 = 最终看到的光
- 示例:黑暗房间里,一张桌子反射了微弱的月光,同时桌上的手机屏幕自身在发光。你眼中看到的手机屏幕亮度,就是月光反射的亮度与屏幕自发光亮度的总和。这是一个纯粹的叠加过程,所以是加法。
理解了这一点,你就掌握了控制 Three.js 中颜色与亮度的核心逻辑。
其他方案与对比
除了 emissive
,还有一些其他方法可以影响单个物体的亮度,它们适用于不同的场景。
方法 | 原理 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
自发光 (Emissive) | 材质自身发光,与光源无关 | 最直接、简单、常用,效果可控 | 可能看起来有点“假”,像霓虹灯,缺乏阴影细节 | UI元素、发光体、魔法效果,或简单粗暴地提亮某个模型 |
调整基础色 (Color) | 改变材质对光的反射率,调成更亮的颜色 | 简单,符合物理直觉 | 完全依赖现有光照,亮度上限受光源限制 | 当你只想让一个物体在同样光照下显得比其他物体更“白”或更“亮”时 |
调整PBR属性 | 改变 roughness (粗糙度) 和 metalness (金属度) |
物理效果真实,能创造出质感差异 | 效果比较微妙,更侧重于质感而非单纯的亮度 | 调整金属、塑料、玻璃等不同材质的视觉表现,低粗糙度会产生更亮的高光 |
专用光源 + 图层 | 添加一个只影响特定物体的光源(如PointLight ) |
效果真实,有光影和方向感 | 设置复杂,增加性能开销 | 精确的光照控制,如台灯只照亮书本,或给主角添加“天使光” |
总结
要在 Three.js 中单独调亮一个模型,使用 material.emissive
是最推荐的首选方案。
- 若要保持原色仅提升亮度,请设置
emissive: 0xffffff
(白色),并用emissiveIntensity
精确调节。 - 若要创造自发光物体或混合色彩,请将
emissive
设置为目标颜色。
理解“光照是乘法,自发光是加法”这一核心原理,将帮助你更自由地创造出丰富而逼真的三维视觉效果。现在,去施展你的光影魔法吧!