Is it possible to use Geometry shader with surface shaders?

Hi,

Playing around with geometry shaders in dx111 and wonder if its possible to incorporate them directly into surface shaders?

My experience so far says no. You can’t add the #pragma geometry and there appears to be no Unity directives to allow a reference to a GS (e.g. like vertex for defining your own vertex shader for surface shaders).

Obviously it should be possible to use debug to extract all the relevant parts of a surface shader and retro-actively add a GS into it, but I was hoping for a easier solution.

Thanks

Ok, this is proving more difficult than I expected.

I went with using a debug version of the standard Unity normal-diffuse shader to extract the Fwd_Base pass code from it, hoping to just integrate the geometry shader into it. However all i get are errors, well warnings, but the upshot is the full shader fails to compile.

Below you’ll find firstly the basic GS shader. Its taken from the old june2010 DirectX SDK, tutorial 13 and simply ‘explodes’ each triangle in a mesh into a pyramid. Apply this shader to a material and place it on a mesh (in dx11 mode) and it works fine.

After that i’ve posted my progress with trying to integrate this GeometryShader into the code Unity creates for a fwd_Base surface shader and the errors it reports.

If anyone has any ideas how to get this to compile correctly i’d be very grateful. Alternatively i’m still looking to see if there is an easier method of integrating a GS into a surface shader or a good way to use Unity to obtain lighting data to shade the GS.

The basic GS shader

// Taken from DirectX SDK (June 2010) - Tutorial 13
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb205122%28v=vs.85%29.aspx

Shader "|Unity 4/Geomtery/GeometryPyramid" 
{
	Properties 
	{
		_MainTex ("Particle Texture", 2D) = "white" {}
		_Explode ("Explode factor", Range(0.0, 4.0)) = 1.0
	}
	
	SubShader 
	{
		Pass
		{			
			CGPROGRAM
				#pragma target 5.0
			
				#pragma vertex vert
				#pragma geometry geom
				#pragma fragment frag
			
				#include "UnityCG.cginc"
					
				sampler2D 	_MainTex;				
				float4 		_MainTex_ST;			
				float 		_Explode;
				float4x4 	_ViewMatrix;
								
				struct VS_INPUT
				{
				    float4 Pos	: POSITION;         
				    float3 Norm	: NORMAL;           
				    float2 Tex	: TEXCOORD0;        
				};

				// GEOMETRY
				struct GSPS_INPUT 
				{							
					float4 Pos  : SV_POSITION;
    				float3 Norm : TEXCOORD0;
    				float2 Tex  : TEXCOORD1;
				};
								
				// Vertex Shader
				GSPS_INPUT vert( VS_INPUT input )
				{
				    GSPS_INPUT output = (GSPS_INPUT)0; 
				    
				    output.Pos  = input.Pos;  // mul( float4(input.Pos,1), _Object2World);  // mul( float4(input.Pos,1), World );
				    output.Norm = input.Norm; // mul(input.Norm, (float3x3)_Object2World ); // mul( input.Norm, (float3x3)World );
				    output.Tex  = TRANSFORM_TEX(input.Tex, _MainTex);  // input.Tex;
				    			    
				    return output;
				}

				// Geometry Shader
				[maxvertexcount(12)]
				void geom( triangle GSPS_INPUT input[3], inout TriangleStream<GSPS_INPUT> outStream )
				{
				    GSPS_INPUT output;

				    // Calculate the face normal
				    float3 faceEdgeA = input[1].Pos - input[0].Pos;
				    float3 faceEdgeB = input[2].Pos - input[0].Pos;
				    float3 faceNormal = normalize( cross(faceEdgeA, faceEdgeB) );
				    float3 ExplodeAmt = faceNormal*_Explode;
				    
				    // Calculate the face center				 
				    float3 centerPos = (input[0].Pos.xyz + input[1].Pos.xyz + input[2].Pos.xyz)/3.0;
				    float2 centerTex = (input[0].Tex + input[1].Tex + input[2].Tex)/3.0;
				    centerPos += faceNormal*_Explode;				    
				
				    // Output the pyramid			
				    for( int i=0; i<3; i++ )
				    {
				        output.Pos = input[i].Pos + float4(ExplodeAmt,0);			        			        		    
				        output.Pos = mul(UNITY_MATRIX_MVP, output.Pos);
	        			output.Norm = input[i].Norm;
				        output.Tex = input[i].Tex;
				        outStream.Append( output );
				        
				        int iNext = (i+1)%3;
				        output.Pos = input[iNext].Pos + float4(ExplodeAmt,0);
				        output.Pos = mul(UNITY_MATRIX_MVP, output.Pos);	
				        output.Norm = input[iNext].Norm;
				        output.Tex = input[iNext].Tex;
				        outStream.Append( output );
				        
				        output.Pos = float4(centerPos,1) + float4(ExplodeAmt,0);
				    	output.Pos = mul(UNITY_MATRIX_MVP, output.Pos);				      
				        output.Norm = faceNormal;
				        output.Tex = centerTex;
				        outStream.Append( output );
				        
				        outStream.RestartStrip();
				    }
				    
				    for( int i=2; i>=0; i-- )
				    {
				        output.Pos = input[i].Pos + float4(ExplodeAmt,0);
				    	output.Pos = mul(UNITY_MATRIX_MVP, output.Pos);
				        output.Norm = -input[i].Norm;
				        output.Tex = input[i].Tex;
				        outStream.Append( output );
				    }
				    outStream.RestartStrip();
				}
				
				
				// FRAG
				fixed4 frag (GSPS_INPUT i ) : COLOR0
				{					
					fixed4 col = tex2D(_MainTex, i.Tex);	
					return col;
				}		
			ENDCG			
		}
	}
	Fallback Off
}

This is the current state of the version that attempts to integrate with Unity lighting via using the compiled/debug code from the normal-diffuse surface shader.

// Taken from DirectX SDK (June 2010) - Tutorial 13
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb205122%28v=vs.85%29.aspx

Shader "|Unity 4/Geomtery/GeometryPyramidLit" 
{
	Properties 
	{
		_Color ("Main Color", Color) = (1,1,1,1)
		_MainTex ("Particle Texture", 2D) = "white" {}
		_Explode ("Explode factor", Range(0.0, 4.0)) = 1.0
	}

	SubShader 
	{
		Tags { "RenderType"="Opaque" }
		LOD 200
			
		Pass 
		{	
			Name "FORWARD"
			Tags { "LightMode" = "ForwardBase" }
	
			CGPROGRAM
			#pragma target 		5.0
			
			#pragma vertex 		vert
			#pragma geometry 	geom
			#pragma fragment 	frag			
			
			#pragma fragmentoption ARB_precision_hint_fastest
			#pragma multi_compile_fwdbase
			
			#include "HLSLSupport.cginc"
			#include "UnityShaderVariables.cginc"
			
			#define UNITY_PASS_FORWARDBASE
			
			#include "UnityCG.cginc"
			#include "Lighting.cginc"
			#include "AutoLight.cginc"
			
			#define INTERNAL_DATA
			#define WorldReflectionVector(data,normal) data.worldRefl
			#define WorldNormalVector(data,normal) normal

			#pragma only_renderers d3d11
			//#pragma surface surf Lambert									
			//#pragma debug
						
			sampler2D 	_MainTex;
			fixed4 		_Color;
			float 		_Explode;
												
			struct Input 
			{
				float2 uv_MainTex;
			};
						
			void surf (Input IN, inout SurfaceOutput o) 
			{
				fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
				o.Albedo = c.rgb;
				o.Alpha  = c.a;
			}		
			
			struct v2f_surf 
			{
				float4 pos : SV_POSITION;
				float2 pack0 : TEXCOORD0;
				fixed3 normal : TEXCOORD1;
				fixed3 vlight : TEXCOORD2;
				LIGHTING_COORDS(3,4)
			//	float3 _LightCoord : TEXCOORD3;
			//	float3 _ShadowCoord : TEXCOORD4;				
			};
								
			float4 _MainTex_ST;
						
			v2f_surf vert (appdata_full v) 
			{
				v2f_surf o;
					 
				o.pos = v.vertex; // mul (UNITY_MATRIX_MVP, v.vertex);
				o.pack0.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
										
				float3 worldN = mul((float3x3)_Object2World, SCALED_NORMAL);
				
				o.normal = worldN;
			
				float3 shlight = ShadeSH9 (float4(worldN,1.0));
				o.vlight = shlight;
				
				#ifdef VERTEXLIGHT_ON
					float3 worldPos = mul(_Object2World, v.vertex).xyz;
					o.vlight += Shade4PointLights (
					unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
					unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
					unity_4LightAtten0, worldPos, worldN );
				#endif // VERTEXLIGHT_ON
			
				TRANSFER_VERTEX_TO_FRAGMENT(o);
			  
				return o;
			}
			
		
				// Geometry Shader
				[maxvertexcount(12)]
				void geom( triangle v2f_surf input[3], inout TriangleStream<v2f_surf> outStream )
				{				   
					v2f_surf output; 
				
				    // Calculate the face normal
				    float3 faceEdgeA = input[1].pos - input[0].pos;
				    float3 faceEdgeB = input[2].pos - input[0].pos;
				    float3 faceNormal = normalize( cross(faceEdgeA, faceEdgeB) );
				    float3 ExplodeAmt = faceNormal*_Explode;
				    
				    // Calculate the face center				 
				    float3 centerPos = (input[0].pos.xyz + input[1].pos.xyz + input[2].pos.xyz)/3.0;
				    float2 centerTex = (input[0].pack0 + input[1].pack0 + input[2].pack0)/3.0;
				    centerPos += faceNormal*_Explode;				    
				
				    // Output the pyramid			
				    for( int looper=0; looper<3; looper++ )
				    {
				        output.pos = input[looper].pos + float4(ExplodeAmt,0);	        			        		    
				        output.pos = mul(UNITY_MATRIX_MVP, output.pos);			        
				        output.normal = input[looper].normal;
				        output.pack0 = input[looper].pack0;
				        output.vlight = input[looper].vlight; 
				   //     output._ShadowCoord = input[looper]._ShadowCoord; 
				   //     output._LightCoord  = input[looper]._LightCoord; 
				        outStream.Append( output );
				        				        
				        int iNext = (looper+1)%3;
				        output.pos = input[iNext].pos + float4(ExplodeAmt,0);
				        output.pos = mul(UNITY_MATRIX_MVP, output.pos);	
				        output.normal = input[iNext].normal;
				        output.pack0 = input[iNext].pack0;
				        output.vlight = input[iNext].vlight;
				  //      output._ShadowCoord = input[iNext]._ShadowCoord; 
				  //      output._LightCoord  = input[iNext]._LightCoord;  
				        outStream.Append( output );
				        				        
				        output.pos = float4(centerPos,1) + float4(ExplodeAmt,0);
				    	output.pos = mul(UNITY_MATRIX_MVP, output.pos);				      
				        output.normal = faceNormal;
				        output.pack0 = centerTex;
				        output.vlight = input[looper].vlight; 
				   //     output._ShadowCoord = input[looper]._ShadowCoord; 
				   //     output._LightCoord  = input[looper]._LightCoord; 
				        outStream.Append( output );
				        
				        outStream.RestartStrip();
				    }
				    
				    for( int looper=2; looper>=0; looper-- )
				    {
				        output.pos = input[looper].pos + float4(ExplodeAmt,0);
				    	output.pos = mul(UNITY_MATRIX_MVP, output.pos);
				        output.normal = -input[looper].normal;
				        output.pack0 = input[looper].pack0;
				        output.vlight = input[looper].vlight; 
				   //     output._ShadowCoord = input[looper]._ShadowCoord; 
				   //     output._LightCoord  = input[looper]._LightCoord; 
				        outStream.Append( output );
				    }
				    outStream.RestartStrip();
				}					
			
			fixed4 frag (v2f_surf IN) : COLOR 
			{
				Input surfIN;
				surfIN.uv_MainTex = IN.pack0.xy;
				
				#ifdef UNITY_COMPILER_HLSL
				  	SurfaceOutput o = (SurfaceOutput)0;
				#else
					SurfaceOutput o;
				#endif
				
				o.Albedo 	= 0.0;
				o.Emission 	= 0.0;
				o.Specular 	= 0.0;
				o.Alpha 	= 0.0;
				o.Gloss 	= 0.0;	
				o.Normal = IN.normal;
								
				surf (surfIN, o);
				fixed atten = LIGHT_ATTENUATION(IN);
				fixed4 c = 0;				  
				
				c = LightingLambert (o, _WorldSpaceLightPos0.xyz, atten);			
				c.rgb += o.Albedo * IN.vlight;	
				
				return c;
			}
			
			ENDCG			
		} // Pass ForwardBase		
		
	} // subshader

	Fallback Off
}

This shader gives the following warnings and fails to compile

None of which occur in the initial GS shader example above, and some of which are rather weird, e.g. looper redefinition?

I tried adding _LightCoord _ShadowCoord to the GS (see commented out lines) as I suspect they will be needed, but bizarrely that throws up different warnings in the vertex shader? [quote]
Shader warning in ‘|Unity 4/Geomtery/GeometryPyramidLit’: Program ‘vert’, invalid subscript ‘_LightCoord’ (compiling for d3d11) at line 29
[/quote]

I also tried defining _LightCoord _ShadowCoord directly in the struct, but as the types change depending on the incoming light type that didn’t work either.

So any ideas?

you can not use anything but VS and PS in surface shaders.
Surface shaders are meant to work on all unity platforms, not only Windows 7+ with DX11+ gpu which is the only place where DX11 will work.

Technically its also related to the fac that Unity rewrites parts of it and expands it.

But you could add the debug pragma to see the output and then expand that one for example, that gives you the surface shader aspect while you can freely modify it for your DX11 only version while you take the original code as another fallback shader in case DX11 is not supported for example

1 Like

No. There’s also some support for tessellation (hull domain shaders).

No. “Work on all platforms” was never the goal of surface shaders, and will never be (though of course it’s desirable). The goal of surface shaders is “make working with lighting simpler”, that’s the only problem they try to solve.

Answer to noisecrime: no, it’s not possible to use geometry shaders with surface shaders right now, so you’ll have to write the shader “by hand”. I haven’t looked at the errors you’re getting with your approach (not on a Windows box right now either). But DX11 shader compiler (HLSL) is much more strict than Cg, e.g. it doesn’t like output parameters that aren’t initialized, and so on.

Thanks Aras, though I did ensure that all output parameters are initialized, though doing so for the GS caused additional errors.
I’d be very grateful if you could find some time to check out the shader errors at some point, or alternatively if you could maybe write out how you’d write a shader that can utilise Unity lighting and shadows with a GS.

Do you have any plans to incorporate support for GS in surface shaders in the future like how you were able to with tessellation shaders?

Hello Noisecrime,
I took a look at your shader and fix the issues you mentioned above:

Shader "Geom/GeometryPyramidLit" 

{

    Properties 

    {

        _Color ("Main Color", Color) = (1,1,1,1)

        _MainTex ("Particle Texture", 2D) = "white" {}

        _Explode ("Explode factor", Range(0.0, 4.0)) = 1.0

    }

 

    SubShader 

    {

        Tags { "RenderType"="Opaque" }

        LOD 200

            

        Pass 

        {   

            Name "FORWARD"

            Tags { "LightMode" = "ForwardBase" }

    

            CGPROGRAM

            #pragma target      5.0

            

            #pragma vertex      vert

            #pragma geometry    geom

            #pragma fragment    frag            

            

            #pragma fragmentoption ARB_precision_hint_fastest

            #pragma multi_compile_fwdbase

            

            #include "HLSLSupport.cginc"

            #include "UnityShaderVariables.cginc"

            

            #define UNITY_PASS_FORWARDBASE

            

            #include "UnityCG.cginc"

            #include "Lighting.cginc"

            #include "AutoLight.cginc"

            

            #define INTERNAL_DATA

            #define WorldReflectionVector(data,normal) data.worldRefl

            #define WorldNormalVector(data,normal) normal

 

            #pragma only_renderers d3d11

            //#pragma surface surf Lambert                                  

            //#pragma debug

                        

            sampler2D   _MainTex;

            fixed4      _Color;

            float       _Explode;

                                                

            struct Input 

            {

                float2 uv_MainTex;

            };

                        

            void surf (Input IN, inout SurfaceOutput o) 

            {

                fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;

                o.Albedo = c.rgb;

                o.Alpha  = c.a;

            }       

            

            struct v2f_surf 

            {

                float4 pos : SV_POSITION;

                float2 pack0 : TEXCOORD0;

                fixed3 normal : TEXCOORD1;

                fixed3 vlight : TEXCOORD2;

                LIGHTING_COORDS(3,4)

            //  float3 _LightCoord : TEXCOORD3;

            //  float3 _ShadowCoord : TEXCOORD4;                

            };

                                

            float4 _MainTex_ST;

                        

            v2f_surf vert (appdata_full v) 

            {

                v2f_surf o;

                     

                o.pos = v.vertex; // mul (UNITY_MATRIX_MVP, v.vertex);

                o.pack0.xy = TRANSFORM_TEX(v.texcoord, _MainTex);

                                        

                float3 worldN = mul((float3x3)_Object2World, SCALED_NORMAL);

                

                o.normal = worldN;

            

                float3 shlight = ShadeSH9 (float4(worldN,1.0));

                o.vlight = shlight;

                

                #ifdef VERTEXLIGHT_ON

                    float3 worldPos = mul(_Object2World, v.vertex).xyz;

                    o.vlight += Shade4PointLights (

                    unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,

                    unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,

                    unity_4LightAtten0, worldPos, worldN );

                #endif // VERTEXLIGHT_ON

            

                TRANSFER_VERTEX_TO_FRAGMENT(o);

              

                return o;

            }

            

        

                // Geometry Shader

                [maxvertexcount(12)]

                void geom( triangle v2f_surf input[3], inout TriangleStream<v2f_surf> outStream )

                {                  

                    v2f_surf output;
					               

                    // Calculate the face normal

                    float3 faceEdgeA = input[1].pos - input[0].pos;

                    float3 faceEdgeB = input[2].pos - input[0].pos;

                    float3 faceNormal = normalize( cross(faceEdgeA, faceEdgeB) );

                    float3 ExplodeAmt = faceNormal*_Explode;

                    

                    // Calculate the face center                 

                    float3 centerPos = (input[0].pos.xyz + input[1].pos.xyz + input[2].pos.xyz)/3.0;

                    float2 centerTex = (input[0].pack0 + input[1].pack0 + input[2].pack0)/3.0;

                    centerPos += faceNormal*_Explode;                   
					                
                    // Output the pyramid           

                    for( int looper=0; looper<3; looper++ )

                    {

                        output.pos = input[looper].pos + float4(ExplodeAmt,0);                                          

                        output.pos = mul(UNITY_MATRIX_MVP, output.pos);                 

                        output.normal = input[looper].normal;

                        output.pack0 = input[looper].pack0;

                        output.vlight = input[looper].vlight; 
						 TRANSFER_VERTEX_TO_FRAGMENT(output);
                       // output._ShadowCoord = input[looper]._ShadowCoord; 

                   //     output._LightCoord  = input[looper]._LightCoord; 

                        outStream.Append( output );
        
                        uint iNext = looper+1;

						if(iNext>2)
						{
							iNext=0;
						}
                        output.pos = input[iNext].pos + float4(ExplodeAmt,0);

                        output.pos = mul(UNITY_MATRIX_MVP, output.pos); 

                        output.normal = input[iNext].normal;

                        output.pack0 = input[iNext].pack0;

                        output.vlight = input[iNext].vlight;

                  //      output._ShadowCoord = input[iNext]._ShadowCoord; 

                  //      output._LightCoord  = input[iNext]._LightCoord;  

                        outStream.Append( output );

                                                

                        output.pos = float4(centerPos,1) + float4(ExplodeAmt,0);

                        output.pos = mul(UNITY_MATRIX_MVP, output.pos);                   

                        output.normal = faceNormal;

                        output.pack0 = centerTex;

                        output.vlight = input[looper].vlight; 

                   //     output._ShadowCoord = input[looper]._ShadowCoord; 

                   //     output._LightCoord  = input[looper]._LightCoord; 

                        outStream.Append( output );

                        

                        outStream.RestartStrip();

                    }

                    

                    for(int cpt=2; cpt>=0; cpt-- )

                    {

                        output.pos = input[cpt].pos + float4(ExplodeAmt,0);

                        output.pos = mul(UNITY_MATRIX_MVP, output.pos);

                        output.normal = -input[cpt].normal;

                        output.pack0 = input[cpt].pack0;

                        output.vlight = input[cpt].vlight; 

                   //     output._ShadowCoord = input[looper]._ShadowCoord; 

                   //     output._LightCoord  = input[looper]._LightCoord; 

                        outStream.Append( output );

                    }

                    outStream.RestartStrip();

                }                   

            

            fixed4 frag (v2f_surf IN) : COLOR 

            {

                Input surfIN;

                surfIN.uv_MainTex = IN.pack0.xy;

                

                #ifdef UNITY_COMPILER_HLSL

                    SurfaceOutput o = (SurfaceOutput)0;

                #else

                    SurfaceOutput o;

                #endif

                

                o.Albedo    = 0.0;

                o.Emission  = 0.0;

                o.Specular  = 0.0;

                o.Alpha     = 0.0;

                o.Gloss     = 0.0;  

                o.Normal = IN.normal;

                                

                surf (surfIN, o);

                fixed atten = LIGHT_ATTENUATION(IN);

                fixed4 c = 0;                 

                

                c = LightingLambert (o, _WorldSpaceLightPos0.xyz, atten);           

                c.rgb += o.Albedo * IN.vlight;  

                

                return c;

            }

            

            ENDCG           

        } // Pass ForwardBase       

        

    } // subshader

 

    Fallback Off

}

I am actually making a shader using approximatively the same idea. I need to collect and cast shadows too, take me aware if it helped and if you improved your shader.

Hey, well that’s a coincidence, I was just looking at this old shader the other day whilst doing some related tessellation work. I’d not checked in on this thread for a long time, but after fixing a number of issues with the original shader, I thought i’d come and check out my original post.

I’d fixed most of the errors I mentioned at the time, not sure why I couldn’t back then, maybe i’d just been working on it for too long and got fed up with it. However your code addressed the last two issues i’d ran into, the first being the modulus function that you replaced ( plus unit to int), the second being the addition of TRANSFER_VERTEX_TO_FRAGMENT(); though that’s partly just to fix the output not being fully initialized error.

Overall the shader does provide some correct lighting now, but its not perfect (its predetermined by the original faces) and doesn’t seem to support shadows properly yet either. I susepct both of these issues are due to using the TRANSFER_VERTEX_TO_FRAGMENT function as it relies on using v.vertex and not the newly generated vertices from the geom shader. I’m hopeful that this can be fixed relatively easily with a custom function. I’ll post any further fixes here when/if I get time to look into it.

Think I’ve finally nailed it and so will post the source here for others in case its of any use. There are still issues with getting the correct lighting/shading on the pyramids, but that is an issue with the original code (see msdn link) not the shader code.

This is basically a proof-of-concept, without being able to use surface shader code directly the code has ended up being very long, a bit messy, non-optimal and only supports forward base, so no forward-add, no deferred and is Dx11 only. It should also be noted that this is just a replacement for the standard diffuse shader with a geometry shader designed to turn triangles into pyramids, so specifically it has limited uses.

I have no idea if recent Unity releases (e.g. 4.5) have addressed the overall issue of supporting Geometry shaders directly within surface shaders, I intend to check it out, but continued with this as a learning process. However as I said above the end result is very long and messy, so assuming Surface shaders do not support geometry shaders there will be considerable amount of effort to go through and clean up the code, more so to get something that is re-usable for different shader types.

In order to get it working I had to append code to both AutoLight.cginc and UnityCG.cginc, so these need to be place at the same directory level as the shaders that use them in your project in order for them to override the Unity defaults.

The main stumbling block in getting this working was replacing the various function defines that Unity/surface shaders use such as TRANSFER_VERTEX_TO_FRAGMENT, TRANSFER_SHADOW,TRANSFER_SHADOW_COLLECTOR and TRANSFER_SHADOW_CASTER.

The Triangle To Pyramid Geometry Shader

// Taken from DirectX SDK (June 2010) - Tutorial 13
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb205122%28v=vs.85%29.aspx
Shader "|Unity 4/Geometery/GeometryPyramidDiffuse"
{
   Properties
   {
     _Color ("Main Color", Color) = (1,1,1,1)
     _MainTex ("Particle Texture", 2D) = "white" {}
     _Explode ("Explode factor", Range(0.0, 4.0)) = 1.0
   }
 
   SubShader
   {
     Tags { "RenderType"="Opaque" }
     LOD 200
   
     Pass
     {
       Name "FORWARD"
       Tags { "LightMode" = "ForwardBase" }
     
       CGPROGRAM
       #pragma target  5.0
     
       #pragma vertex  vert
       #pragma geometry  geom
       #pragma fragment  frag
     
       #pragma fragmentoption ARB_precision_hint_fastest
       #pragma multi_compile_fwdbase
     
       #include "HLSLSupport.cginc"
       #include "UnityShaderVariables.cginc"
     
       #define UNITY_PASS_FORWARDBASE
     
       #include "UnityCG.cginc"
       #include "Lighting.cginc"
       #include "AutoLight.cginc"
     
       #define INTERNAL_DATA
       #define WorldReflectionVector(data,normal) data.worldRefl
       #define WorldNormalVector(data,normal) normal
     
       #pragma only_renderers d3d11
       // #pragma surface surf Lambert
       // #pragma debug
     
       sampler2D  _MainTex;
       fixed4  _Color;
       float  _Explode;
     
       struct Input
       {
         float2 uv_MainTex;
       };
     
       void surf (Input IN, inout SurfaceOutput o)
       {
         fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
         o.Albedo = c.rgb;
         o.Alpha  = c.a;
       }
     
       struct v2f_surf
       {
         float4 pos : SV_POSITION;
         float2 pack0 : TEXCOORD0;
         fixed3 normal : TEXCOORD1;
         fixed3 vlight : TEXCOORD2;
         LIGHTING_COORDS(3,4)
       };
     
       float4 _MainTex_ST;
     
       v2f_surf vert (appdata_full v)
       {
         v2f_surf o;
         o.pos = v.vertex; // mul (UNITY_MATRIX_MVP, v.vertex);
         o.pack0.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
       
         float3 worldN = mul((float3x3)_Object2World, SCALED_NORMAL);
         o.normal = worldN;
         float3 shlight = ShadeSH9 (float4(worldN,1.0));
         o.vlight = shlight;
       
         #ifdef VERTEXLIGHT_ON
           float3 worldPos = mul(_Object2World, v.vertex).xyz;
           o.vlight += Shade4PointLights (
           unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
           unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
           unity_4LightAtten0, worldPos, worldN );
         #endif // VERTEXLIGHT_ON
       
         // TRANSFER_VERTEX_TO_FRAGMENT(o);
         return o;
       }
     
       // Geometry Shader
       [maxvertexcount(12)]
       void geom( triangle v2f_surf input[3], inout TriangleStream<v2f_surf> outStream )
       {
         v2f_surf output;
       
         // Calculate the face normal
         float3 faceEdgeA = input[1].pos - input[0].pos;
         float3 faceEdgeB = input[2].pos - input[0].pos;
         float3 faceNormal = normalize( cross(faceEdgeA, faceEdgeB) );
         float3 ExplodeAmt = faceNormal*_Explode;
       
         // Calculate the face center
         float3 centerPos = (input[0].pos.xyz + input[1].pos.xyz + input[2].pos.xyz)/3.0;
         float2 centerTex = (input[0].pack0 + input[1].pack0 + input[2].pack0)/3.0;
         centerPos += faceNormal*_Explode;
       
         // Output the pyramid
         for( int looper=0; looper<3; looper++ )
         {
           output.pos = input[looper].pos + float4(ExplodeAmt,0);
           output.pos = mul(UNITY_MATRIX_MVP, output.pos);
           output.normal = input[looper].normal;
           output.pack0 = input[looper].pack0;
           output.vlight = input[looper].vlight;
           TRANSFER_GEOM_TO_FRAGMENT(output); // Probably need to do this by hand since it uses v.vertex!!     
           outStream.Append( output );
         
           // uint iNext = (looper+1)%3; // Note: this causes errors in compilation
           uint iNext = looper+1;
           if (iNext>2) iNext = 0;
         
           output.pos = input[iNext].pos + float4(ExplodeAmt,0);
           output.pos = mul(UNITY_MATRIX_MVP, output.pos);
           output.normal = input[iNext].normal;
           output.pack0 = input[iNext].pack0;
           output.vlight = input[iNext].vlight;
           TRANSFER_GEOM_TO_FRAGMENT(output);     // TRANSFER_VERTEX_TO_FRAGMENT 
           outStream.Append( output );
         
           output.pos = float4(centerPos,1) + float4(ExplodeAmt,0);
           output.pos = mul(UNITY_MATRIX_MVP, output.pos);
           output.normal = faceNormal;
           output.pack0 = centerTex;
           output.vlight = input[looper].vlight;
           TRANSFER_GEOM_TO_FRAGMENT(output);
           outStream.Append( output );
         
           outStream.RestartStrip();
         }
       
         for(int cpt=2; cpt>=0; cpt-- )
         {
           output.pos = input[cpt].pos + float4(ExplodeAmt,0);
           output.pos = mul(UNITY_MATRIX_MVP, output.pos);
           output.normal = -input[cpt].normal;
           output.pack0 = input[cpt].pack0;
           output.vlight = input[cpt].vlight;
           TRANSFER_GEOM_TO_FRAGMENT(output);
           outStream.Append( output );
         }
       
         outStream.RestartStrip();
       }
     
     
       fixed4 frag (v2f_surf IN) : COLOR
       {
         Input surfIN;
         surfIN.uv_MainTex = IN.pack0.xy;
       
         #ifdef UNITY_COMPILER_HLSL
           SurfaceOutput o = (SurfaceOutput)0;
         #else
           SurfaceOutput o;
         #endif
       
         o.Albedo  = 0.0;
         o.Emission  = 0.0;
         o.Specular  = 0.0;
         o.Alpha  = 0.0;
         o.Gloss  = 0.0;
         o.Normal = IN.normal;
       
         surf (surfIN, o);
         fixed atten = LIGHT_ATTENUATION(IN);
         fixed4 c = 0;
       
         c = LightingLambert (o, _WorldSpaceLightPos0.xyz, atten);
         c.rgb += o.Albedo * IN.vlight;
         return c;
       }
     
       ENDCG     
     } // Pass ForwardBase
   
       
     // Pass to render object as a shadow caster
     Pass
     {
       Name "ShadowCaster"
       Tags { "LightMode" = "ShadowCaster" }
     
       Fog {Mode Off}
       ZWrite On ZTest LEqual Cull Off
       Offset 1, 1
       CGPROGRAM
       #pragma target  5.0
     
       #pragma vertex      vert
       #pragma geometry  geom
       #pragma fragment    frag
     
       #pragma multi_compile_shadowcaster
       #pragma only_renderers d3d11
     
       #include "UnityCG.cginc"
       #include "HLSLSupport.cginc"
     
       float  _Explode;
       struct v2f
       {
         V2F_SHADOW_CASTER;
       };
       v2f vert( appdata_base v )
       {
         v2f o;
         //o.pos = v.vertex;
         TRANSFER_SHADOW_CASTER(o)
         o.pos = v.vertex;
         return o;
       }
       // Geometry Shader
       [maxvertexcount(12)]
       void geom( triangle v2f input[3], inout TriangleStream<v2f> outStream )
       {
         v2f output;
       
         // Calculate the face normal
         float3 faceEdgeA = input[1].pos - input[0].pos;
         float3 faceEdgeB = input[2].pos - input[0].pos;
         float3 faceNormal = normalize( cross(faceEdgeA, faceEdgeB) );
         float3 ExplodeAmt = faceNormal*_Explode;
       
         // Calculate the face center
         float3 centerPos = (input[0].pos.xyz + input[1].pos.xyz + input[2].pos.xyz)/3.0;
         centerPos += faceNormal*_Explode;
       
         // Define for TRANSFER_GEOM_SHADOW_CASTER
         float clamped;
       
         // Output the pyramid
         for( int looper=0; looper<3; looper++ )
         {
           output.pos = input[looper].pos + float4(ExplodeAmt,0); 
           TRANSFER_GEOM_SHADOW_CASTER(output) 
         
           output.pos = input[looper].pos + float4(ExplodeAmt,0);     
           output.pos = mul(UNITY_MATRIX_MVP, output.pos);           
           outStream.Append( output );
         
           uint iNext = looper+1;
           if (iNext>2) iNext = 0;
         
           output.pos = input[iNext].pos + float4(ExplodeAmt,0); 
           TRANSFER_GEOM_SHADOW_CASTER(output) 
         
           output.pos = input[iNext].pos + float4(ExplodeAmt,0); 
           output.pos = mul(UNITY_MATRIX_MVP, output.pos);
           outStream.Append( output );
         
           output.pos = float4(centerPos,1) + float4(ExplodeAmt,0);
           TRANSFER_GEOM_SHADOW_CASTER(output)
         
           output.pos = float4(centerPos,1) + float4(ExplodeAmt,0);
           output.pos = mul(UNITY_MATRIX_MVP, output.pos);         
           outStream.Append( output );
         
           outStream.RestartStrip();
         }
       
         for(int cpt=2; cpt>=0; cpt-- )
         {
           output.pos = input[cpt].pos + float4(ExplodeAmt,0); 
           TRANSFER_GEOM_SHADOW_CASTER(output) 
         
           output.pos = input[cpt].pos + float4(ExplodeAmt,0); 
           output.pos = mul(UNITY_MATRIX_MVP, output.pos);         
           outStream.Append( output );
         }
       
         outStream.RestartStrip();
       }
     
     
       float4 frag( v2f i ) : COLOR
       {
         SHADOW_CASTER_FRAGMENT(i)
       }
       ENDCG
     }
 
     // Pass to render object as a shadow collector
     Pass
     {
       Name "ShadowCollector"
       Tags { "LightMode" = "ShadowCollector" }
     
       Fog {Mode Off}
       ZWrite On ZTest LEqual
       CGPROGRAM
       #pragma target  5.0
     
       #pragma vertex      vert
       #pragma geometry  geom
       #pragma fragment    frag
     
       #pragma multi_compile_shadowcollector
       #pragma only_renderers d3d11
     
       #define SHADOW_COLLECTOR_PASS
     
       #include "UnityCG.cginc"
       #include "HLSLSupport.cginc"
     
       float  _Explode;
     
       struct appdata
       {
         float4 vertex : POSITION;
       };
       struct v2f
       {
         V2F_SHADOW_COLLECTOR;
       };
   
       v2f vert (appdata v)
       {
         v2f o;
         TRANSFER_SHADOW_COLLECTOR(o)
         o.pos = v.vertex;
         return o;
       }
     
       // Geometry Shader
       [maxvertexcount(12)]
       void geom( triangle v2f input[3], inout TriangleStream<v2f> outStream )
       {
         v2f output;
       
         // Calculate the face normal
         float3 faceEdgeA = input[1].pos - input[0].pos;
         float3 faceEdgeB = input[2].pos - input[0].pos;
         float3 faceNormal = normalize( cross(faceEdgeA, faceEdgeB) );
         float3 ExplodeAmt = faceNormal*_Explode;
       
         // Calculate the face center
         float3 centerPos = (input[0].pos.xyz + input[1].pos.xyz + input[2].pos.xyz)/3.0;
         centerPos += faceNormal*_Explode;
       
         // Define for TRANSFER_GEOM_SHADOW_CASTER
         float    clamped;
         float4    vert;
         float4    wpos;
       
         // Output the pyramid
         for( int looper=0; looper<3; looper++ )
         {
           vert = input[looper].pos + float4(ExplodeAmt,0); 
           TRANSFER_GEOM_SHADOW_COLLECTOR(output, vert)           
           outStream.Append( output );
         
           uint iNext = looper+1;
           if (iNext>2) iNext = 0;
         
           vert = input[iNext].pos + float4(ExplodeAmt,0); 
           TRANSFER_GEOM_SHADOW_COLLECTOR(output, vert) 
           outStream.Append( output );
         
           vert = float4(centerPos,1) + float4(ExplodeAmt,0);
           TRANSFER_GEOM_SHADOW_COLLECTOR(output, vert)       
           outStream.Append( output );
         
           outStream.RestartStrip();
         }
       
         for(int cpt=2; cpt>=0; cpt-- )
         {
           vert = input[cpt].pos + float4(ExplodeAmt,0); 
           TRANSFER_GEOM_SHADOW_COLLECTOR(output, vert)         
           outStream.Append( output );
         }
       
         outStream.RestartStrip();
       }
     
       fixed4 frag (v2f i) : COLOR
       {
         SHADOW_COLLECTOR_FRAGMENT(i)
       }
       ENDCG
     }
   } // subshader
   // FallBack "Diffuse" // Use for testing shadows via Normal-VertexLit.shader fallback.
 
   FallBack Off
}

The AutoLight.cginc replacement

#ifndef AUTOLIGHT_INCLUDED
#define AUTOLIGHT_INCLUDED

#include "HLSLSupport.cginc"

// Unity 4.3.4
// 2014.06.09 - Amended to support Geometry Shader


// ------------ Shadow helpers --------


// ---- Screen space shadows
#if defined (SHADOWS_SCREEN)

uniform float4 _ShadowOffsets[4];

#if defined(SHADOWS_NATIVE)
UNITY_DECLARE_SHADOWMAP(_ShadowMapTexture);
#else
uniform sampler2D _ShadowMapTexture;
#endif

#define SHADOW_COORDS(idx1) float4 _ShadowCoord : TEXCOORD##idx1;

#if (defined(SHADER_API_GLES) || defined(SHADER_API_GLES3)) && defined(SHADER_API_MOBILE)
#define TRANSFER_SHADOW(a)      a._ShadowCoord = mul( unity_World2Shadow[0], mul( _Object2World, v.vertex ) );
// GEOMETRY SHADER
#define TRANSFER_GEOM_SHADOW(a) a._ShadowCoord = mul( unity_World2Shadow[0], mul( _Object2World, a.pos ) );

inline fixed unitySampleShadow (float4 shadowCoord)
{
    #if defined(SHADOWS_NATIVE)

    fixed shadow = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, shadowCoord.xyz);
    shadow = _LightShadowData.r + shadow * (1-_LightShadowData.r);
    return shadow;

    #else

    float dist = tex2Dproj( _ShadowMapTexture, UNITY_PROJ_COORD(shadowCoord) ).x;

    // tegra is confused if we use _LightShadowData.x directly
    // with "ambiguous overloaded function reference max(mediump float, float)"
    half lightShadowDataX = _LightShadowData.x;
    return max(dist > (shadowCoord.z/shadowCoord.w), lightShadowDataX);

    #endif
}

#else // !((defined(SHADER_API_GLES) || defined(SHADER_API_GLES3)) && defined(SHADER_API_MOBILE))

#define TRANSFER_SHADOW(a) a._ShadowCoord = ComputeScreenPos(a.pos);
// GEOMETRY SHADER: WHAT TO DO HERE????
#define TRANSFER_GEOM_SHADOW(a) a._ShadowCoord = ComputeScreenPos(a.pos);

inline fixed unitySampleShadow (float4 shadowCoord)
{
    fixed shadow = tex2Dproj( _ShadowMapTexture, UNITY_PROJ_COORD(shadowCoord) ).r;
    return shadow;
}

#endif

#define SHADOW_ATTENUATION(a) unitySampleShadow(a._ShadowCoord)

#endif


// ---- Depth map shadows

#if defined (SHADOWS_DEPTH) && defined (SPOT)

#if !defined(SHADOWMAPSAMPLER_DEFINED)
UNITY_DECLARE_SHADOWMAP(_ShadowMapTexture);
#endif
#if defined (SHADOWS_SOFT)
uniform float4 _ShadowOffsets[4];
#endif

inline fixed unitySampleShadow (float4 shadowCoord)
{
    #if defined (SHADOWS_SOFT)

    // 4-tap shadows

    float3 coord = shadowCoord.xyz / shadowCoord.w;
    #if defined (SHADOWS_NATIVE)
    half4 shadows;
    shadows.x = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, coord + _ShadowOffsets[0]);
    shadows.y = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, coord + _ShadowOffsets[1]);
    shadows.z = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, coord + _ShadowOffsets[2]);
    shadows.w = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, coord + _ShadowOffsets[3]);
    shadows = _LightShadowData.rrrr + shadows * (1-_LightShadowData.rrrr);
    #else
    float4 shadowVals;
    shadowVals.x = UNITY_SAMPLE_DEPTH (tex2D( _ShadowMapTexture, coord + _ShadowOffsets[0].xy ));
    shadowVals.y = UNITY_SAMPLE_DEPTH (tex2D( _ShadowMapTexture, coord + _ShadowOffsets[1].xy ));
    shadowVals.z = UNITY_SAMPLE_DEPTH (tex2D( _ShadowMapTexture, coord + _ShadowOffsets[2].xy ));
    shadowVals.w = UNITY_SAMPLE_DEPTH (tex2D( _ShadowMapTexture, coord + _ShadowOffsets[3].xy ));
    half4 shadows = (shadowVals < coord.zzzz) ? _LightShadowData.rrrr : 1.0f;
    #endif

    // average-4 PCF
    half shadow = dot (shadows, 0.25f);

    #else

    // 1-tap shadows

    #if defined (SHADOWS_NATIVE)
    half shadow = UNITY_SAMPLE_SHADOW_PROJ(_ShadowMapTexture, shadowCoord);
    shadow = _LightShadowData.r + shadow * (1-_LightShadowData.r);
    #else
    half shadow = UNITY_SAMPLE_DEPTH(tex2Dproj (_ShadowMapTexture, UNITY_PROJ_COORD(shadowCoord))) < (shadowCoord.z / shadowCoord.w) ? _LightShadowData.r : 1.0;
    #endif

    #endif

    return shadow;
}
#define SHADOW_COORDS(idx1) float4 _ShadowCoord : TEXCOORD##idx1;
#define TRANSFER_SHADOW(a) a._ShadowCoord = mul (unity_World2Shadow[0], mul(_Object2World,v.vertex));
#define SHADOW_ATTENUATION(a) unitySampleShadow(a._ShadowCoord)
// GEOMETRY SHADER:
#define TRANSFER_GEOM_SHADOW(a) a._ShadowCoord = mul (unity_World2Shadow[0], mul(_Object2World, a.pos));

#endif


// ---- Point light shadows

#if defined (SHADOWS_CUBE)

uniform samplerCUBE _ShadowMapTexture;
inline float SampleCubeDistance (float3 vec)
{
    float4 packDist = texCUBE (_ShadowMapTexture, vec);
    return DecodeFloatRGBA( packDist );
}
inline float unityCubeShadow (float3 vec)
{
    float mydist = length(vec) * _LightPositionRange.w;
    mydist *= 0.97; // bias

    #if defined (SHADOWS_SOFT)
    float z = 1.0/128.0;
    float4 shadowVals;
    shadowVals.x = SampleCubeDistance (vec+float3( z, z, z));
    shadowVals.y = SampleCubeDistance (vec+float3(-z,-z, z));
    shadowVals.z = SampleCubeDistance (vec+float3(-z, z,-z));
    shadowVals.w = SampleCubeDistance (vec+float3( z,-z,-z));
    half4 shadows = (shadowVals < mydist.xxxx) ? _LightShadowData.rrrr : 1.0f;
    return dot(shadows,0.25);
    #else
    float dist = SampleCubeDistance (vec);
    return dist < mydist ? _LightShadowData.r : 1.0;
    #endif
}
#define SHADOW_COORDS(idx1) float3 _ShadowCoord : TEXCOORD##idx1;
#define TRANSFER_SHADOW(a) a._ShadowCoord = mul(_Object2World, v.vertex).xyz - _LightPositionRange.xyz;
#define SHADOW_ATTENUATION(a) unityCubeShadow(a._ShadowCoord)
// GEOMETRY SHADER:
#define TRANSFER_GEOM_SHADOW(a) a._ShadowCoord = mul(_Object2World, a.pos).xyz - _LightPositionRange.xyz;

#endif



// ---- Shadows off
#if !defined (SHADOWS_SCREEN) && !defined (SHADOWS_DEPTH) && !defined (SHADOWS_CUBE)

#define SHADOW_COORDS(idx1)
#define TRANSFER_SHADOW(a)
#define SHADOW_ATTENUATION(a) 1.0
// GEOMETRY SHADER:
#define TRANSFER_GEOM_SHADOW(a)

#endif



// ------------ Light helpers --------

#ifdef POINT
#define LIGHTING_COORDS(idx1,idx2) float3 _LightCoord : TEXCOORD##idx1; SHADOW_COORDS(idx2)
uniform sampler2D _LightTexture0;
uniform float4x4 _LightMatrix0;
#define TRANSFER_VERTEX_TO_FRAGMENT(a) a._LightCoord = mul(_LightMatrix0, mul(_Object2World, v.vertex)).xyz; TRANSFER_SHADOW(a)
#define LIGHT_ATTENUATION(a)    (tex2D(_LightTexture0, dot(a._LightCoord,a._LightCoord).rr).UNITY_ATTEN_CHANNEL * SHADOW_ATTENUATION(a))
// GEOMETRY SHADER:
#define TRANSFER_GEOM_TO_FRAGMENT(a)   a._LightCoord = mul(_LightMatrix0, mul(_Object2World, a.pos)).xyz; TRANSFER_GEOM_SHADOW(a)
#endif

#ifdef SPOT
#define LIGHTING_COORDS(idx1,idx2) float4 _LightCoord : TEXCOORD##idx1; SHADOW_COORDS(idx2)
uniform sampler2D _LightTexture0;
uniform float4x4 _LightMatrix0;
uniform sampler2D _LightTextureB0;
#define TRANSFER_VERTEX_TO_FRAGMENT(a) a._LightCoord = mul(_LightMatrix0, mul(_Object2World, v.vertex)); TRANSFER_SHADOW(a)
// GEOMETRY SHADER:
#define TRANSFER_GEOM_TO_FRAGMENT(a)   a._LightCoord = mul(_LightMatrix0, mul(_Object2World, a.pos)); TRANSFER_GEOM_SHADOW(a)

inline fixed UnitySpotCookie(float4 LightCoord)
{
    return tex2D(_LightTexture0, LightCoord.xy / LightCoord.w + 0.5).w;
}
inline fixed UnitySpotAttenuate(float3 LightCoord)
{
    return tex2D(_LightTextureB0, dot(LightCoord, LightCoord).xx).UNITY_ATTEN_CHANNEL;
}
#define LIGHT_ATTENUATION(a)    ( (a._LightCoord.z > 0) * UnitySpotCookie(a._LightCoord) * UnitySpotAttenuate(a._LightCoord.xyz) * SHADOW_ATTENUATION(a) )
#endif


#ifdef DIRECTIONAL
    #define LIGHTING_COORDS(idx1,idx2) SHADOW_COORDS(idx1)
    #define TRANSFER_VERTEX_TO_FRAGMENT(a) TRANSFER_SHADOW(a)
    #define LIGHT_ATTENUATION(a)    SHADOW_ATTENUATION(a)
    // GEOMETRY SHADER:
    #define TRANSFER_GEOM_TO_FRAGMENT(a) TRANSFER_GEOM_SHADOW(a)
#endif


#ifdef POINT_COOKIE
#define LIGHTING_COORDS(idx1,idx2) float3 _LightCoord : TEXCOORD##idx1; SHADOW_COORDS(idx2)
uniform samplerCUBE _LightTexture0;
uniform float4x4 _LightMatrix0;
uniform sampler2D _LightTextureB0;
#define TRANSFER_VERTEX_TO_FRAGMENT(a) a._LightCoord = mul(_LightMatrix0, mul(_Object2World, v.vertex)).xyz; TRANSFER_SHADOW(a)
// GEOMETRY SHADER:
#define TRANSFER_GEOM_TO_FRAGMENT(a)   a._LightCoord = mul(_LightMatrix0, mul(_Object2World, a.pos)).xyz; TRANSFER_GEOM_SHADOW(a)

#define LIGHT_ATTENUATION(a)    (tex2D(_LightTextureB0, dot(a._LightCoord,a._LightCoord).rr).UNITY_ATTEN_CHANNEL * texCUBE(_LightTexture0, a._LightCoord).w * SHADOW_ATTENUATION(a))
#endif

#ifdef DIRECTIONAL_COOKIE
#define LIGHTING_COORDS(idx1,idx2) float2 _LightCoord : TEXCOORD##idx1; SHADOW_COORDS(idx2)
uniform sampler2D _LightTexture0;
uniform float4x4 _LightMatrix0;
#define TRANSFER_VERTEX_TO_FRAGMENT(a) a._LightCoord = mul(_LightMatrix0, mul(_Object2World, v.vertex)).xy; TRANSFER_SHADOW(a)
// GEOMETRY SHADER:
#define TRANSFER_GEOM_TO_FRAGMENT(a)   a._LightCoord = mul(_LightMatrix0, mul(_Object2World, a.pos)).xy; TRANSFER_GEOM_SHADOW(a)

#define LIGHT_ATTENUATION(a)    (tex2D(_LightTexture0, a._LightCoord).w * SHADOW_ATTENUATION(a))
#endif


#endif

The UnityCG.cginc replacement

// Unity 4.3.4
// 2014.06.09 - Amended to support Geometry Shader

#ifndef UNITY_CG_INCLUDED
#define UNITY_CG_INCLUDED

#include "UnityShaderVariables.cginc"




#if SHADER_API_FLASH
uniform float4 unity_NPOTScale;
#endif
#if defined(SHADER_API_PS3)
#    define UNITY_SAMPLE_DEPTH(value) (dot((value).wxy, float3(0.996093809371817670572857294849, 0.0038909914428586627756752238080039, 1.5199185323666651467481343000015e-5)))
#elif defined(SHADER_API_FLASH)
#    define UNITY_SAMPLE_DEPTH(value) (DecodeFloatRGBA(value))
#else
#    define UNITY_SAMPLE_DEPTH(value) (value).r
#endif

uniform fixed4 unity_ColorSpaceGrey;

// -------------------------------------------------------------------
//  helper functions and macros used in many standard shaders


#if defined (DIRECTIONAL) || defined (DIRECTIONAL_COOKIE) || defined (POINT) || defined (SPOT) || defined (POINT_NOATT) || defined (POINT_COOKIE)
#define USING_LIGHT_MULTI_COMPILE
#endif

#define SCALED_NORMAL (v.normal * unity_Scale.w)

struct appdata_base {
    float4 vertex : POSITION;
    float3 normal : NORMAL;
    float4 texcoord : TEXCOORD0;
};

struct appdata_tan {
    float4 vertex : POSITION;
    float4 tangent : TANGENT;
    float3 normal : NORMAL;
    float4 texcoord : TEXCOORD0;
};

struct appdata_full {
    float4 vertex : POSITION;
    float4 tangent : TANGENT;
    float3 normal : NORMAL;
    float4 texcoord : TEXCOORD0;
    float4 texcoord1 : TEXCOORD1;
    fixed4 color : COLOR;
#if defined(SHADER_API_XBOX360)
    half4 texcoord2 : TEXCOORD2;
    half4 texcoord3 : TEXCOORD3;
    half4 texcoord4 : TEXCOORD4;
    half4 texcoord5 : TEXCOORD5;
#endif
};

// Computes world space light direction
inline float3 WorldSpaceLightDir( in float4 v )
{
    float3 worldPos = mul(_Object2World, v).xyz;
    #ifndef USING_LIGHT_MULTI_COMPILE
        return _WorldSpaceLightPos0.xyz - worldPos * _WorldSpaceLightPos0.w;
    #else
        #ifndef USING_DIRECTIONAL_LIGHT
        return _WorldSpaceLightPos0.xyz - worldPos;
        #else
        return _WorldSpaceLightPos0.xyz;
        #endif
    #endif
}

// Computes object space light direction
inline float3 ObjSpaceLightDir( in float4 v )
{
    float3 objSpaceLightPos = mul(_World2Object, _WorldSpaceLightPos0).xyz;
    #ifndef USING_LIGHT_MULTI_COMPILE
        return objSpaceLightPos.xyz - v.xyz * _WorldSpaceLightPos0.w;
    #else
        #ifndef USING_DIRECTIONAL_LIGHT
        return objSpaceLightPos.xyz * unity_Scale.w - v.xyz;
        #else
        return objSpaceLightPos.xyz;
        #endif
    #endif
}

// Computes world space view direction
inline float3 WorldSpaceViewDir( in float4 v )
{
    return _WorldSpaceCameraPos.xyz - mul(_Object2World, v).xyz;
}

// Computes object space view direction
inline float3 ObjSpaceViewDir( in float4 v )
{
    float3 objSpaceCameraPos = mul(_World2Object, float4(_WorldSpaceCameraPos.xyz, 1)).xyz * unity_Scale.w;
    return objSpaceCameraPos - v.xyz;
}

// Declares 3x3 matrix 'rotation', filled with tangent space basis
#define TANGENT_SPACE_ROTATION \
    float3 binormal = cross( v.normal, v.tangent.xyz ) * v.tangent.w; \
    float3x3 rotation = float3x3( v.tangent.xyz, binormal, v.normal )




float3 Shade4PointLights (
    float4 lightPosX, float4 lightPosY, float4 lightPosZ,
    float3 lightColor0, float3 lightColor1, float3 lightColor2, float3 lightColor3,
    float4 lightAttenSq,
    float3 pos, float3 normal)
{
    // to light vectors
    float4 toLightX = lightPosX - pos.x;
    float4 toLightY = lightPosY - pos.y;
    float4 toLightZ = lightPosZ - pos.z;
    // squared lengths
    float4 lengthSq = 0;
    lengthSq += toLightX * toLightX;
    lengthSq += toLightY * toLightY;
    lengthSq += toLightZ * toLightZ;
    // NdotL
    float4 ndotl = 0;
    ndotl += toLightX * normal.x;
    ndotl += toLightY * normal.y;
    ndotl += toLightZ * normal.z;
    // correct NdotL
    float4 corr = rsqrt(lengthSq);
    ndotl = max (float4(0,0,0,0), ndotl * corr);
    // attenuation
    float4 atten = 1.0 / (1.0 + lengthSq * lightAttenSq);
    float4 diff = ndotl * atten;
    // final color
    float3 col = 0;
    col += lightColor0 * diff.x;
    col += lightColor1 * diff.y;
    col += lightColor2 * diff.z;
    col += lightColor3 * diff.w;
    return col;
}


float3 ShadeVertexLights (float4 vertex, float3 normal)
{
    float3 viewpos = mul (UNITY_MATRIX_MV, vertex).xyz;
    float3 viewN = mul ((float3x3)UNITY_MATRIX_IT_MV, normal);
    float3 lightColor = UNITY_LIGHTMODEL_AMBIENT.xyz;
    for (int i = 0; i < 4; i++) {
        float3 toLight = unity_LightPosition[i].xyz - viewpos.xyz * unity_LightPosition[i].w;
        float lengthSq = dot(toLight, toLight);
        float atten = 1.0 / (1.0 + lengthSq * unity_LightAtten[i].z);
        float diff = max (0, dot (viewN, normalize(toLight)));
        lightColor += unity_LightColor[i].rgb * (diff * atten);
    }
    return lightColor;
}


// normal should be normalized, w=1.0
half3 ShadeSH9 (half4 normal)
{
    half3 x1, x2, x3;
  
    // Linear + constant polynomial terms
    x1.r = dot(unity_SHAr,normal);
    x1.g = dot(unity_SHAg,normal);
    x1.b = dot(unity_SHAb,normal);
  
    // 4 of the quadratic polynomials
    half4 vB = normal.xyzz * normal.yzzx;
    x2.r = dot(unity_SHBr,vB);
    x2.g = dot(unity_SHBg,vB);
    x2.b = dot(unity_SHBb,vB);
  
    // Final quadratic polynomial
    float vC = normal.x*normal.x - normal.y*normal.y;
    x3 = unity_SHC.rgb * vC;
    return x1 + x2 + x3;
}


// Transforms 2D UV by scale/bias property
#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
// Transforms 4D UV by a texture matrix (use only if you know exactly which matrix you need)
#define TRANSFORM_UV(idx) mul (UNITY_MATRIX_TEXTURE##idx, v.texcoord).xy



struct v2f_vertex_lit {
    float2 uv    : TEXCOORD0;
    fixed4 diff    : COLOR0;
    fixed4 spec    : COLOR1;
};

inline fixed4 VertexLight( v2f_vertex_lit i, sampler2D mainTex )
{
    fixed4 texcol = tex2D( mainTex, i.uv );
    fixed4 c;
    c.xyz = ( texcol.xyz * i.diff.xyz + i.spec.xyz * texcol.a ) * 2;
    c.w = texcol.w * i.diff.w;
    return c;
}


// Calculates UV offset for parallax bump mapping
inline float2 ParallaxOffset( half h, half height, half3 viewDir )
{
    h = h * height - height/2.0;
    float3 v = normalize(viewDir);
    v.z += 0.42;
    return h * (v.xy / v.z);
}


// Converts color to luminance (grayscale)
inline fixed Luminance( fixed3 c )
{
    return dot( c, fixed3(0.22, 0.707, 0.071) );
}

// Decodes lightmaps:
// - doubleLDR encoded on GLES
// - RGBM encoded with range [0;8] on other platforms using surface shaders
inline fixed3 DecodeLightmap( fixed4 color )
{
#if (defined(SHADER_API_GLES) || defined(SHADER_API_GLES3)) && defined(SHADER_API_MOBILE)
    return 2.0 * color.rgb;
#else
    // potentially faster to do the scalar multiplication
    // in parenthesis for scalar GPUs
    return (8.0 * color.a) * color.rgb;
#endif
}


// Helpers used in image effects. Most image effects use the same
// minimal vertex shader (vert_img).

struct appdata_img {
    float4 vertex : POSITION;
    half2 texcoord : TEXCOORD0;
};
struct v2f_img {
    float4 pos : SV_POSITION;
    half2 uv : TEXCOORD0;
};

float2 MultiplyUV (float4x4 mat, float2 inUV) {
    float4 temp = float4 (inUV.x, inUV.y, 0, 0);
    temp = mul (mat, temp);
    return temp.xy;
}

v2f_img vert_img( appdata_img v )
{
    v2f_img o;
    o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    o.uv = MultiplyUV( UNITY_MATRIX_TEXTURE0, v.texcoord );
    return o;
}


// Encoding/decoding [0..1) floats into 8 bit/channel RGBA. Note that 1.0 will not be encoded properly.
inline float4 EncodeFloatRGBA( float v )
{
    float4 kEncodeMul = float4(1.0, 255.0, 65025.0, 160581375.0);
    float kEncodeBit = 1.0/255.0;
    float4 enc = kEncodeMul * v;
    enc = frac (enc);
    enc -= enc.yzww * kEncodeBit;
    return enc;
}
inline float DecodeFloatRGBA( float4 enc )
{
    float4 kDecodeDot = float4(1.0, 1/255.0, 1/65025.0, 1/160581375.0);
    return dot( enc, kDecodeDot );
}

// Encoding/decoding [0..1) floats into 8 bit/channel RG. Note that 1.0 will not be encoded properly.
inline float2 EncodeFloatRG( float v )
{
    float2 kEncodeMul = float2(1.0, 255.0);
    float kEncodeBit = 1.0/255.0;
    float2 enc = kEncodeMul * v;
    enc = frac (enc);
    enc.x -= enc.y * kEncodeBit;
    return enc;
}
inline float DecodeFloatRG( float2 enc )
{
    float2 kDecodeDot = float2(1.0, 1/255.0);
    return dot( enc, kDecodeDot );
}


// Encoding/decoding view space normals into 2D 0..1 vector
inline float2 EncodeViewNormalStereo( float3 n )
{
    float kScale = 1.7777;
    float2 enc;
    enc = n.xy / (n.z+1);
    enc /= kScale;
    enc = enc*0.5+0.5;
    return enc;
}
inline float3 DecodeViewNormalStereo( float4 enc4 )
{
    float kScale = 1.7777;
    float3 nn = enc4.xyz*float3(2*kScale,2*kScale,0) + float3(-kScale,-kScale,1);
    float g = 2.0 / dot(nn.xyz,nn.xyz);
    float3 n;
    n.xy = g*nn.xy;
    n.z = g-1;
    return n;
}

inline float4 EncodeDepthNormal( float depth, float3 normal )
{
    float4 enc;
    enc.xy = EncodeViewNormalStereo (normal);
    enc.zw = EncodeFloatRG (depth);
    return enc;
}

inline void DecodeDepthNormal( float4 enc, out float depth, out float3 normal )
{
    depth = DecodeFloatRG (enc.zw);
    normal = DecodeViewNormalStereo (enc);
}

inline fixed3 UnpackNormalDXT5nm (fixed4 packednormal)
{
    fixed3 normal;
    normal.xy = packednormal.wy * 2 - 1;
#if defined(SHADER_API_FLASH)
    // Flash does not have efficient saturate(), and dot() seems to require an extra register.
    normal.z = sqrt(1 - normal.x*normal.x - normal.y*normal.y);
#else
    normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));
#endif
    return normal;
}

inline fixed3 UnpackNormal(fixed4 packednormal)
{
#if (defined(SHADER_API_GLES) || defined(SHADER_API_GLES3)) && defined(SHADER_API_MOBILE)
    return packednormal.xyz * 2 - 1;
#else
    return UnpackNormalDXT5nm(packednormal);
#endif
}


// Z buffer to linear 0..1 depth (0 at eye, 1 at far plane)
inline float Linear01Depth( float z )
{
    return 1.0 / (_ZBufferParams.x * z + _ZBufferParams.y);
}
// Z buffer to linear depth
inline float LinearEyeDepth( float z )
{
    return 1.0 / (_ZBufferParams.z * z + _ZBufferParams.w);
}


// Depth render texture helpers
#if defined(UNITY_MIGHT_NOT_HAVE_DEPTH_TEXTURE)
    #define UNITY_TRANSFER_DEPTH(oo) oo = o.pos.zw
    #if SHADER_API_FLASH
    #define UNITY_OUTPUT_DEPTH(i) return EncodeFloatRGBA(i.x/i.y)
    #else
    #define UNITY_OUTPUT_DEPTH(i) return i.x/i.y
    #endif
#else
    #define UNITY_TRANSFER_DEPTH(oo)
    #define UNITY_OUTPUT_DEPTH(i) return 0
#endif
#define DECODE_EYEDEPTH(i) LinearEyeDepth(i)
#define COMPUTE_EYEDEPTH(o) o = -mul( UNITY_MATRIX_MV, v.vertex ).z
#define COMPUTE_DEPTH_01 -(mul( UNITY_MATRIX_MV, v.vertex ).z * _ProjectionParams.w)
#define COMPUTE_VIEW_NORMAL mul((float3x3)UNITY_MATRIX_IT_MV, v.normal)


// Projected screen position helpers
#define V2F_SCREEN_TYPE float4
inline float4 ComputeScreenPos (float4 pos) {
    float4 o = pos * 0.5f;
    #if defined(UNITY_HALF_TEXEL_OFFSET)
    o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w * _ScreenParams.zw;
    #else
    o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w;
    #endif
  
    #if defined(SHADER_API_FLASH)
    o.xy *= unity_NPOTScale.xy;
    #endif
  
    o.zw = pos.zw;
    return o;
}

inline float4 ComputeGrabScreenPos (float4 pos) {
    #if UNITY_UV_STARTS_AT_TOP
    float scale = -1.0;
    #else
    float scale = 1.0;
    #endif
    float4 o = pos * 0.5f;
    o.xy = float2(o.x, o.y*scale) + o.w;
    o.zw = pos.zw;
    return o;
}

// snaps post-transformed position to screen pixels
inline float4 UnityPixelSnap (float4 pos)
{
    float2 hpc = _ScreenParams.xy * 0.5;
    #ifdef UNITY_HALF_TEXEL_OFFSET
    float2 hpcO = float2(-0.5,0.5);
    #else
    float2 hpcO = float2(0,0);
    #endif  
    float2 pixelPos = floor ((pos.xy / pos.w) * hpc + 0.5);
    pos.xy = (pixelPos + hpcO) / hpc * pos.w;
    return pos;
}


inline float2 TransformViewToProjection (float2 v) {
    return float2(v.x*UNITY_MATRIX_P[0][0], v.y*UNITY_MATRIX_P[1][1]);
}

inline float3 TransformViewToProjection (float3 v) {
    return float3(v.x*UNITY_MATRIX_P[0][0], v.y*UNITY_MATRIX_P[1][1], v.z*UNITY_MATRIX_P[2][2]);
}




// Shadow caster pass helpers


#ifdef SHADOWS_CUBE
    #define V2F_SHADOW_CASTER float4 pos : SV_POSITION; float3 vec : TEXCOORD0
    #define TRANSFER_SHADOW_CASTER(o) o.vec = mul( _Object2World, v.vertex ).xyz - _LightPositionRange.xyz; o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    #define SHADOW_CASTER_FRAGMENT(i) return EncodeFloatRGBA( min(length(i.vec) * _LightPositionRange.w, 0.999) );
    #define TRANSFER_GEOM_SHADOW_CASTER(o) o.vec = mul( _Object2World, o.pos ).xyz - _LightPositionRange.xyz; o.pos = mul(UNITY_MATRIX_MVP, o.pos);
#else
    #if defined(UNITY_MIGHT_NOT_HAVE_DEPTH_TEXTURE)
        #define V2F_SHADOW_CASTER float4 pos : SV_POSITION; float4 hpos : TEXCOORD0
        #define TRANSFER_SHADOW_CASTER(o) o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.pos.z += unity_LightShadowBias.x; \
        float clamped = max(o.pos.z, o.pos.w*UNITY_NEAR_CLIP_VALUE); o.pos.z = lerp(o.pos.z, clamped, unity_LightShadowBias.y); o.hpos = o.pos;
        #define TRANSFER_GEOM_SHADOW_CASTER(o) o.pos = mul(UNITY_MATRIX_MVP, o.pos); o.pos.z += unity_LightShadowBias.x; \
        clamped = max(o.pos.z, o.pos.w*UNITY_NEAR_CLIP_VALUE); o.pos.z = lerp(o.pos.z, clamped, unity_LightShadowBias.y); o.hpos = o.pos;
    #else
        #define V2F_SHADOW_CASTER float4 pos : SV_POSITION
        #define TRANSFER_SHADOW_CASTER(o) o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.pos.z += unity_LightShadowBias.x; \
        float clamped = max(o.pos.z, o.pos.w*UNITY_NEAR_CLIP_VALUE); o.pos.z = lerp(o.pos.z, clamped, unity_LightShadowBias.y);
        #define TRANSFER_GEOM_SHADOW_CASTER(o) o.pos = mul(UNITY_MATRIX_MVP, o.pos); o.pos.z += unity_LightShadowBias.x; \
        clamped = max(o.pos.z, o.pos.w*UNITY_NEAR_CLIP_VALUE); o.pos.z = lerp(o.pos.z, clamped, unity_LightShadowBias.y);
    #endif
    #define SHADOW_CASTER_FRAGMENT(i) UNITY_OUTPUT_DEPTH(i.hpos.zw);
#endif

// Shadow collector pass helpers
#ifdef SHADOW_COLLECTOR_PASS


#if !defined(SHADOWMAPSAMPLER_DEFINED)
UNITY_DECLARE_SHADOWMAP(_ShadowMapTexture);
#endif


#define V2F_SHADOW_COLLECTOR float4 pos : SV_POSITION; float3 _ShadowCoord0 : TEXCOORD0; float3 _ShadowCoord1 : TEXCOORD1; float3 _ShadowCoord2 : TEXCOORD2; float3 _ShadowCoord3 : TEXCOORD3; float4 _WorldPosViewZ : TEXCOORD4
#define TRANSFER_SHADOW_COLLECTOR(o)    \
    o.pos = mul(UNITY_MATRIX_MVP, v.vertex); \
    float4 wpos = mul(_Object2World, v.vertex); \
    o._WorldPosViewZ.xyz = wpos; \
    o._WorldPosViewZ.w = -mul( UNITY_MATRIX_MV, v.vertex ).z; \
    o._ShadowCoord0 = mul(unity_World2Shadow[0], wpos).xyz; \
    o._ShadowCoord1 = mul(unity_World2Shadow[1], wpos).xyz; \
    o._ShadowCoord2 = mul(unity_World2Shadow[2], wpos).xyz; \
    o._ShadowCoord3 = mul(unity_World2Shadow[3], wpos).xyz;

#define TRANSFER_GEOM_SHADOW_COLLECTOR(o, v)    \
    o.pos = mul(UNITY_MATRIX_MVP, v); \
    wpos = mul(_Object2World, v); \
    o._WorldPosViewZ.xyz = wpos; \
    o._WorldPosViewZ.w = -mul( UNITY_MATRIX_MV, v ).z; \
    o._ShadowCoord0 = mul(unity_World2Shadow[0], wpos).xyz; \
    o._ShadowCoord1 = mul(unity_World2Shadow[1], wpos).xyz; \
    o._ShadowCoord2 = mul(unity_World2Shadow[2], wpos).xyz; \
    o._ShadowCoord3 = mul(unity_World2Shadow[3], wpos).xyz;
  
  
#if defined (SHADOWS_NATIVE)
    #define SAMPLE_SHADOW_COLLECTOR_SHADOW(coord) \
    half shadow = UNITY_SAMPLE_SHADOW(_ShadowMapTexture,coord); \
    shadow = _LightShadowData.r + shadow * (1-_LightShadowData.r);
#else
    #define SAMPLE_SHADOW_COLLECTOR_SHADOW(coord) \
    float shadow = UNITY_SAMPLE_DEPTH(tex2D( _ShadowMapTexture, coord.xy )) < coord.z ? _LightShadowData.r : 1.0;
#endif

#define COMPUTE_SHADOW_COLLECTOR_SHADOW(i, weights, shadowFade) \
    float4 coord = float4(i._ShadowCoord0 * weights[0] + i._ShadowCoord1 * weights[1] + i._ShadowCoord2 * weights[2] + i._ShadowCoord3 * weights[3], 1); \
    SAMPLE_SHADOW_COLLECTOR_SHADOW(coord) \
    float4 res; \
    res.x = saturate(shadow + shadowFade); \
    res.y = 1.0; \
    res.zw = EncodeFloatRG (1 - i._WorldPosViewZ.w * _ProjectionParams.w); \
    return res;  

#if defined (SHADOWS_SPLIT_SPHERES)
#define SHADOW_COLLECTOR_FRAGMENT(i) \
    float3 fromCenter0 = i._WorldPosViewZ.xyz - unity_ShadowSplitSpheres[0].xyz; \
    float3 fromCenter1 = i._WorldPosViewZ.xyz - unity_ShadowSplitSpheres[1].xyz; \
    float3 fromCenter2 = i._WorldPosViewZ.xyz - unity_ShadowSplitSpheres[2].xyz; \
    float3 fromCenter3 = i._WorldPosViewZ.xyz - unity_ShadowSplitSpheres[3].xyz; \
    float4 distances2 = float4(dot(fromCenter0,fromCenter0), dot(fromCenter1,fromCenter1), dot(fromCenter2,fromCenter2), dot(fromCenter3,fromCenter3)); \
    float4 cascadeWeights = float4(distances2 < unity_ShadowSplitSqRadii); \
    cascadeWeights.yzw = saturate(cascadeWeights.yzw - cascadeWeights.xyz); \
    float sphereDist = distance(i._WorldPosViewZ.xyz, unity_ShadowFadeCenterAndType.xyz); \
    float shadowFade = saturate(sphereDist * _LightShadowData.z + _LightShadowData.w); \
    COMPUTE_SHADOW_COLLECTOR_SHADOW(i, cascadeWeights, shadowFade)
#else
#define SHADOW_COLLECTOR_FRAGMENT(i) \
    float4 viewZ = i._WorldPosViewZ.w; \
    float4 zNear = float4( viewZ >= _LightSplitsNear ); \
    float4 zFar = float4( viewZ < _LightSplitsFar ); \
    float4 cascadeWeights = zNear * zFar; \
    float shadowFade = saturate(i._WorldPosViewZ.w * _LightShadowData.z + _LightShadowData.w); \
    COMPUTE_SHADOW_COLLECTOR_SHADOW(i, cascadeWeights, shadowFade)
#endif
  
#endif

#endif

I’ve tried to implement the above code, it seems to work mostly but having problems with the geometry shader not writing correct depth texture. It seems to only write the input vertices and not the geometry output vertices. Any ideas on how to solve that?

Last time I checked it worked fine for me, had various overlapping objects and didn’t notice any depth issues. However there are problems with shadowing (can’t remember off-hand if that was all shadowing or just self-shadowing) which were related to either depth or offsets.

Something to keep in mind, if the geometry shader generates a mesh that considerably changes its volume or position then other aspects of Unity’s renderer may fail or produce weird results (e.g. culling).

I’m afraid i’m rather busy on other topics at present so can’t investigate this further, but like I said this code is really just presented as a starting point for others to save some time. Its more of an initial framework with which to write geometry shaders and not a complete solution. Indeed its actual effects are pretty useless :wink:

Thanks for the quick reply, indeed the objects itself don’t show any depth issues. It’s just that any deferred effect gets incorrect depth information (so shadows, ssao etc don’t work correctly). My volume does indeed change quite a bit since a single vertex goes in and a cube comes out, if I ever figure it out i’ll post an update here…

Wouldn’t it be nice though if we could just add geometry: geom to a #pragma surface instead :wink:

Sounds like deferred is not playing nice with forward rendering then, since my shader only supports forward rendering. If you are able, try adding a deferred shader pass to my shader and see if that fixes the issue.

Agree about it being nice if we could just add geom to a surface shader, however I seem to remember when writing the shader that it wasn’t so straight forward to do.

I’ve faced similar difficulties trying to create my custom grass billboards in geometry shader and make them receive shadows from trees and also cast them on the ground. I used vertex/geometry/fragment shaders since I couldn’t use geometry shader with surface shader. This topic helped me a lot so I thought it would be a good idea to link it to my solution trough this post.

Solution is presented in answer to this question:

This solution doesn’t require to change AutoLight.cginc, UnityCG.cginc or any other .cginc file. It’s a one shader solution with base and shadowcaster passes.

Source included at the bottom of the answer!

I hope that someone would find it helpful. Enjoy!

3 Likes