原文地址 http://www.adriancourreges.com/blog/2016/09/09/doom-2016-graphics-study/
有所改动……随着认知的一些领悟,所以修改一些内容 ……
现在认可 也是一种技术 方案,尽管这个技术方案,我本人 目前也是并不十分 赞同!!!
由于本人英语水平有限,对于图形学技术领悟有限,错误之处在所难免,读者体谅,
也欢迎高手 翻译此篇,
抛砖引玉 。 本文你可以随意转载,但请你最好注明出处,毕竟我翻译也不容易。
请尊重一下劳动成果!
DOOM2016 画面 研究
(原文此处有 图片,但是我 删除了,我感觉 删除掉很多 图片,才是很多技术实现原理方式)
DOOM在1993年开创了游戏设计和开发的根本变革,这是一个世界性的现象,
推动了像 约翰·卡马克(John Carmack)
和 约翰·罗梅洛(John Romero)这样的标志性 人物。
23年后,id Software现在属于Zenimax,所有的原创始人 暂时 离开……
但 并没有什么能阻挡 该机构的 所有 人 来做出一个 超级伟大 的 游戏。
新的DOOM(2016)是专利权的完美补充,
使用新一代的“ID Tech 6引擎。 ”( 整个 游戏源代码 的一部分框架内容 ……)
前Crytek Tiago Sousa在约翰·卡马克(John Carmack)离职后,
现在 担任 首 席渲染程序员 的 角色。
在id公司的历史上,引擎 (我对比其实有疑问 ……)源码 开源广为人知,
新一代游戏引擎,通常几年后会 开源引擎, 这通常会导致很好的重制和改造。
id Tech 6 (游戏源代码 的一部分…… )是否开源,但仍然有待观察,
但我们并不一定需要源代码来了解在引擎中实现的 漂亮 图形技术。
How a Frame is Rendered(一幁 是如何被渲染的)
我们将在下面的场景中 检查 玩家攻击 由一些拥有的敌人防守的
gore nest (戈尔巢,doom2016游戏的地图之一) 的 场景,
首先在游戏开始之前先取得Praetor Suit(doom2016 的装甲服装)。
(原文图片删除了,容易干扰阅读,图片容易干扰阅读很多原理实现方式……)
与现在发布的大多数Windows 游戏 不同,
DOOM 2016 并不使用Direct3D 接口,
但提供了一个OpenGL和 Vulkan后端。
Vulkan是新的热点,
Baldur Karlsson最近在RenderDoc(一个 图形调试器,支持DX12.dx11,Opengl)
中增加了支持,DOOM2016 是一个很困难的的选择。
使用 Vulkan 运行 游戏 在NVIDIA GTX 980上的设置为 最高级别
(
译者注,这么翻译 是根据我的 理解,
Vulkan是一个支持低端机器运行Opengl的渲染接口,
因此 开到了 最高模式
),
一些猜测来自于 Siggraph大会上Tiago Sousa(id首席程序员) 和Jean Geffroy.的展示。
Megetexture Update (巨型紋理更新)
首先第一步是Mega-Texture更新,
这是一种已经存在于RAGE中使用的ID Tech 5中的技术,
现在也在DOOM中使用。
如果要阐述这个算法的 基本原理 的话,
这个 算法是在GPU内存上分配了几个巨大的纹理(16K x 8k的DOOM),
每个纹理都是 128 x 128个 Tile( 瓷砖拼图) 拼贴的集合。
16k x 8k storage with 128 x 128 pages
所有这些Tile(瓷砖)都应该在好的mipmap 级别 代表理想的 实际纹理集合,
像 素着色器稍后将会 渲染你现在正在看的 游戏画面。
当Pixel Shader(像素着色器)从“虚拟纹理”读取数据时,
它只能从这些 128x128的某些物理Tile(瓷砖) 中 读取。
当然这取决于玩家看的地方,玩家移动位置的时候,
这个画面将会改变:新的 模型会出现在屏幕上,
然后 引用 它 所关联的 虚拟纹理,
新的Tile图块 将会被 stream in(流式传入),
旧的Tile流 将被 导出...
所以在一帧的开始,
DOOM2016 引擎 使用 vkCmdCopyBufferToImage命令来更新 了一些 Tile瓷砖,
将一些实际的纹理数据 带入 GPU内存。
对于 idtech5 megetexture 技术的更详细的一个介绍,
乃是出自,2012年的Siggraph,id程序员所写的论文
如果感兴趣, 05_ip_id_tech5_challenges.pdf
H3D姚勇写的 RAGE来了-Megatexture信息更新.pdf 论文对于megetexture的一个叙述
对于Megetexture描述更加清晰的论文,
由id程序员写的Software-Virtual-Textures.pdf
如果感兴趣的话,其实可以看看……
shadow Map atlas (阴影贴图图集)
对于每个灯投射一个阴影,生成一个独特的深度图,
并将其保存到巨大的8k x 8k texture atlas (纹理图集)的一个Tile(瓷砖)中。
然而,并不是每个帧都需要计算出每一个深度图:
DOOM4大量地重新 使用 前一帧的结果,
并只重新生成需要更新的 深度图。
灯 是 静态 的,并且仅在 静态对象 上 投射 阴影 时,
只需 简单地将 其 深度图 保存 8k * 8k的深度缓存器前一幁
8k*8k的深度缓冲器 当前一幁 ( 此处 翻译可能有误 ……)
原样,而不是进行 不必要的 重新计算是有 意义的。
但是如果一些敌人在光线下移 动,则深度图必须再次生成。
深度图尺寸可以根据与相机的光距离 来变化,
重新生成的深度图也不一定保持 在图集内的同一 Tile 内。
DOOM2016 具有某些特定的优化,例如 缓存深度图 的 静态部分,
仅计算 动态网格 投影并 合成 结果。
Depth Pr-Pass( 深度 预处理)
现在渲染所有不透明的网格,仅将其 深度信息 输出到 深度图中。
首先是玩家的 weapon,
然后是 静态 几何数据,最后是 动态几何 数据。
速度图
但实际上深度并不是在深度预处理期间输出的唯一信息。
虽然动态对象(僵尸,电缆,玩家的weapon)被渲染到深度图,
但是它们每像素的速度也被计算并写入另一个缓冲区以创建 velocity map速度图。
这通过在
顶点着色器中 计算 前一帧 和 当前 帧 之间 的 每个 顶点 的 位置差 来完成。
(原文图片 已删除……,有需要请看原文 …… )
Velocity我们 只需要2个channel(通道)来存储 速度:
红色是沿着水平轴的速度,沿着垂直轴是绿色的。
这个怪物迅速向玩家(绿色)移动,
但是玩家手中的武器几乎没有移动(黑色)。
黄色区域(红色和绿色等于1)?它实际上是缓冲区的 原始默认 颜色,
没有动态网格没有触及过:它是所有的“静态网格区域”。
为什么DOOM跳过静态网格的速度计算?
因为静态像素速度可以从其深度简单地推断出来,
而相机的状态自上一帧起就是新的state(状态),
所以不需要在每个网格的 基础上 进行计算。
the velocity速度图 在稍后应用一些 运动模糊 将是有用的。
occlusion Quieries( 遮挡查询)
我们想发送尽可能少的 几何渲染 到 GPU,
所以实现这一点的最好方法是剔除玩家视 野里面不能直接 看到的所有网格。
DOOM2016 中的大部分遮挡查询都是通过 Umbra中间件 完成的,
但引擎 仍然会执行一些 GPU遮挡查询 来进一步 削减 视野外的数据……
那么GPU遮挡查询背后是怎么实现的呢?
首先是将游戏场景的几个网格组合成一个包含它们的虚拟框,
然后请求GPU根据当前的深度缓冲区渲染此框。
如果没有一个光栅化像素通过深度测试,
这意味着 盒子被完全遮挡,
并且渲染时可以安全地省略该盒子中的 所有 世界对象。
那么事情是这些遮挡查询结果是不可用的,你不想阻止GPU管道阻挡一个查询。
通 常,读取结果 被推迟到 以下 帧,
因此 有必要 使 算法 有点 保守,
以避免 对象 弹出。
Clustered-Forward-Rendering of Opeque Objects
(不透明对象的前向渲染)
渲染 所有不透明的几何和贴花。
照明信息存储在HDR缓冲区中:
深度测试函数被设置为EQUAL避免任何无用的overdraw(过绘制)计算,
由于 之前的深度预处理(pre-pass),
我们知道 每个像素 应具有哪个深度值。
当渲染网格时,贴图也直接应用, 它们存储在 纹理图集 中。
它已经看起来很好,
但我们仍然缺少一些透明的材料,如玻璃,或颗粒,还没有环境反射。
关于这个游戏的一趟渲染(pass):它使用一个聚集的前向渲染器,
它受到Emil Person和Ola Olsson的作品的启发。
历史上,前向渲染的一个缺点是它无法处理大量的灯光,
在 延迟渲染 中 更容易处理。
(译者注:前向渲染在单机fps中 光源数量 可以设定,
同时一幁内的怪物数量已 知不是mmoprg 玩家人数增多,
像 中国玩家的有的 “一大群模型在屏幕上呈现”,几何数据未知。)
那么cluster(集群)渲染器是如何运行的呢?
首先,您将您的视口划分成Tile(图块):DOOM创建一个16 x 8细分。
一些渲染器会停在这里,并计算每个Tile(图块)的灯光列表,
这有助于 减少照明计算量,
但 仍然受到一些边缘情况的影响。
cluster Rendering(集群渲染)将从2D到3D进一步扩展,而不是停止在2D视口 细分,
它实际上 通过 沿 Z轴 创建 切片 来 执行 整个 相机 平截头体 的3D细分。
每个“块”被 称为“群集”,
您也可以 将其 称为“平截头体” 体素 或 “froxels”。
右侧 是 一个 简单 的 4 x 2 视口 细分 的 可视化,
5个 深度切片 将 平截头体 分为 40个 群集。
在DOOM中,相机平截头体被分为3072个群集(16×8×24个细分),
深度片 沿着Z轴以对数 方式 定位。
使用集群渲染器,一个典型的渲染流程 是:
首先,CPU计算影响每个集群内的照明的项目列表:灯,贴花 和 立方体...
为此,所有这些项目都“体素化”,
因此 可以 测试 其 影响 区域 与 集群 的 交集。
数据 作为 索引 列表 存储 在 GPU缓冲区 中,以便 着色器 可以 访问它。
每个群集 最多 可以 容纳256个灯,256个贴花 和 256个立方米。
然后当GPU呈现像素时:
从 像素 坐标 和 深度,确定 其 属于 的 集群
检索 该 特定 集群 的 贴花 / 灯 的 列表。
它涉及 偏移 间接 和 索引计算,如下 所示。
代码 循环 遍历 集群 的 所有 贴花/灯光,计算 并 添加 其 贡献。
实际上,像素着色器可以在此通过期间检索灯光和贴花的列表:
还有探针列表(上图中未显示,),可以以完全相同的方式访问,
但在此 通行证 中 未使用,所以 我们 稍后 再回来。
在CPU上 预先 生成 每个 簇 的 项目 列表 的 开销 是 非常 值得 考虑 的,
它可以 显着 降低 GPU上的 渲染 计算 复杂度。
集群 向前 渲染 最近 越来 越 受到 关注:
它具有 处理 比 基本转发(此处可能有误.......) 更多 的 亮度 的 好处,
而比 延迟 更快,必须 从 多个 G-Buffers 写入/ 读取。
但有一些 我 还没有 提到 的:
我们 刚刚 检查 的 这个pass(通道)不仅仅 是一个 写给 照明缓冲区的 前进 的 一个;
而 执行 2次 也 使用 MRT 产生 了 2个 G-Buffers
Normal Map
所以 DOOM 实际 上 巧妙 地 混合 了 前进 和 推迟 与 混合 方法。
最后一件事 我 省略了:同时也 产生 了一个 160 x 120 的 反馈 缓冲区,
大型 纹理 引擎 以反应的方式工作:
GPU Particles(GPU 粒子)
compute shader (计算着色器) 被 分配 到 更新 粒子 仿真:位置,速度 和 时长 。
它 读取 粒子 当前 状态 以及 正常 和 深度缓冲区( 用于 碰撞检测),
SSAO Map
在此步骤中,现在 生成SSAO地图。
其目的是使狭窄的接缝,褶皱变暗。
它也 用于 应用specular occlusion 镜面遮挡
以 避免出现在被遮挡的网格上的 明亮照明上 出现 伪影。
在从 深度缓冲区,正常和 镜面 地图 读取 的 像素着色器 中,
以原始分辨率的 一半 计算。
获得 的 第一个 结果 是 noise( 嘈杂 的)。
图像(左边)
Screen Space Reflections( 屏幕 空间 反射)
一个 像素着色器 现在 用于 生成 SSR 映射。
它仅 使用 存在于屏幕上 的 信息 的 光线 追踪反射,
使得 光线 在 视口 的 每个 像素 上 反弹,
读取 由 它们 击中 的 像素 的 颜色。
着色器 的 输入 是 深度图(用于 计算 像素 世界 空间 位置),
法线贴图( 知道 如何 使 光线反弹),
specular镜面图(知道 反射量)
和
前一帧渲染( 在 pre-tonemapping 阶段,但 后透明度,有一些 颜色 信息)。
先前 的 帧 摄像机 配置 也 提供 给 像素 着色器,
因此 可以 跟踪 片段 位置 的 变化。
SSR是一个不错的,不太 开销大 的技术,可以在现场 进行 实时 动态 反射,
以保持不变 的 成本,真正 有助于 沉浸 和 现实 主义 的 感觉。
但由于它纯粹在屏幕空间中工作,缺少“全局”信息,因此它自带的工件。(此处翻译可能有误……)
所以 你 可能 会 在一个 场景 中 看到 很好 的反射,
但是当你 开始 向下看 时,反射的 程度 会 减少,
直到 看到 你 的 脚 就完全 没有 反思。
我发现 DOOM 中 的 SSR 很好地 整合 在 一起,它们提高了视觉质量,
但是 除非 你 真的 专注于 这些,
否则 你 不会 注意 到 它们 消失。
Static Cubemap Reflections(静态立体图反射)
之前所有的动态 反射趟数目(及其 限制) 现在 都 来自 使用 IBL 的 静态 反射。
该技术 基于 预先 生成 的 128 x 128 立方米 表示 地图 不同 位置处 的 环境 照明 信息,
也 称为 “环境探测”。
正如 我们 以前 在 截锥体 聚类 中 所看到的 光 和 贴花 一样,
探针 也 按照 与 每个 聚类 相同 的 方式 进行 索引。
级别的所有立方体都存储在一个数组中,其中有几十个,
但这里是这个场景的5个 主要贡献者(这个 房间 内 的 立方体):
一个像素着色器 从 深度,法线 的 镜面 缓冲区 中 读取 立方体 影响 像素 的 群集 结构
( 立方图 越 靠近 其 影响 越强 ) 并 生成 静态 反射 图:
Blending Maps Together(地图所有 映射 混合)
在这个步骤中,计算着色器组合了之前生成的所有映射。
它读取深度和镜面地图,并将前进的照明与:
(此处翻译可能有误……)
SSAO信息
当SSR可用于有问题的像素时
当SSR信息丢失时,静态反射映射数据被用作Fallback(回退)
还计算了一些雾效应
Particle lighting(粒子照明)
我们在这个场景中有一些烟雾粒子,照明实际上是根据精灵计算的。
每个精灵的渲染就像是在世界空间中:从它的位置,
一些光列表和它们各自的阴影贴图被检索,
并且quad(四边形)上的照明被计算出来。
然后将结果 存储到 4k地图集的 瓦片中,
基于距离相机的粒子距离,瓦片可以具有不同的分辨率,
质量设置...图集具有相同分辨率的精灵的专用区域,这里是64 x64个精灵:
而这只是以这种低分辨率存储的照明信息。
之后,当实际绘制一个粒子时,使用全分辨率纹理,
并将照明四面体放大并与之混合。
这是DOOM将粒子照明计算与游戏的实际主渲染分离的位置:
无论您在播放的分辨率(720p,1080p,4k ...),
粒子照明总是计算并存储在 这些微小的 固定大小的图块中。
Downscale and blur(低档和模糊,downscale我不知道该怎么翻译)
场景被缩放了几次,下降到40像素。
使用分离的垂直和水平通过,最小的缩小水平模糊。
(原图删除了,有需要请阅读原文,有时候图片是一种干扰……多了 未必就好……)
为什么这个模糊如此早?这样的过程通常在 后期处理过程 中完成,
以从明亮的地方产生 绽放效应。
但是,在渲染玻璃折射时,所有这些不同的模糊级别将在下一趟中派上用场。
透明对象
所有透明物体(glass(我翻译成视罩,因为doom4有头盔嘛),颗粒) 都呈现在场景的顶部:
glass(此处翻译为玻璃)在DOOM中呈现非常好的特别是 磨砂 或肮脏的玻璃:
贴花仅用于影响玻璃的某些部分,使其 折射 或多或少 模糊。
像素着色器计算折射“模糊度”因子,
并从模糊链中选择最接近该模糊因子的2个图。
它从这两个地图中读取,然后在2个值之间线性插值,
以逼近折射应具有的最终模糊颜色。
这是由于这个过程,
glass可以在基于像素的基础上以不同的模糊水平产生良好的折射。
Distortion Map 失真地图
非常热的地方可以在图像中产生热变形。
在这里,戈尔巢地图稍微扭曲了形象。
对深度缓冲区进行失真,以创建低分辨率的失真图。
红色和绿色通道表示水平和垂直轴的失真量。
蓝色通道包含要应用的 模糊量。
实际效果稍后应用于使用失真图的后期处理来知道应该移动哪些像素。
虽然在这个场景中, 特别是只有一个微妙的失真并不明显。
UI 用户界面
UI被渲染到以LDR格式存储的预乘法alpha模式中的不同渲染目标。
将所有UI都放入单独的缓冲区,而不是直接绘制在最终的框架之上,
这样的优点 是游戏可以一次性在所有UI小部件上应用一些过滤器/后处理,
如色差或视觉失真。
在单程中。
渲染不会特别使用任何批处理技术,
它会逐个绘制UI项目,大约有120个绘图调用。
在后面的过程中,UI缓冲区被混合在游戏图像之上,以产生最终结果。
Temporal Anti-Aliasing and Motion-Blur
(时间抗锯齿和运动模糊)
使用 速度图和先前帧 的渲染结果应用 TAA和 运动 模糊。
片段可以被 重新投影,因此 像素着色器 知道 当前正在处理的像素位于前一帧中。
渲染实际上每隔一帧将网格投影稍微移动一半像素:
这有助于 删除子像素的别名伪像。
结果是非常好的:不仅网格边缘变得光滑,
而且镜像混叠(其中一个亮像素将单独弹出一帧)也被照顾。
通过像FXAA这样的后期处理方法,
质量远远好于可以实现的质量。
Scene Luminance(场景亮度)
该步骤计算场景的平均luminace(亮度),
这是晚些时候提供给tonemapper的参数之一。
HDR照明缓冲区在一个循环中被缩减到其分辨率的一半,直到它变为2×2纹理,
每次迭代计算像素颜色值作为其较高分辨率图中其4个父像素的亮度的平均值。
应用亮通滤镜来调暗场景最黑暗的区域。
然后,亮通滤波器的结果在一个循环中被缩小,
并且在我们之前看到的类似过程 中模糊。
层被模糊,
(此处翻译可能有误……)
高斯模糊分为垂直和水平遍历,
其中像素着色器计算沿着一个方向的
加权平均值。 然后将模糊的层组合起来,
以创建在原始分辨率的1/4的HDR纹理的绽放。
Final Post-Processing(最终后期处理)
所有这一步都在一个像素着色器中执行:
读取失真图数据应用 热失真
在HDR照明缓冲区的顶部添加了 绽放纹理 执行如渐晕,污垢/镜片闪光 的效果
通过对2×2亮度图的中心进行采样并使用附加的曝光参数 来获取 平均亮度,
应用 调色板 和 颜色分级。
Tonemapping:之前
之后
以前下一个1
2
/******************/
tonemapping采用HDR照明缓冲器,其颜色在广泛的亮度范围内变化,
并将其转换为每个组件(LDR)的8位,
因此可以将框架显示在显示器上。
使用基于方程式的A filmic tonemapping operator(电影节拍操作员)
(x(Ax+BC)+DE) / (x(Ax+B)+DF) - (E/F),
它是“ 神秘海域2”色谱图,也出现在GTA 5(一个电子游戏 )中。
请注意,场景的所有普通红色都来自于颜色校正。
UI and Film Grain (界面和胶片颗粒)
最后,UI被混合在游戏框架的顶部,同时film-grain(胶片颗粒)被应用。
唷!我们完成了一幁的流程,现在可以将其发送到屏幕的监视器进行显示,
这是相当多的计算,但所有这些都发生在不到16ms。
DOOM通过巧妙地重新使用以前的帧中计算出的旧数据,
从而高性能地产生高质量的视觉效果。
总共有1331个draw call(绘制批次),132个纹理和50个渲染目标被使用。
Bonus Notes (额外备注)
Close-Up on Glass(玻璃上的特写镜头)
玻璃渲染是非常好的,它是通过我们以前看到的相对简单的步骤实现的:
准备几层不透明网格渲染的模糊
在Foward(前向模式)下使用 贴花/照明/probe(探zhen)反射,
在前面的方向 绘制 半透明物品,
使用前面处理过各种 玻璃折射的 模糊值,
因此每个 像素 可以具有 自身的折射值。
景深
在在幁数的分析中没有真正显示任何景深,
所以让我们考虑在应用DoF之前 和 之后 的以下场景:
并不是所有的游戏都能正确执行DoF:传统的方法通常是使用高斯模糊,
并根据像素的深度在一次通过中进行所有的模糊。
这种方法简单而廉价,但是有几个问题:
而高斯模糊对于boken(绽放)来说是不错的,
创造a bright pixel spread(散景)是不正确的:
你真的需要一个平坦的内核,
使一个明亮的像素的光散开在一个圆盘或六边形的形状..
.高斯不能创造漂亮的散景形状。
在单次像素着色器中执行DoF可能会导致 “电子游戏中 显示效果 红色 伪像”。
DOOM能正确地执行DoF,在我的经验之中doom4使用的方法能够产生最好的结果:
创建Far_field 远场和near-field (近场)图像:
根据 其 深度和DoF参数 完成 像素 选择。
near-fiedl (近处视野)可以产生增强的模糊效果,
它会越多流入像素后面越好。
远场也模糊,但没有从对焦/近场区域读取任何像素,
因此避免了前景对象错误地 渗透到背景中的任何问题。
为了创建boken blur(散景模糊),DOOM以半分辨率工作,
并使用64个纹理抽头执行disk -blur(磁盘模糊),
每个样本都拥有相同的权重,因此亮度真正与高斯模糊不同。
磁盘直径可以根据像素的CoC值在每像素的基础上变化。
然后,它进一步扩展了16分针模糊的模糊,但这次它不计算加权平均值,
它只是累积样本值并保持邻居抽头的最高值,
因此不仅会扩大第一个模糊它也修复了第一次通过的小物体(采样间隙)。
最后一部分是McIntosh的工作灵感。
考虑到获得的最终磁盘模糊的宽半径,
这种在多次通过中的迭代技术可以产生非常好的大模糊,
同时仍然保持很好的性能,
每像素执行的实际纹理抽头的数量仍然相当低。
远处的图像和近处的图像最终通过Alpha(蒙道)混合在原始场景的顶部合成,
以创建最终的景深效果。这个通过在应用运动模糊之前执行。
更多阅读
如果您想更深入地了解 “idTech 6技术”,幸运的是有很多演讲和公共材料可供选择
The devil is in the details: idTech 666: Tiago Sousa和Jean Geffroy的,
对于id tech6游戏引擎的一个更加详细的描述,有很多的相关技术。
(Siggraph 2016) 如果感兴趣的话,可以下载(有的说实话,有可以钻研的部分,
也有未必可以...的,越是画面好的游戏,越是硬件成本,人员素质,各种成本有时候太高了!
实际quake3,doom3很多代码 实现方式 其实就挺好的,……
Rage后面的很多代码 未必就一定 开发游戏就好,掌握各个部分已经多了,
很多难以避免的复杂度 越来越高,
很多就未必比以前的老的游戏代码 改动一部分就一定结果好,
对于游戏开发,我现在个人觉得...)
doom2016的一个论文,名字翻译过来为 (“魔鬼”隐藏在 细节 之中)
olick的论文
技术采访:“数字铸造企业 ”Doom
id软件技术采访 DSOGaming
QuakeCon 2016:Doom Uncapped - Part1和Part 2
Doom:VentureBeat的最终采访
图形学宝典CryEngine 3Sousa_Graphics_Gems_CryENGINE3.pptx(Siggraph 2013),