固定视角地牢爬行游戏,RealityKit,滚动走廊行。截图左半部分是修改前,右半部分是修改后。相同种子,相同走廊,只改变了光照。(绿色六边形是调试叠加层,忽略它们。)
RealityKit不提供雾效和半球光。我搜索了框架以确保自己没有遗漏什么。两者都必须从头构建,在此过程中我发现场景被某种我未知的东西照亮了。
关键的完整性检查
我将场景中所有光源强度设为零,预期全黑。但走廊仍以sRGB亮度约131渲染。比我的目标亮度更亮,而所有光源都已关闭。
RealityKit用明亮默认的基于图像的环境光来照亮每个RealityView。它几乎完成了所有光照工作。我构建的显式光源系统在其基础上几乎没有贡献。
这解释了场景设置中积累的一系列奇怪常量:一个50勒克斯的主光源,一个将纹理压到8%的反照率色调。两者都在补偿一个没人意识到已开启的光源。
解决方案是提供自己的EnvironmentResource,在运行时从一个很小的等距柱状CGImage生成,以替换默认环境。
没有雾效API的雾效
先前的方法是在固定深度放置8个面向相机的四边形。每个四边形都进行前后深度测试,因此在平面与地面交叉处会产生可见的接缝。走廊看起来像带状而非隧道。
我测量了带状在y=444/544/656/783/927处的硬台阶,然后计算这些四边形应该投影到的位置:443.0/543.1/655.4/782.1/926.3。误差在一个像素内,这让我在重写之前就确认了诊断结果,令人满意。
将所有八个四边形替换为一个以相机为父级的渐变四边形。相机固定,因此屏幕高度单调映射到走廊深度。每个纹素的不透明度通过投射该行的视线、与地面相交,并在落点行处评估雾曲线得到。平滑渐变,一次绘制调用代替八次,透明覆盖大大减少。
取舍:现在雾是屏幕高度的函数,而非真正的逐片元深度,因此高几何体会略微提前雾化。在相机下只有一行误差,所以我接受它。
伪造半球光:先是糟糕,后是正确
也没有半球光。第一次尝试是用一个向下的DirectionalLight代替天空瓣。
结果在整个地面上铺了一层光泽。点光源产生集中的GGX瓣,而地面粗糙度为0.45,看起来像抛光石头而非地牢。
环境光/IBL贡献不会这样。其镜面是宽而均匀的覆盖,没有热点。正确的原语是基于图像的光:一个从天空到地面的渐变,以(cos θ + 1)/2为坡。完全消除了光泽,而且它更接近真正的半球。
光单位
方向光用勒克斯(RealityKit默认是1000)。点光源用流明,以lm / 4πd²衰减。
将坎德拉式点光源强度通过4π转换,在实践中过冲约1.9倍。RealityKit没有文档说明其衰减窗口化,显然不是简单的带有范围截止的反平方。我最终采用经验标定因子,使火炬落在之前的手动调整值几个百分点内,这个值在我“纠正”之前就已经存在。
阴影
完全没有阴影。给主光源添加了DirectionalLightComponent.Shadow。
我不得不将主光源移动到走廊中心线上才能生效。任何横向偏移都会使光线偏离YZ平面,并将墙壁阴影投射到可玩通道上。在中心线上,墙壁只能沿自身线投射阴影。
值得注意:阴影没有改变整体亮度。地面中位数亮度在添加前后相同。它们只拉低了最暗的十分之一。提供的是接地感和深度感,而非曝光。
火炬
每次运行开始,一侧的墙壁细节不可见。火炬位置按块交替,而块零总是在同一侧,所以一面墙被照亮,另一面是平的。
我希望屏幕上始终有3个火炬。可见视窗为12行,这强制周期为4。保证屏幕上的数量与随机间隔在数学上不兼容,因此旧的抖动必须取消。现在两面墙附近总是有光,两侧的石材纹理都可见。
地面颜色
baseColor.tint接受sRGB的UIColor,RealityKit在相乘前进行线性化。因此0.55的色调实际上是0.21的线性乘数。旧值0.32实际上是0.084,意味着只有8%的纹理反照率通过。
将照亮地面的sRGB亮度从88降到39,这是你在截图中看到的大部分氛围变化。
另外要知道:色调仅缩放漫反射。电介质的镜面F0保持约0.04,因此原则上重度色调会使表面看起来相对更亮。
方法
这些很少是凭感觉决定的。我从帧中读取像素并比较数值。
通过启动环境变量固定运行种子是我做的最好的事,因为A/B截图随后显示相同的走廊,只有测试的变量不同。我还通过构建粗糙度强制为1.0的变体并将其与正式版本进行差分,直接测量了镜面贡献。
我搞错的事
可能这部分最有用。
- 假设光泽是材质问题。不是。材质参数一直没变。是光源类型。
- 预测在较暗色调下镜面会占地面的20-25%,并会带回来光泽。测量得到:3.5%。色调映射在较低水平也压缩,所以比例几乎没有移动。这个警告毫无根据。
- 假设重新启用已禁用的场景图会导致屏幕切换时的卡顿。直接测试,372ms vs 383ms。没有变化。抛弃了这个理论。
乐意对其中任何部分提供更多细节。
评论 (0)