幾何着色器在不增加drawcall的前提下完成假陰影

項目之前想要做一個假陰影,一般有幾種做法:

第一種是直接把角色的頂點的世界座標的y座標映射到地面高度的位置,用另一個pass渲染他,讓他的顏色可以控制就好。這樣能產生一個頂部燈光的效果,但是角色有多少部件就會有多多少個drawcall。二點頂點數也是翻倍的。

第二種是直接用shadowmap,其實本質上也會多一個pass去渲染他的投射陰影,所以dc沒減少。

第三種是後處理,得到屏幕貼圖後疊加在角色底部。這樣其實效率也不高。dc其實也一點沒減少。

第四種就是我們在考慮opengl3.2以上的機型的情況下可以考慮用幾何着色器來做。

幾何着色器可以爲輸入的圖元附加其它信息,同時還可以訪問圖元的鄰接頂點信息。

具體就是在頂點後做一次幾何着色器讓他的三角形多一倍,放到腳下就好了。


Shader "xxxxx/xxxxx" {
    Properties{
        _MainTex("Albedo Tex", 2D) = "white" {}
        _TintColor("TintColor",color) = (1,1,1,1)

        [HDR]_Emission("EmissionColor(MainTex.a)",Color)=(0,0,0,0)

        _HighlightPow("Highlight Power", Range(0,4)) = 2
        _HighlightAmt("Highlight Strength", Range(0,4)) = 2

        [HDR]_RimColor("RimColor", Color) = (1,0,0,1)
        _RimPower("RimPower", Range(0, 8)) = 4.5

        _EdgeWidth("EdgeWidth",Range(0,0.5)) = 0.1
        _EdgeColor_1("EdgeColor_1",Color) = (1,0,0,1)
        _EdgeColor_2("EdgeColor_2",Color) = (1,1,0,1)
        _DissolveTex("DissolveTex",2D) = "white" {}
        _Dissolve("Dissolve",Range(0,1)) = 0

        _ShadowColor("ShadowColor", color) = (0,0,0,1)
        _ShadowXOffset("ShadowXOffset", Range(0,5)) = 0.6
        _ShadowYOffset("ShadowYOffset", Range(0,5)) = 2.0
        _ShadowZOffset("ShadowZOffset", Range(0,5)) = 0.0
        _ShadowWidth("ShadowWidth", Range(0,5)) = 1.0
    }
    

    Subshader{
    
        Fog{ Mode Off }
        Lighting Off
        Cull Back
        
        
        Tags{ "RenderType" = "Opaque" "Queue" = "Geometry" "IgnoreProjector" = "true" }
        
        Pass
        {
            CGPROGRAM
            #pragma target 3.5

            #include "UnityCG.cginc"
            #pragma vertex vert
            #pragma geometry geom
            #pragma fragment frag
            #pragma fragmentoption ARB_precision_hint_fastest
        
            // =========================================================
        
            uniform sampler2D _MainTex;
            uniform float4 _MainTex_ST;

            uniform fixed4 _Emission;
        
            uniform half _HighlightAmt;
            uniform half _HighlightPow;
        
            uniform float3 _LightPos;
            uniform float4 _LightColor;
            uniform float _LightRangeMin;
            uniform float _LightRangeMax;
            uniform half _lightBrightness;
            uniform float4 _ShadeColor;
            uniform float _shadeBrightness;
            
            uniform fixed4 _TintColor;
        
            uniform float _RimPower;
            uniform fixed4 _RimColor;

            uniform fixed4 _ShadowColor;
            uniform fixed _ShadowXOffset;
            uniform fixed _ShadowYOffset;
            uniform fixed _ShadowZOffset;
            uniform fixed _ShadowWidth;
        
            fixed _EdgeWidth;
            fixed4 _EdgeColor_1;
            fixed4 _EdgeColor_2;
            sampler2D _DissolveTex;
            float4 _DissolveTex_ST;
            fixed _Dissolve;
           
            //-------頂點向幾何階段傳遞數據 
            struct v2g {
                float4 pos:SV_POSITION;
                float4 texture_uv:TEXCOORD0;
                float3 world_nrm : TEXCOORD1;
                float3 light_dir : TEXCOORD2;
                float worldPos : TEXCOORD3;
                fixed RimFresnel : TEXCOORD4;
            };

            //-------幾何階段向片元階段傳遞數據 
            struct g2f
            {
                float4 vertex : SV_POSITION;
                float4 texture_uv : TEXCOORD0;
                float3 world_nrm : TEXCOORD1;
                float3 light_dir : TEXCOORD2;
                float worldPos : TEXCOORD3;
                fixed RimFresnel : TEXCOORD4;
                float isShadow : TEXCOORD5;
            };
            // =========================================================
        
            v2g vert(appdata_tan v)
            {
                v2g o;
                o.pos = v.vertex;
                
                //o.texture_uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                // todo: 原本中的區別
                o.texture_uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.texture_uv.zw = TRANSFORM_TEX(v.texcoord,_DissolveTex);
                
                o.world_nrm = UnityObjectToWorldNormal(v.normal);
        
                float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                o.light_dir = normalize(_LightPos - worldPos);
                //o.light_dst = smoothstep(_LightRangeMax, _LightRangeMin, distance(worldPos, _LightPos));
                o.worldPos = worldPos;
        
                float3 worldViewDir = normalize(WorldSpaceViewDir(v.vertex));
                o.RimFresnel =  pow( 1.0 - dot(o.world_nrm, worldViewDir ), _RimPower );
                return o;
            }
        
            g2f AddVertex(v2g input)
            {
                g2f o = (g2f)0;
                o.vertex = UnityObjectToClipPos(input.pos);
                o.texture_uv = input.texture_uv;
                o.world_nrm = input.world_nrm;
                o.light_dir = input.light_dir;
                //o.light_dst = input.light_dst;
                o.RimFresnel = input.RimFresnel;
                o.isShadow = 0;
                return o;
            }
            g2f AddShadowVertex(v2g input)
            {
                g2f o = (g2f)0;
                o.vertex = UnityObjectToClipPos(float4(-(input.pos.x * _ShadowWidth + _ShadowXOffset), (input.pos.y - _ShadowYOffset), input.pos.z + _ShadowZOffset, input.pos.w));
                o.texture_uv = input.texture_uv;
                o.world_nrm = input.world_nrm;
                o.light_dir = input.light_dir;
                //o.light_dst = input.light_dst;
                o.RimFresnel = input.RimFresnel;
                o.isShadow = 1;
                return o;
            }
            //-------靜態制定單個調用的最大頂點個數 
            [maxvertexcount(6)]
            void geom(triangle v2g input[3], inout TriangleStream<g2f> outStream) {
                //for (int i = 0; i < 3; i++) {
                g2f o1 = AddVertex(input[0]);
                g2f o2 = AddVertex(input[1]);
                g2f o3 = AddVertex(input[2]);

                //-----將一個頂點添加到輸出流列表 
                outStream.Append(o1);
                outStream.Append(o2);
                outStream.Append(o3);
                //}
                //-------restart strip可以模擬一個primitives list 
                outStream.RestartStrip();


                g2f on1 = AddShadowVertex(input[0]);
                g2f on2 = AddShadowVertex(input[1]);
                g2f on3 = AddShadowVertex(input[2]);
                outStream.Append(on1);
                outStream.Append(on2);
                outStream.Append(on3);
                outStream.RestartStrip();
                
            }
        
            fixed4 frag(g2f i) : COLOR
            {
                fixed4 colortex = tex2D(_MainTex, i.texture_uv);
                fixed4 outcolor = colortex * _TintColor;
                fixed3 nrm = i.world_nrm;
        
                fixed dotProd = dot(nrm, i.light_dir);
                fixed dotLit = 1 + pow(max(0, dotProd), _HighlightPow) * _HighlightAmt;
                fixed dotDark = 0.5 * max(0, -dotProd);
        	    outcolor *= dotLit - dotDark;

                half light_dst = smoothstep(_LightRangeMax, _LightRangeMin, distance(i.worldPos, _LightPos));
        	    outcolor *= lerp(_ShadeColor, _LightColor, light_dst);
        	    
        	    // RimColor部分
                fixed3 EmissionCol =  colortex.a * _Emission.rgb * _Emission.a;
                outcolor.rgb += _RimColor.rgb*i.RimFresnel*_RimColor.a + EmissionCol;
                
                fixed dissolve = tex2D(_DissolveTex,i.texture_uv.zw).r - _Dissolve;
                fixed t =(1 - smoothstep(0,_EdgeWidth,dissolve))* step(0.0001,_Dissolve);
                fixed3 EdgeColor = pow(lerp(_EdgeColor_1,_EdgeColor_2,t),5);
                outcolor.rgb=lerp(outcolor.rgb,EdgeColor,t);	 
                    
                clip(dissolve);
                
                outcolor.rgb = i.isShadow == 0 ? outcolor.rgb : _ShadowColor.rgb;
                return outcolor;
            }
            ENDCG
        }
        
       
    }
}

主要可以看geom這個部分。而且要注意頂點到幾何,幾何到片源的結構傳遞。

隨便擺一個位置來看效果

可以看到dc沒有因爲陰影而增加。

但是也因爲他支持的opengl比較高。所以要謹慎使用。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章