前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Unity通用渲染管线(URP)系列(十五)——粒子(Color and Depth Textures)

Unity通用渲染管线(URP)系列(十五)——粒子(Color and Depth Textures)

作者头像
放牛的星星
发布2021-01-11 14:59:33
4.3K0
发布2021-01-11 14:59:33
举报
文章被收录于专栏:壹种念头壹种念头

目录

· 1 Unlit 粒子

· 1.1 粒子系统

· 1.2 不受光粒子着色器

· 1.3 顶点色

· 1.4 Flipbooks

· 1.5 Flipbook 混合

· 2 接近相机后淡化

· 2.1 片段数据

· 2.2 片段深度

· 2.3 正交深度

· 2.4 基于距离的淡化

· 3 Soft 粒子

· 3.1 分离深度Buffer

· 3.2 拷贝深度

· 3.3 不带Post FX的拷贝深度

· 3.4 重建视口空间深度

· 3.5 可选的深度纹理

· 3.6 丢失纹理

· 3.7 接近背景时淡化粒子

· 3.8 不支持拷贝纹理

· 3.9 Gizmos 和深度

· 4 Distortion

· 4.1 颜色拷贝纹理

· 4.2 采样缓存颜色

· 4.3 Distortion 向量

· 4.4 扰动混合

本文重点内容: 1、支持 flipbook, near fade, soft, 和 distortion类型的粒子 2、为正交和透视投影确定片段深度 3、拷贝和采样颜色和深度缓存

这是有关创建自定义脚本渲染管线的系列教程的第15部分。我们将基于颜色和深度纹理来创建基于深度的淡入和扭曲粒子。

本教程是CatLikeCoding系列的一部分,原文地址见文章底部。

本教程使用Unity 2019.4.14f1制作。

(使用粒子创建混乱的气流)

修正和改进 当没有阴影时,WebGL 2.0构建会产生错误。发生这种情况是因为WebGL无法匹配缺少纹理的阴影采样器。我已通过确保始终存在阴影纹理来对此进行补救。这是对Shadows的更改:

除此之外,我将Post FX着色器菜单标签更改为“Hidden/Custom RP/Post FX Stack”,因此在为材质选择着色器时不会显示该标签。我也将fxUV重命名为screenUV。

1 Unlit 粒子

粒子系统可以使用任何材质,因此我们的RP已经可以渲染它们,但它有一定限制。在本教程中,我们将仅考虑不受光的粒子。受光的粒子以相同的方式工作,只是具有更多的着色器属性和光照计算。

我为粒子设置了一个新场景,它是已经存在的测试场景的变体。它有几个长的垂直立方体和一个明亮的黄色灯泡,用作粒子系统的背景。

(没有粒子 没有Post FX的后处理)

1.1 粒子系统

通过GameObject / Effects / Particle System创建一个粒子系统,并将其定位在地平面以下。我假设你已经知道如何配置粒子系统,我不会对此进行详细介绍。如果还不会,请查看Unity的文档以了解特定模块及其设置。 https://docs.unity3d.com/Manual/class-ParticleSystem.html

Visual Effects Graph 是什么? VFX Graph基于计算着色器,目前与URP和HDRP紧密结合。自定义SRP不能轻易使用它。

默认系统使粒子向上移动并填充锥形区域。如果我们将不受光的材质分配给它,则粒子将显示为与相机平面对齐的纯白色正方形。它们会突然出现并消失,但由于它们从平面以下开始,因此似乎从地面升起。

(默认的例子系统使用了不受光材质,位置在地表以下)

1.2 不受光粒子着色器

我们可以将不受光的着色器用于粒子,但是让我们为它们创建专用的着色器。它以unlit着色器的拷贝开始,其菜单项更改为“Custom RP/Particles/Unlit”。另外,由于粒子始终是动态的,因此不需要Meta Pass。

使用此着色器为unlit的粒子创建专用的材质,然后让粒子系统使用它。当前,它等同于较早的unlit材质。如果同时为材质和粒子系统启用了阴影,也可以将粒子系统设置为渲染网格,甚至是阴影。但是,GPU实例化不起作用,因为粒子系统为此使用了过程绘制,我们将不在本教程中介绍。取而代之的是像广告牌粒子一样,将所有粒子网格合并为一个网格。

(球Mesh粒子,带有阴影)

从现在开始,我们只会关注广告牌粒子(billboard particles),并且不会产生阴影。下面是单个粒子的贴图,其中包含一个简单的平滑淡色的白色圆盘。

(单个例子的Base map 黑色的背景上)

当使用该纹理作为淡入淡出粒子时,我们得到的效果很简单,看起来就像白烟从地面冒出来。为了使其更具说服力,可将发射率增加到大约100。

(纹理化后的广告牌粒子,发射频率增加到100)

1.3 顶点色

每个粒子可以使用不同的颜色。证明这一点的最简单方法是将起始颜色设置为在黑白之间随机值。但是,这样做目前不会更改粒子的外观表现。为了完成这项工作,我们需要在着色器中添加对顶点颜色的支持。为UnlitPass添加对它的支持,而不是为粒子创建新的HLSL文件。

也将其添加到Varyings中,并通过UnlitPassVertex Pass,但仅在定义了_VERTEX_COLORS的情况下。这样,我们可以根据需要启用和禁用顶点颜色支持。

接下来,在UnlitInput的InputConfig中添加一种颜色,默认情况下将其设置为不透明的白色,并将其分解为GetBase的结果。

返回UnlitPass,如果内插的顶点颜色存在于UnlitPassFragment中,则将其复制到config。

要最终向UnlitParticles添加对顶点颜色的支持,请向其中添加一个开关着色器属性。

以及定义关键字的相应着色器特性。如果你想让普通的Unlit着色器也支持顶点颜色,也可以这么做。

(使用了顶点色,没有和有距离排序)

现在,我们得到了彩色的粒子。现在,粒子分类成为了新的问题。如果所有粒子的颜色相同,则绘制顺序无关紧要,但是如果它们不同,则需要按距离对它们进行排序以得到正确的结果。请注意,基于距离进行排序时,粒子可能会由于视图更改的位置而突然互换绘制顺序,就像任何透明对象一样。

1.4 Flipbooks

通过循环浏览不同的底图,可以对广告牌粒子进行动画处理。Unity将它们称为Flipbooks粒子。这是通过使用以规则网格布局的纹理图集来完成的,就像包含一个循环噪声模式的4×4网格的纹理一样。

(黑色背景上的,flipbook粒子的贴图)

创建一个新的unlit的粒子材质,该材质使用 flipbook 贴图,然后复制我们的粒子系统,并使其使用该该材质使用flipbook材质。停用单粒子版本,因此我们仅看到flipbook系统。由于每个粒子现在都代表一个小的云朵,因此其大小增加到大约2倍。启用粒子系统的Texture Sheet Animation模块,将其配置为4×4活页簿,使其从随机帧开始,并在执行过程中经历一个周期 粒子的寿命。

可以通过在任意旋转开始的同时沿50%的时间沿X和Y方向随机翻转粒子,并使粒子以随机速度旋转来添加额外的变化。

(Flipbook 粒子系统)

1.5 Flipbook 混合

当系统处于运动状态时,很明显粒子会循环几个帧,因为Flipbook的帧率非常低。对于一个寿命为五秒的粒子,则为每秒3.2帧。可以通过在连续帧之间进行融合来消除这种情况。这就要求我们向着色器发送第二对UV坐标和一个动画混合因子。我们通过在Renderer模块中启用自定义顶点流来实现。添加UV2和AnimBlend。你也可以删除普通流,因为我们不需要它。

(自定义顶点流)

在添加了流之后,会显示一个错误,表明粒子系统和当前使用的着色器不匹配。这个错误将在我们在着色器中使用这些流之后消失。添加一个shader关键字来切换属性到UnlitParticle来控制我们是否支持flipbook混合。

以及着色器功能。

如果flipbook混合有效,则通过TEXCOORD0提供两个UV对,因此必须是float4而不是float2。混合因子通过TEXCOORD1作为单个浮点提供。

如果需要,我们会将新数据作为单个float3 flipbookUVB字段添加到Varyings。

调整UnlitPassVertex,以便在适当时将所有相关数据复制到其中。

将flipbookUVB以及一个布尔值添加到InputConfig中,以指示是否启用了flipbook混合,默认情况下为否。

如果启用了flipbook混合,我们必须使用flipbook UV在GetBase中第二次对Base Map进行采样,然后根据混合因子从第一个样本插入到第二个样本。

最后激活flipbook混合,在适当的时候覆盖默认配置的UnlitPassFragment。

(Flipbook 混合)

2 接近相机后淡化

当相机位于粒子系统内部时,粒子最终会非常靠近相机附近的位置,并且还会从一侧穿过到另一侧。粒子系统具有Renderer / Max Particle Size属性,可防止单个广告牌粒子覆盖过多的窗口。一旦它们达到最大可见尺寸,它们就会滑开,而不是随着它们接近近平面而变大。

处理靠近近平面的粒子的另一种方法是根据粒子的碎片深度使其淡出。当移动通过代表大气效应的粒子系统时,看起来会更好。

2.1 片段数据

我们已经在out片段函数中有了片段深度。它是通过带有SV位置语义的float4提供的。我们已经使用了它的XY组件来进行抖动,但是现在让我们完全地使用片段数据。

在片段函数中,SV_POSITION表示顶点的裁剪空间位置,为4D齐次坐标。但是在片段函数中,SV_POSITION表示片段的屏幕空间(也称为窗口空间)位置。空间转换由GPU执行。为了明确起见,让我们在所有Varyings结构中将postionCS重命名为positionCS_SS。

在附带的顶点功能中也进行调整。

接下来,我们将介绍一个新的Fragment HLSL包含文件,该文件包含一个Fragment结构和一个GetFragment函数,该函数在给定float4屏幕空间位置矢量的情况下返回该片段。最初,片段仅具有2D位置,该位置来自屏幕空间位置的XY分量。这些是具有0.5偏移的texel坐标。屏幕左下角的纹素为(0.5,0.5),屏幕右边的纹素为(1.5,0.5),依此类推。

在所有其他include语句之后将此文件包含在Common中,然后调整ClipLOD,使其第一个参数为Fragment而不是float4。

现在,我们还要在Common中定义常见的线性和点钳位采样器状态,因为稍后将在多个位置使用它们。在包含Fragment之前,请执行此操作。

然后从PostFXStackPasses中删除通用采样器定义,因为现在这是重复的定义,可能会导致编译器错误。

接下来,将一个片段添加到LitInput和UnlitInput的InputConfig结构中。然后将屏幕空间位置矢量作为第一个参数添加到GetInputConfig函数,以便它们可以调用GetFragment。

在调用GetInputConfig的所有位置添加参数。

然后调整LitPassFragment,使其在获取配置后调用ClipLOD,以便将片段传递给它。还将片段的位置传递给InterleavedGradientNoise,而不是直接使用input.positionCS_SS。

ShadowCasterPassFragment也需要更改,以便在获取配置后进行剪辑。

2.2 片段深度

为了使靠近相机的粒子褪色,我们需要知道片段的深度。因此,向Fragment添加一个深度字段。

片段深度存储在屏幕空间位置向量的最后一个分量中。它是用于执行透视划分以将3D位置投影到屏幕上的值。这是视图空间的深度,因此它是距相机XY平面而不是其近平面的距离。

什么是视图空间? 它是旋转和平移后的世界空间,因此相机最终在原点是没有旋转的。

我们可以通过直接返回LitPassFragment和UnlitPassFragment中的片段深度(按比例缩小)来验证这是正确的,因此我们可以将其视为灰度渐变。

(片段深度,除以20)

2.3 正交深度

以上方法仅在使用透视相机时有效。使用正交摄影机时,不会进行透视划分,因此屏幕空间位置矢量的最后一个分量始终为1。

我们可以通过将float4 unity_OrthoParams字段添加到UnityInput来确定是否正在使用正交相机,Unity通过该字段将有关正交摄影机的信息传达给GPU。

如果是正交相机,则其最后一个分量将为1,否则将为零。向使用此事实的Common添加一个IsOrthographicCamera函数,该函数在包含Fragment之前已定义,因此我们可以在其中使用它。如果你永远不会使用正交相机,则可以对其进行硬编码以返回false,或者通过shader关键字进行控制。

对于正交相机,我们能做的最好的就是依靠屏幕空间位置矢量的Z分量,该分量包含转换后片段的片段空间深度。这是用于深度比较的原始值,如果启用了深度写入,则会将其写入深度缓冲区。它是0~1范围内的值,对于正投影而言是线性的。要将其转换为视图空间深度,我们需要根据相机的近距离范围对其进行缩放,然后加上近平面距离。近距离和远距离存储在_ProjectionParams的Y和Z分量中。如果使用了反向深度缓冲区,我们还需要反向原始深度。在新的OrthographicDepthBufferToLinear函数中执行此操作,该函数在包含Fragment之前也在Common中定义。

现在,GetFragment可以检查是否使用了正交相机,如果使用了正交相机,则可以依靠OrthographicDepthBufferToLinear确定片段深度。

(正交相机的片段深度)

在验证片段深度对于两种摄像机类型都是正确的之后,从LitPassFragment和UnlitPassFragment中删除调试可视化的代码。

2.4 基于距离的淡化

返回UnlitParticles着色器,添加一个Near Fade关键字的toggle属性,以及使其距离和范围可配置的属性。该距离决定了粒子应完全消失在相机平面附近的程度。但这是相机平面,而不是其近平面。因此,需要添加使用近平面的值。1是合理的默认值。该范围控制过渡区域的长度,在该区域内粒子将线性淡出。同样,1是一个合理的默认值,或者至少需要是一个小的正值。

添加一个着色器特性来启用near fading。

然后在UnlitInput的UnityPerMaterial缓冲区中包含距离和范围。

接下来,在InputConfig中添加一个布尔值nearFade字段,以控制near fading是否处于活动状态,默认情况下不启用。

通过简单地降低片段的基本alpha值,即可在靠近相机时候淡化。fade因子等于片段深度减去fade距离,然后除以fade范围。结果是在将其淡化为底图的alpha之前,它可以是负饱和的。适当时在GetBase中执行此操作。

最后,要激活该功能,请将_NEAR_FADE关键字(如果已定义)在UnlitPassFragment中将片段的nearFade字段设置为true。

‍ (调整 near fade距离)

3 Soft 粒子

当广告牌粒子与几何形状相交时,尖锐的过渡在视觉上既震撼人心,又使它们的平坦性质变得明显。解决方案是使用柔软的粒子,当它们后面有不透明的几何形状时,它们会淡出。为了使这项工作有效,需要将粒子的碎片深度与之前在相机缓冲区中相同位置绘制的任何物体的深度进行比较。这意味着我们需要对深度缓冲区进行采样。

3.1 分离深度Buffer

到目前为止,我们一直为相机使用单个帧缓冲区,其中包含颜色和深度信息。这是典型的帧缓冲区配置,但是颜色和深度数据始终存储在单独的缓冲区中,称为帧缓冲区附件。要访问深度缓冲区,我们需要分开定义这些附件。

第一步是用两个标识符替换CameraRenderer中的_CameraFrameBuffer标识符,我们将其命名为_CameraColorAttachment和_CameraDepthAttachment。

在Render中,我们现在需要将颜色附件传递给PostFXStack.Render,其功能与我们之前所做的等效。

在Setup中,我们现在需要获得两个独立缓冲区,而不是一个复合缓冲区。颜色缓冲区没有深度,而深度缓冲区的格式为RenderTextureFormat.Depth,其过滤模式为FilterMode.Point,因为混合深度数据没有意义。可以通过一次调用SetRenderTarget来设置两个附件,并为每个附件使用相同的加载和存储操作。

两个缓冲区也需要被释放。一旦完成,我们的RP仍然可以像以前一样工作,但是现在有了帧缓冲区附件,我们可以单独访问它们。

3.2 拷贝深度

我们不能在深度缓冲区用于渲染的同时对其进行采样。我们需要复制它。因此,引入_CameraDepthTexture标识符,并添加一个布尔值字段以指示我们是否正在使用深度纹理。仅应在需要时才考虑复制深度,这将在获取相机设置后在Render中确定。但是我们一开始只是始终启用它。

创建一个新的CopyAttachments方法,该方法将在需要时获取一个临时的重复深度纹理,并将深度附件数据复制到其中。这可以通过在命令缓冲区上使用源纹理和目标纹理调用CopyTexture来完成。这比通过全屏draw call进行操作要有效得多。另外,请确保在Cleanup中释放额外的深度纹理。

在绘制了所有不透明的几何图形之后,我们将仅复制一次附件,因此在Render中的天空盒之后。这意味着深度纹理仅在渲染透明对象时可用。

3.3 不带Post FX的拷贝深度

仅当我们需要复制的深度附件时,复制深度才有效(当前仅在启用post FX的情况下)。为了不使用post FX,我们还需要在使用深度纹理时使用中间帧缓冲区。引入useIntermediateBuffer布尔值字段以对此进行追踪,并在可能获取附件之前在安装程序中对其进行了初始化。现在,无论是使用深度纹理还是启用FX后,都应该执行此操作。Cleanup 也受到相同的影响。

但是现在,当没有Post FX处于活动状态时,渲染将失败,因为我们仅渲染到中间缓冲区。还需要执行到摄像机目标的最终复制。不幸的是,我们只能使用CopyTexture复制到渲染纹理,而不能复制到最终的帧缓冲区。我们可以使用FX copy后的Pass来完成此操作,但是此步骤特定于相机渲染器,因此我们将为其创建专用的CameraRenderer着色器。它的开始与PostFX着色器相同,但只有一个Copy Pass,并且包括自己的HLSL文件。

新的CameraRendererPasses HLSL文件具有与PostFXStackPasses相同的Varyings结构和DefaultPassVertex函数。它还具有_SourceTexture纹理和CopyPassFragment函数,该函数仅返回采样的源纹理。

接下来,将一个材质字段添加到CameraRenderer。要初始化它,请创建一个带有着色器参数的公共构造函数方法,并以着色器作为参数调用CoreUtils.CreateEngineMaterial。该方法将创建一个新材质并将其设置为在编辑器中隐藏,以确保不会将其另存为资产,因此我们不必自己专门进行此操作。如果缺少着色器,它会记录一个错误。

再添加一个公共Dispose方法,该方法通过将其Pass给CoreUtils.Destroy来销毁该材质。该方法会定期或立即破坏材质,具体取决于Unity是否处于播放模式。我们之所以需要这样做,是因为每当修改RP资产时都会创建新的RP实例,从而创建渲染器,这可能会导致在编辑器中创建过多材质。

现在,CustomRenderPipeline在构造其渲染器时必须提供一个着色器。因此,我们将在其自己的构造函数方法中进行此操作,并为其添加照相机渲染器着色器的参数。

从现在开始,它也必须在自身销毁的时候在renderer上调用Dispose。我们已经为它创建了一个Dispose方法,但是仅用于编辑器代码。将该版本重命名为DisposeForEditor,并且它会重置灯光映射委托。

然后添加一个新的Dispose方法,该方法不是仅用于编辑器的,它调用其基本实现,编辑器的版本,并最终处理renderer。

在顶层,CustomRenderPipelineAsset必须获得一个着色器配置属性并将其传递给管道构造函数。然后我们可以最终连接着色器。

(相机的renderer shader 连接上了)

此时,CameraRenderer具有功能材质。还要向其中添加_SourceTexture标识符,并为其提供一个Draw方法,类似于PostFXStack中的Draw方法,除了没有用于Pass的参数外,因为此时我们只有一个Pass。

为了最终修复渲染器,如果Post FX没有激活,但是我们使用中间缓冲区,则通过调用Draw将颜色附件复制到Render中的摄影机目标。

但是,如果相机恰好以渲染纹理为目标,那么我们可以改用CopyTexture。

3.4 重建视口空间深度

要采样深度纹理,我们需要在屏幕空间中的片段的UV坐标。可以通过将其位置除以屏幕像素尺寸来找到这些像素,Unity通过float4 _ScreenParams的XY组件可以使用这些像素,因此将其添加到UnityInput。

然后我们可以将片段UV和缓冲区深度添加到Fragment中。使用Point钳位采样器通过SAMPLE_DEPTH_TEXTURE宏对摄像机深度纹理进行采样,以检索缓冲区深度。该宏与SAMPLE_TEXTURE2D相同,但仅返回R通道。

这给了我们原始的深度缓冲值。要将其转换为视图空间深度,可以在使用正交摄影机的情况下再次调用OrthographicDepthBufferToLinear,例如当前片段的深度。透视深度也需要转换,为此我们可以使用LinearEyeDepth。它需要_ZBufferParams作为第二个参数。

_ZBufferParams是Unity提供的另一个float4,其中包含从原始深度到线性深度的转换因子。将其添加到UnityInput。

要检查我们是否正确采样了缓冲区深度,请像之前测试片段深度一样,以UnlitPassFragment缩放比例返回它。

(缓冲区深度,透视和正交投影)

清除采样深度正确后,删除调试可视化文件。

3.5 可选的深度纹理

复制深度需要额外的工作,尤其是在不使用Post FX的情况下,因为这还需要中间缓冲区和向摄像机目标的额外复制。因此,让我们对其RP是否支持复制深度进行配置。为此,我们将创建一个新的CameraBufferSettings结构,并将其放入自己的文件中,该文件用于将与摄像机缓冲区相关的所有设置分组。除了用于复制深度的切换开关外,还放置了切换开关以允许HDR进入其中。并且还引入了一个单独的切换开关来控制渲染反射时是否复制深度。这很有用,因为反射是在没有post FX的情况下渲染的,并且粒子系统也不会出现在反射中,因此反射的深度复制非常昂贵,而且可能毫无用处。我们之所以做了,是因为深度也可以用于其他效果,而这些效果在反射中可能是可见的。即使这样,也请记住,每个立方体贴图反射面的深度缓冲区是不同的,因此沿着立方体贴图边缘会有深度接缝。

用这些相机缓冲区设置替换CustomRenderPipelineAsset的当前HDR切换。

也将更改应用到CustomRenderPipeline。

现在,CameraRenderer.Render必须根据其是否渲染反射来使用适当的设置。

(相机的缓存设置,HDR 和不使用反射拷贝深度 功能启用)

除了整个RP的设置外,我们还可以向CameraSettings添加一个复制深度开关,默认情况下启用。

(相机拷贝深度开关)

然后,对于常规摄像机,仅当RP和摄像机都启用深度纹理时,才使用深度纹理,这与控制HDR的方式类似。

3.6 丢失纹理

由于深度纹理是可选的,因此可能不存在。无论如何,当着色器对其进行采样时,结果将是随机的。它可能是空的纹理,可能是旧的副本,也可能是其他相机的副本。在不透明的渲染阶段,着色器也可能过早采样深度纹理。我们要做的至少是确保无效样本也能得到正确的结果。为此,我们在CameraRender的构造方法中创建默认的缺省纹理。没有用于纹理的CoreUtils方法,因此我们将其隐藏标志设置为HideFlags.HideAndDontSave。将其命名为Missing,因此很明显在通过帧调试器检查着色器属性时查看到使用了错误的纹理。将其设为所有通道均设置为0.5的简单1×1纹理。放置渲染器时也要适当销毁它。

在Setup结束时使用缺失的纹理作为深度纹理。

3.7 接近背景时淡化粒子

现在我们有了功能深度纹理,可以继续操作以最终支持软粒子。第一步是向UnlitParticles添加软粒子关键字切换的着色器属性,距离和范围,类似于近淡化属性。在这种情况下,距离是从粒子后面的任何东西开始测量的,因此我们默认将其设置为零。

也为其添加着色器特性。

与near fading一样,如果定义了关键字,则在UnlitPassFragment中将适当的配置字段设置为true。

在UnlitInput中,将新的着色器属性添加到UnityPerMaterial,将字段添加到InputConfig。

然后,根据片段的缓冲区深度减去其自身的深度,在GetBase中应用另一个near 衰减。

(软粒子,调整淡化范围)

3.8 不支持拷贝纹理

现在所有结果都很好,但前提是至少在基本级别上支持通过CopyTexture直接复制纹理。大多数情况下是这样,但WebGL 2.0则不然。因此,如果我们还想支持WebGL 2.0,我们将转而使用着色器进行复制,效率较低,但至少可以正常工作。

通过CameraRenderer中的静态布尔字段跟踪是否支持CopyTexture。最初将其设置为false,因此即使我们的开发机器都支持它,我们也可以测试后备方法。

在CopyAttachments中,如果支持,则通过CopyTexture复制深度深度,否则退回到使用我们的Draw方法。

由于Draw更改了渲染目标,因此最初无法产生正确的结果,因此进一步的绘制会出错。之后,我们必须将渲染目标设置回相机缓冲区,再次加载附件。

发生的第二件事是,深度根本不会被复制,因为我们的复制过程仅写入默认的着色器目标,该目标是针对颜色数据的,而不是深度。要复制深度,我们需要向CameraRenderer着色器添加第二个复制深度通道,以写入深度而不是颜色。我们通过将其ColorMask设置为零并打开ZWrite来实现。它还需要一个特殊的片段函数,我们将其命名为CopyDepthPassFragment。

新的fragment函数需要对深度进行采样,并将其作为具有SV_DEPTH语义的单个float而不是具有SV_TARGET语义的float4来返回。这样,我们对原始深度缓冲区值进行采样,并将其直接用于片段的新深度。

接下来,返回CameraRenderer并在Draw中添加一个布尔参数,以指示我们是否从深度绘制到深度,默认情况下设置为false。如果是这样,请使用第二Pass而不是第一Pass。

然后在复制深度缓冲区时表明我们正在使用深度。

在确认此方法同样有效后,通过检查SystemInfo.copyTextureSupport来确定是否支持CopyTexture。任何高于零的支持都是可以的。

3.9 Gizmos 和深度

现在我们有了绘制深度的方法,可以结合Post FX或使用深度纹理时,使用它来让Gizmos再次具有深度感知能力。在DrawGizmosBeforeFX中,在绘制第一个Gizmos之前,如果我们使用中间缓冲区,则将深度复制到相机目标。

(Gizmos识别了深度)

4 Distortion

我们还将支持的Unity粒子的另一个功能是变形/扰动,该变形可用于创建诸如热量引起的大气折射之类的效果。这需要对颜色缓冲区进行采样,就像我们已经对深度缓冲区进行采样一样,但是要添加UV偏移。

4.1 颜色拷贝纹理

我们首先添加用于将颜色复制到CameraBufferSettings的切换开关,对于常规相机和反射相机而言,又添加了一个单独的开关。

(拷贝颜色和深度)

还可以按摄像机配置拷贝颜色。

(相机也开启)

现在,CameraRendering还需要追踪颜色纹理的标识符以及是否使用颜色纹理。

现在是否使用中间缓冲区还取决于是否使用了颜色纹理。并且我们还应该首先将颜色纹理设置为缺少的纹理。清理时也将其释放。

现在,当使用颜色或深度纹理或同时使用两者时,我们需要复制相机附件。让我们以此来调用CopyAttachments。

然后,我们可以让它分别复制两个纹理,然后重置渲染目标并执行一次缓冲区。

4.2 采样缓存颜色

要采样相机的颜色纹理,请将其添加到Fragment中。我们不会在Fragment中添加缓冲区颜色属性,因为我们对颜色的确切位置不感兴趣。相反,我们引入了一个GetBufferColor函数,该函数将片段和UV偏移作为参数,从而重新调整采样的颜色。

要测试此结果,请在UnlitPassFragment中返回两个方向上具有较小偏移量(例如5%)的缓冲区颜色。

(采样相机颜色缓存,带有偏移)

请注意,因为颜色是在不透明的阶段之后复制的,因此会透明对象。因此,粒子会擦除在它们之前绘制的所有透明对象,或者粒子彼此之间相互擦除。同时,深度在这种情况下不起作用,因此比片段本身更靠近摄影机平面的片段的颜色也会被复制。清除调试可视化文件后,请清除它。

是否可以避免在片段前面采样? 是的,在一定程度上。有关示例,请参阅“Looking Through Water”教程。

4.3 Distortion 向量

为了产生有用的扰动效果,我们需要平滑过渡的扰动矢量图。这是单个圆形粒子的简单映射。下面是一个法线贴图,因此应将其导入。

(粒子扰动贴图)

将关键字切换着色器属性以及变形贴图和强度属性添加到UnlitParticles。变形将作为屏幕空间的UV偏移应用,因此需要较小的值。让我们使用0~0.2的强度范围,默认值为0.1。

(扰动开启)

添加所需的着色器特性。

然后将扰动贴图和强度属性添加到UnlitInput。

引入一个新的GetDistortion函数,该函数返回float2向量。让它对变形贴图进行采样并像基础贴图一样应用flipbook混合,然后通过distortion strength解码法向缩放的法线。我们只需要向量的XY分量,因此丢弃Z。

在UnlitPassFragment中,如果启用了distortion,则将其检索并将其用作获取缓冲区颜色(覆盖基础颜色)的偏移量。裁剪后执行此操作。

(颜色缓冲区扰动)

结果是,粒子在径向上会扭曲颜色纹理,但在角落处除外,因为那里的扰动矢量为零。但是变形效果应取决于粒子的视觉强度,该强度由原始基本Alpha控制。因此,使用基本alpha调制扰动偏移矢量。

(调整后的扰动)

现在,我们仍然会遇到硬边,因为粒子彼此完全重叠并且是矩形。我们通过保留粒子的原始alpha来隐藏它。

(淡化扰动)

现在,扰动的颜色纹理采样也逐渐消失,这使未扰动的背景和其他粒子再次部分可见。结果是没有物理意义的平滑,但足以提供大气折射的幻觉。通过调整扰动强度以及通过在其使用寿命期间调整其颜色来平滑地淡入和淡出粒子,可以进一步改善此效果。而且,偏移矢量与屏幕对齐,并且不受粒子方向的影响。因此,如果将粒子设置为在其生命周期内旋转,则它们各自的变形模式看起来会是扰动的。

(扰动效果)

4.4 扰动混合

当前,当启用Distortion 时,我们将完全替换粒子的原始颜色,仅保留其alpha。可以通过多种方式将粒子颜色与变形的颜色缓冲区组合。我们将使用与Unity的粒子着色器相同的方法,添加一个简单的distortion blend着色器属性,以在粒子自身的颜色及其引起的扰动之间进行插值。

(Distortion blend 滑动条)

将该属性与一个函数一起添加到UnlitInput。

当混合滑块为1时,我们只会看到扰动。降低它可以显示粒子颜色,但不会完全隐藏扰动。取而代之的是,我们根据变形的alpha值减去混合滑块(饱和)从扰动到粒子颜色进行插值。因此,与禁用扰动相比,启用扰动时,粒子自身的颜色将始终较弱,并且看起来更小,除非完全不透明。在UnlitPassFragment中执行插值。

对于更复杂的粒子,这看起来更好,例如我们的flipbook示例。例如,这是flipbook的扰动纹理。

(flipbook粒子的扰动贴图)

这可用于创建有趣的扰动效果。逼真的效果将是细微的,因为在系统运动时稍微变形就足够了。但是出于演示目的,我使效果很强,因此即使在屏幕截图中,效果也很明显。

(flipbook 和 post FX的扰动效果)

欢迎扫描二维码,查看更多精彩内容。点击 阅读原文 可以跳转原教程。

本文翻译自 Jasper Flick的系列教程

原文地址:

https://catlikecoding.com/unity/tutorials

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-12-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 壹种念头 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档