第8章 透明效果
在 Unity 中,我們通常使用兩種方法來實現(xiàn)透明效果:透明度測試(Alpha Test)
和透明度混合(Alpha Blending)
。
當(dāng)我們渲染不透明物體時,我們不需要特別考慮渲染順序的問題,因為有深度緩沖(depth buffer,也稱 z-buffer)
的存在,它的基本思想是:根據(jù)深度緩存中的來判斷該片元距離攝像機(jī)的距離,當(dāng)渲染一個片元是,需要把它的深度值和已經(jīng)存在于深度緩存中的值來做對比,如果它的值距離攝像機(jī)更遠(yuǎn),那么說明這個片元不應(yīng)該被渲染到屏幕上;否則,這個片元應(yīng)該覆蓋掉此時顏色緩沖中的像素值,并把它的深度值更新到深度緩沖中。
但如果想要實現(xiàn)透明效果,事情就不那么簡單了,這是因為,當(dāng)使用透明度混合時,我們關(guān)閉了深度寫入(Write)
。
簡單來說,透明度測試和透明度混合的基本原理如下:
- 透明度測試:它采用一種“霸道極端”的機(jī)制,只要一個片元的透明度不滿足條件(通常是小于某個閾值),那么它對應(yīng)的片元就會被舍棄。被舍棄的片元將不會再進(jìn)行任何處理,也不會對顏色緩沖產(chǎn)生任何影響;否則,就會按照普通的不透明物體的處理方式來處理它,即進(jìn)行深度測試、深度寫入等。也就是說,透明度測試是不需要關(guān)閉深度寫入的,它和其他不透明物體最大的不同就是它會根據(jù)透明度來舍棄一些片元。雖然簡單,但是它產(chǎn)生的效果也很極端,要么完全透明,要么完全不透明。
- 透明度混合:這種方法可以得到真正的半透明效果。它會使用當(dāng)前片元的透明度作為混合因子,與已經(jīng)存儲在顏色緩沖中的顏色值進(jìn)行混合,得到新的顏色。但是,透明度混合需要關(guān)閉深度寫入,這使得我們要非常小心物體的渲染順序。需要注意的是,透明度混合只關(guān)閉了深度寫入,但沒有關(guān)閉深度測試。這意味著,當(dāng)使用透明度混合渲染一個片元時,還是會比較它的深度值與當(dāng)前深度緩沖中的深度值,如果它的深度值距離攝像機(jī)更遠(yuǎn),那么就不會再進(jìn)行混合操作。這一點(diǎn)決定了,當(dāng)一個不透明物體出現(xiàn)在一個透明物體的前面,而我們先渲染了不透明物體,它仍然可以正常地遮擋住透明物體。也就是說,對于透明度混合來說,深度緩沖是只讀的。
8.1 為什么渲染順序很重要
在使用透明度混合時,需要關(guān)閉深度寫入,此時渲染順序?qū)⒆兊梅浅V匾N覀兛梢运伎家幌乱韵聨追N渲染情況。
半透明物體 A + 不透明物體 B,A 在 B 前
此種情況有兩種渲染順序:
- 先渲染 B 再渲染 A, B 會正常寫入顏色緩沖,然后 A 會和顏色緩沖中的 B 顏色進(jìn)行混合,得到正確的半透明效果
- 先渲染 A 再渲染 B,A 會正常寫入顏色緩沖,但是因為半透明物體關(guān)閉了深度寫入,因此 A 不會修改深度緩沖。接著渲染 B,B會進(jìn)行深度測試,它發(fā)現(xiàn),“咦,深度緩存中還沒有人來過,那我就放心地寫入顏色緩沖了!”,結(jié)果就是 B 會直接覆蓋 A 的顏色,從視覺上來看,B就出現(xiàn)在了A的前面,而這是錯誤的。
由此可知,我們應(yīng)該在渲染完不透明物體后再渲染半透明物體。
A B 均為半透明,A 在 B 前
此種情況有兩種渲染順序:
- 先渲染 B 再渲染 A, B 會正常寫入顏色緩沖,然后 A 會和顏色緩沖中的 B 顏色進(jìn)行混合,得到正確的半透明效果
- 先渲染 A 再渲染 B,A 會正常寫入顏色緩沖,然后 B 會和顏色緩沖中的 A 顏色進(jìn)行混合,這樣混合結(jié)果會完全反過來,看起來就好像 B 在 A 前面一樣,得到錯誤的半透明結(jié)構(gòu)。
可以看出,半透明物體之間應(yīng)該先渲染離攝像機(jī)遠(yuǎn)的物體再渲染近的,才能得到正確的效果。
循環(huán)重疊的半透明物體
基于前面兩點(diǎn),渲染引擎通常會對物體的渲染順序做一下兩點(diǎn)優(yōu)化:
- 先渲染所有不透明物體,并開啟它們的深度測試和深度寫入。
- 把半透明物體按它們距離攝像機(jī)的遠(yuǎn)近進(jìn)行排序,然后按照從后往前的順序渲染這些半透明物體,并開啟它們的深度測試,但關(guān)閉深度寫入。
但就算做了這兩點(diǎn)優(yōu)化,有些時候還是會出現(xiàn)錯誤的透明效果,比如存在循環(huán)重疊的物體:
這是因為深度緩沖中的值其實是像素級別的,即每個像素有一個深度值,但是現(xiàn)在我們對單個物體級別進(jìn)行排序,這意味著排序結(jié)果是,要么物體 A 全部在 B 前面渲染,要么 A 全部在 B 后面渲染。但如果存在循環(huán)重疊的情況,我們永遠(yuǎn)不可能得到一個正確的排序順序。這種時候,我們可以選擇把物體拆分成兩個部分,然后再進(jìn)行正確的排序。但即便我們通過分割的方法解決了循環(huán)覆蓋的問題,還是會有其他的錯誤情況。
無法判定物體排序規(guī)則的情況
這里的問題是:如何排序?我們知道,一個物體的網(wǎng)格結(jié)構(gòu)往往占據(jù)了空間中的某一塊區(qū)域,也就是說,這個網(wǎng)格上每一個點(diǎn)的深度值可能都是不一樣的,我們選擇哪個深度值來作為整個物體的深度值和其他物體進(jìn)行排序呢?是網(wǎng)格中點(diǎn)嗎?還是最遠(yuǎn)的點(diǎn)?還是最近的點(diǎn)?不幸的是,對于上圖中的情況,選擇哪個深度值都會得到錯誤的結(jié)果,我們的排序結(jié)果總是 A 在 B 的前面,但實際上 A 有一部分被 B 遮擋了。這也意味著,一旦選定了一種判斷方式后,在某些情況下半透明物體之間一定會出現(xiàn)錯誤的遮擋問題。這種問題的解決方法通常也是分割網(wǎng)格。
盡管總是會有一些情況打亂我們的陣腳,但由于上述方法足夠有效并且容易實現(xiàn),因此大多數(shù)游戲引擎都使用了這樣的方法。為了減少錯誤排序的情況,我們可以盡可能讓模型是凸面體,并且考慮將復(fù)雜的模型拆分成可以獨(dú)立排序的多個子模型等。
8.2 Unity Shader 中的渲染順序
Unity 為了解決渲染順序的問題設(shè)計了渲染隊列(render queue),我們可以使用 SubShader 的 Queue 標(biāo)簽
來決定我們的模型將歸于哪個渲染隊列。Unity 在內(nèi)部使用一系列整數(shù)索引來表示每個渲染隊列,且索引號越小表示越早被渲染,并且Unity提前定義了5個渲染隊列:
要設(shè)置 Shader 的渲染隊列,可以通過在 SubShader 中指定相應(yīng)標(biāo)簽:
SubShader {
// 指定渲染隊列為 AlphaTest
Tags { "Queue"="AlphaTest" }
Pass {
...
}
}
也可以直接在編輯器中直接指定 Shader 的渲染隊列:
8.3 透明度測試
透明度測試原理:只要一個片元的透明度不滿足條件(通常是小于某個閾值),那么它對應(yīng)的片元就會被舍棄。被舍棄的片元將不會再進(jìn)行任何處理,也不會對顏色緩沖產(chǎn)生任何影響;否則,就會按照普通的不透明物體的處理方式來處理它。
通常我們在片元著色器中使用 clip 函數(shù)
來進(jìn)行透明度測試,它是 Cg 中的一個函數(shù)。
下面我們實現(xiàn)一個包含透明度測試的 Shader :
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
Shader "Chaptor 8/SHA_AlphaTest"
{
Properties
{
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
// 用于控制透明度測試的邊界值
_CutOff ("Alpha CutOff", Range(0, 1.0)) = 0.5
}
SubShader
{
// 通過標(biāo)簽 Queue 指定渲染隊列為 AlphaTest
// RenderType 標(biāo)簽可以讓Unity把這個Shader歸入到提前定義的組中,以指明該 Shader 是一個使用了透明度測試的Shader。
// RenderType標(biāo)簽通常被用于著色器替換功能
// IgnoreProjector 設(shè)置為 true 意味著這個 Shader 不會受投影器影響
// 通常,一個透明度測試 Shader 都需要設(shè)置這三個標(biāo)簽
Tags {"Queue"="AlphaTest" "IgnoreProjector"="true" "RenderType"="TransparentCutout"}
Pass
{
Tags {"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _CutOff;
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float3 worldNormal = normalize(i.worldNormal);
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
// 使用 clip 函數(shù)進(jìn)行透明度測試,等價于代碼
// if((texColor.a - _CutOff) < 0.0){
// discard
// }
clip(texColor.a - _CutOff);
fixed3 albedo = texColor * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
return fixed4(ambient + diffuse, 1.0);
}
ENDCG
}
}
Fallback "Specular"
}
效果如下,左側(cè)為正常方塊,右側(cè)為開啟了透明度測試的方塊:
可以在編輯器中通過參數(shù) CutOff 參數(shù)來設(shè)置透明度效果。
可以看出,透明度測試得到的透明效果很“極端”——要么完全透明,要么完全不透明,它的效果往往像在一個不透明物體上挖了一個空洞。而且,得到的透明效果在邊緣處往往參差不齊,有鋸齒,這是因為在邊界處紋理的透明度的變化精度問題。為了得到更加柔滑的透明效果,就可以使用透明度混合。
8.4 透明度混合
透明度混合:使用當(dāng)前片元的透明度作為混合因子,與已經(jīng)存儲在顏色緩沖中的顏色值進(jìn)行混合,得到新的顏色。但是,透明度混合需要關(guān)閉深度寫入,這使得我們要非常小心物體的渲染順序。
為了進(jìn)行混合,我們需要使用 Unity 提供的混合命令Blend
。Blend 是 Unity 提供的設(shè)置混合模式的命令。想要實現(xiàn)半透明的效果就需要把當(dāng)前自身的顏色和已經(jīng)存在于顏色緩沖中的顏色值進(jìn)行混合,混合時使用的函數(shù)就是由該指令決定的。
只有開啟了混合,設(shè)置片元的透明通道才有意義。
下面是一個透明度混合的例子,其中采用Blend SrcFactor DstFactor
語義,并設(shè)置 SrcFactor
為 SrcAlpha
,DstFactor
為 OneMinusSrcAlpha
,這樣講采用如下的混合算法:
更多混合語義的說明可查看官方文檔。下面是 Shader 代碼:
Shader "Chaptor 8/SHA_AlphaBlend"
{
Properties
{
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
// 用于控制整體的透明度
_AlphaScale ("Alpha Scale", Range(0, 1.0)) = 1
}
SubShader
{
// 通過標(biāo)簽 Queue 指定渲染隊列為 Transparent
Tags {"Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="Transparent"}
Pass
{
Tags {"LightMode" = "ForwardBase"}
// 深度寫入設(shè)置為關(guān)閉狀態(tài)
ZWrite off
// 開啟混合,并設(shè)置相應(yīng)混合因子
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _AlphaScale;
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float3 worldNormal = normalize(i.worldNormal);
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
fixed3 albedo = texColor * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
}
ENDCG
}
}
Fallback "Specular"
}
效果如下,左側(cè)為透明度測試效果,右側(cè)為透明度混合效果:
8.5 開啟深度寫入的半透明效果
由于在進(jìn)行透明度混合時關(guān)閉了深度寫入,所以當(dāng)模型中有互相交叉的結(jié)構(gòu)時,往往會得到錯誤的半透明效果:
為了解決這個問題,我們可以使用兩個 Pass 來渲染,第一個 Pass 開啟深度寫入,但不輸出顏色,它的目的僅僅是把模型的深度值寫入深度緩沖中;第二個 Pass 進(jìn)行正常的透明度混合,由于上一個 Pass 已經(jīng)得到逐像素的正確的深度信息,該 Pass 就可以按照像素級別的深度排序結(jié)果進(jìn)行透明渲染。但這種的方法的缺點(diǎn)在于多使用了一個 Pass,會對性能造成一定影響。
實現(xiàn)的 Shader 如下:
Shader "Chaptor 8/SHA_AlphaBlendZWrite"
{
Properties
{
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
// 用于控制整體的透明度
_AlphaScale ("Alpha Scale", Range(0, 1.0)) = 1
}
SubShader
{
// 通過標(biāo)簽 Queue 指定渲染隊列為 Transparent
Tags {"Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="Transparent"}
Pass {
// 第一個 Pass 開啟深度寫入
ZWrite on
// ColorMask 為渲染命令,用于設(shè)置顏色通道的寫掩碼
// ColorMask 0 表示該 Pass 不寫入任何顏色通道,即不會輸出任何顏色
ColorMask 0
}
Pass
{
Tags {"LightMode" = "ForwardBase"}
// 深度寫入設(shè)置為關(guān)閉狀態(tài)
ZWrite off
// 開啟混合,并設(shè)置相應(yīng)混合因子
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _AlphaScale;
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float3 worldNormal = normalize(i.worldNormal);
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
fixed3 albedo = texColor * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
}
ENDCG
}
}
Fallback "Specular"
}
效果如下:
ShaderLab 的混合命令
混合就是通過混合等式(blend equation)
將源顏色(source color)
和目標(biāo)顏色(destination color)
進(jìn)行相應(yīng)計算產(chǎn)生輸出顏色(output color)
。
- 源顏色,我們用 S 表示,指的是由片元著色器產(chǎn)生的顏色值
- 目標(biāo)顏色,我們用 D 表示,指的是從顏色緩沖中讀取的顏色值
- 輸出顏色,我們用 O 表示,指的是混合完成我們得到的結(jié)果顏色值
- S、D、O 都是包含了 RGBA 四個通道的顏色值。
- 混合等式就是用于操作計算 S 和 D 的函數(shù)。當(dāng)進(jìn)行混合時,通常我們需要使用兩個混合等式,一個用于混合 RGB 通道,一個用于混合 A 通道。當(dāng)設(shè)置混合狀態(tài)時,我們實際設(shè)置的就是混合等式中的操作和因子。
混合等式和混合因子
Unity ShaderLab 使用渲染命令 Blend 來設(shè)置混合等式的混合因子:
ShaderLab 支持的常用混合因子如下:
更詳細(xì)信息見Unity 官方手冊
混合操作
我們可以使用渲染命令BelndOp
來設(shè)置混合操作類型,默認(rèn)的操作類型為加法,支持的常見混合操作如下:
更詳細(xì)信息見Unity 官方手冊
常見的混合類型
下面列出了常見的幾種混合類型:
// 正常(Normal),即透明度混合
Blend SrcAlpha OneMinusSrcAlpha
// 柔和相加(Soft Additive)
Blend OneMinusDstColor One
// 正片疊底(Multiply),即相乘
Blend DstColor Zero
// 兩倍相乘(2x Multiply)
Blend DstColor SrcColor
// 變暗(Darken)
BlendOp Min
Blend One One
// 變亮(Lighten)
BlendOp Max
Blend One One
// 濾色(Screen)
Blend OneMinusDstColor One
// 等同于
Blend One OneMinusSrcColor
// 線性減淡(Linear Dodge)
Blend One One
對應(yīng)的混合效果如下圖:
雙面渲染的透明效果
在現(xiàn)實生活中,如果一個物體是透明的,意味著我們不僅可以透過它看到其他物體的樣子,也可以看到它內(nèi)部的結(jié)構(gòu)。但在前面實現(xiàn)的透明效果中,無論是透明度測試還是透明度混合,我們都無法觀察到正方體內(nèi)部及其背面的形狀,導(dǎo)致物體看起來就好像只有半個一樣。這是因為,**默認(rèn)情況下渲染引擎剔除了物體背面(相對于攝像機(jī)的方向)的渲染圖元,而只渲染了物體的正面。**如果我們想要得到雙面渲染的效果,可以使用 Cull
指令來控制需要剔除哪個面的渲染圖元。
Cull Back | Front | Off
- Back:剔除背面,背對著攝像機(jī)的圖元不會被渲染,Shader 默認(rèn)使用此種剔除模式
- Front:剔除正面,炒香攝像機(jī)的圖元不會被渲染
- Off:關(guān)閉剔除,所有圖元都會被渲染,但圖元數(shù)會成倍增加,謹(jǐn)慎使用
透明度測試的雙面渲染
在 Pass 中關(guān)閉剔除即可:
Shader "Chaptor 8/SHA_AlphaTest"
{
Properties
{
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
_CutOff ("Alpha CutOff", Range(0, 1.0)) = 0.5
}
SubShader
{
Tags {"Queue"="AlphaTest" "IgnoreProjector"="true" "RenderType"="TransparentCutout"}
Pass
{
Tags {"LightMode" = "ForwardBase"}
// 關(guān)閉剔除
Cull off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _CutOff;
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float3 worldNormal = normalize(i.worldNormal);
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
clip(texColor.a - _CutOff);
fixed3 albedo = texColor * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
return fixed4(ambient + diffuse, 1.0);
}
ENDCG
}
}
Fallback "Specular"
}
效果如下:
透明度混合的雙面渲染
和透明度測試相比,想要讓透明度混合實現(xiàn)雙面渲染會更復(fù)雜一些,這是因為透明度混合需要關(guān)閉深度寫入,所以我們需要保證渲染順序的正確性。我們想要保證圖元是從后往前渲染的。為此,我們選擇把雙面渲染的工作分成兩個Pass,第一個Pass只渲染背面,第二個Pass只渲染正面,由于Unity會順序執(zhí)行SubShader中的各個Pass,因此我們可以保證背面總是在正面被渲染之前渲染,從而可以保證正確半透明效果:文章來源:http://www.zghlxwxcb.cn/news/detail-496857.html
Shader "Chaptor 8/SHA_AlphaBlend"
{
Properties
{
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
_AlphaScale ("Alpha Scale", Range(0, 1.0)) = 1
}
SubShader
{
Tags {"Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="Transparent"}
Pass {
Tags {"LightMode" = "ForwardBase"}
// 先剔除正面渲染背面
Cull Front
ZWrite off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _AlphaScale;
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float3 worldNormal = normalize(i.worldNormal);
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
fixed3 albedo = texColor * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
}
ENDCG
}
Pass
{
Tags {"LightMode" = "ForwardBase"}
// 再剔除背面渲染正面
Cull Back
ZWrite off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _AlphaScale;
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float3 worldNormal = normalize(i.worldNormal);
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
fixed3 albedo = texColor * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
}
ENDCG
}
}
Fallback "Specular"
}
效果如下:文章來源地址http://www.zghlxwxcb.cn/news/detail-496857.html
到了這里,關(guān)于《Unity 入門精要》第8章 透明效果的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!