通俗讲解光栅渲染中的重投影(Reprojection)——投影矩阵法

通俗讲解光栅渲染中的重投影(Reprojection)——投影矩阵法


背景知识

重投影:具体指将在实时渲染中,将历史帧的渲染结果(主要指像素值)应用到当前帧的渲染中,重投影解决的是怎么将前后帧对应的问题。

所以很多利用历史帧的技术都会用到重投影技术,本文将这类技术称为时间性技术。最经典的是TAA(Temporal AA),时间性反走样技术(游戏中常用的抗锯齿技术)。

最好先了解TAA技术的大致算法流程,再来看重投影技术。

本文主要是比较简单的原理,目的是扫盲。


重投影在历史帧中找的是什么?

历史帧中对当前帧有用的东西,自然是当前帧中也会做渲染的内容(或者说也会显示的内容),说白了,就是找当前帧中的东西在历史帧中是长怎么样的,在哪个像素上。


重投影怎么找历史帧中对应的像素?

(1)先举个最简单的静态场景案例
我现在设定一个空白的场景中只有一个悬空的球体。假如前后两帧中,球体没有发生变换,保持静止不动,然后摄像机也静止不动,意味着没有发生视角变化。——那么当前即使是实时渲染,整个画面看起来也是跟一直静态图片一样。

此时当前帧中的某个像素如 (2, 2),显示的是小球中某个位置点(三维顶点),那么可以轻易知道在上一帧中,小球中同一个位置也是出现在像素点 (2, 2) 中——也就是说,当前帧的像素点 (2, 2),对应历史帧中的像素点 (2, 2)。重投影完毕。


(2)实现重投影的关键媒介
从上文提炼一下,能够将当前帧和历史帧联系起来的关键媒介,其实就是场景中同一个三维顶点,如果当前帧中能够看到的这个三维顶点,在历史帧中也能看得到,那就意味着能够通过重投影找到对应的历史像素点,否则就无法重投影。


(3) 假如摄像机发生变换
假如这个静态场景中,前后帧的摄像机发生了变换,如移动了,旋转了,或者缩放了,那该怎么找, 其实这个例子就增加了一个变化量,也就是摄像机的投影矩阵,关键就是把握住这个摄像机的变化(记录并运用起来)。最直观去讲应该是说对摄像机做个仿射变换,但是我们不关注这个问题,因为真正影响了渲染帧中显示内容的因素,落在摄像头的投影矩阵身上。

投影矩阵是啥东西,不太了解的话建议要去补习一下。简单讲投影矩阵是用来将三维场景中能被摄像机看到的三维顶点,变换为像素坐标。三维顶点经过投影矩阵变换一下,就能知道它在会出现在哪个像素点上。

而投影矩阵是利用的摄像机坐标系去导出来,就是套参数的事。

那我现在抽象一下这个过程

当前像素点 = 当前摄像机投影矩阵 * 三维顶点A

历史像素点 = 历史摄像机投影矩阵 * 同一个三维顶点A

上面两条公式中,只有一个未知量——历史像素点,即我们需要的结果,两条公式可以用三维顶点A联系起来,其余的当前像素点、前后帧的投影矩阵,都是已知的,所以历史像素点可以这样算出来。

具体描述是:
我要找到当前像素点对应的历史像素点,那我通过当前摄像机的投影矩阵,去找出我正在显示的是场景中的三维顶点A,然后我再用历史摄像机的投影矩阵,去算一下三维顶点A在历史帧中出现在了哪个像素点上,该像素点就是我要找到的对应历史像素点。假如算出来的像素点超过了像素范围,说明当前像素点没有对应的历史历史像素点。

结合图片去理解:
在这里插入图片描述


(4)动态场景的做法

现在要让场景中的小球加入运动(即做旋转位移缩放),同时摄像机也同样保持运动,那这样完全是个动态场景了。

对比前文的案例,此处又增加了三维物体变换的整个变化量,复杂度继续加大。同样地,要处理好这个问题,需要继续把握好物体的变化(记录并运用起来)。这个影响量就是三维物体的变换矩阵(做仿射变换),将历史帧的三维位置点变换到了当前帧的新三维位置点,所以要将这个量加入到公式中。

那我现在同样抽象一下这个过程

三维顶点A的新位置 = 变换矩阵 * 历史三维顶点A

当前像素点 = 当前摄像机投影矩阵 * ( 变换矩阵 * 历史三维顶点A)

历史像素点 = 历史摄像机投影矩阵 * 历史三维顶点A

上面公式中,历史三维顶点A实际上还是未知的,但是“三维顶点A的新位置”是已知量,变换矩阵也是已知的,所以可以算出来历史顶点A,同样就只剩下历史像素点A这个目标量需要去计算。

具体描述是:
我要找到当前像素点对应的历史像素点,那我通过当前摄像机的投影矩阵,去找出我正在显示的是场景中的三维顶点A,我再找其再历史帧中所在像素前,还需要知道这个顶点A在历史帧中处于三维场景中的那个位置,所以需要先对变换矩阵求逆,再算出历史三维顶点A。然后我再用历史摄像机的投影矩阵,去算一下里历史三维顶点A在历史帧中出现在了哪个像素点上,该像素点就是我要找到的对应历史像素点。假如算出来的像素点超过了像素范围,说明当前像素点没有对应的历史历史像素点。


需要多个连续历史帧时要怎么做

一般可能会用到2~4个历史帧,最直观的做法就是递归地按照上述方法去反推出来,这种从当前帧反推的做法,属于反向重投影。也可以按照前向重投影的思路,为历史帧去生成投影缓存以供调用。


前向重投影要怎么做?

一般重投影都是默认指方向重投影,而前向重投影,则是将查找前后像素对应关系的过程从反向思路改为前向思路。

什么叫反向思路——我需要找历史像素时,从当前帧中出发,往历史帧中去找,正如上文的具体过程描述那样。

那前向思路呢——我先从历史帧出发,去推出每个历史像素在当前帧中的像素位置,得到的就是投影帧,投影帧直接提供给当前渲染去做混合(Blend)。

前向重投影也是在当前渲染过程中去完成,当前帧渲染下,此时我还不知道每个像素的渲染结果是什么,但是物体的变换矩阵,和摄像机的投影矩阵都是已知量,那我就能提取预测当前帧大概是长怎么样的——就是为每个历史像素点预测它在当前帧中的位置。

我把过程抽象一下如下:

历史三维顶点A = 历史投影矩阵的逆矩阵 * 历史像素点 (备注:此时需要进一步记录好历史投影矩阵)

三维顶点A的新位置 = 当前物体变换矩阵 * 历史三维顶点

预测的投影像素 = 当前投影矩阵 * 三维顶点A的新位置

上面公式里,比起前文的反向重投影,需要进一步记录好历史投影矩阵,为了找出历史三维顶点A的坐标,那已经知道了从历史到当前帧的物体变换矩阵,自然可以把历史三维顶点A的坐标变换到当前帧中的新位置。同样知道了当前投影矩阵,可以推测出对应到当前帧中的像素位置,因为不知道新位置下顶点有没有被遮挡还是别的原因导致实际上不可见,所以先称为预测的投影像素。

对历史帧中每个像素都如此做前向重投影,当然投影结果超出像素范围的要弃用,最后结果可以生成一个投影帧,用于与后续渲染得到的当前帧做混合等计算。投影帧中肯定会出现投影时没有命中而导致黑色的噪点,这个问题都是到应用时去考虑怎么做决策的事情了。这样在当前帧需要找历史像素时直接去访问投影帧即可。


重投影时命中的像素多还是未命中的像素多呢

实际上大多数像素都能找到对应的历史像素点的,因为前后两三帧在实时渲染下的差异并非巨大的,往往只是视角或者物体的小幅度变化(当一个巨大的运动放到两帧之间也是小幅的差异),所以历史帧还是能够对当前帧作出巨大贡献的。

最近更新

  1. TCP协议是安全的吗?

    2024-03-22 14:18:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-22 14:18:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-22 14:18:01       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-22 14:18:01       20 阅读

热门阅读

  1. 实验3-11 求一元二次方程的根(PTA)

    2024-03-22 14:18:01       22 阅读
  2. 量化交易入门(十二)Python开发-NumPy

    2024-03-22 14:18:01       16 阅读
  3. 负载均衡原理及算法

    2024-03-22 14:18:01       20 阅读
  4. Python如何把PDF进行压缩分割

    2024-03-22 14:18:01       19 阅读
  5. vue3 hash和history模式路由配置

    2024-03-22 14:18:01       16 阅读
  6. 【Vue】基本知识点汇总

    2024-03-22 14:18:01       19 阅读
  7. git仓库推送和删除远程分支

    2024-03-22 14:18:01       20 阅读
  8. 【ESP32 IDF】RTC时钟

    2024-03-22 14:18:01       19 阅读