基于可变分辨率的半透明特效渲染优化方案

2024-03-05 19:12

本文主要是介绍基于可变分辨率的半透明特效渲染优化方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

粒子效果在游戏中无处不在。大颗粒系统常见于烟雾、火灾、爆炸、灰尘和雾。如果这些效果填满屏幕,overdraw会非常严重,帧率会掉得很快,即使是在技术成熟的 3A 游戏中也是如此。以至于来半透明渲染的优化问题一直都是难题。

这里的解决方案是将昂贵的粒子渲染到屏幕外渲染目标,其大小是帧缓冲区大小的一小部分。但是这里的解决方案做了一个假设,认为许多用粒子建模的效果都是模糊且柔和的。更专业地说,烟和雾的图像只有低频,没有锐利的边缘。它们可以用少量样本来表示,而不会损失视觉质量。因此,低分辨率帧缓冲区就足够了。如果是具有高图像频率的粒子,例如飞行的岩石碎片。这里的方案可能就不适用了。

离屏渲染

粒子需要被渲染到离屏渲染目标,大小一般要设置主分辨率的一半以上。起码尺度要是二的幂。

离屏深度测试

要正确遮挡场景中其他的粒子,需要深度测试和深度缓冲区,其大小与我们的离屏渲染目标相匹配。我们通过对主 z 缓冲区进行下采样来获得这一点。基本算法是这样的:

  1. 正常渲染场景中的所有实体对象,并进行深度写入。
  2. 对生成的深度缓冲区进行downsample。
  3. 将粒子渲染到屏幕外渲染目标(也就是额外的rendertarget),并做深度测试。
  4. 将粒子渲染目标合成回主帧缓冲区,进行upsample。
获取深度
获取 DirectX 9 中的深度

在 DirectX 9 下,无法直接访问主 z 缓冲区。必须使用三种方法之一显式创建深度。

可以使用多个渲染目标(MRT):颜色信息写入第一个目标;深度写入第二个。这种方法有一些缺点。所有目标必须具有相同的位深度。后台缓冲区将为 32 位(或更多),但更少的缓冲区可能足以满足深度。更严重的是,MRT 与 DirectX 9 中的多重采样抗锯齿 (MSAA) 不兼容。

或者,可以通过在单独的通道中写入单个渲染目标来创建深度。这有一个明显的缺点,即需要对场景的大部分几何体或至少与粒子效果相交的部分进行额外的传递。从好的方面来说,深度可以直接以所需的离屏分辨率写入,无需downsample操作。

如果尚未使用 Alpha,则还可以将深度写入 RGBA 目标的 Alpha 通道。A8R8G8B8可能没有足够的精度。

获取 DirectX 10 中的深度

DirectX 10 允许直接访问深度缓冲区中的值。具体来说,您可以为深度表面绑定着色器资源视图(SRV)。毫不奇怪,您必须首先将其解除绑定为渲染目标。否则,它是相当简单的。

还有一个问题。无法为多重采样深度缓冲区绑定 SRV。原因还是比较深的。MSAA 解析操作应如何应用于深度值?与解析颜色值不同,平均值是不正确的;点采样也是错误的。其实没有正确答案。对于我们对粒子的应用,点采样可能是可以接受的。

MRT 可与 MSAA 配合使用,并且 MRT 目标可以具有不同的位深度。对于浮点表面格式的 MSAA 的使用也没有限制。

downsample深度

无论我们如何获取深度值,深度表面都需要被downsample到离屏渲染目标的较小尺寸。但是没有普遍正确的深度downsample方法。

点采样深度

简单的点采样深度会产生明显的伪影。

绿色三角形是前景对象,靠近视点,具有较低的深度值。白色背景区域具有最大深度。虚线表示的 16 个像素被下采样为 1 个离屏像素。A 点是我们进行深度采样的地方。它不能代表 16 个输入像素中的大部分;只有像素 B 具有相似的深度。

如果绿色三角形是前景对象,后面有一个粒子,则深度测试将遮挡低分辨率像素和所有 16 个高分辨率像素的粒子。结果可能是前景物体周围出现光晕。如下图b所示

最大深度样本

可以通过进行多个单点采样来改善光晕。上图中的c显示了downsample时应用最大函数的结果。从全分辨率深度数据中对四个深度值进行采样,并取最大的一个。这具有缩小粒子屏幕外图像中的对象轮廓的效果。但这绝对没有物理或理论依据。它只是看起来比光环更好。这是一个权宜之计。这就是深度downsample函数必须由应用程序定义的原因。这些瑕疵足够小,当低分辨率粒子图像添加回主帧缓冲区时,它们很大程度上被线性混合隐藏。

深度测试和软粒子

深度测试是通过像素着色器中的比较来实现的。downsample深度缓冲区绑定为纹理(DirectX 10 中的 SRV)。正在渲染的粒子的深度在顶点着色器中计算并向下传递到像素着色器。

在像素着色器中实现的深度测试
float4 particlePS(VS_OUT vIn): COLOR {   float myDepth = vIn.depth;   float sceneDepth = tex2D(depthSampler, vIn.screenUV).x;   if (myDepth > sceneDepth)     discard;   // Compute color, etc.   . . . } 

软粒子效果会在粒子接近与实体场景对象相交处时淡化粒子 alpha。从而避免了恶劣、尖锐的交叉路口。视觉效果非常好。

软粒子比二元深度测试更好
float4 particlePS(VS_OUT vIn): COLOR {   float myDepth = vIn.depth;   float sceneDepth = tex2D(depthSampler, vIn.screenUV).x;   float zFade = saturate(scale * (myDepth - sceneDepth));   // Compute (r,g,b,a), etc.   . . .   return float4(r,g,b, a * zFade); } 

Alpha 混合

部分透明的粒子效果通常混合到帧缓冲区中。设置 Alpha 混合渲染状态以创建混合方程:

1 = d ( 1 - 1 ) + 1 1 ,

其中1是最终颜色,d是帧缓冲区中已有的目标颜色,1和 1是像素着色器输出的传入源颜色和 Alpha。如果随后在p 1上混合更多粒子,结果将变为

2 = ( 1 )(1 - 2 ) + 2 2 = ( d (1 - 1 ) + 1 1 )(1 - 2 ) + 2 2 ,

3 = ( 2 )(1 - 3 ) + 3 3 = (( d (1 - 1 ) + 1 1 )(1 - 2 ) + 2 2 (1 - 3 ) + 3 3 ,

将p 3 的方程重新排列为组ds项给出

3 = d (1 - 1 )(1 - 2 )(1 - 3 ) + 1 1 (1 - 2 )(1 - 3 ) + 2 2 (1 - 3 ) + 3 3。

将除帧缓冲区之外的所有内容存储离屏渲染目标中。最终的组合操作需要生成3,或更一般地说n

先取d项——这样更简单。它乘以每个混合的 alpha 值的倒数。因此,我们将这些乘法累加到渲染目标的 Alpha 通道中。

接下来,检查对3起作用的s项。请注意,如果目标初始化为零,则它们采用与2、3等的传统 alpha 混合方程相同的形式:

0(1 - 1 ) + 1 1

0(1 - 1 )(1 - 2 ) + 1 1 (1 - 2 ) + 2 2

0(1 - 1 )(1 - 2 )(1 - 3 ) + 1 1 (1 - 2 )(1 - 3 ) + 2 2 (1 - 3 )+ 3 3

用于渲染屏幕外粒子的 Alpha 混合状态
AlphaBlendEnable = true; SrcBlend = SrcAlpha; DestBlend = InvSrcAlpha; SeparateAlphaBlendEnable = true; SrcBlendAlpha  = Zero; DestBlendAlpha = InvSrcAlpha; 

将粒子添加到帧缓冲区中以实现火焰等发射效果也很常见。处理这种情况比前面的 alpha 混合情况简单得多。值会累加到离屏目标中,然后添加到帧缓冲区中。

混合分辨率渲染

使用最大深度值对深度进行下采样可减少光晕。然而,屏幕外的粒子图像仍然可能是不可接受的效果。

当图像动画化时,这些伪影会闪烁并弹出。这在快速移动的动画中可能并不明显。然而,缓慢且连续地移动就会有这种效果。

我们可以通过边缘检测后然后对边缘做全分辨率处理来解决这个问题。

边缘检测

检查渲染目标中的离屏颜色和 Alpha 通道

使用标准 Sobel 滤波器可以轻松实现边缘检测。结果存储在另一个离屏渲染目标中。

使用模板进行排版

边缘检测滤波器选择一小部分块状像素。可以通过仅在出现边缘的地方以全帧缓冲区分辨率渲染粒子来修复像素化区域。

模板缓冲区用于有效屏蔽帧缓冲区的区域。当低分辨率、离屏粒子被组合回主帧缓冲区时,我们同时写入模板缓冲区。模板值无法由像素着色器显式设置。所以需要为所有像素启用模板写入,然后像素着色器可以调用丢弃关键字来禁止输出。这可以防止该像素的模板写入,并且像素着色器可以有效地控制模板值,从而创建蒙版。

然后对粒子进行第二次渲染。模板状态的设置使得像素仅写入先前合成通道未触及的区域 。这里显示下结果。

结果

上图将全分辨率粒子与混合分辨率结果进行了比较,其中屏幕外表面按 4x4 缩放。混合分辨率结果非常接近高分辨率版本。但是要注意低分辨率表面无法充分表示此细节,也要注意高频的图像是无法这样处理的,并且可能会导致画面模糊。

结论

结果表明,低分辨率离屏渲染可以带来显着的性能提升。它有助于使粒子的成本更具可扩展性,以便在过度绘制的最坏情况下(粒子系统填满屏幕)帧速率不会急剧下降。在真实的游戏中,这将转化为更一致的帧速率体验。

从视觉上看,结果永远不可能完全没有伪影,因为没有完美的方法来对深度值进行downsample。所以总会存在一些采样不足的问题。然而,在游戏中,诸如爆炸之类的粒子通常会快速移动,因此小的伪影可能是可以接受的。

这篇关于基于可变分辨率的半透明特效渲染优化方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/777531

相关文章

C#使用HttpClient进行Post请求出现超时问题的解决及优化

《C#使用HttpClient进行Post请求出现超时问题的解决及优化》最近我的控制台程序发现有时候总是出现请求超时等问题,通常好几分钟最多只有3-4个请求,在使用apipost发现并发10个5分钟也... 目录优化结论单例HttpClient连接池耗尽和并发并发异步最终优化后优化结论我直接上优化结论吧,

Java内存泄漏问题的排查、优化与最佳实践

《Java内存泄漏问题的排查、优化与最佳实践》在Java开发中,内存泄漏是一个常见且令人头疼的问题,内存泄漏指的是程序在运行过程中,已经不再使用的对象没有被及时释放,从而导致内存占用不断增加,最终... 目录引言1. 什么是内存泄漏?常见的内存泄漏情况2. 如何排查 Java 中的内存泄漏?2.1 使用 J

MySQL不使用子查询的原因及优化案例

《MySQL不使用子查询的原因及优化案例》对于mysql,不推荐使用子查询,效率太差,执行子查询时,MYSQL需要创建临时表,查询完毕后再删除这些临时表,所以,子查询的速度会受到一定的影响,本文给大家... 目录不推荐使用子查询和JOIN的原因解决方案优化案例案例1:查询所有有库存的商品信息案例2:使用EX

MySQL中my.ini文件的基础配置和优化配置方式

《MySQL中my.ini文件的基础配置和优化配置方式》文章讨论了数据库异步同步的优化思路,包括三个主要方面:幂等性、时序和延迟,作者还分享了MySQL配置文件的优化经验,并鼓励读者提供支持... 目录mysql my.ini文件的配置和优化配置优化思路MySQL配置文件优化总结MySQL my.ini文件

Java解析JSON的六种方案

《Java解析JSON的六种方案》这篇文章介绍了6种JSON解析方案,包括Jackson、Gson、FastJSON、JsonPath、、手动解析,分别阐述了它们的功能特点、代码示例、高级功能、优缺点... 目录前言1. 使用 Jackson:业界标配功能特点代码示例高级功能优缺点2. 使用 Gson:轻量

正则表达式高级应用与性能优化记录

《正则表达式高级应用与性能优化记录》本文介绍了正则表达式的高级应用和性能优化技巧,包括文本拆分、合并、XML/HTML解析、数据分析、以及性能优化方法,通过这些技巧,可以更高效地利用正则表达式进行复杂... 目录第6章:正则表达式的高级应用6.1 模式匹配与文本处理6.1.1 文本拆分6.1.2 文本合并6

Redis KEYS查询大批量数据替代方案

《RedisKEYS查询大批量数据替代方案》在使用Redis时,KEYS命令虽然简单直接,但其全表扫描的特性在处理大规模数据时会导致性能问题,甚至可能阻塞Redis服务,本文将介绍SCAN命令、有序... 目录前言KEYS命令问题背景替代方案1.使用 SCAN 命令2. 使用有序集合(Sorted Set)

MyBatis延迟加载的处理方案

《MyBatis延迟加载的处理方案》MyBatis支持延迟加载(LazyLoading),允许在需要数据时才从数据库加载,而不是在查询结果第一次返回时就立即加载所有数据,延迟加载的核心思想是,将关联对... 目录MyBATis如何处理延迟加载?延迟加载的原理1. 开启延迟加载2. 延迟加载的配置2.1 使用

Android WebView的加载超时处理方案

《AndroidWebView的加载超时处理方案》在Android开发中,WebView是一个常用的组件,用于在应用中嵌入网页,然而,当网络状况不佳或页面加载过慢时,用户可能会遇到加载超时的问题,本... 目录引言一、WebView加载超时的原因二、加载超时处理方案1. 使用Handler和Timer进行超

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义