Unity3D+Post Processing Stack V2自定義後處理效果研究

YuanZiming發表於2020-07-14

背景

眾所周知,Unity3D支援自定義後處理效果,實現過程有三步:

  1. 新增著色器,在著色器裡書寫後處理程式碼;
  2. 新增材質,把材質和著色器繫結;
  3. 給相機新增指令碼,重寫其OnRenderImage方法,將材質傳入Graphics.Blit方法中。

但是在做最近的一個專案時,我使用了Unity3D的官方後處理外掛Post Processing Stack V2(以下簡稱PPV2)來簡化輝光、環境光遮蔽這類後處理效果的使用。但之後我又需要自定義一些後處理效果,此時就出現了問題。我發現我的OnRenderImage方法沒有正常地接收到渲染幀經過外掛處理後的紋理,而是接收到一個純黑紋理,最後輸出的也是純黑,使得我的後處理效果無法正常工作,網上也找不到實際原因,可能和渲染管線的不同有關。為了解決這個問題,必須基於PPV2自定義一個效果,然後在其他程式碼中操作這個效果裡的引數,由PPV2來執行我們的後處理效果。這個國內國外的教程都非常少,我主要參考了PPV2的官方文件,在這裡給出。

程式碼

著色器

Shader "Hidden/Custom/Blend" {
    HLSLINCLUDE

        #include "Packages/com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl"

		TEXTURE2D_SAMPLER2D(_MainTex, sampler_MainTex);
		TEXTURE2D_SAMPLER2D(_DesTex, sampler_DesTex);
        float _Alpha;

        float4 Frag(VaryingsDefault i) : SV_Target {
			float4 col1 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord);
			float4 col2 = SAMPLE_TEXTURE2D(_DesTex, sampler_DesTex, i.texcoord);
			float4 col = lerp(col2, col1, _Alpha);
			return col;
        }

    ENDHLSL

    SubShader {
        Cull Off ZWrite Off ZTest Always

        Pass {
            HLSLPROGRAM

                #pragma vertex VertDefault
                #pragma fragment Frag

            ENDHLSL
        }
    }
}

基本和文件中的一致,只是修改了片元著色器函式裡的程式碼,實現將兩張紋理進行混合的過程,另外由於這個文件比較新,例項的Shader語法好像和以前的CG語言不太一樣,不知道是不是換成了HLSL。

效果自定義

using System;
using UnityEngine;
using UnityEngine.Rendering.PostProcessing;

[Serializable]
[PostProcess(typeof(BlendRenderer), PostProcessEvent.AfterStack, "Custom/Blend")]
public sealed class Blend : PostProcessEffectSettings {
    public TextureParameter DesTex = new TextureParameter();
    public FloatParameter Alpha = new FloatParameter();
}

public sealed class BlendRenderer : PostProcessEffectRenderer<Blend>
{
    public override void Render(PostProcessRenderContext context) {
        var sheet = context.propertySheets.Get(Shader.Find("Hidden/Custom/Blend"));
        sheet.properties.SetTexture("_DesTex", settings.DesTex);
        sheet.properties.SetFloat("_Alpha", settings.Alpha);
        context.command.BlitFullscreenTriangle(context.source, context.destination, sheet, 0);
    }
}

這裡定義了兩個類,一個是設定類,提供了後處理效果所需的各種屬性,其中支援的屬性型別可以在專案下Packages/Post Processing/PostProcessing/Runtime/ParameterOverride.cs裡找到,對應的基礎型別有int、float、color、vector2、vector3、vector4、spline和texture。第二個類用來重寫後處理方法,一般在這裡指定要用的shader和給shader裡的變數賦值。

修改效果引數

    void Start() {
        m_Blend = ScriptableObject.CreateInstance<Blend>();
        m_Blend.enabled.Override(true);
        m_Blend.Alpha.Override(1f);
        m_Blend.DesTex.Override(Texture2D.blackTexture);
        m_Volume = PostProcessManager.instance.QuickVolume(8, 100f, m_Blend);
        // 8是後處理所在的層
    }

這一段是初始化,先建立後處理效果,然後將其加入到後處理體積中。初始化後處理效果引數用Override方法,注意QuickVolume方法的第一個引數非常重要,它對應在PostProcessing Layer元件裡填寫的層的編號。

m_Blend.DesTex.value = tex; // tex -> Texture2D
m_Blend.Alpha.value = Alpha; // Alpha -> float

平常賦值直接修改屬性名的value屬性。

    private void OnDestroy() {
        RuntimeUtilities.DestroyVolume(m_Volume, true, true);
    }

注意在物件被銷燬時要將建立的臨時後處理體積銷燬。如果沒有這一段,更換場景時後處理會繼續工作,不是我們想要的效果。

總結

Unity3D+Post Processing Stack V2自定義後處理效果其實也是隻有三步,就是編寫後處理著色器、編寫後處理效果類、編寫操作後處理引數類。主要還是國內外的教程沒有與時俱進導致資料查詢困難,希望更多的新教程能不斷湧現,方便開發者的學習。

相關文章