第五篇:灯光系统:光与影的魔法

第五篇:灯光系统:光与影的魔法

引言

灯光是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 阴影实现流程
RendererLightObjectsMainPass启用阴影投射标记可投射阴影标记可接收阴影从光源视角渲染深度图使用深度图计算阴影RendererLightObjectsMainPass
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 光源数量限制
室内
室外
角色特写
光源数量
场景类型
3-5个光源
1-2个主光源+环境光
2-3个重点光
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:为什么阴影边缘有锯齿?

  1. 提高阴影贴图分辨率 light.shadow.mapSize.set(2048, 2048)
  2. 启用PCF软阴影 renderer.shadowMap.type = THREE.PCFSoftShadowMap
  3. 增加模糊半径 light.shadow.radius = 3

Q2:点光源阴影性能消耗大怎么办?

  1. 减少点光源阴影使用
  2. 降低阴影贴图分辨率
  3. 使用距离衰减限制影响范围
  4. 静态场景改用光照烘焙

Q3:如何实现软阴影效果?

// 方法1:PCF软阴影
renderer.shadowMap.type = THREE.PCFSoftShadowMap;

// 方法2:VSM阴影(更柔和但需要更多内存)
renderer.shadowMap.type = THREE.VSMShadowMap;
light.shadow.radius = 3;

// 方法3:自定义Shader后处理

8. 总结

通过本文,你已掌握:

  1. 四大核心光源的特性与配置技巧
  2. 阴影系统的实现原理与优化策略
  3. 高级光照技术:HDR环境光、光照烘焙、IES配置
  4. Vue3实现动态光源控制系统
  5. 光照性能优化与实时烘焙技术

核心原理:Three.js的光照系统基于物理渲染(PBR)模型,通过模拟光线与物体表面的交互(反射、散射、吸收),结合阴影贴图技术,实现了真实感的光影效果。


下一篇预告

第六篇:坐标系与变换:3D空间操作指南
你将学习:

  • 3D坐标系原理(世界/局部/视图坐标系)
  • 位置/旋转/缩放的数学基础
  • 四元数(Quaternion)与欧拉角(Euler)的转换
  • 矩阵变换(Matrix4)的底层原理
  • 万向节锁(Gimbal Lock)问题与解决方案
  • Vue3实现3D物体变换编辑器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二川bro

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值