[工作积累] UE4 TAA ReProjection的精度处理
2017-07-21 23:11
330 查看
先贴一个UE4 TAA的slide
https://de45xmedrsdbp.cloudfront.net/Resources/files/TemporalAA_small-59732822.pdf
里面细节问题很多,先记录一下目前想到和遇到的问题,便于备忘,后面有空的话再记录。
TAA用到的Velocity和抖动对精度要求比较高, 特别是大场景下误差容易比较大,UE4做了一系列的处理来保持精度。
投影矩阵的精度
可以看到对投影矩阵的取反做了特殊处理,来提高浮点计算精度。
视图矩阵的精度
将视图矩阵的位移和旋转拆开来(T*R),以避免大地图上,相机位置过大造成的误差。
Reprojection是把当前帧Clip space或NDC space的点重新投影到上一帧的位置
Reprojection = (V*P)-1 * (PrevV*PrevP)
= P-1 * V-1 * PrevV * PrevP
其中V为视图矩阵,P为投影矩阵。
而UE4的视图矩阵实际使用TR的方式 (类似变换矩阵的TRS和RTS的顺序,区别是试图矩阵没有缩放, https://docs.microsoft.com/en-us/dotnet/framework/winforms/advanced/why-transformation-order-is-significant
注意如果是相同的T和R, T*R和R*T结果是不一样的,这里的TR,是在ViewMatrix结果一样的前提上,拆解出的另外两个矩阵,从几何分析的角度也可以得出)
Reprojection = P-1 * (T*R)-1 * (PrevT*PrevR) * prevP
= P-1 * R-1 * T-1 * PrevT * PrevR * PrevP
= P-1 * R-1 * (T-1 * PrevT) * PrevR * PrevP
其中:
Reprojection是UE4代码里的ViewUniformShaderParameters.ClipToPrevClip,
R是ViewMatrix的旋转部分(UE4代码中的GetTranslatedViewMatrix),
T是ViewMatrix的位移部分,
T-1*PrevT 就是UE4代码中的 FTranslationMatrix(DeltaTranslation) 。
这样做的好处是,把两个绝对位置转换为一个相对位移, 从而避免绝对位置过大而导致的矩阵Inverse的精度问题。
同时,视图矩阵的旋转部分 R 是正交矩阵,所以Transpose等价于Inverse,这样不仅是效率的提高,更重要的是避免了Inverse导致的浮点误差。
VelocityBuffer的精度
VelocityBuffer在TAA里用来reproject移动的物体,包括蒙皮动画和位移旋转动画等。
为了提高精度,VelocityBuffer可以使用Float32。Unity使用的是Float16x2 (R16G16F), 不同的是,UE4使用的是INT16x2 (R16G16_INT)。
对于Float16,最小精度是2-10。 如果映射到Int16,去掉等价的符号位,精度是2-15。 这样在占用同样显存大小的情况下,提高了精度。
https://de45xmedrsdbp.cloudfront.net/Resources/files/TemporalAA_small-59732822.pdf
里面细节问题很多,先记录一下目前想到和遇到的问题,便于备忘,后面有空的话再记录。
TAA用到的Velocity和抖动对精度要求比较高, 特别是大场景下误差容易比较大,UE4做了一系列的处理来保持精度。
投影矩阵的精度
static const FMatrix InvertProjectionMatrix( const FMatrix& M ) { if( M.M[1][0] == 0.0f && M.M[3][0] == 0.0f && M.M[0][1] == 0.0f && M.M[3][1] == 0.0f && M.M[0][2] == 0.0f && M.M[1][2] == 0.0f && M.M[0][3] == 0.0f && M.M[1][3] == 0.0f && M.M[2][3] == 1.0f && M.M[3][3] == 0.0f ) { // Solve the common case directly with very high precision. /* M = | a | 0 | 0 | 0 | | 0 | b | 0 | 0 | | s | t | c | 1 | | 0 | 0 | d | 0 | */ double a = M.M[0][0]; double b = M.M[1][1]; double c = M.M[2][2]; double d = M.M[3][2]; double s = M.M[2][0]; double t = M.M[2][1]; return FMatrix( FPlane( 1.0 / a, 0.0f, 0.0f, 0.0f ), FPlane( 0.0f, 1.0 / b, 0.0f, 0.0f ), FPlane( 0.0f, 0.0f, 0.0f, 1.0 / d ), FPlane( -s/a, -t/b, 1.0f, -c/d ) ); } else { return M.Inverse(); } }
可以看到对投影矩阵的取反做了特殊处理,来提高浮点计算精度。
视图矩阵的精度
FVector DeltaTranslation = InPrevViewMatrices.GetPreViewTranslation() - InViewMatrices.GetPreViewTranslation(); FMatrix InvViewProj = InViewMatrices.ComputeInvProjectionNoAAMatrix() * InViewMatrices.GetTranslatedViewMatrix().GetTransposed(); FMatrix PrevViewProj = FTranslationMatrix(DeltaTranslation) * InPrevViewMatrices.GetTranslatedViewMatrix() * InPrevViewMatrices.ComputeProjectionNoAAMatrix(); ViewUniformShaderParameters.ClipToPrevClip = InvViewProj * PrevViewProj;
将视图矩阵的位移和旋转拆开来(T*R),以避免大地图上,相机位置过大造成的误差。
Reprojection是把当前帧Clip space或NDC space的点重新投影到上一帧的位置
Reprojection = (V*P)-1 * (PrevV*PrevP)
= P-1 * V-1 * PrevV * PrevP
其中V为视图矩阵,P为投影矩阵。
而UE4的视图矩阵实际使用TR的方式 (类似变换矩阵的TRS和RTS的顺序,区别是试图矩阵没有缩放, https://docs.microsoft.com/en-us/dotnet/framework/winforms/advanced/why-transformation-order-is-significant
注意如果是相同的T和R, T*R和R*T结果是不一样的,这里的TR,是在ViewMatrix结果一样的前提上,拆解出的另外两个矩阵,从几何分析的角度也可以得出)
Reprojection = P-1 * (T*R)-1 * (PrevT*PrevR) * prevP
= P-1 * R-1 * T-1 * PrevT * PrevR * PrevP
= P-1 * R-1 * (T-1 * PrevT) * PrevR * PrevP
其中:
Reprojection是UE4代码里的ViewUniformShaderParameters.ClipToPrevClip,
R是ViewMatrix的旋转部分(UE4代码中的GetTranslatedViewMatrix),
T是ViewMatrix的位移部分,
T-1*PrevT 就是UE4代码中的 FTranslationMatrix(DeltaTranslation) 。
这样做的好处是,把两个绝对位置转换为一个相对位移, 从而避免绝对位置过大而导致的矩阵Inverse的精度问题。
同时,视图矩阵的旋转部分 R 是正交矩阵,所以Transpose等价于Inverse,这样不仅是效率的提高,更重要的是避免了Inverse导致的浮点误差。
VelocityBuffer的精度
// for velocity rendering, motionblur and temporal AA // velocity needs to support -2..2 screen space range for x and y // texture is 16bit 0..1 range per channel float2 EncodeVelocityToTexture(float2 In) { // 0.499f is a value smaller than 0.5f to avoid using the full range to use the clear color (0,0) as special value // 0.5f to allow for a range of -2..2 instead of -1..1 for really fast motions for temporal AA return In * (0.499f * 0.5f) + 32767.0f / 65535.0f; } // see EncodeVelocityToTexture() float2 DecodeVelocityFromTexture(float2 In) { const float InvDiv = 1.0f / (0.499f * 0.5f); // reference // return (In - 32767.0f / 65535.0f ) / (0.499f * 0.5f); // MAD layout to help compiler return In * InvDiv - 32767.0f / 65535.0f * InvDiv; }
VelocityBuffer在TAA里用来reproject移动的物体,包括蒙皮动画和位移旋转动画等。
为了提高精度,VelocityBuffer可以使用Float32。Unity使用的是Float16x2 (R16G16F), 不同的是,UE4使用的是INT16x2 (R16G16_INT)。
对于Float16,最小精度是2-10。 如果映射到Int16,去掉等价的符号位,精度是2-15。 这样在占用同样显存大小的情况下,提高了精度。
相关文章推荐
- [工作积累] UE4 并行渲染的同步 - Sync between FParallelCommandListSet & FRHICommandListImmediate calls
- [工作积累] Tricks with UE4 PerInstanceRandom
- 工作中积累的日期、数字处理方法
- JS--处理浮点数运算精度失真
- HTML 捕获window.close() 并做窗口关闭前的处理工作
- Spark集群工作异常,无法读取Hadoop集群文件处理办法
- CAD二次开发 命令行和菜单最好只处理本文件的工作
- 跨平台使用Intrinsic函数范例1——使用SSE、AVX指令集 处理 单精度浮点数组求和(支持vc、gcc,兼容Windows、Linux、Mac)
- MATLAB数据精度处理
- 【酷熊科技】工作积累 ----------- unity 动态设置 Animator组件的Controller
- JavaScript异步函数处理工作机制(setTimeout, setInterval)
- 点滴积累--工作总结
- WPF中UI及时更新,如何在处理长时间工作时,保持界面的持续更新
- fastjson 处理 double 的精度问题
- CC2541 BLE源码阅读知识积累之外设从机Peripheral工作模式
- java中小数的处理:高精度运算用bigDecimal类,精度保留方法,即舍入方式的指定
- 我所积累的一些常见dos处理
- 【Uva1639】概率 + 期望 + 对数处理精度
- 工作中遇到异常处理的问题
- 批处理设置当前路径为工作路径