1、基本概念
1.1、基本概念
在實時渲染中實作透明效果,需要在渲染模型時控制它的透明通道(Alpha Channel),
透明度為1代表該像素是完全不透明的;0代表完全透明,
Unity中可以使用兩種方式得到透明效果:透明度測驗(alpha test,無法得到真正的透明效果)、透明度混合(alpha blending),
對于不透明物體,深度緩沖(depth buffer)就能正確判斷物體的遮擋關系,
- 透明度測驗:一個片元的透明度達不到某個標準,那么就會被舍棄;否則就按不透明物體進行處理,因此,透明度測驗不需要關閉深度寫入,
- 透明度混合:使用當前片元作為混合因子,與已經存盤在顏色緩沖區中的顏色進行混合,透明度混合需要關閉深度寫入(即離鏡頭更近的片元不會覆寫原有的同一位置的片元),因此我們需要小心物體的渲染順序,但是仍然可以進行深度測驗,
1.2、渲染順序
為了保證半透明物體后面的物體能夠被我們看到,渲染時我們必須關閉深度寫入,
因此,渲染順序會很大程度上影響顯示效果,
理想狀況下,我們需要先正常渲染所有不透明物體,再由遠及近渲染透明物體,
但是現實情況中,可能涉及到不同透明物體交錯排列的情況,因此這種方法會遇到問題,
盡管這種方法存在問題,但是實作簡單,因此大多數游戲引擎都使用了這樣的方法,
為了減少bug,我們可以讓模型盡可能是凸面體,將復雜的模型拆分成多個獨立排序的子模型,
2、Unity Shader的渲染順序
Unity 使用渲染佇列 (render queue) 解決渲染順序問題,
使用SubShader的Queue標簽來決定我們的模型屬于哪個渲染佇列,Unity 內部使用一系列渲染數表示每個渲染佇列,索引號越小表示越早被渲染,
Unity5 中, Unity 提前定義了5個渲染佇列,
下面是五個渲染佇列及其描述:
透明度測驗代碼中應該包含如下代碼:
SubShader{
Tags {"Queue" = "AlphaTest"}
Pass{
...
}
}
透明度混合則是這樣的:
SubShader{
Tags {"Queue" = "Transparent"}
Pass{
Zwrite Off
...
}
}
也可以在SubShader打開Zwrite Off,這樣一個SubShader中的全部Pass都會關閉深度寫入,
3、透明度測驗
上面已經介紹了透明度測驗:一個片元的透明度達不到某個標準,那么就會被舍棄;否則就按不透明物體進行處理,
使用Cg中的Clip函式:給定的引數的任何一個分量是負數,就會舍棄當前像素的輸出顏色,等同于下面的代碼:
void clip(float4 x){
if (any(x < 0)){
discard;
}
}
在SubShader中,我們定義Tags:
Tags{"Queue" = "AlphaTest" "IgnoreProjector"="True" "RenderType" = "TransparentCutout"}
- 將佇列Queue設定為AlphaTest,
- 將RenderType標簽指定為TransparentCutout,讓Unity將此Shader歸入提前定義的組中,該標簽通常用于著色器替換功能,
- 將IgnoreProjector設定為True,意味著這個Shdaer不會受到投影器(Projectors)的影響,
代碼為:
Shader "Unity Shader Book/Chapter 8/AlphaTest"
{
Properties
{
_Color ("Main Tint", Color) = (1,1,1,1)
_MainTex("Main Tex", 2D) = "white" {}
_Cutoff("Alpha CutOff", Range(0, 1)) = 0.5//透明度測驗的判斷條件
}
SubShader
{
Tags{"Queue" = "AlphaTest" "IgnoreProjector"="True" "RenderType" = "TransparentCutout"}
Pass{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
//屬性
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;//紋理縮放
fixed _Cutoff;
//輸入輸出結構體
struct a2v{
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert (a2v v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
//Alpha Test
clip(texColor.a - _Cutoff);
fixed3 albedo = texColor.rgb * _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 "Transparent/Cutout/VertexLit"
}
4、透明度混合
使用Blend命令控制混合,
Shader "Unity Shader Book/Chapter 8/AlphaBlend"
{
Properties
{
_Color ("Main Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
_AlphaScale ("Alpha Scale", Range(0, 1)) = 1
}
SubShader
{
Tags{"Queue" = "Transparent" "IgnoreProjector"="True" "RenderType" = "Transparent"}
Pass{
Tags {"LightMode"="ForwardBase"}
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
//屬性
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;//紋理縮放
fixed _AlphaScale;
//輸入輸出結構體
struct a2v{
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert(a2v v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
fixed3 albedo = texColor.rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
//設定回傳值的透明通道:即紋理像素的透明通道與材質引數_AlphaScale的乘積,
return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
產生的效果如下所示:
需要注意的是,由于我們關閉了深度寫入,因此在顯示重疊物體的時候,前后關系顯示會出現問題,
5、開啟深度寫入的半透明效果
可以使用兩個Pass來渲染模型,第一個Pass開啟深度寫入,但是不輸出顏色;第二個Pass使用前一個Pass寫入的深度緩沖中的值進行透明度混合,
使用這種方法,可以實作正常的遮擋關系,但是在半透明物體后面的半透明物體會被完全覆寫,不能顯示,
在新創建的Pass中,我們使用新的渲染命令——ColorMask,ShaderLab中,ColorMask用于設定顏色通道的寫掩碼(write mask),
ColorMask RGB | A | 0 | 其他任何R、G、B、A的組合
當ColorMask設定為0時,意味著該Pass不寫入任何顏色通道,
6、Shdaer Lab 的混合命令
這是使用加法混合的混合公式(S為源顏色,D為目標顏色):

下面是混合因子可能的值:
6.1、其他混合操作
使用BlendOP BlendOperation命令(混合操作命令),使用其他運算模式,
混合操作命令通常與混合因子命令一同作業,
6.2、常見混合
//正常混合,即透明度混合
Blend SrcAlpha OneMinusSrcAlpha
//柔和相加(Soft Additive)
Blend OneMinusDstColor One
//正片疊底(Multiply),即相乘
Blend DstColor SrcColor
//兩倍相乘(2X Multiply)
Blend DstColor SrcColor
//變暗(Darken)
BlendOp Min
Blend One One
//變亮(Lighten)
BlendOp Max
Blend One One
//濾色(Screen)
Blend OneMinusDstColor One
//等同于
Blend One MinusSrcColor
//線性減淡(Linear Dodge)
Blend One One
7、雙面渲染的透明效果
可以使用Cull指令選擇需要剔除那個面的渲染圖元,
Cull Back | Front | Off
- Back:背對攝像機的圖元不會被渲染(默認)
- Front:朝向攝像機的圖元不會被渲染
- Off:關閉剔除功能,所有圖元都會被渲染
7.1、透明度測驗的雙面渲染
直接在Pass的Tags后面添加
Cull Off
7.2、透明度混合的雙面渲染
復制透明度混合的Pass,形成兩個Pass,
第一個Pass只渲染背面,第二個Pass只渲染正面,這樣就能保證背面總是在正面渲染之前被渲染,
本篇文章為讀《Unity Shader 入門精要》時寫的筆記
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/498605.html
標籤:其他
