模型勾邊outline
阿新 • • 發佈:2018-12-16
簡述 在卡通渲染中一般會涉及到模型勾邊。效率最高的方式是在shader中去做。render to texture的實現方式這裡不討論。 Shader勾邊實現流程大致為:對模型進行2遍(2個pass)繪製,第一遍(勾邊pass)在vertex shader中對模型沿頂點法線方向放大,fragment shader設定輸出顏色為勾邊顏色;第二遍正常繪製模型,除被放大的部分外,其餘被覆蓋,這樣就有了勾邊的效果。 實現 SubShader { Tags { "RenderType"="Opaque"} pass { ZWrite Off CGPROGRAM #include "UnityCG.cginc" struct v2f_outline { float4 pos : SV_POSITION; }; v2f_outline vert_outline(appdata_full v) { // vertex data scaled according to normal direction v2f_outline o; v.vertex.xyz += v.normal*0.01; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); return o; } half4 frag_outline( v2f_outline i) :COLOR { return half4(0, 1, 0, 1); } #pragma vertex vert_outline #pragma fragment frag_outline #pragma fragmentoption ARB_precision_hint_fastest ENDCG } Pass { CGPROGRAM v2f_full vert (appdata_full v) { v2f_full o; o.pos = mul (UNITY_MATRIX_MVP, v.vertex); o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex); return o; } fixed4 frag (v2f_full i) : COLOR0 { fixed4 tex = tex2D (_MainTex, i.uv.xy); return tex; } #pragma vertex vert #pragma fragment frag #pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON #pragma fragmentoption ARB_precision_hint_fastest ENDCG } } 其中需要注意的是勾邊pass的ZWrite設為關閉,保證接下來的正常繪製不會因為深度檢測被剔除。 改進 1.如何保證勾邊在不同的攝像機距離下大小一致? 之前實現的放大繪製是在模型空間下做的,這使得勾邊跟模型一樣存在近大遠小的問題,遠處模型的勾邊會比近處模型的勾邊細,所以我們需要對勾邊根據離攝像機的遠近進行一個縮放。 v2f_outline vert_outline(appdata_full v) { // vertex data scaled according to normal direction v2f_outline o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); float3 norm = mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal); float3 offset = TransformViewToProjection(normalize(norm.xyz)); o.pos.xy += normalize(offset) * 0.01 * o.pos.w; return o; } 模型放大放到裁剪空間去做,o.pos存錯的是裁剪空間的頂點資訊,其中o.pos.w儲存了頂點與攝像機的距離,可做為一個模型放大係數。 2.如何解決在與其他模型重疊的情況下出現不合理的勾邊? 某個攝像機距離下重疊的邊界出現勾邊,移動攝像機勾邊交替閃爍,時有時無,這是z-fighting現象,解決的辦法是設定勾邊pass的Offset語句。 pass { Offset 3, 0 ZWrite Off ... 3.如何在被遮擋的情況下顯示勾邊? 物體被遮擋,仍然需要顯示,需要設定勾邊pass的語句ZTest為Always,深度檢測一直通過,這樣勾邊pass的片段不會被深度剔除。 pass { Offset 3, 0 ZWrite Off ZTest Always ... 在這個基礎上如果只需顯示被遮擋的勾邊,就需要利用Stencil Buffer。繪製順序需要反過來:先執行正常繪製,寫入stencil值,然後執行勾邊pass,對stencil的值做比較,如果相等,則片段被stencil剔除,這樣,除了放大的部分外,其餘部分都被剔除了。 SubShader { Tags { "RenderType"="Opaque"} Pass { Stencil { Ref 2 Comp always Pass replace ZFail replace } CGPROGRAM v2f_full vert (appdata_full v) { v2f_full o; o.pos = mul (UNITY_MATRIX_MVP, v.vertex); o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex); return o; } fixed4 frag (v2f_full i) : COLOR0 { fixed4 tex = tex2D (_MainTex, i.uv.xy); return tex; } #pragma vertex vert #pragma fragment frag #pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON #pragma fragmentoption ARB_precision_hint_fastest ENDCG } pass { Stencil { Ref 2 Comp NotEqual } Offset 3, 0 ZWrite Off ZTest Always CGPROGRAM #include "UnityCG.cginc" struct v2f_outline { float4 pos : SV_POSITION; }; v2f_outline vert_outline(appdata_full v) { // vertex data scaled according to normal direction v2f_outline o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); float3 norm = mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal); float3 offset = TransformViewToProjection(normalize(norm.xyz)); o.pos.xy += normalize(offset) * 0.01 * o.pos.w; return o; } half4 frag_outline( v2f_outline i) :COLOR { return half4(0, 1, 0, 1); } #pragma vertex vert_outline #pragma fragment frag_outline #pragma fragmentoption ARB_precision_hint_fastest ENDCG } } --------------------- 作者:bill2ccssddnn 來源:CSDN 原文:https://blog.csdn.net/bill2ccssddnn/article/details/52148647 版權宣告:本文為博主原創文章,轉載請附上博文連結!