欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-03-18 17:03:16
...

项目之前想要做一个假阴影,一般有几种做法:

第一种是直接把角色的顶点的世界坐标的y坐标映射到地面高度的位置,用另一个pass渲染他,让他的颜色可以控制就好。这样能产生一个顶部灯光的效果,但是角色有多少部件就会有多多少个drawcall。二点顶点数也是翻倍的。

几何着色器在不增加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这个部分。而且要注意顶点到几何,几何到片源的结构传递。

随便摆一个位置来看效果

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

可以看到dc没有因为阴影而增加。

但是也因为他支持的opengl比较高。所以要谨慎使用。

 

 

相关标签: 渲染