之前转载了前两篇,可能因为没有时间,上一个译者并没有翻译第三篇的内容,因此我这里补充翻译一下,如果哪里有问题,欢迎指出来。

原文链接:How Unreal Renders a Frame part 3

作者:Kostas Anagnostou, Lead Graphics Programmer at Radiant Worlds

这篇文章,我们继续探索虚幻引擎的渲染流程,包括图像空间光照(Image Space Lighting),透明渲染以及后期处理。

Image space lighting

下一步,以全屏大小计算屏幕空间反射(screen space reflections),RenderTarget格式是RGBA16_FLOAT。

shader还使用在帧起始处计算的Hi-Z buffer来加速光线追踪的相交计算,通过根据表面的粗糙度来选择不同的Hi-Z mip。比如在越粗糙的表面上可以使用更粗略计算的光线追踪,由于这些粗糙表面的反射的细节会越不可见)。最终,每一帧光线起始的位置互有一些抖动,需要结合TAA增加反射图像的质量。

当光线追踪命中之后,shader会从上一帧的渲染目标中采样颜色,可以从上图看到这点,上图可以看到反射的体积雾以及反射的透明物件(雕像)。也可以在右侧椅子的下方看到粒子效果的痕迹。由于我们缺少透明表面的正确深度去计算正确的光线追踪结果,且反射结果通常都是拉伸的,但大部分情况下,结果还是比较可信的。

将SSR的结果应用到main rendertarget 上是通过一个计算着色器(ReflectionEnvironment pass)。这个着色器还会应用场景中的两个反射探针(reflection probes)采样到的环境反射(environmental reflections)的结果。每个探针的反射采样结果存储在一个mipmap的cubemap中:

这些环境反射探针会在游戏开始阶段生成采样结果,并且她们只会采集静止(static)的几何体,所以上面移动的石块是没有在上面的cubemap中出现的。

此时在屏幕中,应用了SSR和环境反射之后,看起来是这个样子。

Fog and atmospheric effects

接下来是雾和大气效果,如果你的场景中有激活的话。

首先,创建一个四分之一分辨率大小的lightshaft occlusion mask,它会指定哪些像素将接收到光束(只适用于场景中的方向光)。

然后render会通过TAA继续提升mask的质量,并应用3个blurring pass去计算这张mask(由于它大部分都是白的,我不得不增强一些)。

从GPU截取的图像中,这里我并不是完全清楚为什么TAA会在blur之前就应用到mask上,因为最终的结果是非常低的分辨率。这可能需要更多不同环境的例子来搞清楚这一点。

在将雾和光束应用到场景之前,renderer需要暂停一下去应用大气效果到全屏大小的main rendertarget上。

这看起来像是一个完整的散射计算,使用了预先计算的透射率,辐射度和内散射,类似于 Bruneton et al。

很不幸,这是一个室内的场景,很难看出来模拟的效果。

最终,renderer会应用指数雾和光束到场景。

着色器会使用jigepass之前生成的体积雾体纹理,使用一个立体几何位置对其进行采样。这里也会应用上面计算好的lightshaft mask。

Transparency rendering

在雾效被应用到不透明物件之后,renderer开始处理半透明的物件以及效果。

场景里,我添加了两个玻璃雕像,它们首先被渲染,在 main rendertarget上使用正常的 alpha blending。

这两个透明体看起来效果还不错,有受到局部光源,方向光,环境反射,以及雾等等的影响。默认,renderer会使用一个比较高质量的shader来渲染透明体,计算的数据包括大气模拟预先计算的纹理,烘焙好的光照贴图数据(baked lightmap data),包含局部光和方向光的光照的透明光照体,反射探针的cubemap。我并没有看到shader读取体积雾体纹理,它似乎只计算高度/距离为基础的雾,可能我在哪里遗漏了一些设置。关于距离雾,想大气散射一样在 顶点着色器 中计算。

粒子效果会有renderer写入到一个单独的rendertarget上(全屏大小)。

就像透明体,大气散射和雾在顶点着色器中计算。另外,根据粒子系统的设置,renderer可以使用半透明光照体来照亮粒子(我在一个例子中的像素着色器中看到了这么做)。

renderer在结束透明有关的计算之前,会执行另一个pass来计算折射。

透明体和粒子(设置为需要折射的)都会在再次渲染并输出到一个全屏分辨率大小的buffer,buffer中存储了distortion vectors,之后会被用来计算折射(我对图像进行了增强,让vector更加可见)。模板缓冲在这个pass期间也是激活的,用于标记需要折射的像素。

在计算折射的pass(DistortionApply)中,renderer读取 main rendertarget(目前为止的)中的数据 和 上面计算的distortion vectors,然后写出到一张奇怪的折射纹理中。

由于模板缓冲是激活的,用于标记了那些接受折射的像素,renderer不需要去clear texture。

最终的折射的pass只是 拷贝 折射纹理到 main rendertarget,使用我们提到的模板缓冲。

你可能会主要到右边椅子上的折射,这是由于我们还有应用的粒子造成的。对于透明体的折射的渲染,是在透明体被渲染之后才会再渲染的。

下一个pass(BokehDOFRecombine),会最终应用粒子到场景中。这是一个很简单的shader,它实际所做的比pass名称所建议的要少(可能取决于渲染设置)。

Post processing

帧的最后一部分是一些后期处理的pass,我们会简单的介绍一下。

根据当前的场景设置,renderer会应用 TAA,运动模糊,自动曝光计算,bloom以及tonemapping 到 main rendertarget。

虚幻的 TAA 使用一个history buffer 随着时间的推移去累加样本,它会通过两个pass来渲染。第一个pass为没有模板(在本例中是一些粒子)的像素实现TAA,使用了 main rendertarget,history buffer 以及 为了重投影的velocity buffer:

然后执行一个相似的TAA pass,用于stenciled bits来生成最终的抗锯齿图像:

两个TAA 的pass之间的区别是 第一个 使用的在history buffer和当前渲染目标之间的blend factor( feedback )是可变的,依赖于像素的光度luminosity,距离,renderer提供的权重等等(跟设置有关),而第二个pass使用固定的blend facotr 0.25,这表示最终的抗锯齿像素将会大部分包含当前的采样。我猜测这么做是为了减少快速移动的粒子的“ghosting”现象,因为这些我们没有速度信息。

下一步是 运动模糊,通过一个velocity flattening and dilation pass。

在这个场景中,运动模糊的效果并不是很明显,因为当前的场景摄像机是静止的,唯一运动的有速度的物件是石块(由于运动和TAA 已经轻微的模糊了)。

实现自动曝光(眼睛自适应),renderer使用 compute shader创建了一个当前场景亮度的直方图。这个直方图放入了像素的强度值,然后计算每个强度柱中有多少了像素。

这种实现方式的优势在于我们可以很轻松的忽略图像中非常黑或者非常亮的值,然后做一个更加可信的场景平均亮度的估算。使用这个平均亮度,renderer通过调整曝光来计算眼睛自适应(比较亮的图像会导致比较低的曝光,比较暗的图像会导致比较高的曝光)。

通过高斯滤波进行一系列的向下缩放的pass,然后再进行一系列向上缩放的pass,最终组合实现bloom效果(调整了图像使其更加可见,没有曝光控制)。

PostProcessCombineLUTs pass使用 一个几何着色器和一个相当长的 像素着色器,来创建 颜色分级查询表 (colourgrading LUT),一个32*32*32 RGB10A2的体纹理,这会用于tonemapping阶段:

这帧最后的pass,Tonemapper,组合上面生成的bloom以及 main rendertarget,根据眼睛自适应调整图像的曝光值,最终传入colour 通过colourgrading LUT 得到最终的像素颜色值:

Wrapping up

我必须强调,这只是渲染的一个路径,很多的参数和设置都可能会影响它,我们只是触及了一些表面的东西。

总的来说,这是一个有趣的练习,虽然在一个框架中完成了大量的工作,但渲染器做的更多的是“做什么”,而不是我更想要的“如何做”,所以我留下了很多我没有探索想重新研究的东西。

虚幻的渲染代码并没有被很多的文档化,但是她相当干净,容易理解,并且通过跟着它的draw call的list可以很容易的找到对应的代码。在很多情况下,通常通过研究代码很难能够一直跟着shader的处理逻辑,尽管它使用了可扩展的条件编译。如果有一些经过处理的中间缓存,“可编译”的shader specialisations(将名称注入到drawcall列表中),用于做检查和性能分析,那就太好了。

默认情况下,虚幻的renderer似乎更强调生成高质量的图像。它尽可能的寄来与烘焙的数据(环境,光源,体积等等),然后使用TAA来提升图像质量。

如果你在场景中有很多的物件,而没有很多机会去遮挡(即很多的大的occuluders),那看一下 occlusion pass的耗费应该是很值得的。另外,透明物件以及粒子的折射都会迫使他们渲染两次。最终,很多stationary 或者 movable的局部光源在Lighting pass中单独渲染时都可能会有影响(并且增加了透明体以及体积的 light injection pass 的耗费)。

最后,感谢Baldurk 非常好的RenderDoc和Epic愿意让所有人都可以去使用和学习虚幻引擎的源码。

译:UE4是如何渲染一帧的(3)相关推荐

  1. 克服VR眩晕之帧数:提升UE4内容实时渲染效率

    克服VR眩晕之帧数:提升UE4内容实时渲染效率 Li Wen Lei, HuNing 在 2015/10/29 23:00:31 | 新闻 Share on Facebook Share on Twi ...

  2. qt 读取gif一帧_译:Unreal渲染一帧详解(Unreal Frame Breakdown)

    译者前言: 上一篇文章,我补充翻译了"How Unreal renders 1 Frame?"的第三篇,在找资料过程中,又发现了一篇挺好的文章,同样是基于"How Unr ...

  3. UE4中使用RenderDoc截帧

    UE4中使用RenderDoc截帧 https://www.cnblogs.com/kekec/p/11760288.html UE4中使用RenderDoc截帧 RenderDoc(src,doc, ...

  4. UE4使用nDisplay渲染到多个显示设备(一)

    UE4使用nDisplay渲染到多个显示设备(一) 文档链接 交互式内容不局限于显示在一个屏幕上,甚至不局限于一个像VR头盔一样的双屏幕设备.越来越多的可视化系统旨在通过多个同时显示的实时内容来更有效 ...

  5. QT实现渲染到帧缓冲区,创建其纹理.

    QT实现渲染到帧缓冲区,创建其纹理 项目简介 项目技术 项目展示 主要源码片段解析 获取完整项目源码传送门 项目简介 渲染到帧缓冲区中并将其用作纹理. 帧缓冲区示例显示了如何渲染到帧缓冲区,创建其纹理 ...

  6. iOS之深入探究动画渲染降帧

    一.为什么要对动画降帧? 众所周知,刷新频率越高体验越好,对于 iOS app 的刷新频率应该是越接近越 60fps 越好,主动给动画降帧,肯定会影响动画的体验.但是另一方面,我们也知道动画渲染的过程 ...

  7. 备忘录-UE4切出焦点掉帧问题

    防止链接丢失自己记录一下,掉帧问题是因为UE4编辑器自己的优化导致的,所以在编辑器的设置里可以关掉这个选项 可以解决一些sequence打点结果没有触发的问题 据说在实际运行中不会有这个问题,这个bu ...

  8. D2D D3D12 渲染视频帧思路及实现

    写在之前 耗时2个月,写完公司的音视频处理系统.对于整个音视频处理有了基本的了解.个人感觉最坑的地方有三: 编解码 音视频录制的同步 视频预览渲染(视频帧的渲染) 由于在以后要支持同时多路1080P录 ...

  9. OpenGL ES之离屏渲染的帧缓冲区对象FBO的说明和使用

    一.什么是 FBO ? FBO(Frame Buffer Object)即帧缓冲区对象,实际上是一个可添加缓冲区的容器,可以为其添加纹理或渲染缓冲区对象(RBO). FBO 本身不能用于渲染,只有添加 ...

最新文章

  1. ajax csrf php,Laravel中Ajax调用时的CSRF对策
  2. unicode 字符集环境下的mfc 读写 ini 配置文件的_WSL:在Windows下优雅地玩Linux
  3. 基于mysql实现的网站_[源码和文档分享]基于JavaScript和MySQL的文化平台网站的设计与实现...
  4. swiper动态加载数据左右切换失效
  5. 计算机网络无法连接共享打印机驱动,Windows 10 安装网络共享打印机失败,提示0x00009c4a 无法连接到打印机解决办法...
  6. 直线回归和相关------(四)直线相关系数和决定系数(原理与公式推导)
  7. 使用wps进行数据去重
  8. SHA生成消息摘要的过程
  9. 汉洛塔问题(c解决)
  10. Latch的产生和避免
  11. iOS AVFoundation
  12. 构建大型云计算平台分布式技术的实践
  13. Pytorch 冻结网络层
  14. HttpClient请求范文示例,及注意点提示
  15. 存储资源盘活助力网络强国
  16. 正版软件 Windows系统、Office 软件、Microsoft 365 合集
  17. WebApp之JQuery Mobile实现火车列表信息查询
  18. android4.4.2康佳电视,康佳电视怎么连接手机 康佳电视连接手机步骤【图文介绍】...
  19. 面向对象的六大原则之 接口隔离原则——ISP
  20. 【BZOJ4899】记忆的轮廓

热门文章

  1. python字节跳动面试_字节跳动实习面试
  2. 字节跳动秋招面试经验分享
  3. 828 选华为云,实惠更实用——为什么选择 CDN 的企业多?
  4. 用 Python 给全球女神颜值排个序
  5. 以PDF转DjVu为例分析软件和人工转换的区别
  6. 算法实践:林克的背包 (动态规划)
  7. android recovery 模式启动进入流程
  8. ILI9341的使用之【二】ILI9341介绍
  9. Docker这些none:none的镜像,难道就不配拥有名字吗
  10. Git 配置 Beyond Compare