一、创建一个用来控制效果开启和关闭的开关,获取处理过的贴图的管理类
using UnityEngine; public class GlassCtrl : MonoBehaviour { public static int Iterations = 2;//高斯模糊处理次数 public static int takeShot = 0;//1只处理一次获取贴图,-1一直处理更新贴图 public static RenderTexture renderTexture = null;//保存处理过的贴图 public void StartTakeShot(int width, int height, int takeShot) { ReleaseRenderTexture(); renderTexture = new RenderTexture(width, height, 32); GlassCtrl.takeShot = takeShot; } public void StopTakeShot() { takeShot = 0; } public void SetIterations(int time) { Iterations = time; } public void ReleaseRenderTexture() { if (renderTexture != null) { Destroy(renderTexture); renderTexture = null; } } public RenderTexture GetShotTexture() { return renderTexture; } }
二、创建一个ScriptableRenderPass
using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; public class GlassBlurRenderPass : ScriptableRenderPass { private CommandBuffer cmd; private string cmdName; private RenderTargetHandle dest; private Material m_blurMat; private RenderTargetIdentifier source { get; set; } RenderTargetHandle m_temporaryColorTexture; RenderTargetHandle blurredID; RenderTargetHandle blurredID2; public GlassBlurRenderPass(GlassBlurRenderPassFeature.Settings param) { renderPassEvent = param.renderEvent; cmdName = param.cmdName; m_blurMat = param.blurMat; blurredID.Init("blurredID"); blurredID2.Init("blurredID2"); } public void Setup(RenderTargetIdentifier src, RenderTargetHandle _dest) { this.source = src; this.dest = _dest; } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { if (GlassCtrl.takeShot == 0) return; if (renderingData.cameraData.isSceneViewCamera) return; //如果cullingMask非UI层的camera,返回 // if (!((renderingData.cameraData.camera.cullingMask & 1<<LayerMask.NameToLayer("UI")) > 0)) // return; cmd = CommandBufferPool.Get(cmdName); Vector2[] sizes = { new Vector2(Screen.width, Screen.height), new Vector2(Screen.width / 2, Screen.height / 2), new Vector2(Screen.width / 4, Screen.height / 4), new Vector2(Screen.width / 8, Screen.height / 8), }; RenderTextureDescriptor opaqueDesc = renderingData.cameraData.cameraTargetDescriptor; opaqueDesc.depthBufferBits = 0; cmd.GetTemporaryRT(m_temporaryColorTexture.id, opaqueDesc, FilterMode.Bilinear); cmd.Blit(source, m_temporaryColorTexture.Identifier()); for (int i = 0; i < GlassCtrl.Iterations; ++i) { cmd.GetTemporaryRT(blurredID.id, opaqueDesc, FilterMode.Bilinear); cmd.GetTemporaryRT(blurredID2.id, opaqueDesc, FilterMode.Bilinear); cmd.Blit(m_temporaryColorTexture.Identifier(), blurredID.Identifier()); cmd.SetGlobalVector("offsets", new Vector4(2.0f / sizes[i].x, 0, 0, 0)); cmd.Blit(blurredID.Identifier(), blurredID2.Identifier(), m_blurMat); cmd.SetGlobalVector("offsets", new Vector4(0, 2.0f / sizes[i].y, 0, 0)); cmd.Blit(blurredID2.Identifier(), blurredID.Identifier(), m_blurMat); cmd.Blit(blurredID.Identifier(), m_temporaryColorTexture.Identifier()); } //把最终内容Blit到一个RenderTexture上。 cmd.Blit(blurredID.Identifier(), GlassCtrl.renderTexture); if (GlassCtrl.takeShot == 1) GlassCtrl.takeShot = 0; context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); //Debug.LogError("glass pass render"); } }
使用的shader为:
Shader "PostEffect/SeparableGlassBlur" { Properties { _MainTex ("Base (RGB)", 2D) = "" {} } HLSLINCLUDE #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" struct appdata_img { float4 vertex:POSITION; float2 texcoord:TEXCOORD0; }; struct v2f { float4 pos : POSITION; float2 uv : TEXCOORD0; float4 uv01 : TEXCOORD1; float4 uv23 : TEXCOORD2; float4 uv45 : TEXCOORD3; }; float4 offsets; sampler2D _MainTex; v2f vert (appdata_img v) { v2f o; VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz); o.pos = vertexInput.positionCS; o.uv.xy = v.texcoord.xy; o.uv01 = v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1); o.uv23 = v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1) * 2.0; o.uv45 = v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1) * 3.0; return o; } half4 frag (v2f i) : COLOR { half4 color = float4 (0,0,0,0); color += 0.40 * tex2D (_MainTex, i.uv); color += 0.15 * tex2D (_MainTex, i.uv01.xy); color += 0.15 * tex2D (_MainTex, i.uv01.zw); color += 0.10 * tex2D (_MainTex, i.uv23.xy); color += 0.10 * tex2D (_MainTex, i.uv23.zw); color += 0.05 * tex2D (_MainTex, i.uv45.xy); color += 0.05 * tex2D (_MainTex, i.uv45.zw); return color; } ENDHLSL Subshader { Pass { ZTest Always Cull Off ZWrite Off HLSLPROGRAM #pragma vertex vert #pragma fragment frag ENDHLSL } } }
三、创建一个ScriptableRendererFeature
using UnityEngine; using UnityEngine.Rendering.Universal; public class GlassBlurRenderPassFeature : ScriptableRendererFeature { //设置一个RendererFeature的配置,写上要用的参数 [System.Serializable] public class Settings { public RenderPassEvent renderEvent = RenderPassEvent.BeforeRenderingPostProcessing; public LayerMask layerMask = -1; public Material blurMat; public string textureName = ""; public string cmdName = ""; public string passName = ""; } GlassBlurRenderPass m_ScriptablePass; private RenderTargetHandle dest; public Settings settings; //这个在可视化中设置 public override void Create() { m_ScriptablePass = new GlassBlurRenderPass(settings); m_ScriptablePass.renderPassEvent = settings.renderEvent; } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { var src = renderer.cameraColorTarget; dest = RenderTargetHandle.CameraTarget; //把camera渲染到的画面src 传入GlassBlurRenderPass里。 m_ScriptablePass.Setup(src,this.dest); //注入队列 renderer.EnqueuePass(m_ScriptablePass); } }
四、将ScriptableRendererFeature添加到Render Feature,填写自己定义的参数内容
五、业务控制和注意
通过逻辑控制GlassCtrl然后获取贴图就可以了。注意的是URP这个处理只对主相机渲染的内容进行处理(Base Camera),对添加到主相机下面的Overlay相机不会影响,所以必要时刻需要调整需要玻璃化的相机为主相机。一般都是背景做毛玻璃效果,内容还是在上面的,所以这应该不会有问题。