Shader從入門到跑路:螢幕後處理效果

遊資網發表於2020-03-17
前言

我們在第二篇文章用網格的uv座標來設定四邊形的顏色,那麼螢幕的uv座標是什麼呢?

Shader從入門到跑路:螢幕後處理效果

正文

你可以將螢幕看成一個大的正方形,別噓我,底層的圖形API也是這麼看待螢幕的。

Shader從入門到跑路:螢幕後處理效果

在第一章中我們提到了shader的計算流程,並且介紹到fragment shader會將計算好的顏色傳給顏色緩衝,並結束工作,因為之後的內容就是交由底層圖形API來將顏色緩衝內的資料內容以法向量的形式渲染到螢幕上,而這個過程將根據執行平臺的不同而選擇不同的圖形API,所以我沒有詳細說,想了解只要隨便找個圖形API比如openGL什麼的學一下就好了。

Shader從入門到跑路:螢幕後處理效果

謝謝這位臨時演員幫助了我們理解螢幕渲染效果是怎麼一回事。圖形渲染的底層實現是由unity來決定的,當unity指定了執行平臺後,圖形API就可以開始噴射影象了,儲存在水槍裡面的都是以法向量存在的渲染資料,包含顏色、深度資訊等,只要影象API做一些中間處理,就可以將其射到螢幕上。

但是,在顏色緩衝區的內容真正被噴到螢幕上之前,我們仍然有機會更改其中的內容。而unity也為我們考慮到了這一點,因此它提供了一個叫OnRenderImage的函式,這是個monobehaviour事件。關於這個函式可移步Unity Doc:

https://docs.unity3d.com/Manual/ExecutionOrder.htmldocs.unity3d.com

Post-processing overviewdocs.unity3d.com1

這個函式有一個source渲染紋理作為輸入,這個輸入包含了顏色緩衝裡面所有的資料資訊,包括顏色、深度資訊等等。以及一個destination作為輸出,在這我們通常認為就是相機螢幕了。

Shader從入門到跑路:螢幕後處理效果

場景準備(隨便搭一搭就好了):

Shader從入門到跑路:螢幕後處理效果
為了展示接下來的效果,我先隨便建立了一個遊戲介面

接下來寫一個簡單的C#程式來了解它的用法:

  1. using UnityEngine;

  2. [ExecuteInEditMode]
  3. public class PostEffects : MonoBehaviour
  4. {
  5.     public Material material;
  6.     private void OnRenderImage(RenderTexture source, RenderTexture destination)
  7.     {
  8.         Graphics.Blit(source, destination, material);
  9.     }
  10. }
複製程式碼

首先,ExecuteInEditMode表示我們可以在Edit mode中觀察程式的執行。然後定義了一個材質,用來渲染螢幕,然後呼叫OnRenderImage這個函式,而重點則在於Graphics.Blit這個方法,它的意思大概是用這個material把這個source渲染到那個destination。

然後我會用第二節講到的shader:

  1. Shader "Custom/ShaderLearning"
  2. {
  3.         SubShader
  4.         {
  5.                 Tags
  6.                 {
  7.                 "Queue" = "Transparent"
  8.                 }
  9.                 Pass
  10.                 {
  11.                         Blend SrcAlpha OneMinusSrcAlpha
  12.                         CGPROGRAM
  13.                         #pragma vertex vert
  14.                         #pragma fragment frag
  15.                         #include "UnityCG.cginc"

  16.                         struct appdata
  17.                         {
  18.                                 float4 vertex : POSITION;
  19.                                 float2 uv: TEXCOORD0;
  20.                         };

  21.                         struct v2f
  22.                         {
  23.                                 float4 vertex : SV_POSITION;
  24.                                 float2 uv: TEXCOORD1;
  25.                         };

  26.                         v2f vert(appdata v)
  27.                         {
  28.                                 v2f o;
  29.                                 o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
  30.                                 o.uv = v.uv;
  31.                                 return o;
  32.                         }

  33.                         float4 frag(v2f i) : SV_Target
  34.                         {
  35.                                 float4 color = float4(i.uv.r,i.uv.g, 0, 1);
  36.                                 return color;
  37.                         }
  38.                         ENDCG
  39.                 }
  40.         }
  41. }
複製程式碼

接下來把PostEffects拖給場景內的相機。並新建一個新的material,將上面的shader賦給新材質後,將材質賦值給PostEffects的材質引數

Shader從入門到跑路:螢幕後處理效果

最終的效果如上圖所示。我們把整個螢幕的uv座標作為顏色引數輸出了,但這確實不是什麼好玩的東西,我們可以在shader中新建一個主紋理來接收在顏色緩衝中儲存的紋理(這裡就是螢幕),並將其輸出到螢幕上吧。

值得注意的是,Graphics.Blit方法會把source傳入的紋理賦值給mateiral中的主紋理。必須將shader中的主紋理命名為_MainTex才行,不然會報錯。

加入一個Properties區域,並且宣告一個_MainTex的二維屬性,以白色為預設顏色:

  1. Properties{
  2.         _MainTex("Main Texture", 2D) = "white" {}
  3. }
複製程式碼

在CGPROGRAM內加入宣告:

sampler2D _MainTex;

修改fragment shader,這次我們不僅輸出紋理本身,而且還要將它乘上uv座標:

  1. float4 frag(v2f i) : SV_Target
  2. {
  3.         float4 color = tex2D(_MainTex, i.uv);
  4.         color *= float4(i.uv.r, i.uv.g, 0, 1);
  5.         return color;
  6. }
複製程式碼

Shader從入門到跑路:螢幕後處理效果
以顏色緩衝內的渲染紋理與uv座標想乘的渲染結果

那麼總算是做出一些比較有趣的渲染效果了,當然這可能也就是在你做些什麼嚇唬人的遊戲時才有些用處了。

自問自答

當我把紋理作為輸入傳給OnRenderImage時,我其實不過是在將整個要渲染到整個螢幕上的東西拿出來做處理而已,這些"要渲染的東西"就是顏色咯,我可不可以把紋理看作存有很多個顏色值的資料表呢?

Shader從入門到跑路:螢幕後處理效果
為了更深刻地展示這個問題,我把我老婆的照片畫素化了

“一張紋理就是儲存了一個陣列,而裡面的資料就是顏色”這樣的想法是不完全對的。我們可以把任何資料內容儲存進紋理中,有些紋理在我們眼中是不符合現實的

Shader從入門到跑路:螢幕後處理效果
一張看起來很奇怪的紋理

但對於我們的shader來說,他有可能是一張查詢表,有可能是一張高度圖,或者就是一些偽隨機的噪音引數,但無論它是什麼,對於shader都是有用的內容。

Shader從入門到跑路:螢幕後處理效果
一張位移圖

舉個例子,看到這張圖你能想到什麼?要說“啊,是Minecraft裡面的磚塊材質!”的同學清先坐下。我想說的是,紅色的顏色代表的是在uv的x值上的位移,而綠色則是在uv的y值上的位移。最終的螢幕後處理效果能呈現一種空間被扭曲的感覺,我們下章來做它的實現。

相關閱讀:
Shader從入門到跑路:樸實無華的圖形學基礎
Shader從入門到跑路:自定義紋理輸入

作者:俊銘
專欄地址:https://zhuanlan.zhihu.com/p/85958322

相關文章