Unity运用CommandBuffer实现发光特效

Posted 刘一码

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity运用CommandBuffer实现发光特效相关的知识,希望对你有一定的参考价值。

首先看下效果:

 我们想要实现以下效果:

  • 发光物体边缘模拟丁达尔效应,产生光芒。
  • 可以被其它物体遮挡。
  • 任意形状物体都可以产生以上效果。

首先确定实现的思路:

1、将发光物体单独绘制一遍,并计算在环境中的遮挡,剔除被遮挡的像素,保存绘制后的纹理。

2、将发光物体单独绘制后的纹理,添加模糊效果,并向外扩散。

3、将模糊处理后的纹理,与相机渲染的纹理进行叠加,形成最终的效果。

第一步:

如何将发光的物体单独绘制一遍并保存纹理?这时候就用到Unity中的CommandBuffer了。CommandBuffer可以简单理解为:创建一系列渲染指令,然后在某个阶段执行这些渲染指令。网上有很多相关的教程和案例,在这就不细说了。

我们用CommanBuffer将发光物体全都绘制一遍,然后渲染到一个RenderTexture上:

    /// <summary>
    /// 绘制需要有模糊效果的物体
    /// </summary>
    void DrawObjects()
    
        drawObjsCmd = new CommandBuffer();
        drawObjsCmd.name = "绘制发光物体";
        //绘制到一个RenderTexture上
        drawObjsCmd.SetRenderTarget(colorTexture);
        drawObjsCmd.ClearRenderTarget(true, true, Color.clear);

        //绘制所有发光物体
        foreach (var item in renderers)
        
            if (!item.enabled)
                continue;

            //设置材质球参数
            var colorMat = new Material(postRenderColor);
            colorMat.SetFloat("_Scale", item.scale);
            colorMat.SetColor("_Color", item.color);
            drawObjsCmd.DrawRenderer(item.meshRenderer, colorMat, 0, 0);
        
        //执行CommandBuffer
        Graphics.ExecuteCommandBuffer(drawObjsCmd);
    

postRenderColor这个shader,具有绘制纯色,并根据深度图计算遮挡关系的功能。

单独绘制发光物体

第二步:模糊效果

关于模糊效果,我们一般采用高斯模糊,关于高斯模糊,本文也不多做细说,网上相关文章也很多,我们直接拿来用即可。但是需要注意的是,为了保证效果,我们可能会对纹理进行多次采样,这时就要注意性能和效果间的平衡。

模糊处理这步,我们单独用一个CommandBuffer进行处理:

/// <summary>
    /// 模糊效果
    /// </summary>
    private void AddGlowCommand()
    
        blurCmd = new CommandBuffer();
        blurCmd.name = "模糊处理";
        //创建一个纹理,用于绘制模糊效果
        int temp = Shader.PropertyToID("_TempImage");
        blurCmd.GetTemporaryRT(temp, -1, -1, 0, FilterMode.Bilinear);

        float dir = 1;
        for (int i = 0; i < samplingIteration; i++)
        
            //竖向采样一次
            blurCmd.SetGlobalVector("_Dir", new Vector4(0, dir, 0, 0));
            blurCmd.Blit(colorTexture, temp, blurMat);
            //横向采样一次
            blurCmd.SetGlobalVector("_Dir", new Vector4(dir, 0, 0, 0));
            blurCmd.Blit(temp, colorTexture, blurMat);
            //每次采样后,扩展一次模糊中的采样距离,这样效果会更好
            dir += glowAdd;
        
        blurCmd.SetGlobalTexture("_AddTex", colorTexture);
        cam.AddCommandBuffer(CameraEvent.BeforeImageEffects, blurCmd);
    

模糊效果

第三步:叠加

到这里,就快大功告成了,只需通过shader的叠加算法,将相机渲染出的纹理和我们前面模糊处理后的纹理叠加即可:

//shader中的叠加算法
float4 blend(float4 main, float4 add)
			
				float4 color = float4(0, 0, 0, 1);
				float l = max(max(main.r, main.g), main.b);
				color.r = main.r*(1 - add.a*saturate(main.r - add.a)) + add.r;
				color.g = main.g*(1 - add.a*saturate(main.g - add.a)) + add.g;
				color.b = main.b*(1 - add.a*saturate(main.b - add.a)) + add.b;
				return color;
			

 这里我只用了我认为比较好的叠加算法,如果你有更好的算法的话,希望可以留言交流一下。

处理前效果

处理后效果

 最后,附上github链接:GitHub - liuyima/UnityGlow: 发光物体特效

Unity CommandBuffer渲染MTR到自定义的RenderTexture

目标:

延迟渲染技术下,有时我们要先把模型的Diffuse、Normal、Depth、Roughness等数据先渲到自定义的RenderTexture中(CPU中,以几张贴图的形式保存到Disk),然后进行后面的延迟光照等计算。

思路:

  • 方法一
    可以用Camera直接渲,只要将自定义的RT设置为相机的target即可(MRT要使用Camera的SetTargetBuffers (RenderBuffer[] colorBuffer, RenderBuffer depthBuffer)方法设置渲染目标);
  • 方法二
    即使用CommandBuffer自定义渲染指令来渲,这样可以针对某个模型来渲贴图,获取模型的Render对象,调用CommandBuffer的DrawRenderer(render,material)方法并在合适的时机执行CommandBufferGPU指令。

CommandBuffer方法实现

先自定义几张RT并初始化用来缓冲渲染结果,自定义一个新的CommandBuffer设置渲染目标和绘制指令,然后在合适的时机执行CommandBuffer即可:

    public Transform targetObj; // 要渲染的模型

    RenderTexture[] rtGBuffers = new RenderTexture[3]; // 自定义RT
    RenderTexture depthBuffer = null; // 深度RT
    public Material GBufferMaterial; // 渲染MRT的材质,自定义shader
    Renderer targetRender; // 模型的Render对象

    void Start()
    	
        targetRender = targetObj.GetComponent<MeshRenderer>();
        CommandBuffer cmd = new CommandBuffer();
        cmd.name = "TestGBufferCMD";
        RenderTargetIdentifier[] rtGBuffersID = new RenderTargetIdentifier[rtGBuffers.Length];
        for (int i = 0; i < rtGBuffers.Length; ++i)
        
            rtGBuffers[i] = RenderTexture.GetTemporary(Screen.width, Screen.height, 0);
            rtGBuffersID[i] = rtGBuffers[i];
        
        depthBuffer = RenderTexture.GetTemporary(Screen.width, Screen.height, 0);

        cmd.SetRenderTarget(rtGBuffersID, depthBuffer); // 设置渲染目标
        cmd.ClearRenderTarget(true, true, Color.clear, 1);
        cmd.DrawRenderer(targetRender, GBufferMaterial); // 绘制指令
        
        // 这里可以在相机的渲染事件中自动执行CommandBuffer
        Camera.main.AddCommandBuffer(CameraEvent.BeforeForwardOpaque,cmd);
        

另外也可以在合适的位置手动执行CommandBuffer:
Graphics.ExecuteCommandBuffer(cmd);

自定义的MRT shader示例:

Shader "Unlit/GBuffer"

	Properties
	
		_MainTex ("MainTex", 2D) = "white" 
	
	SubShader
	
		Tags  "RenderType"="Opaque" 
		LOD 100
        Cull Back

		Pass
		
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

			struct appdata
			
				float4 vertex : POSITION;
                float3 normal : NORMAL;
				float2 uv : TEXCOORD0;
			;

			struct v2f
			
				float2 uv : TEXCOORD0;
                float4 normal : TEXCOORD1;
				float4 vertex : SV_POSITION;
			;

			sampler2D _MainTex;
			float4 _MainTex_ST;
			
			v2f vert (appdata v)
			
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.normal = float4(v.normal,0);
				return o;
			
			
			void frag (v2f i,
            out half4 GRT0:SV_Target0,
            out half4 GRT1:SV_Target1,
            out half4 GRT2:SV_Target2,
            out float GRTDepth:SV_Depth
            )
			                
				// sample the texture
				float4 col = tex2D(_MainTex, i.uv);
                GRT0 = col;
                GRT1 = i.normal;
                GRT2 = float4(0,0,1,0);
                GRTDepth = 0.5;
			
			ENDCG
		
	

查看结果

运行Unity,打开Frame Debuger截一帧,找到我们的CommandBuffer那条指令,然后可以看到有我们的三个RT,选择不同RT查看贴图结果。自定义RT已在内存中,可以保存图片到本地供后续使用。

以上是关于Unity运用CommandBuffer实现发光特效的主要内容,如果未能解决你的问题,请参考以下文章

Unity CommandBuffer的一些学习整理

unity CommandBuffer学习

Unity CommandBuffer渲染MTR到自定义的RenderTexture

小功能⭐️Unity中利用材质自发光实现物体闪烁效果

小功能⭐️Unity中利用材质自发光实现物体闪烁效果

小功能⭐️Unity中利用材质自发光实现物体闪烁效果