Z緩衝區(Z-Buffer)算法
當要渲染一個片元時候,需要把它的深度值和已經存在於z緩衝器中的深度值進行比較(如果開啓了深度測試),
如果該片元值較小,該片元的z值應該覆蓋掉對應單元顏色緩衝區中的顏色值。並將該片元深度值寫入z緩衝器中。
如果該片元深度值和z緩衝器中對應單元已有的值比較,值較大。表明有物體遮住了它,該片元不被渲染。
參考:https://www.cnblogs.com/cnblog-wuran/p/9830994.html
透明度操作-測試&混合
- 透明度測試 一個片元透明度不滿足某個條件(通常是小於某個設置的閾值),對應的片元會被捨棄。不會對顏色緩衝區產生任何影響。否則就按照普通不透明片元來處理。
深度測試不需要關閉深度寫入。
- 透明度混合 使用當前透明度作爲混合因子,與已經存在顏色緩衝區中的顏色值進行混合,得到新的顏色。
深度混合需要關閉深度寫入。不需要關閉深度測試。如果開啓深度寫入,靠近攝像機有個半透明物體,此物體深度寫入深度緩衝區,再更遠距離後邊有個模型由於深度大於半透物體,被認爲是被半透物體遮罩。不渲染,透過半透物體,看不到遠距離物體。。。不合理。。。
因此,關閉深度寫入纔可能得到正確渲染結果。 但是關閉深度寫入,需要設置正確的渲染順序,才能得到正確的渲染結果。
UnityShader 的渲染順序
爲了解決渲染順序,Unity爲我們提供了渲染隊列(render queue),我們可以用SubShader的Queue標籤決定模型歸於哪個渲染隊列。索引號越小越早渲染。
名稱 | 索引號 | 描述 |
Background | 1000 | 最先渲染,通常該隊列用來渲染需要繪製在背景上的物體 |
Geometry | 2000 | 默認渲染隊列,大多數物體都是用這個隊列,不透明物體使用這個隊列 |
AlphaTest | 2450 | 需要透明度測試的物體使用,Unity5之後把它從Geometry隊列中單獨分出來,這是因爲在所有不透明物體渲染之後再渲染他們會更高效 |
Transparent | 3000 | 從後往前渲染,任何使用了透明度混合(關閉了深度寫入的Shader)的物體,都應該使用該隊列。 |
Overlay | 4000 | 實現疊加效果,需要在最後渲染的物體使用該隊列 |
透明度測試(AlphaTest)
Shader "Chan/Chapter8_AlphaTest" {
Properties
{
_Color("Main Tint",Color) = (1,1,1,1)
_MainTex("Main Tex",2D) = "white"{}
_CutOff("Alpha Cutoff",Range(0,1)) = 0.5
}
SubShader
{
//Queue = 定義該SubShader的渲染隊列 IgnoreProject = 不受投影影響 RenderType = 提前定義組???
Tags{"Queue" = "AlphaTest" "IgnoreProject" = "True" "RenderType" = "TransparentCutout"}
Pass
{
Tags{"LightMode" = "Forwardbase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
float _CutOff;
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);
/*
if(texColor.a - _Cutoff) 爲負
{
discard;//丟棄片元
}
*/
//同上 紋理A通道值小於_Cutoff,該片元被丟棄
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"
}
Clip()爲負,該片元被丟棄。紋理A通道值小於_Cutoff,該片元被丟棄。否則按普通紋理,採樣顯示。
透明度混合(AlphaBlend)
透明度混合需要關閉深度寫入。爲了混合,需要Unity提供的混合模式的命令。設置了混合因子,就默認開啓了混合模式。
語義 | 描述 |
Blend Off | 關閉混合 |
Blend On | 開啓混合 |
Blend 混合因子A 混合因子B | 開啓混合,並設置混合因子,具體混合操作看混合因子定義 |
BlendOp BlendOperation | 開啓混合,使用BlendOperation已定義好的其他混合操作 |
關閉深度吸入的透明度混合效果 :
Shader "Chan/Chapter8_AlphaBlend" {
Properties
{
_Color("Color Tint",Color) = (1,1,1,1)
_MainTex("Main Tex",2D) = "white"{}
_AlphaScale("Alpha Scale",Range(0,1.0)) = 1.0
}
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;
float _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));
//設置了透明度,開啓透明度混合設置纔有意義
return fixed4(ambient + diffuse,texColor.a * _AlphaScale);
}
ENDCG
}
}
Fallback "Transparent/VertexLit"
}
關閉深度寫入後,模型自身有遮擋的情況就會出現右圖情況。。。解決問題如下:
開啓深度吸入的透明度混合效果 :
Shader "Chan/Chapter8_AlphaBlendZWrite" {
Properties
{
_Color("Color Tint",Color) = (1,1,1,1)
_MainTex("Main Tex",2D) = "white"{}
_AlphaScale("Alpha Scale",Range(0,1.0)) = 1.0
}
SubShader
{
//透明度操作必有的三個標籤
Tags{"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}
//多了一個Pass
Pass
{
ZWrite On //開啓深度寫入
ColorMask 0; //不寫入顏色通道,只寫入深度緩衝器中寫入緩存
}
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;
float _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));
//設置了透明度,開啓透明度混合設置纔有意義
return fixed4(ambient + diffuse,texColor.a * _AlphaScale);
}
ENDCG
}
}
Fallback "Transparent/VertexLit"
}
對了一個Pass通道,1.開啓深度寫入,2.幀緩衝器中不寫入顏色,只在深度緩衝器中寫入深度值。
ColorMask 設置那些顏色通道可以寫入幀緩存中。可以是RGBA四個通道中的任意組合,或者是0。例如:
ColorMask 0 不寫入任何顏色
ColorMask RGA 寫入顏色通道中的RGA通道。
開啓深度寫入效果如下:
透明度混合中的混合因子
混合操作時候:
- 有兩個操作對象,源顏色S(片元着色器產生顏色)和目標顏色D(顏色緩衝區中讀取的顏色),混合後得到輸出顏色O(寫入顏色緩衝區)。
- 混合時候,RGB通道和A通道時分開混合的,因此需要設置兩個混合因子,如果只設置一個,A通道默認複用RGB通道混合因子
- 混合因子,默認是相加操作。使用其他操作,設置BlendOp BlendOperation
參數 | 描述 |
One | 因子爲1 |
Zero | 因子爲0 |
SrcColor | 因子爲源顏色值,當用作RGB通道混合時,使用SrcColor(源顏色值)的RGB分量作爲混合因子。當用於混合A通道時候,使用SrcColor(源顏色值)的A分量作爲混合因子。 |
SrcAlpha | 因子爲源顏色的透明度值(A通道) |
DstColor | 因子爲源顏色值,當用作RGB通道混合時,使用DstColor(目標顏色值)的RGB分量作爲混合因子。當用於混合A通道時候,使用DstColor(目標顏色值)的A分量作爲混合因子。 |
DstAlpha | 因子爲目標顏色的透明度值(A通道) |
OneMinusSrcColor | 因子爲(1-源顏色)。當用於混合RGB通道時候,使用結果的RGB分量作爲混合因子。當用於混合A通道時候,使用結果的A分量作爲混合因子。 |
OneMinusSrcAlpha | 因子爲(1-源顏色的透明度值) |
OneMinusDstColor | 因子爲(1-目標顏色)。當用於混合RGB通道時候,使用結果的RGB分量作爲混合因子。當用於混合A通道時候,使用結果的A分量作爲混合因子。 |
OneMinusDstAlpha | 因子爲(1-目標顏色的透明度值) |
透明度混合中的混合操作
操作 | 描述 |
Add |
將混合後的源顏色和目的顏色相加,默認的操作。 = SrcFactor x + DstFactor x = SrcFactorA x + DstFactorA x |
Sub |
用混合後的源顏色減去混合後的目的顏色。 = SrcFactor x - DstFactor x = SrcFactorA x - DstFactorA x |
RevSub |
混合後的目的顏色減去混合後的源顏色 = DstFactor x - SrcFactor x = DstFactorA x - SrcFactorA x |
Min |
使用源顏色和目的顏色中比較小的值,是逐分量比較的。 = |
Max | = |
其他邏輯操作 | 僅在DirectX 11.1中支持 |
常用混合類型
混合設置 | 混合效果 |
Blend SrcAlpha OneMinusSrcAlpha | 透明度混合 |
Blend OneMinusDstColor One | 柔和相加 |
Blend DstColor Zero | 正片疊底,即相乘 |
Blend DstColor SrcColor | 兩倍相乘 |
BlendOp Min/Blend One One | 變暗 |
BlendOp Max/Blend One One | 變亮 |
Blend OneMinusDstColor One Blend One OneMinusDstColor |
濾色 |
Blend One One | 線性減淡 |
雙面渲染的透明效果
透明物體,能看到物體內部及背面形狀。因爲渲染引擎默認剔除背對攝像機的圖元。可通過Cull設置正反面剔除狀態。
Cull Back/Front Off/On = 剔除 背面/正面 開啓/關閉
雙面透明度測試效果:
在AlphaTest Shader中添加 Cull Off不剔除背面即可。
雙面透明度混合效果:
透明度混合關閉了深度寫入,因此要控制物體正面背面渲染順序。因此第一個Pass渲染背面,第二個Pass渲染正面。
相比AlphaBlend Shader中,添加一個跟原來一樣的Pass。
然後第一個Pass 剔除正面-Cull Front 渲染背面,第二個Pass剔除背面-Cull Back渲染正面。