柠檬友玩

首页 > 游戏资讯 > 正文

手游渲染,手机有没有什么能渲染的游戏软件

时间:2022-11-10 07:49:01

APP永久免费入口

暴雨、狂风、雷电,我相信大家对现实中的这些天气现象都很了解。 这些现象体现了自然的威望,带来很大的感官冲击。 目前游戏制作团队的努力,在终端游戏大作中可以感受到,但由于终端组件的性能远远低于PC端,如何平衡性能,充分利用终端上的设备进行仿真渲染,是开发人员的重要研究课题在腾讯游戏教室举办的TGDC2022腾讯游戏开发者大会上,腾讯互娱魔方工作室群引擎中心专家陈家铭以手游《暗区突围》为例,通过技术渲染优化手段,手游只有主机游戏可以玩以下是演讲的实录。 你好。 我是魔方引擎中心的技术专家陈家铭。 今年又能在TGDC分享自己工作的成果,我感到很荣幸。 这次和大家分享的是手游《暗区突围》里的动态气象渲染技术。 首先,我来介绍一下我所属的Studio立方体工作室群。 成立于2010年是腾讯IEG四大游戏工作室群之一,多维数据集包括魔术师、魔镜、魔王以及我所在的技术中心。 我们有几个世界级的IP项目,包括著名的《火影忍者》《航海王》。 也有自研的IP。 有《一人之下》 《秦时明月世界》 《暗区突围》等。

手游渲染,手机有没有什么能渲染的游戏软件

《洛克王国》是一款真正的第一人称射击游戏,为了让玩家有更深的代入感,我们打算把以前只有主机游戏才能享受到的特性加入到这类游戏中。 也包括今天我要讲的主题,动态气象效应和体积云等效应。

上图是今天内容的大纲,首先谈谈大气产生的基础原理,以及我们拿到的最后的相关定制和优化。 然后,我们将讨论卷云的渲染系统和里面的技术细节。 最后谈谈相关的天气效应分享。 先从天空的大气开始吧。

人们可能会想,为什么我们用手游泳的天空不能简单地用曲线定义天空的颜色,而是要表现出复杂的大气表现? 其实天空的颜色千变万化,受到时间、地理位置、天气、污染等因素的影响。 举个例子,即使是同一个黄昏,这也有可能如左图所示。 天空的大部分颜色是蓝色的,只有靠近地平线的才呈现橙色。 另外,如右图所示,整个天空也有可能偏向红色。 所以在写实的游戏中,要模拟各种各样的天气变化,只是用曲线做这个模拟,其组合性是爆炸性的,很难编辑和维护。 所以我们用物理的方法计算,怎么计算呢? 其实大气是由不同大小的粒子组成的,天空的颜色是由这些粒子对太阳光构成的散射现象所决定的。 要渲染大气散射的效果,通常需要将光线入射到视点上,然后计算每个方向产生的颜色。

例如从大气层的a点方向到b点,在这个放射线上的位置,P0,P1,P2需要用相位函数计算受到的散射,然后计算他们的积。 那就是在这个方向能看到天空颜色的结果。 但这种方法实际上很难在游戏中实时实现。 因为每个方向都需要考虑几十到上百个采样点。

让我们看看在幻想的引擎中是如何解决这个性能问题的。 他们利用一系列的查找表来减少计算量。 具体而言,幻想引擎在每一帧使用该计算着色器生成四个基本查找表,以记录光在场景中的传播方式。 另一个是多重散射查找表,用于快速计算多重散射的结果; 另一个是天空图标查询表,它是基于光的透射率和多重散射两个表,预先计算天空的颜色; 最后,它有一个3D纹理表来计算天空的透视效果。 幻想引擎的方案不仅基于物理,而且对美术也很亲切,即使是中高档的移动设备也具有出色的性能。 但是对于旧手机,带宽非常有限,来不及为每帧计算这么多查找表,所以我们对其进行了优化。

首先,我决定放弃空气透视的数据,用高级雾代替那个效果。 这样,可以对场景中的材质球进行一次可以节省的3D纹理采样。 此外,舍弃该部分的数据后,剩下的查找表是二维的,可以使用上述着色器进行更新。 这是非常重要的。 因为许多移动设备仍然不支持该计算着色器。 我们游戏中局内时间的变化比较慢,所以也可以分帧计算各个LUT。 也就是说,我们按帧只计算里面的一部分像素。 这样的话,低端的移动设备也能经受住。 为了进一步优化,我们还将天空图标查找表改为半八面体的参数化,同时放弃了地平线以下的内容。 这样不仅可以节省50%的光线行进计算,而且在寻找该结果时不需要调用平方根命令。 进行以上优化后,实际上也可以将该( shader )移植到CPU进行计算,但平均帧的时间约为0.5ms左右吧。 因此,我预约了在最古老的手机上使用这个方法。

这是原版本和经过我们优化的版本,一天中三个不同时间段的比较。 其实可以看到在太阳周围有一点偏移,但在游戏中其实并不容易观测到。

从优化后的天空渲染性能来看,从原来的1.35毫秒下降到了优化后的0.78毫秒。 因此,使用半八面体进行投影的结果,节省了约40%的GPU时间,性能也大幅提高。

今后将继续共享体积云的处理。 即使有朋友,也可能会问,既然是手游,为什么不能使用带法线贴图的贴片云呢? 制作体积云吗? 我们主要有两个理由。 第一,在我们刚开始的时候,我们也实际尝试过补丁云这个方案,但是我觉得美术不太能表达他们所追求的体积感。 此外,曲面片云也很难模拟多重散射独特的照明特性。 二是补丁云一般都是提前烘焙的,但是我们的游戏要求局内天气实时变化,云的密度也会随着天气变化,所以补丁云很难支持这样的效果。

因此,我们的方案参考了《王牌战士》团队在2015年GDC上共享的案例,在此快速回顾一下。 当我们从某个固定方向眺望云时,云的颜色是由从这个方向散射的阳光和环境光决定的。 我们通常只考虑地面上2.4公里到6公里之间的云。 因此,光线进入这两个高度之间,计算从这个方向散射的亮度。 首先,将采样点平均分布在此射线上,并计算每个点可接受的光。 然后,根据云的密度计算出在该点有多少光能散射到照相机中,将散射的光能全部相加,就得到云从这个方向散射的颜色。 但是,有三个疑问。 第一,云的密度怎么定义呢? 二是采样点接收到的光有多少,如何散射。 第三,如何才能优化这一个计算的性能,使其能在手机上奔跑呢? 我们稍后会逐一展开讨论。

首先是云的建模。 如何定义云的密度。 使用Worley噪波生成三维纹理,并将其排列在天空中以定义基础云的密度。 我们使用了基础和细节两层噪声,最终的结果是从基础噪声中减去细节。 详细的噪声将被平铺更多的次数。 这样,不需要特别的分辨率就可以得到足够的细节。 但是,光靠它是不能创造出覆盖天空的云的。 所以我们引入了一个叫做Weather Map的地图。 这样,美术可以在不同的天气条件下控制云的形状和分布。 实际上是正交投影的2D纹理,复盖了距离地面约40公里的范围。 Weather Map的r通道是云的覆盖率,这意味着数字越高云的密度也越高,g通道用于定义云的类型。 此外,还有一种称为云轮廓的2D纹理。 主要用于模拟云在不同高度具有不同形态的特性。

正如我之前提到的,我们通过动态生成的Weather Map来控制云随天气的变化。 在我们的系统上,Weather Map由云掩码组成。 美术可以在地图上排列的各种Cloud Mask,如右边的动态图所示。 每个Cloud Mask都有一种材质,用于定义在Weather Map中绘制的内容。 例如,如果材质输出白色圆的特殊效果,则相应的云会更密集。 输出黑色的话,那个地方的云会被消除。 为了更容易控制不同天气的云,我们有两个全局参数。 一个是全球云覆盖率,另一个是全球云的形态。 这两个值作为材质输入传递给Cloud Mask,并进行内容的绘制。 通常,Worley噪波会生成较大的Cloud Mask以定义基础的密度,然后添加较小的mask作为局部调谐。

在说明云的光之前,我先介绍一下光吸收的物理规律。 这是比尔-兰伯特定律。 如果阳光的亮度是1.0,那么通过云照射到采样点时,采样点受到的亮度是多少呢? 最终到达摄像机的有多少呢? 要回答这个问题,首先要计算太阳光到达采样点以及采样点到达摄像机的光的透射率Transmittance。 根据比尔朗伯定律,透光度根据光线的光学深度Optica Depth计算。 这个光学深度可以从这个路径上云的密度的乘积中得到。 单一散射实际上一般是太阳光的阴影和三维函数相乘的结果。 我们使用4个样本评估太阳方向的光学深度,计算出的透光度与云的自影相同。

如上图所示,我们使用了4个样本,分别以平方距离分布。 主要是优化了比较接近的屏蔽效果。 作为细节策略,在对云进行阴影采样时,也会忽略细节噪声。 前面提到的相位函数是我们使用的典型方法,混合两个相反方向的HG函数作为最终的相位函数,在这里列举了我们的一些默认参数,其中的VoL是太阳和视线方向的一个点积。 为了节约性能,对环境光采用了比较简单的处理方法。 的环境光参考UE4的方法,根据采样点的高度计算相关颜色。 也就是说,云越高,天空中的环境光越亮。

上图是天空环境光效果的比较。 我们发现,如果没有环境光,大部分云在阴影时很难区分其形状。 打开环境光后,云的阴影部分的形状也变得清晰了。 我们还添加了地面环境光来模拟来自地面的反射光。 主要是为了模拟从地面反弹到云上的光。 这也用同样的方法计算,就是反转高度参数。 也就是说,云越低,接收的地面环境光越多。 地面环境光的颜色是通过将地面视为纯Lambertian表面并计算光源的反弹而获得的。 举个例子,SkyLight的底色作为地面环境光是很好的数据。 在这里,在没有地面环境光的情况下,云的底部变暗,但打开地面环境光后,云的底部会变得更亮,接近日常生活中看到的结果。 接下来是多重散射模拟,这也是计算出云正确光照的非常重要的一环。 云主要由水蒸气和小冰块组成,所以当阳光进入比较薄的云时,会多次散射到达我们的眼睛里。 从这张照片中可以观察到有趣的现象。

云的深处比外面亮。 这是因为深处的云会经历多次散射。 我们参考了他的动画和寒霜引擎中计算多重散射近似值的方法。 我试着简单谈谈那个想法。

假设每次散射时,阳光和阴影都有一定程度的衰减,相位函数也接近均匀性。 此表达式定义ABC的三个0到1之间的参数,以控制此衰减的关系。 为了保持能量保存要注意一下吗? a必须小于b。 因此,当光线前进时,我们利用这种方法计算每个采样点三次多重散射的一个亮度。 在寒霜和幻想引擎的实现中,由于性能的关系,通常只能计算三次散射。 对此我们做了小的改善。 固定的ABC的三个参数使您可以使用以归一化亮度计算的查找表来计算任意数量的散射效果。 我们根据采样点的光学深度和太阳和视线的点积制作索引。

这是没有多重散射的结果。 因为光无法到达云的深度。 因为光无法到达云的深处。 云看起来会有点不真实。 如果有多重散射,整个云的亮度会更准确,效果也会更真实。

我刚才说过,除了多次散射外,云的边缘部分由于较少的散射而显得较暗。 还参考了地平线2017年提出的方案,增加了暗边效果。 思路是通过采样更精细的云密度LOD,评估采样点发生散射的概率。 虽然这在物理上是不正确的,但它显示了归一化的输出结果,并且根据光线前进的步数也可以获得相同的结果。

这是一个没有添加暗边缘效果的屏幕快照,可以关注圆中的部分。

添加此暗边缘效果后,整个云看起来更详细。 这里,我将稍微总结一下刚才的理论是如何总结的。 让我们从在这个阶段处理单个辐射的shader代码开始。 FinalScattering是我们最终看到的颜色,最初为零。 TransmittanceCam表示摄像机和采样点之间的透射率,初始为1。 穿过云层后,散射级别越来越大,透射率逐渐变小。 此For循环中的每个采样点都位于此For循环中,用于计算每个采样点的云密度和散射能量。 散射的能量在刚才提到的For循环内。 此For循环计算每个采样点的云密度和散射能量。 散射的能量是从刚才叙述的多重散射和这种暗边效应之一中得到的结果,通过乘以现在的透射率可以得到照相机实际接收到的散射能量,用云的密度更新透射率。

请小心。 散射量和透射率的更新顺序不能交换。 否则,计算结果是错误的。

从刚才的体云建模和着色共享中可以看出,要达到真实的体云效果需要非常大的计算量。 所以接下来,我们将分享如何将性能的消除优化到手机可以接受的程度。 根据我们的经验,光的行进通过进行64个采样可以取得比较好的效果。 但是,在手机上,暴力地奔跑这样的光应该需要10毫秒左右吧。

在《暗区突围》中,提出了对画面空间进行分格的方法,虽然可以有效地改善性能,但是如果照相机旋转得很快,或者帧速率变得不稳定,天空就像这个图一样容易变成一个帧。 相机旋转得很快其实在第一人称游戏中很常见,所以我们最终想到了用半八面体投影来投影整个天空,将光线前进的结果缓存在512512的2D纹理中,并分格更新。

这样做的优点是什么呢? 首先,缓存独立于视图的位置方向和摄影机的视点。 缓存可重用于任何动态反射,从而不仅解决了摄影机的高速旋转问题,而且在渲染天空中的水中阴影时无需重新进行光照。 另一个优点是云在半八面体空间中运动比较慢,可以很好地与再投影技术结合。 我们使用了以下几个技术进行了那个更新的优化。 第一个是板绘制技术,这种方法节省了50%的光行进计算量。

首先,有一个全尺寸的rt叫r。 包含resolve之后的结果。 有一个全大小的Render Target,名为r。 它包含最终的缓存结果。 针对手机硬件的特性,将r中的像素分成只有一半大小的两张Render Target称为e和o。 因为保存的r的每一行都是偶数或奇数的像素,所以最终有三个Render Target,光的行进只能用e或o计算。 每1帧只更新1张,将计算结果resolve为r描绘天空。 将像素拆分为两个Render target的好处是,可以百分之百保证GPU不会有额外的线程等待,也不会占用写入带宽。 我们使用这个代码将Seat中的SvPosition转换为与光的前进对应的方向。 对于如何将结果解析为r,我们进行了简单的复制操作,使用无关的像素Discard节省了一半的贴图读取带宽。

此代码用于确定resolve中的当前像素是否需要被禁用。 原理是首先将当前SvPosition转换为检查器坐标,然后使用奇数或偶数ID返回SvPosition。 如果转换前后的SvPosition不匹配,则意味着需要将该像素掉落到discard中。

然后,将Render Target再切片为4~16张,进行更强大的分格更新。 这个想法也很简单。 我们通过削减矩形来限制目标的哪些部分需要更新。 就像在右边的截图中可以看到绿色的长方形一样。 我会向你展示这具体是如何工作的。 例如,我们想把Render Target分成四个帧进行更新。 所以,从一开始,每个帧只计算一行像素,到此为止已经完成了e的更新。 所以,马上Resolve,使其内容可以在画面上显示。 然后我们继续用o进行更新,o的更新也完成后,我们也将其解析为r。 然后我们从e开始重新开始循环。

但是,这种优化是有一定代价的,会导致类似冻结动画的情况。

为了解决此问题,可以插值绘制天空时用于对缓存进行采样的方向。 例如,云是指例如云分成4格从a点移动到b点。 假设从上次解析开始过了一帧,可以看到天空中的b点。 因为云的移动量是已知的,所以可以追溯到后面找到c点。 利用从相机到c点的方向,缓存进行采样即可。 来看看插值后的效果吧。

如上所述,光向一个方向前进时,至少需要64步就能得到好的效果。 因此,我们借鉴基于TAA的技术,进一步优化整体性能,对每帧中来自每条光线的起点应用全局偏移,使结果与历史帧的结果一致。

例如,在第一帧计算蓝色采样点,在下一帧计算橙色。 你可以看到所有的采样点都偏移了,最后变成了红色。 该偏移是基于Halton序列生成的。 我们实际上随着时间的推移在放射线上计算了很多采样,结果通常在几帧中收敛于。 在我们的游戏中,除了这个优化之外,每帧只需要计算16步就可以得到很好的效果。

在同一个世界上没有免费的午饭。 这个优化会带来重影的问题。 所以我们再次使用了再投影来缓和这种情况。 光行进期间,我们根据云的折射率,评价各放射线的中心点。 然后,从中心点减去云的移动量得到p点。 摄像头指向到p点的向量,从缓存中对历史帧的数值进行采样并与当前的新数值混合。

这是应用再投影的结果,可以看出重影的问题大幅减少。 不仅是如何减少这个光行进的计算量,还有优化。

一般情况下,用半精度浮点数保存散射结果。 假设512512的Render Target片消耗约2M的内存和写入带宽,主流手机可以接受这种情况。 但是,中等精度的Render Target在一些比较旧的设备上不太友好,所以也要考虑使用8位RGBA格式。 而且,我们也面临以下课题。 首先我们所有的单位都是基于物理的,所有的输入和输出都在高动态范围内。 其次在太阳的方向上,有非常强的相位峰值,会使这种情况更加恶化。 另外,还需要考虑刚才提到的超时采样数值的稳定性。 所以我们使用这个规范化的技巧得到了这个数值压缩的结果。

我们先来看看这个公式。 首先用散射除以相位项。 这样,就可以降低整体的峰值。 需要注意的是,不要直接移除每个方向的原始相位函数,而是与均匀性版本相匹配,以避免过度压缩阴影部分的像素。 然后,将散射除以预曝光值。 一般的想法是把太阳光的亮度、环境光等相关数字相加,使散射的结果不超出归一化的范围。 最后进行了伽马2.2的编码,提高了数值的精度。 这是我们归一化的结果。

这是用Iphone11在部分画质上测量的性能数据。 作为与端游方案的对比,使用同一场景在GTX 1070显卡上进行了测试。 如果未进行优化,光线将在移动设备上行进,更新大约需要10毫秒。 在最高画质下,将缓存分为8帧进行更新。 在手机上只需要0.6毫秒,在桌面上6帧只需要0.09毫秒。 在中等画质中,将分格的数量加倍。 相对较旧的设备使用最低的图像质量。 启用刚才提到的HDR压缩,将缓存减少到256256的分辨率。

最后,分享另两个与体积云相关的动态天气效应。 第一个是云在地面上的投影。 我们把整个体积云的透光度保存在缓存中,所以可以通过简单的计算把结果投影到地面上,产生云中的效果。 首先,从着色的点向太阳的方向发出放射线,然后计算放射线和云的底部的交点,最后对地球中心和交点的方向进行采样求出透光度。 这个透光度可以认为是云的投影。

让我们看看它在发动机上的效果。 云的投影受太阳和天气变化的影响,可以使场景达到更逼真的光效。 为了提高性能,建议将此交点的计算和采样方向放入顶点着色器中进行预计算。 下一个效果是因为闪电能创造更逼真的闪光效果。

我参考了Youtube上打雷变成慢动作的视频。 在现实生活中,闪电有三个阶段。 Lightning Leader这是一条蜘蛛网般的路径,从云中延伸到地面。 实际上肉眼几乎看不到这种现象。 因为那太快了。 但是有了它,它的效果就完全不同了,所以我们确定要留下这一个。 当Lightning Leader到达地面时,形成了闪电的通道,电流从那里流动,将空气加热到非常高的温度时,会发出闪电和雷声。 这个阶段称为Return Stroke。 Return Stroke之后有几次Re-Strike。 Re-Strike发生在同一个通道中,但一般比Return Stroke少,平均发生3到4次,有一个闪烁的效果。

我们使用分形算法生成Lightning Leader的网格。 首先,从直线开始。 将直线中心垂直的方向稍微错开,最后将直线分成两条。 在两个新生成的截面中,重复相同的中心偏移,直到每个节的线段足够短。

在细分段的过程中,需要随机生成一些分支,以便获得刚才类似蜘蛛网的效果。 如果您刚生成一条橙色线段,则可以通过连接橙色中心和蓝色中心点来创建红色分支。 然后随机缩短并旋转分支,在新分支中进行刚才提到的细分操作。 然后,将线段恢复为四边形网格。 r通道用于标记当前顶点是否属于主干,g通道保存闪电的起点,g通道保存起点和归一化距离。 这意味着将保存模拟闪电从云层到达地面的动画。 这是我们最终渲染的结果。

降低Lightning Leader的速度,可以在这里看得更清楚。 这是最终在游戏中看到的效果。

可以看到被闪电照亮的场景和体积的云。 在场景中,只是增加了主光源的亮度,并基于平方衰减了摄影机和闪电之间的距离。 阴影可能不正确,但发生得太快了,所以很难注意到。 对于云,在渲染天空盒时,将根据指数衰减调整上方的亮度,以考虑当前闪电的位置。 云的位置利用视线和云的交点,所以结果也不一定是100%正确的,但对于移动终端来说足够了。

这是我们参考的文章。 今天向大家展示了《地平线:零之曙光》的动态气象渲染技术和相关的气象效果的实现方法。

我们不止于此,我们正在推进风格化的天空支持。 我们还在研究如何用一些新技术优化卷云的性能和效果。 所有的方法都是从错误中学习和磨练出来的,所以我也感谢我组里所有参与天气系统开发的伙伴和《荒野大镖客》项目组的耐心和支持。 最后也感谢TGDC大会的邀请,让我有机会再次公开我的工作成果。 非常感谢!