UE5 各类型灯光学习

一般规律是:

复制代码
方向光 Directional Light:正交投影
聚光灯 Spot Light:透视投影
点光源 Point Light:透视投影 / cube shadow
矩形光 Rect Light:类似聚光/透视投影的 shadow setup

1、方向光

UE 也会用类似思想,但不一定就是这句 GLSL 代码。

你这句:

复制代码
float bias = max(0.002, 0.01 * (1.0 - dot(normal, LIGHT_DIR)));

意思是:

复制代码
表面越斜着面对光源,bias 越大
表面越正对光源,bias 越小

方向光 shadow 的核心一句话:

复制代码
方向光没有真实位置。
UE 会根据当前相机视锥,给每个 cascade 算一个虚拟 shadow 中心,
然后用方向光方向 + 正交投影生成 shadowmap。

整体流程:

复制代码
主相机视锥
    ↓
按距离切成多个 cascade
    ↓
每个 cascade 算一个视锥切片
    ↓
用切片 8 个角点拟合包围球
    ↓
包围球中心 = 虚拟 shadow 中心
    ↓
包围球半径 = 正交投影范围
    ↓
沿方向光方向渲染 shadowmap

图上看是这样:

复制代码
主相机视锥,俯视图

Camera
  ●
   \ 
    \   Cascade 0       Cascade 1              Cascade 2
     \  近处,高精度      中距离,中精度           远处,低精度
      \-----------|-----------------|----------------------|
       \          |                 |                      |
        \         |                 |                      |
         \--------|-----------------|----------------------|
          Near   Split1            Split2                 Far

每个 cascade 都有自己的 shadowmap。近处覆盖小,所以清晰;远处覆盖大,所以模糊。

单独看一个 cascade:

复制代码
某个 cascade 的视锥切片

             SplitFar
        P4 ------------- P6
          \             /
           \           /
            \    ●    /       ● = CascadeSphere.Center
             \       /
          P0  ----- P2
             SplitNear

UE 会用这个视锥切片的 8 个角点拟合一个包围球:

复制代码
             SplitFar
        P4 ------------- P6
          \     ___     /
           \  /     \  /
            \|   ●   |/       ● = 包围球中心
             \ ___ / 
          P0  ----- P2
             SplitNear

这个 就是 UE 的虚拟 shadow 中心。

源码概念上就是:

复制代码
PreShadowTranslation = -Bounds.Center;
WorldToLight = 根据方向光方向构造的旋转矩阵;
Scales = 1.0f / Bounds.W;

其中:

复制代码
Bounds.Center = 当前 cascade 的虚拟 shadow 中心
Bounds.W      = 包围球半径

然后方向光从自己的方向"看"这个区域:

复制代码
方向光方向
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

        正交 shadow camera 覆盖范围
     +--------------------------------+
     |                                |
     |          cascade 包围球         |
     |             ___                |
     |           /     \              |
     |          |   ●   |             |
     |           \ ___ /              |
     |                                |
     +--------------------------------+

● = shadow 中心
矩形大小 ≈ Bounds.W * 2

重点是:方向光用正交投影,不靠"灯光距离"决定图片大小。

复制代码
透视相机:
离得近大,离得远小。

方向光正交相机:
离多远都一样大。

所以 shadowmap 的画面大小由这个决定:

复制代码
X/Y 范围:Bounds.W,也就是包围球半径
Z 范围:MinSubjectZ / MaxSubjectZ / MinLightW

可以理解成一个盒子:

复制代码
              方向光方向
                 ↓

        +-------------------+
        |                   |
        |   正交投影盒子     |  ← X/Y 决定 shadowmap 覆盖大小
        |                   |
        +-------------------+
             ↑         ↑
           Near       Far       ← Z 决定深度范围

还有一个关键点:视锥切片是锥形 / 梯形,但 shadowmap 是矩形,所以一定会浪费一部分区域。

复制代码
视锥切片:

        +-------------+
         \           /
          \         /
           \       /
            +-----+

正交 shadowmap:

+-----------------------+
|        _______        |
|      /         \      |
|     | +-------+ |     |
|     |  \     /  |     |
|     |   \   /   |     |
|      \___+-+___/      |
+-----------------------+

UE 为什么用包围球,而不是刚好贴合视锥的矩形?

因为稳定。

复制代码
更紧的 AABB:
精度高,但相机转动时容易抖。

包围球:
浪费一点精度,但相机旋转时更稳定。

所以 UE 的取舍是:

复制代码
牺牲一部分 shadowmap texel
换取阴影稳定性

最后,UE 还会做 texel snapping:

复制代码
把 shadow 中心对齐到 shadowmap texel 网格
减少相机移动时的阴影闪烁

最终你可以这样记:

复制代码
方向光 CSM = 多个正交 shadow camera

每个 cascade:
    1. 取主相机视锥的一段
    2. 算 8 个角点
    3. 拟合包围球
    4. 球心作为 shadow 虚拟中心
    5. 半径作为正交投影大小
    6. 沿方向光方向渲染 shadowmap
    7. 做 texel snapping 稳定阴影

总结:

复制代码
相机视锥 slice
      ↓
用球包住这个 slice
      ↓
球心 C
      ↓
方向光确定 LightSpace 的方向
      ↓
用一个沿方向光摆正的正交盒子包住这个球
      ↓
正交投影生成 shadowmap

相机决定要阴影的 cascade 区域,UE 给它套球;方向光决定 LightSpace;再用 LightSpace 下的正交盒子包住这个球,渲染 shadowmap。


点光源 Shadow 怎么生成

传统做法是 cubemap shadow。

可以理解为:

在点光源位置放 6 个 90° 相机,分别朝六个方向拍深度图。

复制代码
+X
-X
+Y
-Y
+Z
-Z

每个 face:

复制代码
ViewOrigin = LightPosition;
ViewDirection = CubeFaceDirection;
FOV = 90°;
Near = small value;
Far = LightRadius;

最后得到的是一张 cubemap shadowmap。


3. Shadowmap 里存什么

点光源 shadowmap 存的是:

从点光源出发,沿某个方向看到的最近遮挡物距离。

复制代码
          P 像素
         /
        /
       █ 遮挡物
      /
     ● PointLight

如果遮挡物比像素更靠近灯,那么像素在阴影里。


Spot Light 可以理解成:点光源 + 只照一个锥形范围

它比点光源简单,因为它不需要 6 张 cubemap,只需要从灯的位置朝一个方向拍 1 张透视 shadowmap。

1. Spotlight 的形状

Spot Light 有位置,也有方向。

复制代码
        SpotLight
           ●
          /|\
         / | \
        /  |  \
       /   |   \
      /____|____\
       光照锥体

它只照这个锥体里面的物体。


2. Spotlight Shadow 怎么生成

把 spotlight 当成一个相机:

复制代码
ViewOrigin = LightPosition;
ViewDirection = LightDirection;
FOV = SpotOuterConeAngle * 2;
Near = small value;
Far = LightRadius;

然后从这个"灯光相机"拍一张深度图。

复制代码
        ● SpotLight
       / \
      /   \
     /     \
    /_______\

这张图就是 spotlight shadowmap

所以 spotlight shadow 是:

1 张透视投影 shadowmap。


3. Shadowmap 里存什么

shadowmap 里存的是:

从 spotlight 出发,在锥体方向里看到的最近遮挡物深度。

复制代码
        ● SpotLight
         \
          \
           █ 遮挡物
            \
             P 像素

如果遮挡物比 P 更靠近灯,那么 P 在阴影里。


RectLight 是什么

RectLight 是一个有宽高的矩形面积光:

复制代码
┌──────────┐
│ RectLight│
└──────────┘
     ↓

它不是一个点,而是一整块面在发光。


2. 对一个像素 P 怎么算贡献

对像素 P 来说,RectLight 的贡献主要看:

复制代码
这个矩形光源在 P 看来占了多大视角

也就是立体角。

复制代码
P 越近、越正对矩形:
    看到的矩形越大
    光照越强

P 越远、越偏离中线:
    看到的矩形越小/越斜
    光照越弱

3. UE 不是逐点采样

UE 不会这样暴力算:

复制代码
for 每个矩形采样点:
    累加对 P 的光照

而是:

复制代码
取矩形四个角
      ↓
从 P 指向四个角
      ↓
normalize 到单位球面
      ↓
形成一个球面四边形
      ↓
用解析近似算整体贡献

一句话:

UE 用四个角描述整个矩形在 P 眼里的范围,而不是在矩形上采很多点。

  1. UE 传统路径更像复用 PointLight shadow

可以理解成:

复制代码
从 RectLight 中心生成局部光源 shadow depth
类似 point light cubemap / one-pass point shadow

也就是近似:

复制代码
6 个 90° face 覆盖周围

不是一张真正的 180° shadowmap

  1. 最终只用 RectLight 正面区域

虽然 shadow 数据可能像 point light 那样准备,但光照时会判断 RectLight 朝向:

复制代码
dot(ToLight, RectLightDirection)

背面区域不受 RectLight 影响,相当于被丢掉。

  1. 所以它有浪费

因为 RectLight 只照正面,但 cubemap-style shadow 会覆盖更多方向。

复制代码
优点:复用 point light shadow 系统,稳定简单
缺点:有性能浪费,不是真正物理面积阴影

核心一句:

RectLight 的光照按矩形面积算,但传统实时 shadow 更像复用 point light 的 cubemap/one-pass shadow;物理上只需要正面半球,工程上常准备更多方向,再在光照阶段只使用正面区域。

相关推荐
New农民工1 小时前
射频芯片学习-dBm概念
学习·射频学习
十月的皮皮4 小时前
C语言学习笔记20260703-牛牛与后缀表达式(逆波兰表达式)
c语言·笔记·学习
通信小呆呆17 天前
当算法有了“五感”:多模态数据融合如何向人体感官协同学习?
人工智能·学习·算法·机器学习·机器人
H__Rick17 天前
自动对焦学习-3
人工智能·学习·计算机视觉
Daisy Lee17 天前
量化学习-第1章-什么是量化金融
学习·金融·datawhale
Alsn8617 天前
等待学习-学习目录:Docker 容器安全攻防
学习·安全·docker
YM52e17 天前
买菜计算器小应用 - HarmonyOS ArkUI 开发实战-PC版本
学习·华为·harmonyos·鸿蒙·鸿蒙系统
小雨下雨的雨17 天前
HarmonyOS ArkUI训练营入门-组件掌握系列-Animation 动画效果实现-PC版本
学习·华为·harmonyos·鸿蒙
cqbzcsq17 天前
CellFlow虚拟细胞论文阅读
论文阅读·人工智能·笔记·学习·生物信息