找回密码
 立即注册
查看: 284|回复: 0

Unreal4.27 High Quality Reflection在手机上的问题

[复制链接]
发表于 2022-4-28 08:23 | 显示全部楼层 |阅读模式
问题描述

Unreal4.27中材质开启High Quality Reflection后在手机上效果有问题,跟PC编辑器效果不一致,测试机型是小米11pro,不同机型问题的表现不一样,iphone12还没有问题。
测试场景有一个静态模型,用的引擎自带的地板SM_Template_Map_Floor,缩放为6,也就是长宽是60米,用的Chrome材质并开启了High Quality Reflection,还有三个静态点光源,光源强度为1000lm,三个Sphere Reflection Capture,一个静态Sky Light,Reflection Capture跟点光源的位置只有高度不同,点光源高度是500,Reflection Capture高度是200,效果如下:


手机上的效果就有问题了(在华为Mate40 pro上为黑屏):


不过Box Reflection Capture在手机上和编辑器中的效果是一致的:





4.25中Sphere Reflection Capture也没问题:




问题分析

Unreal中Sky Light和Reflection Capture都是通过Cubemap提供的光照,Sky Light提供无限远处的环境光,Reflection Capture提供局部的环境光。
不开启HQ Reflection和Box Reflection Capture都没有问题,所以应该从Box和Sphere、HQ Reflection开启和关闭之间的不同入手。

  • Reflection Capture的形状用来控制场景中的哪些物体会被Capture到Cubemap,哪些物体会使用这个Cubemap,以及矫正后反射向量的计算方法。
  • 开启HQ Reflection会使用矫正后的反射向量,不开启HQ Reflection不会矫正。
综上所述,嫌疑最大的就是反射向量的矫正。
反射向量矫正

对比4.25和4.27的Shader代码:




最大的不同是4.25是在世界坐标系计算,4.27是在局部坐标系计算,4.27还对计算进行了简化,按理说这不应该导致编辑器和手机上效果不一致啊,但是把4.25的代码Copy过去,效果竟然一致了。
emmm,找到这次提交的日志,是整理了一下代码、提升了clear coat,按理说不应该整理出来bug呀:


突然灵光一闪,PC和手机的区别其中一点就是手机上有半精度,而PC没有,半精度的问题之前也遇到过很多[1],再仔细看4.25和4.27的代码,发现4.25用的float,4.27用的half。然后把4.27相关的计算改成float,效果就一致了,这样就确定了是半精度导致的计算问题。
问题解决

简单的解决方法就是把反射向量的矫正改成全精度,但是这会导致更大的开销,更好的解决办法是查出哪里计算有问题,然后避免这个问题,由于Shader调试的环境有问题,所以用了比较笨的方法,用RenderDoc修改Shader来查问题。
第一步先验证一下之前猜测的原因,把float的默认精度从mediump改成highp,结果就正确了:


验证了是计算精度的问题,第二步就是找出来哪一步计算出了问题,我用的方法就是把相关的变量改成highp,然后一个一个改成mediump,哪个改成mediump出了问题,就是哪里计算出了问题,最后查出来是这两个变量需要是highp:


这里的v94是Shader中的QuadraticCoef,二次函数的系数,v96是Shader中的LocalPosition,是当前渲染的像素在Sphere Capture坐标系中的位置,所以这个问题就出在dot(LocalPosition, LocalPosition)这里,计算结果超出了half能表示的最大值,好像也没什么好办法避免,所以最终的解决方案是提高这三个变量的精度:


进一步可以得出结论,当渲染的像素距离影响它的Sphere Reflection Capture中心很远时(很远也没有多远),就会出现计算错误。
<hr/>Update:后来发现当Reflection Capture半径大了后,还是有问题,所以还是全部改成了float。
<hr/>反射向量矫正的原理

解决完了问题顺便记录一下反射向量矫正的计算方法。只有当Cubemap提供无限远处的光照时,才可以直接使用反射向量采样,如Unreal的Sky Light:


当Cubemap提供局部的光照时,直接使用反射向量采样就会有问题,比如在一个反光的地板上走的时候,从相同的角度会一直看到相同的反射,这是因为反射向量一直没变,而真实世界中反射不仅取决于观察角度,还取决于观察位置:


解决方法就是把每个Cubemap跟一个虚拟的几何体绑定,根据这个几何体对反射向量进行矫正再采样,结果如下:


虚拟的几何体有两种:Sphere和Box,对应Unreal的两种Reflection Capture,这个几何体决定了场景中哪些物体会被Capture到Cubemap,哪些物体会使用这个Cubemap,以及矫正反射向量的计算方法。Sphere几何体在类似球形的模型上有更好的效果,整体的效果比较统一,但是在大的平面上距离Cubemap的中心点越远,误差越大;Box适用于矩形的房间或走廊,在box的边缘误差较大。
如下图,计算出反射向量R跟几何体的交点,矫正后的反射向量就是Cubemap中心C到交点P,这两种计算方法就只是计算交点的方式不同:


可以这样理解,当这个反光的平面在Cubemap的中心时,此时用反射向量是准确的,当逐渐把平面往下移动时,看到的反射的点也应该往下移动,到了上图中的位置,反射的点应该是P,所以要用CP采用Cubemap。
Ray-Sphere Intersection

射线和球的交点有两种求法,第一种是几何的方法:


只要求出了  和  就能知道 ,所以步骤如下:

  • 求出
  • 根据 、  求出
  • 根据 、  求出
第二种是代数的方法,射线方程:


球的方程:


把射线方程代入到球的方程,解出t来,就能求出交点:


Unreal就是用的这种方法,代码中的QuadraticCoef就是二次方程的系数,Determinant就是二次方程的判别式
Ray-Box Intersection

射线和Box求交点的方法叫做slab method,把一个Box看成三个对面(两个平行的平面)的交集。
关键思想,光线进入所有对面才算进入Box,光线只要离开一个对面就算离开Box,求出进入时间 和离开时间 ,如果  ,那就说明光线在Box里待了一段时间,又因为考虑的是射线,不是直线,所以 时也没有交点,所以相交的条件就是:

&&
那如何计算射线跟其中一个slab的交点呢?轴对齐的Box计算简单,Unreal中的Box可以旋转,不过是在Box的局部坐标系计算的。比如考虑跟yz平面平行的这个对面,随着t的变化,它们的x是不变的,只需要x分量就能求出来跟这个对面的交点:


射线方程是:


这个slab的方程是:


所以:


这样还可以利用SIMD同时计算出跟x、y、z三个slab的交点,要是求跟任意一个平面的交点,计算就复杂多了。
Reference

[1] https://zhuanlan.zhihu.com/p/266223117
[2] https://developer.arm.com/documentation/102179/latest/
[3] https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-sphere-intersection
[4] https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-box-intersection
[5] https://www.bilibili.com/video/BV1X7411F744?p=13

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-22 13:28 , Processed in 0.092117 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表