第五篇:灯光系统:光与影的魔法
引言
灯光是3D场景的灵魂,它赋予物体体积感、空间感和情绪氛围。Three.js提供了完整的物理光照系统,从基础的环境光到复杂的IES聚光灯,本文将深入解析四大核心光源类型,并通过Vue3实现动态灯光控制系统,带你掌握3D光影艺术的科学原理。
1. 光照基础:物理渲染的核心
1.1 光照模型原理
1.2 光照计算要素
要素 | 描述 | 数学表示 |
---|---|---|
入射光 | 光源发出的光线 | LiL_iLi |
表面法线 | 垂直于表面的向量 | nnn |
观察方向 | 相机到表面的向量 | vvv |
材质属性 | 反射/散射特性 | BRDF函数 |
2. 四大核心光源详解
2.1 环境光(AmbientLight)
<script setup>
import * as THREE from 'three';
// 创建环境光(均匀照亮所有表面)
const ambientLight = new THREE.AmbientLight(
0x404040, // 光线颜色
1.0 // 光照强度
);
scene.add(ambientLight);
</script>
特性:
- 无方向性
- 不产生阴影
- 用于模拟间接光照
2.2 平行光(DirectionalLight)
const directionalLight = new THREE.DirectionalLight(
0xffffff, // 颜色
0.8 // 强度
);
// 设置光源位置(位置决定方向)
directionalLight.position.set(5, 10, 7.5);
// 启用阴影
directionalLight.castShadow = true;
// 阴影质量配置
directionalLight.shadow.mapSize.width = 2048;
directionalLight.shadow.mapSize.height = 2048;
directionalLight.shadow.camera.near = 0.5;
directionalLight.shadow.camera.far = 50;
适用场景:太阳光、室外场景全局光照
2.3 点光源(PointLight)
const pointLight = new THREE.PointLight(
0xff0000, // 红色光源
1.0, // 强度
10, // 照射距离
0.5 // 衰减系数
);
pointLight.position.set(0, 3, 0);
pointLight.castShadow = true;
// 添加光源辅助器
const helper = new THREE.PointLightHelper(pointLight);
scene.add(helper);
衰减公式:
$ intensity / (distance^2) $
2.4 聚光灯(SpotLight)
const spotLight = new THREE.SpotLight(
0x00ff00, // 颜色
1.0, // 强度
20, // 距离
Math.PI/4, // 照射角度(弧度)
0.25, // 边缘模糊度
0.5 // 衰减系数
);
spotLight.position.set(0, 10, 0);
spotLight.target.position.set(0, 0, 0); // 照射目标
scene.add(spotLight.target);
// 聚光灯光锥可视化
const spotHelper = new THREE.SpotLightHelper(spotLight);
scene.add(spotHelper);
3. 阴影系统:真实感的关键
3.1 阴影实现流程
3.2 阴影优化配置
// 渲染器设置
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 柔和阴影
// 物体设置
object.castShadow = true; // 投射阴影
ground.receiveShadow = true; // 接收阴影
// 光源阴影参数
light.shadow.bias = -0.001; // 避免阴影剥离
light.shadow.normalBias = 0.05; // 解决阴影痤疮
light.shadow.radius = 2; // 阴影模糊半径
3.3 阴影贴图分辨率
分辨率 | 质量 | 性能影响 | 适用场景 |
---|---|---|---|
512x512 | 低 | ★☆☆☆☆ | 移动端简单场景 |
1024x1024 | 中 | ★★☆☆☆ | 桌面端一般场景 |
2048x2048 | 高 | ★★★☆☆ | 主光源阴影 |
4096x4096 | 超高 | ★★★★☆ | 电影级画质 |
4. 高级光照技术
4.1 HDR环境光照
<script setup>
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
onMounted(async () => {
// 加载HDR环境贴图
const loader = new RGBELoader();
const hdrTexture = await loader.loadAsync('environments/industrial_sunset.hdr');
// 设置场景环境贴图
hdrTexture.mapping = THREE.EquirectangularReflectionMapping;
scene.environment = hdrTexture;
scene.background = hdrTexture;
// 启用IBL(基于图像的照明)
material.envMap = hdrTexture;
material.envMapIntensity = 1.5;
});
</script>
4.2 光照烘焙技术
// 生成光照贴图
const bakeTexture = new THREE.TextureLoader().load('baked/baked_lightmap.jpg');
bakeTexture.flipY = false;
// 应用烘焙材质
const bakedMaterial = new THREE.MeshBasicMaterial({
map: bakeTexture,
lightMap: bakeTexture,
lightMapIntensity: 2.0
});
// 静态物体使用烘焙材质
staticObjects.forEach(obj => {
obj.material = bakedMaterial;
obj.receiveShadow = false; // 不需要实时阴影
});
4.3 IES灯光配置文件
import { IESLoader } from 'three/addons/loaders/IESLoader.js';
const iesLoader = new IESLoader();
iesLoader.load('ies/spotlight.ies', (iesTexture) => {
const spotLight = new THREE.SpotLight(0xffffff, 1);
spotLight.iesMap = iesTexture; // 应用IES配置文件
scene.add(spotLight);
});
5. Vue3实战:动态光源控制系统
5.1 项目结构
src/
├── components/
│ ├── LightController.vue // 光源控制面板
│ ├── LightVisualizer.vue // 光源可视化
│ └── ShadowDebugger.vue // 阴影调试器
└── App.vue
5.2 光源控制核心代码
<!-- LightController.vue -->
<script setup>
import { ref, reactive, watch } from 'vue';
// 光源类型选项
const LIGHT_TYPES = {
AMBIENT: 'AmbientLight',
DIRECTIONAL: 'DirectionalLight',
POINT: 'PointLight',
SPOT: 'SpotLight'
};
// 光源参数响应式对象
const lightState = reactive({
type: LIGHT_TYPES.DIRECTIONAL,
color: '#ffffff',
intensity: 1,
distance: 10,
angle: 45,
penumbra: 0.5,
castShadow: true,
position: { x: 5, y: 10, z: 7.5 }
});
// 创建光源实例
const createLight = () => {
const color = new THREE.Color(lightState.color);
switch(lightState.type) {
case LIGHT_TYPES.AMBIENT:
return new THREE.AmbientLight(color, lightState.intensity);
case LIGHT_TYPES.DIRECTIONAL:
const dl = new THREE.DirectionalLight(color, lightState.intensity);
dl.position.set(lightState.position.x, lightState.position.y, lightState.position.z);
dl.castShadow = lightState.castShadow;
return dl;
case LIGHT_TYPES.POINT:
const pl = new THREE.PointLight(
color,
lightState.intensity,
lightState.distance
);
pl.position.set(lightState.position.x, lightState.position.y, lightState.position.z);
pl.castShadow = lightState.castShadow;
return pl;
case LIGHT_TYPES.SPOT:
const sl = new THREE.SpotLight(
color,
lightState.intensity,
lightState.distance,
THREE.MathUtils.degToRad(lightState.angle),
lightState.penumbra
);
sl.position.set(lightState.position.x, lightState.position.y, lightState.position.z);
sl.castShadow = lightState.castShadow;
return sl;
}
};
// 更新场景光源
watch(lightState, () => {
emit('update', createLight());
}, { deep: true });
</script>
<template>
<div class="light-controller">
<select v-model="lightState.type">
<option v-for="(type, key) in LIGHT_TYPES" :value="type">{{ key }}</option>
</select>
<div v-if="lightState.type !== LIGHT_TYPES.AMBIENT">
<label>位置X: {{ lightState.position.x }}</label>
<input type="range" v-model.number="lightState.position.x" min="-20" max="20" step="0.1">
<!-- Y/Z位置控制 -->
</div>
<div>
<label>颜色</label>
<input type="color" v-model="lightState.color">
</div>
<div>
<label>强度: {{ lightState.intensity }}</label>
<input type="range" v-model.number="lightState.intensity" min="0" max="5" step="0.1">
</div>
<div v-if="lightState.type === LIGHT_TYPES.SPOT">
<label>角度: {{ lightState.angle }}°</label>
<input type="range" v-model.number="lightState.angle" min="1" max="90" step="1">
</div>
<div>
<label>
<input type="checkbox" v-model="lightState.castShadow">
投射阴影
</label>
</div>
</div>
</template>
5.3 光源可视化组件
<!-- LightVisualizer.vue -->
<script setup>
import { onMounted, watch } from 'vue';
import * as THREE from 'three';
const props = defineProps(['light']);
// 创建光源辅助器
const createHelper = (light) => {
switch(light.type) {
case 'DirectionalLight':
return new THREE.DirectionalLightHelper(light, 1);
case 'PointLight':
return new THREE.PointLightHelper(light, 0.5);
case 'SpotLight':
return new THREE.SpotLightHelper(light);
default:
return null;
}
};
onMounted(() => {
if (props.light) {
const helper = createHelper(props.light);
if (helper) scene.add(helper);
}
});
watch(() => props.light, (newLight) => {
// 移除旧辅助器
scene.children.filter(c => c.isLightHelper).forEach(h => scene.remove(h));
// 添加新辅助器
if (newLight) {
const helper = createHelper(newLight);
if (helper) {
helper.name = 'light-helper';
scene.add(helper);
}
}
});
</script>
5.4 阴影调试组件
<!-- ShadowDebugger.vue -->
<script setup>
import { ref, onMounted } from 'vue';
import * as THREE from 'three';
const props = defineProps(['light']);
const debugCamera = ref(null);
onMounted(() => {
if (props.light && props.light.shadow) {
// 创建阴影相机可视化
const helper = new THREE.CameraHelper(props.light.shadow.camera);
scene.add(helper);
// 创建阴影贴图调试材质
const debugMaterial = new THREE.MeshBasicMaterial({
map: props.light.shadow.map.texture,
side: THREE.DoubleSide
});
const debugPlane = new THREE.Mesh(
new THREE.PlaneGeometry(2, 2),
debugMaterial
);
debugPlane.position.set(0, 0, -5);
debugCamera.value.add(debugPlane);
}
});
</script>
<template>
<!-- 小窗口显示阴影贴图 -->
<div class="debug-window">
<PerspectiveCamera ref="debugCamera" position="0,0,5" />
</div>
</template>
6. 光照性能优化
6.1 光源数量限制
6.2 光源优先级排序
// 根据重要性排序
scene.lights.sort((a, b) => {
if (a.isDirectionalLight) return -1; // 平行光优先
if (b.isDirectionalLight) return 1;
return b.intensity - a.intensity; // 高强度优先
});
// 仅前N个光源生效
renderer.maxLights = 4;
6.3 动态光照烘焙
// 实时生成光照贴图
const rt = new THREE.WebGLRenderTarget(1024, 1024);
const bakeScene = new THREE.Scene();
// 添加静态物体和光源
staticObjects.forEach(obj => bakeScene.add(obj.clone()));
bakeScene.add(light);
// 烘焙渲染
renderer.setRenderTarget(rt);
renderer.render(bakeScene, bakingCamera);
renderer.setRenderTarget(null);
// 应用烘焙结果
const bakedTexture = rt.texture;
material.lightMap = bakedTexture;
7. 常见问题解答
Q1:为什么阴影边缘有锯齿?
- 提高阴影贴图分辨率
light.shadow.mapSize.set(2048, 2048)
- 启用PCF软阴影
renderer.shadowMap.type = THREE.PCFSoftShadowMap
- 增加模糊半径
light.shadow.radius = 3
Q2:点光源阴影性能消耗大怎么办?
- 减少点光源阴影使用
- 降低阴影贴图分辨率
- 使用距离衰减限制影响范围
- 静态场景改用光照烘焙
Q3:如何实现软阴影效果?
// 方法1:PCF软阴影
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// 方法2:VSM阴影(更柔和但需要更多内存)
renderer.shadowMap.type = THREE.VSMShadowMap;
light.shadow.radius = 3;
// 方法3:自定义Shader后处理
8. 总结
通过本文,你已掌握:
- 四大核心光源的特性与配置技巧
- 阴影系统的实现原理与优化策略
- 高级光照技术:HDR环境光、光照烘焙、IES配置
- Vue3实现动态光源控制系统
- 光照性能优化与实时烘焙技术
核心原理:Three.js的光照系统基于物理渲染(PBR)模型,通过模拟光线与物体表面的交互(反射、散射、吸收),结合阴影贴图技术,实现了真实感的光影效果。
下一篇预告
第六篇:坐标系与变换:3D空间操作指南
你将学习:
- 3D坐标系原理(世界/局部/视图坐标系)
- 位置/旋转/缩放的数学基础
- 四元数(Quaternion)与欧拉角(Euler)的转换
- 矩阵变换(Matrix4)的底层原理
- 万向节锁(Gimbal Lock)问题与解决方案
- Vue3实现3D物体变换编辑器