• [工作积累] UE4 TAA ReProjection的精度处理


    先贴一个UE4 TAA的slide
    https://de45xmedrsdbp.cloudfront.net/Resources/files/TemporalAA_small-59732822.pdf

    里面细节问题很多,先记录一下目前想到和遇到的问题,便于备忘,后面有空的话再记录。

    TAA用到的Velocity和抖动对精度要求比较高, 特别是大场景下误差容易比较大,UE4做了一系列的处理来保持精度。

     投影矩阵的精度

     1     static const FMatrix InvertProjectionMatrix( const FMatrix& M )
     2     {
     3         if( M.M[1][0] == 0.0f &&
     4             M.M[3][0] == 0.0f &&
     5             M.M[0][1] == 0.0f &&
     6             M.M[3][1] == 0.0f &&
     7             M.M[0][2] == 0.0f &&
     8             M.M[1][2] == 0.0f &&
     9             M.M[0][3] == 0.0f &&
    10             M.M[1][3] == 0.0f &&
    11             M.M[2][3] == 1.0f &&
    12             M.M[3][3] == 0.0f )
    13         {
    14             // Solve the common case directly with very high precision.
    15             /*
    16             M = 
    17             | a | 0 | 0 | 0 |
    18             | 0 | b | 0 | 0 |
    19             | s | t | c | 1 |
    20             | 0 | 0 | d | 0 |
    21             */
    22 
    23             double a = M.M[0][0];
    24             double b = M.M[1][1];
    25             double c = M.M[2][2];
    26             double d = M.M[3][2];
    27             double s = M.M[2][0];
    28             double t = M.M[2][1];
    29 
    30             return FMatrix(
    31                 FPlane( 1.0 / a, 0.0f, 0.0f, 0.0f ),
    32                 FPlane( 0.0f, 1.0 / b, 0.0f, 0.0f ),
    33                 FPlane( 0.0f, 0.0f, 0.0f, 1.0 / d ),
    34                 FPlane( -s/a, -t/b, 1.0f, -c/d )
    35             );
    36         }
    37         else
    38         {
    39             return M.Inverse();
    40         }
    41     }

    可以看到对投影矩阵的取反做了特殊处理,来提高浮点计算精度。

    视图矩阵的精度

    1     FVector DeltaTranslation = InPrevViewMatrices.GetPreViewTranslation() - InViewMatrices.GetPreViewTranslation();
    2     FMatrix InvViewProj = InViewMatrices.ComputeInvProjectionNoAAMatrix() * InViewMatrices.GetTranslatedViewMatrix().GetTransposed();
    3     FMatrix PrevViewProj = FTranslationMatrix(DeltaTranslation) * InPrevViewMatrices.GetTranslatedViewMatrix() * InPrevViewMatrices.ComputeProjectionNoAAMatrix();
    4 
    5     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的精度

     1 // for velocity rendering, motionblur and temporal AA
     2 // velocity needs to support -2..2 screen space range for x and y
     3 // texture is 16bit 0..1 range per channel
     4 float2 EncodeVelocityToTexture(float2 In)
     5 {
     6     // 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
     7     // 0.5f to allow for a range of -2..2 instead of -1..1 for really fast motions for temporal AA
     8     return In * (0.499f * 0.5f) + 32767.0f / 65535.0f;
     9 }
    10 // see EncodeVelocityToTexture()
    11 float2 DecodeVelocityFromTexture(float2 In)
    12 {
    13     const float InvDiv = 1.0f / (0.499f * 0.5f);
    14     // reference
    15 //    return (In - 32767.0f / 65535.0f ) / (0.499f * 0.5f);
    16     // MAD layout to help compiler
    17     return In * InvDiv - 32767.0f / 65535.0f * InvDiv;
    18 }

    VelocityBuffer在TAA里用来reproject移动的物体,包括蒙皮动画和位移旋转动画等。

    为了提高精度,VelocityBuffer可以使用Float32。Unity使用的是Float16x2 (R16G16F), 不同的是,UE4使用的是INT16x2 (R16G16_INT)。
    对于Float16,最小精度是2-10。 如果映射到Int16,去掉等价的符号位,精度是2-15。 这样在占用同样显存大小的情况下,提高了精度。

  • 相关阅读:
    学习笔记九
    学习笔记八
    python学习笔记七
    学习笔记七
    仿优酷项目
    orm框架分析——仿优酷项目
    python操作MySQL
    数据库—子查询,视图,事务
    数据库—表查询
    mysql表关系
  • 原文地址:https://www.cnblogs.com/crazii/p/7215298.html
Copyright © 2020-2023  润新知