——20.8.18緩存
Unity中,一般用兩種方法來實現透明效果 1)透明度測試 2)透明度混合 這兩個分別是什麼呢app
1.透明度測試指的只要不符合條件(即在物體顏色中的alpha通道的值小於某一個閾值)對應片元會被直接捨棄。(該片元不會對顏色緩衝有任何影響)不然就按照不透明物體處理。進行深度測試和深度寫入。特色:不關閉深度寫入 無法作到半透明要麼不透明要麼全透明學習
2.透明度混合指的是使用當前片元透明度爲混合因子於已經儲存的在顏色緩存中的顏色進行混合。要關閉深度寫入不關閉深度測試。經過深度測試可以正確的肯定非透明物體於透明物體之間的覆蓋關係。 特色:關閉深度寫入 作到半透明測試
在咱們對上面兩個定義進行下一步解讀以前先回顧兩個概念 深度寫入以及深度測試 二者發生的過程是在GPU渲染的時候發生的,即下面這張圖的關係spa
深度測試就是經過攝像機與該片元的距離(也就是深度)於以前存在深度緩存區的深度值進行比較 對應的條件來決定是否捨棄該片元 ZTest3d
深度寫入則是是否要將深度測試的結果覆蓋到深度緩存區中 由於半透明物體的存在是不能夠做爲深度的標準來捨棄由於半透明物體與其餘物體是疊加的關係的 ZWriteorm
接着咱們就能夠引入一個新的重要性的問題 就是渲染順序 這是在Tags中的一個元素 「Queue」blog
由於咱們對半透明物體關閉了深度寫入的操做 則對咱們的渲染順序有必定的要求也就是在關閉了深度寫入的條件下渲染順序的正確與否即是十分重要由於會引起兩個問題 1)對非透明物體與半透明物體之間 由於關閉了深度寫入,當半透明物體先渲染因爲深度緩存爲空則非透明物體直接覆蓋 2)半透明物體與半透明物體之間 渲染順序若是與深度一致(由近到遠)則致使看上去位置相反ip
經常使用方法:1.先渲染全部不透明物體開啓深度測試與寫入(「Queue」=「Geometry」) 2.把半透明物體由遠到近順序渲染 並開啓深度測試關閉寫入(「Queue」=「AlphaTest「)資源
但依舊仍是有問題因此才叫作混亂的開端 由於存在重疊現象一個物體有兩個渲染順序(每一個像素都有深度,逐像素去遍歷沒法選取) 便採起分割網格 而且務必使用凸面體 須要具體問題具體分析
下面就是久違的ShaderLab部分了,個人想法是堅定不作搬運工,下面的部分直接選取部分代碼說本身的理解 仍是要本身敲一遍 最後再上源碼 雖然上面部分就是書上的用個人理解包括一些大佬的理解順序也很像書上的順序 就是慢慢改進吧 嘿嘿
1、透明度測試
void Clip(x) x:float4,float3,flaot2,float
void Clip(float4 x)
{
if(any(x < 0))
discard;
}
參數x是裁剪時所使用的標量或矢量
若是給定參數的任何一個份量是負數則直接捨棄該像素
Tags { "RenderType"="TransparentCutOut" "Queue"="AlphaTest" "IgnoreProjector"="True" }
樣例給的要點主要有要在SubShader中設置」Queue「爲了透明度測試 」IgnoreProjector「爲了設置不受到投影器的影響
clip(texColor.a - _Cutoff);
內部參數爲原圖像的透明度減去_Cutoff(爲能接受的閾值) 當這個值小於0則直接discard
源碼:
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' Shader "Unlit/8-1AlphaTest" { Properties { _Color("Color", Color) = (1,1,1,1) _MainTex ("Texture", 2D) = "white" {} _Cutoff ("Alpha Cutoff", Range(0, 1)) = 0.5 } SubShader { Tags { "RenderType"="TransparentCutOut" "Queue"="AlphaTest" "IgnoreProjector"="True" } Pass { Tags{ "LightMode"="ForwardBase" } CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; }; struct v2f { float4 pos : SV_POSITION; float3 worldNormal : TEXCOORD0; float3 worldPos : TEXCOORD1; float2 uv : TEXCOORD2; }; sampler2D _MainTex; float4 _MainTex_ST; fixed4 _Color; fixed _Cutoff; v2f vert (appdata 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); clip(texColor.a - _Cutoff); fixed3 albedo = texColor.rgb * _Color.rgb; fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo; fixed3 diffuse = _LightColor0 * albedo * saturate(dot(worldNormal, worldLightDir)); return fixed4(diffuse + ambient, 1.0); } ENDCG } }FallBack "Transparent/Cutout/VertexLit" }
2、透明度混合
說到混合就要講混合指令Blend
採用的是如下的公式 源顏色就是該片元的顏色 目標顏色就是顏色緩存的顏色
ZWrite Off Blend SrcAlpha OneMinusSrcAlpha
要關閉深度寫入 同時開啓混合(設置的時候即開啓)若是不打開blend默認是關閉的就以深度測試的結構決定直接覆蓋A=1(?沒找到依據但實驗結果如此)
源碼:
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' Shader "Unlit/8-2AlphaBlend" { Properties { _MainTex ("Texture", 2D) = "white" {} _Color ("Color", Color) = (1,1,1,1) _AlphaScale ("AlphaScale", Range(0,1)) = 1 } SubShader { Tags { "RenderType"="Transparent" "IgnoreProjector"="True" "Queue"="Transparent" } Pass { Tags { "LightMode"="ForwardBase" } ZWrite Off Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; }; struct v2f { float4 pos : SV_POSITION; float3 worldNormal : TEXCOORD0; float3 worldPos : TEXCOORD1; float2 uv : TEXCOORD2; }; sampler2D _MainTex; float4 _MainTex_ST; fixed4 _Color; fixed _AlphaScale; v2f vert (appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); 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.rgb * albedo; fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(worldNormal, worldLightDir)); return fixed4(ambient + diffuse, texColor.a * _AlphaScale); } ENDCG } }FallBack "Transparent/VertexLit" }
3、開啓深度寫入的半透明
咱們能夠看一下上面這個圖能夠發現這個圖全露出就是當模型存在互相交叉的時候會獲得這樣錯誤的效果 本質的緣由就是沒有打開深度寫入致使的單物體內部錯誤 那有什麼解決方方能就是用雙Pass 第一個Pass把最前面的輸出片元的深度值經過比較後存入深度緩存中但不輸出顏色 第二個Pass再關閉深度寫入 能夠直接和正確的比較直接discard
ColorMask 0 | RGB | G |
設置顏色通道的寫掩碼就是指定輸出的顏色通道 0就是不輸出
源碼:
Shader "Unlit/8-3AlphaBlendZWrite" { Properties { _MainTex ("Texture", 2D) = "white" {} _Color ("Color", Color) = (1,1,1,1) _AlphaScale ("AlphaScale", Range(0, 1)) = 1 } SubShader { Tags { "RenderType"="Transparent" "IgnoreProjector"="True" "Queue"="Transparent" } Pass{ ZWrite On ColorMask 0 } Pass { Tags { "LightMode"="ForwardBase" } ZWrite Off //Alpha Blend Blend SrcAlpha OneMinusSrcAlpha //Soft Additive //Blend OneMinusDstColor One //Multiply //Blend DstColor Zero //Multiply 2X //Blend DstColor SrcColor //Darken //BlendOp min //Blend One One CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; }; struct v2f { float4 pos : SV_POSITION; float3 worldNormal : TEXCOORD0; float3 worldPos : TEXCOORD1; float2 uv : TEXCOORD2; }; sampler2D _MainTex; float4 _MainTex_ST; fixed4 _Color; fixed _AlphaScale; v2f vert (appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); 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.rgb * albedo; fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(worldNormal, worldLightDir)); return fixed4(ambient + diffuse, texColor.a * _AlphaScale); } ENDCG } }FallBack "Diffuse" }
這張圖有一點奇怪就是存在投影。能夠看Cp9的半透明投影部分主要是FallBack設置。
4、雙面渲染的透明效果
這個是最後的內容啦 加加油 也就是剔除渲染圖元的關係默認的狀況下爲了節約資源只渲染證實不渲染背面咱們則須要用指令打開
Cull off | back | Front
設置back不渲染back 設置off就是都渲染
那麼對應的透明度測試的雙目渲染我直接放圖代碼即是在以上基礎上在Pass裏面添加以上指令
能夠明顯對比
而後就是透明度混合的雙目渲染 比上述的麻煩了一點就是雙Pass 先渲染背面後渲染正面由於從視角看上去避免了以前說的半透明物體與半透明物體之間由於渲染順序的致使的問題能夠直接比較一下 畢竟圖形學是效果的學科看看啥效果就知道了
能夠很明顯看出第二個比第一個有了對面的背面(奇妙的比喻)也就是雙目渲染 第三個則是渲染順序相反背面先渲染 攝像機進入物體能看到顏色鮮豔的正面
最後最後留個小坑就是BlendOp 我也沒太看懂可能須要具體例子纔會用吧 只有一些效果
謝謝你看到最後 cheers!
ps.剛剛看完Shader入門Cp8記錄記錄一些細碎的知識點以及相關深度研究學習