Untiy URP RenderFeature 屏幕水滴效果
这一方法参考自连接里的“粒子”方法。
思路是将粒子结果绘制进一张RT中,在屏幕上去采样这张RT作为dir扰动信息扰动屏幕画面。(粒子贴一张圆形法线图)
public class RainDrop : ScriptableRendererFeature
{
[Serializable]
public class Setting
{
public Material material;
public float _normalIntensity;
[Range(0,4)]public float downSample;
}
public bool enable = false;
public Setting setting = new Setting();
public RainPass rainPass;
public override void Create()
{
rainPass = new RainPass();
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
if(!enable ) return;
rainPass.SetUp(setting, renderer.cameraColorTarget);
renderer.EnqueuePass(rainPass);
}
}
public class RainPass : ScriptableRenderPass
{
public RainDrop.Setting setting;
//传递进来的屏幕ColorRT
public RenderTargetIdentifier colRT;
//绘制particle的pass名称
ShaderTagId PtagId = new ShaderTagId("ParticleNormalMap");
//particle的ColorRT
private RenderTexture ParticleRt;
public RainPass()
{
//绘制事件在后效之前
renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
}
//过滤设置: 半透明队列里的effect层
FilteringSettings _filteringSettings = new FilteringSettings(RenderQueueRange.transparent, layerMask:LayerMask.GetMask("Effect"));
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
var material = setting.material;
if (material == null || material.shader == null || !material.shader.isSupported) return;
//屏幕雨滴buffer
CommandBuffer cmd = CommandBufferPool.Get("ScreenRain");
cmd.Clear();
//绘制设置: pass, renderingData, 半透明类,
DrawingSettings drawingSettings =
CreateDrawingSettings(PtagId, ref renderingData, SortingCriteria.CommonTransparent);
//根据调用,绘制设置,过滤设置 ref是为了在不同状态更新时复用drawRenderer
context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref _filteringSettings);
//设置绘制的贴图给全局变量 用以后面作为扰动源
Shader.SetGlobalTexture("_ParticleNormalTex", ParticleRt);
//屏幕画面
var descriptor = renderingData.cameraData.cameraTargetDescriptor;
descriptor.msaaSamples = 1;
descriptor.depthBufferBits = 0;
var texId = Shader.PropertyToID("_ScreenRain");
cmd.GetTemporaryRT(texId, descriptor);
material.SetFloat("_NormalIntensity", setting._normalIntensity);
//把shader 0 pass内容根据传递进来的屏幕colorRT绘制到我们的_ScreenRain图上
cmd.Blit(colRT, texId, material, 0);
//将_ScreenRain图复制回屏幕colorRT,这一步是因为我们不能边读边写屏幕colorRT
cmd.Blit(texId, colRT);
//释放我们的临时RT
cmd.ReleaseTemporaryRT(texId);
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
public void SetUp(RainDrop.Setting setting, RenderTargetIdentifier rendererCameraColorTarget)
{
this.setting = setting;
//传递屏幕colorRT进Pass里
colRT = rendererCameraColorTarget;
}
public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
{
if (!ParticleRt)
{
//粒子colorRT 做降采样
ParticleRt = new RenderTexture(512>>(int)setting.downSample, 512 >> (int)setting.downSample, 0);
ParticleRt.Create();
ParticleRt.name = "_ParticalRTNormal";
}
//绘制目标
ConfigureTarget(ParticleRt);
//绘制外内容设置为灰,我们为了过滤精度问题将a通道设置为0在pass中使用
ConfigureClear(ClearFlag.All, new Color(0.5f,0.5f,0.5f,0f));
}
}
Shader //TODO
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_NormalIntensity("NormalIntensity",Float) = 1
}
SubShader
{
Pass
{
name "Forward"
Tags
{
"LightMode" = "ScreenRain"
}
ZWrite off
ZTest Always
half4 frag(Varyings input) : SV_Target
{
// sample the texture
//normaltex是一张非sRGB的2d线性图
half4 normalVector = tex2D(_ParticleNormalTex, input.uv);
//手动unpack
float3 normalDir = normalVector * 2 - 1;
//乘以我们涂白部分就是只有白色部分会影响屏幕
normalDir.xy *= _NormalIntensity * normalVector.a;
//扰动屏幕画面
float2 uv = input.uv + normalDir.xy;
half4 col = SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex, uv);
return half4(col.rgb, 1);
}
ENDHLSL
}
Pass{
name "particle normal map"
Tags
{
"LightMode" = "ParticleNormalMap"
}
Blend One One
ColorMask RGBA
Cull Off Lighting Off ZWrite Off
half4 frag(Varyings input) : SV_Target
{
// sample the texture
half4 col = SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex, input.uv);
col.a = 1;//因为RenderFeature中我们将绘制信息外涂黑为0,所以有内容部分涂白为1
return col;
}
ENDHLSL
}
}
}
大功告成,接下来就是看粒子效果怎么调整像水滴了