光照是一个你可以添加的列表和一个你可以配置的天空。两者每帧装配进 一个所有通道都读取的单一缓冲区,所以阴影、环境光遮蔽、全局光照和 天空本身不可能对太阳的位置各执一词。
BaseApp.Lighting 是一个
SceneLighting 实例:一个
LightSource 对象列表加上一个常量环境项。对象出现时添加光源,
对象离开时移除它们 — 没有逐帧重建可做。
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, // radians
OuterConeAngle = 0.55f,
CastShadows = true,
Priority = 10
});
Kind 是
Directional、Point 或
Spot。IsOpen 默认为 true,所以
关掉一个光源会让它留在列表里而退出烘焙 — 比移除再重新添加更便宜,
也更不容易出错。
SceneLightParams.MaxLights 是 8,而且这是一个硬性的 GPU
限制:uniform 缓冲区有八个槽。当场景里有更多时,烘焙按
Priority 排序并取前八个,平局时按到相机的距离
裁决。不会有警告、异常或变暗。如果一个光源重要,给它高优先级。
每帧只有一个点状光源得到阴影贴图,无论其他光源上的
CastShadows 怎么说。一个自带
KHR_lights_punctual 光源的 glTF 文件会自己追加它们,所以导入的
室内场景可能吃掉整个预算 — 见
模型与动画。
三个太阳级联和一个点状槽共享一个方形深度图集。
RenderQuality 上有十四个阴影旋钮;实际上你只会调两个。
var q = RenderQuality.Current;
q.ShadowDistance = 220f; // how far the sun casts; default 40
q.ShadowSoftnessTexels = 2f; // Vogel-disk radius, in shadow texels
调高 ShadowDistance 会把同一个图集摊到更大的
世界上,所以阴影纹素变大、接触点变软。如果你扩展距离后阴影变糊,
调高 ShadowAtlasSize — 但那个是初始化
值,所以它属于应用构造函数,而不是设置界面的滑块。
粉刺和彼得潘问题由
ShadowNormalOffset(默认 1.5 纹素)处理,而不是把深度
推离光源,这就是为什么深度偏差可以保持很小。完整表格在
光照与阴影。
开启程序化天空后,太阳和月亮的方向来自大气模型,
而不是你放置的光源。Atmosphere 是一个静态的
物理参数类;首先值得了解的是天体。
| 成员 | 默认 | 含义 |
|---|---|---|
SunDirection、MoonDirection | 上、下 | 有昼夜循环运行时由它驱动。 |
SunIrradiance | 12 | 到达地面的太阳强度。 |
MoonIrradiance | 0.6 | 月亮强度。刻意不为零,让夜晚可以通行。 |
NightAirglow | 0.004 | 微弱的自发光,让无月之夜不至于纯黑。 |
StarRadiance | 0.15 | 星星亮度;StarRotation 和 StarPoleAxis 决定星场朝向。 |
Clouds | SkyState.Clear | 云层集合。见下文。 |
ViewAltitudeKm | 0.2 | 相机在大气中所处的位置,它会改变地平线。 |
Rayleigh 和 Mie 系数、地面和大气半径以及臭氧参数也都在那里,设为地球值。 它们是日落看起来像日落、而无需任何手工渐变的原因, 它们也是你应该一次只改一个的原因。
云是一个 SkyState:一组带覆盖度、高度、
厚度和反照率的层。有四个预设,而有用的操作不是挑一个,
而是在两个之间混合。
// A preset.
Atmosphere.Clouds = SkyState.Overcast;
// Or a weather curve: t moves the sky continuously from fair to stormy.
Atmosphere.Clouds = SkyState.Lerp(SkyState.Fair, SkyState.Storm, t);
默认是 SkyState.Clear,它有零个层,所以
从不提及云的应用不为它们付出任何代价。逐层的风偏移在
Atmosphere.CloudWindOffsetKm 里,这就是一个层
漂移而无需重新生成噪声场的方式。
WorldSettings 拥有时钟,
DayNightCycle 做换算。速度和起始小时遵循
与渲染质量相同的双面形态:供应用构造函数使用的静态
Default* 字段、可经由设置往返的实例属性,
以及作为运行时读取的 Current。
// In the app constructor. At the default speed of 0.01 a day takes 100 seconds.
WorldSettings.DefaultDayNightSpeed = 0.004f;
WorldSettings.DefaultStartHour = 5.5f;
// Anywhere afterwards:
float phase = DayNightCycle.PhaseFromHour(18f); // dusk
float hour = DayNightCycle.HourFromPhase(phase);
模型是一个十二小时白天的春分简化,所以
SunriseHour 是 6,正午落在 12,午夜落在 0,没有修正。
月亮按自己的 SynodicDays 周期 29.53 运行,
所以它随游戏内的月份盈亏,而不是每天重复。
渲染质量覆盖其余的旋钮,包括 启动时读取一次的值与每帧都可以写入的值之间的区别 — 这是这套 API 最常见的一个早期错误。