UnityShader快速上手指南(三)

簡介

這一篇仍是一些基本的shader操做:裁剪、透明和法向量的應用
(糾結了好久寫不寫這些,由於代碼很簡單,主要是些概念上的東西)
先來看下大概的效果圖:(從左到右依次是裁剪,透明,加了法向量的透明)
(好奇怪,爲啥我字那麼多,提示我少於150字)
這裏寫圖片描述算法

裁剪

代碼

Shader "LT/Lesson3_Cull"
{
    Properties
    {
         _Color ("Color", Color) = (1, 1, 1, 1) 
    }
    SubShader
    {
        Pass
        {
            Cull Off 
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            uniform float4 _Color;

            appdata_base vert ( appdata_base input)
            {
                input.texcoord = input.vertex ;
                input.vertex  = mul(UNITY_MATRIX_MVP, input.vertex );
                return input;
            }

            fixed4 frag (appdata_base input) : COLOR
            {
                if(input.texcoord.y < 0) {
                    discard;
                }
                return _Color; 
            }
            ENDCG
        }
    }
}
代碼的基本格式我就不介紹了,不熟的請看教程一

這裏引入了一個新的東西: Cull Off緩存

裁剪簡單的能夠分爲三種裁剪:擦除裁剪(discard),正面裁剪(Front)和背面裁剪(Back)
這裏的Cull Off表明關閉unity自帶的背面裁剪(由於若是不寫的話默認爲Cull Back)app

Cull Off 關閉裁剪
Cull Back 背面裁剪(默認)
Cull Front 正面裁剪

而後關於這個面的正反的界定,是經過法線來完成的,法線向量>0則爲正
Cull Back 和 Cull Front Unity替咱們完成了,若是須要簡單的裁掉正面和反面的話直接使用預製的就好了
(Q:若是我想同時裁正面和反面怎麼處理? A:若是你要同時裁掉正面和反面,你的模型就不用顯示了好嘛)
這裏我想經過模型的座標位置來裁面,因此關閉了Unity的自動裁剪
而後就是把頂點信息緩存下來:函數

input.texcoord = input.vertex ;

爲了方便(同時也能提升性能,因爲GPU是頻繁調用這些函數,因此無論什麼空間啊,計算啊都能省就省),我直接將數據存在了input對象中,而後又將input返回給frag使用(這裏隨便選一個不用的字段用來緩存咱們的位置信息就好了,注意vertex是要使用的,因此vertex不能用來緩存這個信息)
而後在frag中判斷咱們緩存的座標信息,知足條件就discard掉性能

if(input.texcoord.y < 0) {
                    discard;
                }

這裏的discard至關於強行中斷該次渲染,也能夠說成取消渲染,這樣就完成了咱們的裁剪功能了
順帶一提,discard很消耗性能的,因此能不用仍是就不用了(況且這裏還多了一步if判斷語句),須要指定裁剪仍是改模型來的高效,固然若是須要動態裁剪就須要這樣的代碼了測試

透明

仍是先來代碼

代碼

Shader "LT/Lesson3_Transparent"
{
    Properties
    {
         _Color ("Color", Color) = (1, 1, 1, 0.5) 
    }
    SubShader
    {
        Tags { "Queue" = "Transparent" } 
        Pass
        {
            ZWrite Off 
            Blend One One 
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            uniform float4 _Color;

            appdata_base vert ( appdata_base input)
            {
                input.vertex  = mul(UNITY_MATRIX_MVP, input.vertex );
                return input;
            }

            fixed4 frag (appdata_base input) : COLOR
            {
                return _Color; 
            }
            ENDCG
        }
    }
}

恩...這裏呢代碼的內容比較少,就是直接把外面設置的顏色應用到物體上就完了
可是多了優化

Tags { "Queue" = "Transparent" } 
ZWrite Off 
Blend One One

這三個東西
Tags { "Queue" = "Transparent" }
先來第一個Tags { "Queue" = "Transparent" }
你們可能看到有個Transparent會想要寫透明是否是必需要這句話,其實否則,這行代碼只是指定一個渲染順序而已,通常狀況下是具體狀況具體分析,好比你但願這個shader是渲染背景物體的時候,你能夠設置爲
"Queue" = "Background",須要時物體的時候設置爲「Geometry」,其實對應下來的就是一個數值,這個數值越小,越先渲染,而後從咱們的渲染順序能夠理解,先渲染的天然在背後了
下面列舉下經常使用的值對應的名字網站

"Background"。值爲1000。好比用於天空盒。
    "Geometry"。值爲2000。大部分物體在這個隊列。不透明的物體也在這裏。這個隊列內部的物體的渲染順序會有進一步的優化(應該是從近到遠,early-z test能夠剔除不需通過FS處理的片元)。其餘隊列的物體都是按空間位置的從遠到近進行渲染。
    "AlphaTest"。值爲2450。已進行AlphaTest的物體在這個隊列。
    "Transparent"。值爲3000。透明物體。
    "Overlay"。值爲4000。好比鏡頭光暈。

還有就是,用戶能夠定義任意值,好比"Queue"="Geometry+10"spa

ZWrite Off
再來ZWriter Off,這句命令表示不寫入深度緩存
呃~~~,而後咱們繼續來科普概念吧(因爲語文很差,後面一大段話是從別的網站複製的)3d

(1)什麼是深度?
深度其實就是該像素點在3d世界中距離攝象機的距離,深度值(Z值)越大,則離攝像機越遠。

(2)什麼是深度緩存?
深度緩存中存儲着每一個像素點(繪製在屏幕上的)的深度值!若是啓用了深度緩衝區,在繪製每一個像素以前,OpenGL會把它的深度值和已經存儲在這個像素的深度值進行比較。新像素深度值<原先像素深度值,則新像素值會取代原先的;反之,新像素值被遮擋,其顏色值和深度將被丟棄,最終屏幕顯示的就是深度緩存中深度對應的像素點的顏色!(深度主要起的是比較的做用)

(3)什麼是深度測試?
在深度測試中,默認狀況是將要繪製的新像素的z值與深度緩衝區中對應位置的z值進行比較,若是比深度緩存中的值小,那麼用新像素的顏色值更新深度緩存中對應像素的顏色值。

(4)爲何須要深度?
在不使用深度測試的時候,若是咱們先繪製一個距離較近的物體,再繪製距離較遠的物體,則距離遠的物體由於後繪製,會把距離近的物體覆蓋掉,這樣的效果並非咱們所但願的。而有了深度緩衝之後,繪製物體的順序就不那麼重要了,都能按照遠近(Z值)正常顯示,這很關鍵。(越後繪製的東西,距離相機就越近)

那麼,在unity中,若是知道了渲染隊列,ZWrite,ZTest,如何肯定哪一個物體先顯示呢?
首先,unity先將渲染隊列中較前的進行渲染,而後再執行ZWrite,ZTest
ZWrite能夠取的值爲:On/Off,默認值爲On,表明是否要將像素的深度寫入深度緩存中
ZTest能夠取的值爲:Greater/GEqual/Less/LEqual/Equal/NotEqual/Always/Never/Off,默認值爲LEqual,表明如何將像素的顏色寫入深度緩存中,例如當取默認值的狀況下,若是將要繪製的新像素的z值小於等於深度緩存中的值,則將用新像素的顏色值更新深度緩存中對應像素的顏色值。須要注意的是,當ZTest取值爲Off時,表示的是關閉深度測試,等價於取值爲Always,而不是Never!Always指的是直接將當前像素顏色(不是深度)寫進顏色緩衝區中;而Never指的是不要將當前像素顏色寫進顏色緩衝區中,至關於消失。

由於ZWrite默認值爲On,ZTest默認值爲LEqual,因此這很好地解釋了爲何在unity中,距離相機近的東西會阻擋住距離相機遠的東西。若是咱們先繪製一個距離較近的物體,再繪製距離較遠的物體,則距離遠的物體由於後繪製,會把距離近的物體覆蓋掉,這時咱們能夠經過修改ZWrite和ZTest來改變物體的遮擋關係!

恩,好,介紹完了深度這個玩意兒咯, 那來解釋下咱們爲啥要關掉了(其實不關掉也能夠,可是關掉GPU能夠少作一步操做啊,提升性能性能性能性能性能性能,因此能關就關吧),由於咱們要寫的是透明啊,無論前後順序的,透明均可以看獲得 - -

Blend One One
最後是Blend One One,這是個大概念,這個命令是寫透明shader所必須的,由於它定義了透明的模式
然而,這玩意兒很簡單的,命令是

Blend SrcFactor DstFactor

而後這個Factor 支持:

One
值爲1,使用此設置來讓源或是目標顏色徹底的經過。
Zero
值爲0,使用此設置來刪除源或目標值。
SrcColor
此階段的值是乘以源顏色的值。
SrcAlpha
此階段的值是乘以源alpha的值。
DstColor
此階段的值是乘以幀緩衝區源顏色的值。
DstAlpha
此階段的值是乘以幀緩衝區源alpha的值。
OneMinusSrcColor
此階段的值是乘以(1 - source color)
OneMinusSrcAlpha
此階段的值是乘以(1 - source alpha)
OneMinusDstColor
此階段的值是乘以(1 - destination color)
OneMinusDstAlpha
此階段的值是乘以(1 - destination alpha)

而後下面是經常使用的搭配:

Blend SrcAlpha OneMinusSrcAlpha // Alpha blending alpha混合
Blend One One // Additive 相加混合
Blend One OneMinusDstColor // Soft Additive 柔和相加混合
Blend DstColor Zero // Multiplicative 相乘混合
Blend DstColor SrcColor // 2x Multiplicative 2倍相乘混合

具體效果,你們能夠本身改代碼看結果,這裏就很少說了

帶法向量計算的透明

代碼

Shader "LT/Lesson3_Silhouette"
{
    Properties
    {
         _Color ("Color", Color) = (1, 1, 1, 0.5) 
    }
    SubShader
    {
        Pass
        {
            ZWrite Off 
            Blend SrcAlpha OneMinusSrcAlpha 
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            uniform float4 _Color;

            appdata_base vert ( appdata_base input)
            {
                fixed3 tempNormal = normalize(mul(fixed4(input.normal, 0.0), _World2Object).xyz);
                fixed3 tempViewDir = normalize(_WorldSpaceCameraPos - mul(_Object2World, input.vertex).xyz);
                input.vertex  = mul(UNITY_MATRIX_MVP, input.vertex );
                input.normal.x = min(1.0, _Color.a / abs(dot(tempNormal, tempViewDir)));
                return input;
            }

            fixed4 frag (appdata_base input) : COLOR
            {
                return float4(float3(_Color.x,_Color.y,_Color.z), input.normal.x); 
            }
            ENDCG
        }
    }
}

這裏沒啥新東西,可是代碼上把法向量用起來了(原理主要是經過法向量和攝像機朝向算出一個新的透明度用來替換而已)
解釋一下吧:

fixed3 tempNormal = normalize(mul(fixed4(input.normal, 0.0), _World2Object).xyz);
    // 計算unity座標系下的法向量
    fixed3 tempViewDir = normalize(_WorldSpaceCameraPos - mul(_Object2World, input.vertex).xyz);
    // 計算unity座標系啊的攝像機的向量
    input.normal.x = min(1.0, _Color.a / abs(dot(tempNormal, tempViewDir)));
    // 將兩個向量點乘而後換算給設定顏色的alpha通道,並緩存起來
    // 至於爲啥要點乘,而後用Color.a來除,這個是算法問題啦:
    // a = min(1, a/ |V·N|),公式是書上來的,數學問題了,不作贅述
    
    return float4(float3(_Color.x,_Color.y,_Color.z), input.normal.x); 
    // 使用設置的顏色的rgb值和算出來的新的alpha值

因爲我的很喜歡這個效果,因此再來次效果展現
這裏寫圖片描述
這樣是否是有一點簡單的立體效果咯捏(這不是光照,真的光照的反射啥的下一篇教程講)

總結

本篇教程的東西都很基礎,可是在之後寫shader的過程當中會用的很平凡,因此仍是單獨拿出來說了一下,與咱們的快速上手指南其實有點背道而馳 o(╯□╰)o ,可是爲了後面不會看不懂,你們仍是多本身寫寫熟悉一下,好比使用discard和Cull配合兩個Pass寫一個正反面渲染不一樣顏色的shader,利用前面講的SinTime和法向量啥的寫一個動態變化效果 仍是那句話,又不懂得,QQ:821580467

相關文章
相關標籤/搜索