3、光源系统:环境光、平行光、点光源、聚光灯,以及阴影的配置与优化
光源这东西,说白了就是3D世界的灵魂。没有光,你场景里再精致的模型也是一团黑。我刚开始做3D项目时,觉得随便打个光就行了,结果渲染出来的画面又平又假,跟纸片似的。后来踩了不少坑,才慢慢摸清每种光源的脾气。
今天咱们就把Three.js里的四种核心光源——环境光、平行光、点光源、聚光灯,一个一个说透。顺便聊聊阴影怎么配、怎么优化。嗯,这部分内容我建议你边看边动手,光看是记不住的。
3.1 环境光:给场景一个基础亮度
环境光,你可以把它理解成「无脑补光」。它没有方向,没有位置,均匀地照亮所有物体表面。说白了,就是防止场景里出现死黑区域。
核心要点:环境光不会产生阴影,也不会产生高光。它只负责提升整体亮度。
// 创建一个环境光,颜色偏暖,强度0.5
const ambientLight = new THREE.AmbientLight(0x404060, 0.5);
scene.add(ambientLight);
我个人习惯把环境光强度控制在0.3到0.6之间。太强了会冲淡其他光源的效果,画面发灰;太弱了暗部细节又出不来。我在项目中遇到过一个问题:环境光设成0.8,结果平行光打出来的阴影几乎看不见了,因为暗部被环境光提得太亮。
小技巧:环境光颜色可以跟主光源互补。比如主光源是暖色(偏黄),环境光就用冷色(偏蓝),这样画面层次感更好。
3.2 平行光:模拟太阳光
平行光,顾名思义,所有光线都是平行的。它模拟的是太阳光——从无限远处射来,方向一致。平行光有位置属性,但实际起作用的是它的方向。
// 创建一个平行光,从右上方向左下方照射
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(10, 20, 5);
scene.add(dirLight);
平行光是最常用的主光源。为什么?因为它能产生清晰的阴影,而且光照均匀。你想想看,太阳底下物体的影子边缘是锐利的,平行光就能做到这一点。
我记得有一次做建筑可视化项目,甲方要求室内有阳光照射的效果。我用了平行光,但一开始阴影边缘太硬,看着不真实。后来加了shadow.camera的调节,才让阴影柔和下来。
注意:平行光的阴影范围由shadow.camera控制。默认范围可能不够大,需要手动调整left、right、top、bottom属性。
3.3 点光源:像灯泡一样发光
点光源从一个点向四面八方均匀发光。灯泡、蜡烛、篝火,都是点光源的典型代表。它的特点是:距离越远,光照越弱(衰减)。
// 创建一个点光源,放在场景中央
const pointLight = new THREE.PointLight(0xffaa00, 1, 50);
pointLight.position.set(0, 5, 0);
scene.add(pointLight);
点光源有三个关键参数:颜色、强度、距离。距离参数控制光照能传播多远,超过这个距离就完全没光了。我个人习惯把距离设成场景尺寸的1.5倍左右,这样边缘会有自然的衰减过渡。
我曾经在做一个游戏场景时,用点光源模拟篝火。一开始没开衰减,结果整个场景亮得像白天,篝火效果全无。后来加了decay参数(默认是2,物理正确),效果才对了。
性能提醒:点光源的阴影计算量比平行光大得多。场景里超过3个点光源带阴影,帧率就会明显下降。我建议最多用2个带阴影的点光源。
3.4 聚光灯:舞台上的追光
聚光灯像手电筒或者舞台追光,光线被限制在一个圆锥体内。它有方向、有角度、有衰减边界。聚光灯的参数比较多,但掌握了核心几个就好。
// 创建一个聚光灯
const spotLight = new THREE.SpotLight(0xffffff, 1);
spotLight.position.set(5, 10, 5);
spotLight.target.position.set(0, 0, 0);
scene.add(spotLight);
scene.add(spotLight.target);
// 设置聚光灯角度和衰减
spotLight.angle = Math.PI / 6; // 30度锥角
spotLight.penumbra = 0.3; // 边缘柔化程度
spotLight.decay = 1; // 衰减速度
聚光灯最常用的场景是:室内射灯、舞台效果、或者需要突出某个物体时。它的penumbra参数特别有意思——控制光照边缘的柔和度。0是硬边,1是完全柔化。我一般设0.2到0.4之间,既有聚光效果,又不至于边缘太生硬。
避坑指南:聚光灯的target必须添加到场景中,否则它不会指向目标物体。我曾经忘了加scene.add(spotLight.target),结果光一直朝下照,排查了半天才发现。
3.5 阴影的配置与优化
阴影是让3D场景「活」起来的关键。没有阴影,物体就像飘在空中。但阴影也是性能杀手,配置不好,帧率直接腰斩。
3.5.1 开启阴影
Three.js里开启阴影需要三步:渲染器开启阴影、光源开启阴影、物体设置投射和接收阴影。
// 1. 渲染器开启阴影
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 柔和阴影
// 2. 光源开启阴影
dirLight.castShadow = true;
// 3. 物体设置
mesh.castShadow = true; // 投射阴影
ground.receiveShadow = true; // 接收阴影
这里要注意,shadowMap.type有几种选择:BasicShadowMap(最快但最粗糙)、PCFShadowMap(柔和一些)、PCFSoftShadowMap(最柔和但最慢)。我一般用PCFShadowMap,平衡性能和效果。
3.5.2 阴影质量与性能优化
阴影质量主要由shadow.mapSize控制。数值越大,阴影越清晰,但性能开销也越大。
| mapSize | 阴影质量 | 适用场景 |
|---|---|---|
| 512x512 | 低 | 移动端、复杂场景 |
| 1024x1024 | 中 | 桌面端通用 |
| 2048x2048 | 高 | 特写镜头、重要物体 |
| 4096x4096 | 极高 | 静态渲染、电影级效果 |
// 设置阴影贴图大小
dirLight.shadow.mapSize.width = 1024;
dirLight.shadow.mapSize.height = 1024;
我个人习惯:场景里主要物体用1024,次要物体用512。如果某个物体是特写焦点,单独给它设2048。别一上来全用4096,除非你电脑是服务器级别的。
3.5.3 阴影相机裁剪
平行光和聚光灯的阴影相机有一个近裁面和远裁面。这个范围如果设得太大,阴影精度会被稀释。我建议把近裁面设得离光源近一点,远裁面刚好覆盖场景即可。
// 调整阴影相机的近远裁面
dirLight.shadow.camera.near = 0.5;
dirLight.shadow.camera.far = 50;
// 调整阴影相机范围(平行光专用)
dirLight.shadow.camera.left = -10;
dirLight.shadow.camera.right = 10;
dirLight.shadow.camera.top = 10;
dirLight.shadow.camera.bottom = -10;
我曾经在做一个大场景时,阴影总是闪烁或者缺失。排查了半天,发现是阴影相机的far设得太大了,导致精度被稀释到几乎看不见。把far从100改到30后,阴影立刻清晰了。
重要:调整阴影相机参数后,记得调用dirLight.shadow.camera.updateProjectionMatrix(),否则修改不会生效。
3.6 四种光源的选择策略
说了这么多,到底什么时候用哪种光源?我总结了一个简单的选择逻辑:
- 环境光:必加,但强度要低。它是场景的「底色」。
- 平行光:室外场景、需要清晰阴影时首选。一个场景最多2个。
- 点光源:室内、局部照明、模拟灯泡/火焰。注意衰减参数。
- 聚光灯:需要定向照明、舞台效果、突出某个物体时用。
实际项目中,我通常用「1个平行光 + 1个环境光」作为基础配置。如果需要局部补光,再加1-2个点光源或聚光灯。记住,光源不是越多越好,每多一个带阴影的光源,性能就下降一截。
最终建议:先调好主光源(平行光或聚光灯),再补环境光,最后用点光源做局部点缀。阴影只给最重要的光源开启。这样既能保证效果,又能控制性能开销。
嗯,光源系统这部分内容就这些。你可能会觉得参数有点多,但用多了就熟了。下次咱们聊材质系统——光打好了,材质不对也是白搭。到时候见。