热门
比肩原生4K画质,《永劫无间》搭载DLSS流程公开
自 2021 年 8 月全球发布以来,大逃杀类新作《永劫无间》在全球掀起了一波又一波的游戏狂潮。游戏源自中国玄幻的美术风格给世界各地的玩家们留下了极为深刻的印象。
作为网易的独立子公司,24 Entertainment 工作室在其首款桌面端游戏《永劫无间》就搏得了开门红,迅速吸引了全球玩家的目光。游戏在发布第一周内便登上 Steam 排行榜前 10 名,活跃玩家数超过《彩虹六号:围攻》、《Splitgate》等热门游戏。
这场大逃杀式的动作冒险将场景设立在五光十色的山巅、郁郁葱葱的森林和断壁残垣的古城中,每一个都蕴含了惊人的细节。为了抓住环境的美感,以足够的性能和帧率支撑多达 60 人的多人游戏,24 Entertainment 与知名技术企业 NVIDIA 和 Unity 展开了亲密合作。
在与 NVIDIA 的合作中,24 Entertainment 提前用上了深度学习超采样 (DLSS) 技术:一项以渲染高帧率、高分辨率实时图形为目的的新渲染技术。DLSS 可借助人工智能举重若轻地增强图形性能和整体质量。
为了维持高性能,《永劫无间》以低分辨率渲染,避免了像素着色计算等流程。在运行期间,DLSS 将利用神经网络生成高分辨率图像,为玩家们保留美术细节。这样一来,游戏不仅能生成高质量的图像,还能借助人工智能填补图像缺失,使得渲染速度提高几乎一倍,这对于如此大体量的多人竞技游戏非常关键。
在 DLSS 的帮助下,24 Entertainment 成功实现了高帧率、高分辨率和高清细节。可以说,DLSS 的 4K 几乎可以比肩原生 4K。Unity 2021.2 版本将支持并维护 DLSS 技术。
经过训练的 AI 能参考前几帧画面进行渲染,辅助抗锯齿等功能。并且,同一游戏的神经网络模型无须再度训练即可处理各种画面。
《永劫无间》中的DLSS
在几年前《永劫无间》的开发初期,团队在 Unity 可编程渲染管线(SRP,一种支持添加自定义C#渲染架构的管线)的基础上建立了自己的渲染管线。
在 NVIDIA 的 Developer Relations 专家及 Unity Core Support 的支持下,24 Entertainment 还率先在 Unity 中应用了 DLSS 技术。
为了帮助其他开发者更深入地了解 DLSS 在实时环境下的运作机制,《永劫无间》的图形开发团队披露了部分技术应用细节、提示及开发时遇到的挑战。
高采样(Upsampling)
DLSS 应用的第一步是在低分辨率图像中进行高采样。
部分24 Entertainment需要解决的难题
为了降低采样对最终画面的影响,24 Entertainment 将这一步放在了泛光、色调映射、特效光等后处理效果之前,所有后处理效果都应用在了采样后的高清图像上。
整个管线的运行流程如下:
第 1 步:将画面设为高质量模式,再使用 NVIDIA 的 getOptimalsettings 接口来计算输入数据大小和最佳清晰度。不同的质量模式有着不同的图像缩放比例。
第 2 步:使用 NVIDIA 的 CreateFeature 接口在各个摄像机中抓取特征,据此设置质量模式、输出图像大小及锐化程度。锐化后的输出图像可包含更多细节。
第 3 步:在后处理之前使用以下代码执行 DLSS 推算:
commandBuffer.ApplyDLSS(m_DLSSArguments);
准备输入数据
团队稍微调整了渲染管线来保证输入图像可以兼容 DLSS。
由于低分辨率图像需要按一定比例进行放大,游戏窗口需要缓存到 G-Buffer(Geometry Buffer,包含色彩、法线、坐标信息的缓存纹理)中,才能正确在管线中行进,而窗口必须以正确的比例重新创建。
pixelRect = new Rect(0.0f, 0.0f,
Mathf.CeilToInt(renderingData.cameraData.pixelWidth * viewportScale),
Mathf.CeilToInt(renderingData.cameraData.pixelHeight * viewportScale));
commandBuffer.SetViewport(pixelRect); // RenderScale Supported
渲染完成后,DLSS的源图像大小、目标大小、目标色彩渲染和目标深度等参数将根据低分辨率原图进行设定。
int scaledWidth =
UpSamplingTools.Instance.GetRTScaleInt(cameraData.pixelWidth); int scaledHeight =
UpSamplingTools.Instance.GetRTScaleInt(cameraData.pixelHeight);
// Set the argument m_DLSSArguments.SrcRect.width = scaledWidth; m_DLSSArguments.SrcRect.height = scaledHeight;
m_DLSSArguments.DestRect.width = cameraData.pixelWidth;
m_DLSSArguments.DestRect.height = cameraData.pixelHeight;
m_DLSSArguments.InputColor = sourceHandle.rt;
m_DLSSArguments.InputDepth = depthHandle.rt
信号抖动偏移(Jitter Offset)
接下来的问题是输入图像的 Jitter Offset,这一步与时域上的样本积累有关。
在渲染时,着色器如果只渲染三角形所覆盖的像素,会导致光栅化图元变得分散,生成有锯齿、不自然、边缘不光滑的图像。
如果渲染的分辨率更高,则图像亦会变得精细、自然起来。然而,如果缺少分散的图像样本,要想生成连续的图像信号会变得非常困难,更有可能产生锯齿。
在 4K 分辨率下,锯齿现象的确可以通过提高分辨率来缓解。但在 8K 分辨率下,此方法会导致渲染速度慢近四倍,并且很可能会产生纹理带宽(内存使用)的问题。
另一种常见的抗锯齿方法是由 GPU 硬件驱动的多重采样抗锯齿(Multisample Anti-aliasing,常称为MSAA)。MSAA 除了检测像素的中心点外,还会检测亚像素位置的样本。三角形片元的色彩将根据图元覆盖的样本数量进行调整,来让图像边缘更显平滑。
时域抗锯齿 (TAA) 是另一种跨帧累积样本的方法。该方法将在每一帧上抖动采样位置,接着利用运动矢量混合帧之间的渲染色彩。
如果每个帧像素过去的色彩都可被识别,我们就可以利用这些过去的像素进行抗锯齿。
抖动(Jitter)通常是指像素采样位置的轻微调整,以达到累积多帧样本的目的,免去了一次性解决欠采样的必要。
24 Entertainment 之所以转而采用 DLSS,就是因为它不仅降低了渲染分辨率的要求,而且还生成边缘平滑的高质量图像。
我们推荐在 DLSS 中使用的采样模式包括 Halton 序列,一种看似随机、覆盖更均匀的低离散度序列。
Halton序列:
https://en.wikipedia.org/wiki/Halton_sequence
在实践中,Jitter Offset 的应用可以非常简单。要想实现高效的应用,可以参考以下步骤:
第 1 步:在特定摄像机中根据 Halton 序列生成不同图像设定下的样本。输出信号的抖动量应该在 -0.5 到 0.5 之间。
Vector2 temporalJitter = m_HalotonSampler.Get(m_TemporalJitterIndex, samplesCount);
第 2 步:将抖动量存储到一个 Vector4 中,将其乘以 2、再除以缩放后的分辨率,来将其应用到屏幕空间的单位像素上。将结果存储到 zw 组件中。
然后,使用这两个值修改投影矩阵、改变总体的渲染结果:
m_TemporalJitter = new Vector4(temporalJitter.x, temporalJitter.y,
temporalJitter.x * 2.0f /
UpSamplingTools.GetRTScalePixels(cameraData.pixelWidth),
temporalJitter.y * 2.0f /
UpSamplingTools.GetRTScalePixels(cameraData.pixelHeight) );
第 3 步:将视图投影矩阵设置为全局属性 Unity_MATRIX_VP。顶点着色器会调用相同的函数来转换屏幕上的世界位置,而将矩阵归入该属性可免去修改着色器的必要。
var projectionMatrix = cameraData.camera.nonJitteredProjectionMatrix;
projectionMatrix.m02 += m_TemporalJitter.z; projectionMatrix.m12 += m_TemporalJitter.w;
projectionMatrix = GL.GetGPUProjectionMatrix(projectionMatrix, true); var jitteredVP = projectionMatrix * cameraData.viewMatrix;
运动矢量
在解决 Jitter Offset 问题后,接下便是借助工具生成运动矢量。
运动着的摄像机和对象会改变屏幕图像,而要根据多帧样本来生成图像,则我们还需要抓取对象的前一位置。
例如,当摄像机的位置从 q 变为 p 时(如图所示),屏幕上的某个点很可能会被投射到另一个点上。将这两个点相减便能得到此次运动的矢量,即前一帧相对于当前帧的位置。
运动矢量可采用如下步骤计算:
第 1 步:在管线的深度信息通道中计算对象的运动矢量。通道中的深度信息主要用于描绘对象距摄像机的距离,辅助系统检测“景深”。
第 2 步:根据前一帧的摄像机 View Projection 矩阵移动窗口、抓取前一窗口位置,再填补新像素。
第 3 步:在 DLSS 中写入矢量相关的属性。
我们可以根据不同的清晰度(分辨率)来计算对应的运动矢量,通过按比例缩放矢量来实现想要的结果。这里,24 Entertainment 将矢量设为了负的宽高比。DLSS 要求运动矢量以像素为单位,而团队在屏幕空间中生成运动矢量,
之所以使用负号,是为了在管线中互换减数(当前帧)和被减数(前一帧)的位置。
m_DLSSArguments.MotionVectorScale = new Vector2(-scaledWidth, -scaledHeight); m_DLSSArguments.InputMotionVectors = motionHandle.rt;
《永劫无间》渲染循环集成概述
24 Entertainment 的渲染管线结合了延迟渲染和前向渲染。为了节省内存,所有渲染对象都在放大之前作了分配。
DLSS 管理器会使用 RTHandle 系统在摄像机创建之际分为渲染对象分配一次内存,省去为每次摄像机循环都分配一次内存的必要。
RTHandle 系统:
https://docs.Unity3d.com/Packages/com.Unity.render-pipelines.core@12.0/manual/rthandle-system-using.html
再到深度通道中生成运动矢量(仅包括运动对象),来实现时域上的 Jitter Offset 等效果。接着,在屏幕通道中生成摄像机的运动矢量。
DLSS 管理器支持在后处理开始时使用 RTHandle 系统为放大后的渲染对象分配内存。
到这里,DLSS 推算已经能获取除信号抖动以外所有参数信息了,再加上信号抖动就可以实现时域化采样了。
《永劫无间》的渲染管线带有一个可以缓存摄像机 Halton 采样阶段索引和矩阵抖动与否等信息的系统,并且所有光栅化步骤都采用了 View Projection 和 Jitter 矩阵,除了矢量通道。矢量通道采用的是无抖动的矩阵。
24 Entertainment
关于充分利用DLSS的提示和技巧
Mip Map Bias
Mip Map 是一种预先计算好的、分辨率逐级递减(后者为前者的二分之一)的图像序列。它们可以有效地逐级过滤纹理,然后在原纹理中对所有组成屏幕像素的纹素进行采样。
在下例中,远离摄像机的对象会在低分辨率的 Mip Map 中被采样,从而生成更符合实际的结果。
在为 DLSS 采集纹理样本时,一定要添加 Mip Map Bias。DLSS 与棋盘渲染等其他类似的上采样方法一样,需要在较高的分辨率下进行采样才能渲染出低分辨率的窗口图像。这时,在高分辨率下还原出的纹理不会显得模糊。
Mip Map Bias可使用以下方法计算:
MipLevelBias = log2(RenderResolution.x / DisplayResolution.x)
如果输出为负,你可以利用偏差值回滚为更高分辨率的图像。只有设置了 Mip Map Bias,DLSS 才能在采集低分辨率纹理样本时维持图像质量不变。
缓存特征供摄像机使用
我们应该在摄像机上缓存抓取的 DLSS 特征。
为了反映摄像机尺寸和质量模式的变更,DLSS 必须抓取新的特征。有时,多个大小和质量模式相同的摄像机需要同时使用 DLSS 渲染。但每个特征都是根据前一帧的信息总结而来,某个特征只能适用于特定摄像机。
在《永劫无间》中,24 Entertainment 以每台摄像机的哈希值作为键值将各个特征缓存到了一个字典中,藉此保证每个特征只能被对应的摄像机使用。
commandBuffer.ApplyDLSS(m_DLSSArguments, cameraDescriptor);
切忌将 DLSS 与其他抗锯齿方法混合使用。最后,不要将 DLSS 与其他抗锯齿方法组合使用。作为一种新技术,DLSS 与其它抗锯齿方法一同使用可能会产生不可预测的瑕疵。
Unity 2021.2中的DLSS
Unity 与 NVIDIA 有着紧密的技术合作关系,Unity 引擎和 HDRP 都已经集成了 DLSS 及上述所有功能,进一步的集成开发也将展开!
Unity 将自 2021.2 版本起支持并维护所有 NVIDIA 技术。以下为 NVIDIA 技术集成至 Unity 的详情:
引擎核心的 DLSS 集成
在引擎内核中,我们专门为 DLSS 模块编写了一层恰当的 C# API。这一层代码还负责处理平台兼容性、引擎内的 #define(用于在运行平台上屏蔽特定的DLSS代码)以及提供正确的说明文档,它属于在 SRP 中调取完整 DLSS 的官方 API,也是其它可编程渲染管线的整合基础。
说明文档:
https://docs.Unity3d.com/2021.2/Documentation/ScriptReference/NVIDIA.GraphicsDevice.html
HDRP 中的图形集成
DLSS 以动态分辨率系统(DRS) 功能的形式应用在了 HDRP 中,功能也使用了上述的脚本 API。
动态分辨率系统(DRS) 功能:
https://docs.Unity3d.com/Packages/com.Unity.render-pipelines.high-definition@12.0/manual/Dynamic-Resolution.html
我们以《永劫无间》的 TAA 抖动算法为基础,在后处理之前类似地应用了分辨率上采样,再并以全分辨率渲染后处理效果。
并且 Unity 的 DLSS 还有了一点改进:应用以动态分辨率系统为基础,允许实时切换分辨率。动态分辨率系统可完全兼容 RTHandle 系统,并支持硬件驱动的 DLSS(基于纹理锯齿)及软件驱动的分辨率修改(基于游戏窗口)。而前边提到的 Mip Map Bias 功能可用于所有 DRS 过滤器和技术。
为了减少粒子重影现象,我们建立了一个特殊的渲染通道,来保证动态分辨率系统与其它 HDRP 功能的兼容。
此外,我们还建立了一个包含 DLSS 版本信息和帧状态等信息的调试面板。最后,DLSS 现在支持 VR、实时光追、DX11、DX12 和 Vulkan。
在Unity 2021.2中使用DLSS
首先确认项目使用的是 Unity 2021.2 版本(Unity 2021.2 版本现已正式发布,可通过 Unity Hub 下载),以及确保项目中使用的渲染管线为 HDRP。
启用 DLSS 的方法如下:
第 1 步:按照下图中的步骤找到安装 NVIDIA 软件包的 Fix 按钮。
打开 Project Settings 窗口
在 Quality 设置下找到 HDRP 配置界面
选择当前项目使用的 HDRP 配置
找到 Rendering 渲染功能配置组
找到 Dynamic resolution(动态分辨率)组(DLSS 是通过 HDRP 的 Dynamic resolution 功能来提供的)
可以看到提示说 DLSS 功能还未被激活:NVIDIA Deep Learning Super Sampling (DLSS) is not active in this project. To activate it, install the NVIDIA package. 点击 Fix(修复)按钮激活 NVIDIA DLSS 功能
第 2 步:安装完毕以后可以在 Dynamic resolution 参数组中启用 DLSS,并选择调整相关参数。
第 3 步:最后在场景主摄像机中启用 DLSS。
电话:010-50951355 传真:010-50951352 邮箱:sales@souvr.com ;点击查看区域负责人电话
手机:13811546370 / 13720091697 / 13720096040 / 13811548270 /
13811981522 / 18600440988 /13810279720 /13581546145