一个方向性太阳、通过
KHR_lights_punctual 导入的点光源,以及来自天空本身的
环境光照。光照状态每帧在
Rendering/SceneLighting.cs 中组装一次,所有通道从
同一个缓冲区读取,因此阴影、GI 和天空不可能在太阳的位置上产生分歧。
SceneLighting 持有一个普通的
List<LightSource> 外加一个恒定环境光项。每帧一次
Bake 把该列表转换成每个通道都读取的 GPU 结构。
引擎中没有任何其他东西保留自己的光照状态副本。
Lighting.Ambient = new Vector4(0.12f, 0.13f, 0.16f, 1f);
Lighting.Add(new LightSource
{
Kind = LightKind.Spot,
Name = "porch",
Position = new Vector3(24f, 6f, 7.5f),
Direction = Vector3.Normalize(new Vector3(0f, -1f, -0.4f)),
Color = new Vector4(1f, 0.86f, 0.66f, 1f),
Intensity = 40f,
Range = 24f,
InnerConeAngle = 0.25f,
OuterConeAngle = 0.55f,
CastShadows = true,
Priority = 10
});
| 成员 | 说明 |
|---|---|
Kind | Directional、Point 或 Spot。 |
IsOpen | 默认 true。关闭一个光源会把它留在列表里,但排除在烘焙之外。 |
Color, Intensity | 线性颜色和一个标量;强度以引擎单位计,而不是坎德拉。 |
Position, Direction | Direction 默认直朝下。Position 对方向光无效。 |
Range | 点光源和聚光灯的衰减距离。 |
InnerConeAngle, OuterConeAngle | 聚光锥,以弧度计。 |
CastShadows | 请求唯一的点光源阴影槽。每帧只有一个光源能拿到它。 |
Priority | 列表超出 GPU 预算时的决胜依据。 |
SceneLightParams.MaxLights 是 8。这是硬性 GPU 限制,
不是软目标:uniform 缓冲区有八个槽。当场景里更多时,
Bake 按 Priority 排序并取前八个,
用相机位置按距离打破平局。
第九个光源不会警告、抛错或变暗——它只是不存在。如果一个光源重要,
就给它高的 Priority。这是引擎不适合由几十个小型局部
光源照明的场景的主要原因;固定预算下的前向着色正是所做出的取舍。
从 glTF 文件导入的光源通过
Model.ImportedPunctualLights 到达,并由模型自身追加,
这意味着一个携带十二个光源的资产自己就会耗尽全部预算。
Model.LightIntensityScale 和
RenderQuality.KhrLightIntensityScale 的存在就是为了
把它们的度量级量级拉回引擎范围。
三个太阳级联和一个点光源槽共享一张正方形深度图集,每个象限是边长 一半的瓦片。四个槽、一张纹理、一个绑定——这正是阴影通道也是唯一 一个设置自己视口的通道的原因。
| 旋钮 | 含义 | 生效时机 |
|---|---|---|
ShadowsEnabled | 总开关;关闭则完全跳过该通道。 | 运行时 |
ShadowAtlasSize | 图集边长,D32 浮点。默认 2048。 | 初始化 |
ShadowCascadeCount | 级联数量,钳制到 2–3。 | 初始化 |
ShadowDistance | 太阳投影的最远距离,受相机远平面钳制。默认 40。 | 运行时 |
CascadeSplitLambda | 均匀到对数的分割混合,默认 0.6。 | 运行时 |
ShadowNormalOffset | 以阴影纹素计的法线偏移偏差;0 表示禁用。 | 运行时 |
ShadowSoftnessTexels | 八点 Vogel 圆盘的半径,以纹素计。 | 运行时 |
ShadowContactHardening | 在接触点附近收窄滤波。默认关闭。 | 运行时 |
ShadowStrength | 缩放完全阴影化表面的暗度。 | 运行时 |
ShadowCasterHeight | 光视锥在级联上方回拉多远,以免高投影体被裁剪。 | 运行时 |
ShadowDepthBias | 恒定深度偏差,以深度单位计。 | 初始化 |
ShadowSlopeScaledDepthBias | 按斜率缩放的深度偏差。 | 初始化 |
ShadowCulling | 逐象限的光空间投影体剔除。 | 运行时 |
ShadowAtlasReuse | 跳过重新渲染矩阵未变化的象限。 | 运行时 |
两个深度偏差被标记为初始化而非运行时是有原因的:D3D12 和 Vulkan 在
创建管线状态时读取它们,因此启动后再赋值只会改变属性而别无他用。
请通过应用构造函数里的
RenderQuality.DefaultShadowDepthBias 设置它们。
相比之下,法线偏移偏差走 uniform 缓冲区,每帧都可以变化——
这就是它成为首选旋钮的原因。
缓慢移动的太阳是让阴影边缘爬行的经典方式:每一帧级联矩阵都轻微 变化,每一帧深度纹素都落在新位置,眼睛把由此产生的闪烁读作噪点。 有两个机制来抑制它。
ShadowLightAngleStep(默认 0.25)控制的网格上,
因此微小的太阳移动根本不会产生矩阵变化。
ShadowTargetStableFrames 帧内保持逐位一致——
默认八帧。
CascadedShadow 把结果暴露出来供检查:
MatricesStable 告诉你保证当前是否成立,
EffectiveLightAngleStep 是实际生效的步长,
Epoch 在图集内容失效时递增。参考应用把三者都显示
在屏幕上,因为「为什么我的阴影在爬行」可以通过读取它们来回答。
| 成员 | 它告诉你什么 |
|---|---|
MaxCascades / SlotCount | 3 和 4——三个太阳级联加一个聚光槽。 |
CascadeViewProj, SpotViewProj | 当前烘焙进图集的矩阵。 |
CascadeSplits, ActiveCascadeCount | 本帧分割落在哪里。 |
SunActive, SpotActive | 每种阴影当前是否在渲染。 |
CullingActive | 光空间投影体剔除是否运行过。 |
GetAtlasViewport | 某个槽的象限矩形,如果你在调试图集的话。 |
想了解间接光而非直接光,请继续阅读 全局光照与环境光遮蔽。