背景
眾所周知,Unity3D支持自定義后處理效果,實現過程有三步:
- 添加着色器,在着色器里書寫后處理代碼;
- 添加材質,把材質和着色器綁定;
- 給相機添加腳本,重寫其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自定義后處理效果其實也是只有三步,就是編寫后處理着色器、編寫后處理效果類、編寫操作后處理參數類。主要還是國內外的教程沒有與時俱進導致資料查找困難,希望更多的新教程能不斷涌現,方便開發者的學習。
更新
使用已有的后處理體積
在上面的代碼中,后處理體積是臨時創建的,所以自定義后處理效果也是由代碼生成的。如果是修改已有的后處理效果的參數,可以使用下面的代碼:
m_Volume = GetComponent<PostProcessVolume>();
volume.profile.TryGetSettings<Blend>(out m_Blend);
先獲取腳本所在物體的后處理體積組件,然后直接獲取已有的后處理效果的設置類,之后就可以修改屬性了。
m_Blend.DesTex.value = tex; // tex -> Texture2D
m_Blend.Alpha.value = Alpha; // Alpha -> float
構建時的注意事項
由於我們在代碼中引用了Hidden/Custom/Blend這個着色器文件,但是場景中並沒有什么物體引用這個shader,所以構建時可能不會打包這個shader文件,導致實際運行時出現錯誤。我的解決方法是在場景中隨意創建一個有渲染器的物體,然后創建一個引用這個shader的材質,再將材質傳給這個物體,這樣打包時就不會忽略了。當然可能有其他的方案有待我學習。