修正半透明頭發(fā)的渲染異常
這兩天在整理 小甜甜 的捏臉系統(tǒng)圈盔,順帶把前項目 半透明頭發(fā)渲染異常的bug 修正了,效果如下:
其實悄雅,前項目的頭發(fā)工作良好驱敲,只是有一點 半透明渲染順序 的小瑕疵,但正如前文 一個自陰影的Bug 所提到的:我在遷移shader的時候因為偷懶宽闲,把頭發(fā)改成了 Surface Shader众眨,從而帶來了一些新的問題。
問題一:關(guān)于Surface Shader的alpha指令
首先容诬,我犯了第一個錯誤娩梨,改 Surface Shader 的過程中,我漏掉了 alpha 指令的設(shè)置览徒,shader變成了 實體渲染狈定,表現(xiàn)如下:
關(guān)于 Surface Shader的alpha指令,可以參考Unity幫助文檔:
alpha or alpha:auto - Will pick fade-transparency (same as alpha:fade) for simple lighting functions, and premultiplied transparency (same as alpha:premul) for physically based lighting functions.
alpha:blend - Enable alpha blending.
alpha:fade - Enable traditional fade-transparency.
alpha:premul - Enable premultiplied alpha transparency.
雖然我指定了 Queue = Transparent 和 Blend SrcAlpha OneMinusSrcAlpha习蓬,但因為沒設(shè)置 alpha纽什,Unity最終生成的代碼給我加了這么一句:
UNITY_OPAQUE_ALPHA(c.a);
UNITY_OPAQUE_ALPHA定義如下:
#define UNITY_OPAQUE_ALPHA(outputAlpha) outputAlpha = 1.0
這里認(rèn)為是 實體渲染,強(qiáng)制的把a(bǔ)lpha設(shè)置為 1 了友雳。
修正這個問題很簡單稿湿,加上 alpha 指令的設(shè)置即可:
#pragma surface surf BGHair fullforwardshadows addshadow vertex:vert alpha
問題二:半透明的渲染順序
加上 alpha 后,頭發(fā)通透了好多押赊,但是新的問題來了饺藤,當(dāng) 頭發(fā)的半透明部分相互交錯 時,渲染表現(xiàn)錯誤:
因為是半透明渲染流礁,Surface Shader生成的代碼開啟了 深度測試涕俗,關(guān)閉了 深度緩沖寫入,當(dāng) 半透部分相互交錯 時神帅,前后關(guān)系難以保證再姑。
這是半透材質(zhì)經(jīng)常遇到的問題,我們可以把頭發(fā)模型拆成 實體 和 半透 兩部分找御,先渲染 實體部分元镀,再渲染 半透部分绍填,在實體的基礎(chǔ)上做Alpha混合,從而盡可能避免半透交錯的機(jī)會栖疑。
不過當(dāng)初做這個頭發(fā)的時候讨永,并沒有拆分模型,做法也很簡單:開啟 深度緩沖寫入遇革,讓頭發(fā)的半透明部分僅局限于 發(fā)尾卿闹,由于半透區(qū)域很小,一般情況下很難穿幫萝快,不過某些角度下細(xì)看還是會有問題锻霎。
比如下圖紅圈標(biāo)注的部位,后面的頭發(fā)因為 深度測試 沒通過揪漩,導(dǎo)致前面的頭發(fā)直接和背景混合旋恼,引發(fā)了表現(xiàn)錯誤。
此外氢拥,因為遷移到了 Surface Shader蚌铜,我發(fā)現(xiàn)如果打開了 alpha 指令,無論我是否指定 ZWrite On嫩海,Unity給我生成的代碼始終是 ZWrite Off 的。
解決方式一:用AlphaTest代替AlphaBlend
如果這里我們用 AlphaTest 來取代 AlphaBlend 囚痴,效果會如何呢叁怪?
作為測試,簡單的加一句 clip深滚,并且關(guān)閉 alpha 指令:
clip(mainColor.a - 0.6);
效果如下:
渲染正確了奕谭,只是發(fā)尾太硬,用美術(shù)的話說:不夠透氣痴荐。
解決方式二:加一個Pass
方式一 不夠完美血柳,不過我們可以用它做一個 額外的Pass,類似拆分模型后的 實體部分渲染生兆,渲染完畢再做之前的 半透明渲染难捌。
代碼如下:
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
half4 _Color;
half4 _Color2;
half _Color2Offset;
struct appdata
{
float3 pos : POSITION;
float3 uv0 : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
UNITY_POSITION(pos);
float4 uv0 : TEXCOORD0;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert (appdata IN)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(IN);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(IN.pos);
o.uv0.xy = TRANSFORM_TEX(IN.uv0, _MainTex);
return o;
}
half4 frag(v2f IN) : SV_Target
{
half4 mainColor = tex2D( _MainTex, IN.uv0.xy );
clip(mainColor.a - 0.999);
half3 hairColor = _Color.rgb * mainColor.r;
half mixStrength = saturate(mainColor.g + _Color2Offset);
hairColor = lerp(hairColor, _Color2.rgb, mixStrength);
return half4(hairColor, 1);
}
ENDCG
}
代碼很簡單,最主要的就是下面這句:
clip(mainColor.a - 0.999);
這里把原來的半透部分盡可能的裁剪掉鸦难,只保留實體的輪廓根吁,如下:
在這個基礎(chǔ)之上,再做一次半透明渲染即可合蔽。
局限
采用 方式二 后击敌,原先 發(fā)尾 部分因為 半透重疊 引發(fā)的 渲染瑕疵 也可以更大程度的緩解了,如下圖:
此外拴事,需要注意的是沃斤,這里能良好工作圣蝎,頭發(fā)的半透部分僅局限于 發(fā)尾 也是一個重要因素。
最后換個臉妝再來一張:
個人主頁
本文的個人主頁鏈接:https://baddogzz.github.io/2019/12/24/Fix-Hair-Transparent/
好了衡瓶,拜拜捅彻。