Hi! I'm drawing point clouds as a huge bunch of cubes using a fancy geometry shader and kind of can't figure out how to properly use the generated cubes as shadow casters. I already found out I have to define shadow passes myself but I'm not sure whether I have to do all the geometry generation again in the following passes or whether I can somehow transfer the whole ordeal. I'd really appreciate any hints as I'm really no expert at shader programming and don't quite get what the shadow passes do (couldn't find proper documentation neither). As a minimal example which shows billboards at the vertex positions but draws the shadow of the underlying mesh. Billboard shader from: http://forum.unity3d.com/threads/169415-Billboard-Geometry-Shader Shadow code from: http://forum.unity3d.com/threads/21...on-shader-in-a-Surface-shader-Shadow-problems Code (csharp): Shader "Custom/GS Billboard" { Properties { _SpriteTex ("Base (RGB)", 2D) = "white" { } _Size ("Size", Range(0, 3)) = 0.5 } SubShader { Pass { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma target 5.0 #pragma vertex VS_Main #pragma fragment FS_Main #pragma geometry GS_Main #include "UnityCG.cginc" // ************************************************************** // Data structures * // ************************************************************** struct GS_INPUT { float4 pos : POSITION; float3 normal : NORMAL; float2 tex0 : TEXCOORD0; } ; struct FS_INPUT { float4 pos : POSITION; float2 tex0 : TEXCOORD0; } ; // ************************************************************** // Vars * // ************************************************************** float _Size; float4x4 _VP; Texture2D _SpriteTex; SamplerState sampler_SpriteTex; // ************************************************************** // Shader Programs * // ************************************************************** // Vertex Shader ------------------------------------------------ GS_INPUT VS_Main(appdata_base v) { GS_INPUT output = (GS_INPUT)0; output.pos = mul(_Object2World, v.vertex); output.normal = v.normal; output.tex0 = float2(0, 0); return output; } // Geometry Shader ----------------------------------------------------- [maxvertexcount(4)] void GS_Main(point GS_INPUT p[1], inout TriangleStream<FS_INPUT> triStream) { //get up vector float3 up = float3(0, 1, 0); //get look vector float3 look = _WorldSpaceCameraPos - p[0].pos; look.y = 0; look = normalize(look); //get right vector float3 right = cross(up, look); //half the size please.. float halfS = 0.5f * _Size; //offsets float4 v[4]; v[0] = float4(p[0].pos + halfS * right - halfS * up, 1.0f); v[1] = float4(p[0].pos + halfS * right + halfS * up, 1.0f); v[2] = float4(p[0].pos - halfS * right - halfS * up, 1.0f); v[3] = float4(p[0].pos - halfS * right + halfS * up, 1.0f); float4x4 vp = mul(UNITY_MATRIX_MVP, _World2Object); FS_INPUT pIn; pIn.pos = mul(vp, v[0]); pIn.tex0 = float2(1.0f, 0.0f); triStream.Append(pIn); pIn.pos = mul(vp, v[1]); pIn.tex0 = float2(1.0f, 1.0f); triStream.Append(pIn); pIn.pos = mul(vp, v[2]); pIn.tex0 = float2(0.0f, 0.0f); triStream.Append(pIn); pIn.pos = mul(vp, v[3]); pIn.tex0 = float2(0.0f, 1.0f); triStream.Append(pIn); } // Fragment Shader ----------------------------------------------- float4 FS_Main(FS_INPUT input) : COLOR { return _SpriteTex.Sample(sampler_SpriteTex, input.tex0); } ENDCG } // 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 fragment frag #pragma multi_compile_shadowcaster #pragma fragmentoption ARB_precision_hint_fastest #include "UnityCG.cginc" struct v2f { V2F_SHADOW_CASTER; }; v2f vert( appdata_base v ) { v2f o; TRANSFER_SHADOW_CASTER(o) return o; } 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 fragment frag #pragma fragmentoption ARB_precision_hint_fastest #pragma multi_compile_shadowcollector #define SHADOW_COLLECTOR_PASS #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; }; struct v2f { V2F_SHADOW_COLLECTOR; }; v2f vert (appdata v){ v2f o; TRANSFER_SHADOW_COLLECTOR(o) return o; } fixed4 frag (v2f i) : COLOR { SHADOW_COLLECTOR_FRAGMENT(i) } ENDCG }//end pass }//end subshader //Fallback "Diffuse" }//end shader
Okay I kinda got shadows - Kinda irks me I need to do the whole geometry shader business again. Surely, there's a better way? Code (csharp): Shader "Custom/billboard2" { Properties { _SpriteTex ("Base (RGB)", 2D) = "white" { } _Size ("Size", Range(0, 3)) = 0.5 } SubShader { Pass { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma target 5.0 #pragma vertex VS_Main #pragma fragment FS_Main #pragma geometry GS_Main #include "UnityCG.cginc" // ************************************************************** // Data structures * // ************************************************************** struct GS_INPUT { float4 pos : POSITION; float3 normal : NORMAL; float2 tex0 : TEXCOORD0; } ; struct FS_INPUT { float4 pos : POSITION; float2 tex0 : TEXCOORD0; } ; // ************************************************************** // Vars * // ************************************************************** float _Size; float4x4 _VP; Texture2D _SpriteTex; SamplerState sampler_SpriteTex; // ************************************************************** // Shader Programs * // ************************************************************** // Vertex Shader ------------------------------------------------ GS_INPUT VS_Main(appdata_base v) { GS_INPUT output = (GS_INPUT)0; output.pos = mul(_Object2World, v.vertex); output.normal = v.normal; output.tex0 = float2(0, 0); return output; } // Geometry Shader ----------------------------------------------------- [maxvertexcount(4)] void GS_Main(point GS_INPUT p[1], inout TriangleStream<FS_INPUT> triStream) { //get up vector float3 up = UNITY_MATRIX_IT_MV[1].xyz;//float3(0, 1, 0); //get look vector float3 look = _WorldSpaceCameraPos - p[0].pos; //look.y = 0; look = normalize(look); //get right vector float3 right = cross(up, look); //half the size please.. float halfS = 0.5f * _Size; //offsets float4 v[4]; v[0] = float4(p[0].pos + halfS * right - halfS * up, 1.0f); v[1] = float4(p[0].pos + halfS * right + halfS * up, 1.0f); v[2] = float4(p[0].pos - halfS * right - halfS * up, 1.0f); v[3] = float4(p[0].pos - halfS * right + halfS * up, 1.0f); float4x4 vp = mul(UNITY_MATRIX_MVP, _World2Object); FS_INPUT pIn; pIn.pos = mul(vp, v[0]); pIn.tex0 = float2(1.0f, 0.0f); triStream.Append(pIn); pIn.pos = mul(vp, v[1]); pIn.tex0 = float2(1.0f, 1.0f); triStream.Append(pIn); pIn.pos = mul(vp, v[2]); pIn.tex0 = float2(0.0f, 0.0f); triStream.Append(pIn); pIn.pos = mul(vp, v[3]); pIn.tex0 = float2(0.0f, 1.0f); triStream.Append(pIn); } // Fragment Shader ----------------------------------------------- float4 FS_Main(FS_INPUT input) : COLOR { return _SpriteTex.Sample(sampler_SpriteTex, input.tex0); } ENDCG } // 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 SHADOW_VS_Main #pragma fragment SHADOW_FS_Main #pragma geometry SHADOW_GS_Main #pragma multi_compile_shadowcaster #pragma fragmentoption ARB_precision_hint_fastest #include "UnityCG.cginc" // ************************************************************** // Data structures * // ************************************************************** struct SHADOW_GS_INPUT { V2F_SHADOW_CASTER; }; struct SHADOW_FS_INPUT { V2F_SHADOW_CASTER; }; // ************************************************************** // Vars * // ************************************************************** float _Size; float4x4 _VP; // ************************************************************** // Shader Programs * // ************************************************************** // Vertex Shader ------------------------------------------------ SHADOW_GS_INPUT SHADOW_VS_Main(appdata_base v) { SHADOW_GS_INPUT output = (SHADOW_GS_INPUT)0; output.pos = mul(_Object2World, v.vertex); //TRANSFER_SHADOW_CASTER(output) return output; } // Geometry Shader ----------------------------------------------------- [maxvertexcount(4)] void SHADOW_GS_Main(point SHADOW_GS_INPUT p[1], inout TriangleStream<SHADOW_FS_INPUT> triStream) { //get up vector float3 up = UNITY_MATRIX_IT_MV[1].xyz; //get look vector float3 look = _WorldSpaceCameraPos - p[0].pos; //look.y = 0; look = normalize(look); //get right vector float3 right = cross(up, look); //half the size please.. float halfS = 0.5f * _Size; //offsets float4 v[4]; v[0] = float4(p[0].pos + halfS * right - halfS * up, 1.0f); v[1] = float4(p[0].pos + halfS * right + halfS * up, 1.0f); v[2] = float4(p[0].pos - halfS * right - halfS * up, 1.0f); v[3] = float4(p[0].pos - halfS * right + halfS * up, 1.0f); float4x4 vp = mul(UNITY_MATRIX_MVP, _World2Object); SHADOW_FS_INPUT pIn=p[0]; //TRANSFER_SHADOW_CASTER(pIn) pIn.pos = mul(vp, v[0]); triStream.Append(pIn); //TRANSFER_SHADOW_CASTER(pIn) pIn.pos = mul(vp, v[1]); triStream.Append(pIn); //TRANSFER_SHADOW_CASTER(pIn) pIn.pos = mul(vp, v[2]); triStream.Append(pIn); //TRANSFER_SHADOW_CASTER(pIn) pIn.pos = mul(vp, v[3]); triStream.Append(pIn); //TRANSFER_SHADOW_CASTER(pIn) } // Fragment Shader ----------------------------------------------- float4 SHADOW_FS_Main(SHADOW_FS_INPUT input) : COLOR { SHADOW_CASTER_FRAGMENT(input) //return _SpriteTex.Sample(sampler_SpriteTex, input.tex0); } 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 fragment frag #pragma fragmentoption ARB_precision_hint_fastest #pragma multi_compile_shadowcollector #define SHADOW_COLLECTOR_PASS #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; }; struct v2f { V2F_SHADOW_COLLECTOR; }; v2f vert (appdata v){ v2f o; TRANSFER_SHADOW_COLLECTOR(o) return o; } fixed4 frag (v2f i) : COLOR { SHADOW_COLLECTOR_FRAGMENT(i) } ENDCG }//end pass }//end subshader //Fallback "Diffuse" }//end shader
Unfortunately, I can't quite seem to get my objects to throw shadows when I render them procedurally in the OnRenderObject Method. Does anyone have any clue how to get shadows with procedural objects drawn in "OnRenderObject()"? Would be really appreciated! Code (csharp): void OnRenderObject() { //Vector3 campos = Camera.current.transform.position; //renderer.material.SetVector("_WorldSpaceCameraPos", new Vector4(campos.x, campos.y, campos.z, 0)); renderer.material.SetPass(0); Graphics.DrawProcedural(MeshTopology.Points, pointCount, 1); if (renderer.material.SetPass(1)) //second pass should draw shadows. { //Debug.Log("Drawing second pass"); Graphics.DrawProcedural(MeshTopology.Triangles, pointCount, 1); } } Basically when I use this following shader on a normal mesh I get a cube per vertex and each throws a shadow - I guess that means the shader is correct: Code (csharp): Shader "DX11/VertexCubeShader" { Properties { _Size ("Size", Range(0.01, 1.0)) = 1.0 } SubShader { Pass { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma target 5.0 #pragma vertex vert #pragma geometry geom #pragma fragment frag #include "UnityCG.cginc" uniform float _Size; // ************************************************************** // Data structures * // ************************************************************** struct gs_input { float4 position : SV_POSITION; float4 col : COLOR; }; struct ps_input { float4 position : SV_POSITION; float4 col : COLOR; }; // ************************************************************** // Vars * // ************************************************************** gs_input vert (appdata_base v) { gs_input output = (gs_input)0; output.position = mul(_Object2World, v.vertex); return output; } // Geometry Shader ----------------------------------------------------- [maxvertexcount(24)] void geom (point gs_input inp[1], inout TriangleStream<ps_input> triStream) { float halfS = 0.5f * _Size; //offsets float4 v[8]; v[0] = float4(inp[0].position.x + halfS, inp[0].position.y + halfS, inp[0].position.z + halfS,1.0f);//float4(1,2,3,4);// v[1] = float4(inp[0].position.x + halfS, inp[0].position.y - halfS, inp[0].position.z + halfS,1.0f);//float4(1,2,3,4);// v[2] = float4(inp[0].position.x - halfS, inp[0].position.y + halfS, inp[0].position.z + halfS,1.0f);//float4(1,2,3,4);// v[3] = float4(inp[0].position.x - halfS, inp[0].position.y - halfS, inp[0].position.z + halfS,1.0f);//float4(1,2,3,4);// v[4] = float4(inp[0].position.x + halfS, inp[0].position.y + halfS, inp[0].position.z - halfS,1.0f);//float4(1,2,3,4);// v[5] = float4(inp[0].position.x + halfS, inp[0].position.y - halfS, inp[0].position.z - halfS,1.0f);//float4(1,2,3,4);// v[6] = float4(inp[0].position.x - halfS, inp[0].position.y + halfS, inp[0].position.z - halfS,1.0f);//float4(1,2,3,4);// v[7] = float4(inp[0].position.x - halfS, inp[0].position.y - halfS, inp[0].position.z - halfS,1.0f);//float4(1,2,3,4);// float4x4 vp = mul(UNITY_MATRIX_MVP, _World2Object); ps_input pIn; //back pIn.position = mul(vp, v[0]); pIn.col = inp[0].col; triStream.Append(pIn); pIn.position = mul(vp, v[2]); pIn.col = inp[0].col; triStream.Append(pIn); pIn.position = mul(vp, v[1]); pIn.col = inp[0].col; triStream.Append(pIn); pIn.position = mul(vp, v[3]); pIn.col = inp[0].col; triStream.Append(pIn); triStream.RestartStrip(); //front pIn.position = mul(vp, v[4]); pIn.col = inp[0].col; triStream.Append(pIn); pIn.position = mul(vp, v[5]); pIn.col = inp[0].col; triStream.Append(pIn); pIn.position = mul(vp, v[6]); pIn.col = inp[0].col; triStream.Append(pIn); pIn.position = mul(vp, v[7]); pIn.col = inp[0].col; triStream.Append(pIn); triStream.RestartStrip(); //right pIn.position = mul(vp, v[0]); pIn.col = inp[0].col; triStream.Append(pIn); pIn.position = mul(vp, v[1]); pIn.col = inp[0].col; triStream.Append(pIn); pIn.position = mul(vp, v[4]); pIn.col = inp[0].col; triStream.Append(pIn); pIn.position = mul(vp, v[5]); pIn.col = inp[0].col; triStream.Append(pIn); triStream.RestartStrip(); //left pIn.position = mul(vp, v[6]); pIn.col = inp[0].col; triStream.Append(pIn); pIn.position = mul(vp, v[7]); pIn.col = inp[0].col; triStream.Append(pIn); pIn.position = mul(vp, v[2]); pIn.col = inp[0].col; triStream.Append(pIn); pIn.position = mul(vp, v[3]); pIn.col = inp[0].col; triStream.Append(pIn); triStream.RestartStrip(); //top pIn.position = mul(vp, v[0]); pIn.col = inp[0].col; triStream.Append(pIn); pIn.position = mul(vp, v[4]); pIn.col = inp[0].col; triStream.Append(pIn); pIn.position = mul(vp, v[2]); pIn.col = inp[0].col; triStream.Append(pIn); pIn.position = mul(vp, v[6]); pIn.col = inp[0].col; triStream.Append(pIn); triStream.RestartStrip(); //bottom pIn.position = mul(vp, v[5]); pIn.col = inp[0].col; triStream.Append(pIn); pIn.position = mul(vp, v[1]); pIn.col = inp[0].col; triStream.Append(pIn); pIn.position = mul(vp, v[7]); pIn.col = inp[0].col; triStream.Append(pIn); pIn.position = mul(vp, v[3]); pIn.col = inp[0].col; triStream.Append(pIn); triStream.RestartStrip(); } float4 frag (ps_input i) : COLOR { return i.col; } ENDCG }//end pass 1 // 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 SHADOW_VS_Main #pragma fragment SHADOW_FS_Main #pragma geometry SHADOW_GS_Main #pragma multi_compile_shadowcaster #pragma fragmentoption ARB_precision_hint_fastest #include "UnityCG.cginc" // ************************************************************** // Data structures * // ************************************************************** struct SHADOW_GS_INPUT { V2F_SHADOW_CASTER; }; struct SHADOW_FS_INPUT { V2F_SHADOW_CASTER; }; // ************************************************************** // Vars * // ************************************************************** float _Size; float4x4 _VP; // ************************************************************** // Shader Programs * // ************************************************************** // Vertex Shader ------------------------------------------------ SHADOW_GS_INPUT SHADOW_VS_Main(appdata_base v) { SHADOW_GS_INPUT output = (SHADOW_GS_INPUT)0; output.pos = mul(_Object2World, v.vertex); return output; } // Geometry Shader ----------------------------------------------------- [maxvertexcount(24)] void SHADOW_GS_Main(point SHADOW_GS_INPUT p[1], inout TriangleStream<SHADOW_FS_INPUT> triStream) { float halfS = 0.5f * _Size; //offsets float4 v[8]; v[0] = float4(p[0].pos.x + halfS, p[0].pos.y + halfS, p[0].pos.z + halfS , 1.0f);//float4(1,2,3,4);// v[1] = float4(p[0].pos.x + halfS, p[0].pos.y - halfS, p[0].pos.z + halfS , 1.0f);//float4(1,2,3,4);// v[2] = float4(p[0].pos.x - halfS, p[0].pos.y + halfS, p[0].pos.z + halfS , 1.0f);//float4(1,2,3,4);// v[3] = float4(p[0].pos.x - halfS, p[0].pos.y - halfS, p[0].pos.z + halfS , 1.0f);//float4(1,2,3,4);// v[4] = float4(p[0].pos.x + halfS, p[0].pos.y + halfS, p[0].pos.z - halfS , 1.0f);//float4(1,2,3,4);// v[5] = float4(p[0].pos.x + halfS, p[0].pos.y - halfS, p[0].pos.z - halfS , 1.0f);//float4(1,2,3,4);// v[6] = float4(p[0].pos.x - halfS, p[0].pos.y + halfS, p[0].pos.z - halfS , 1.0f);//float4(1,2,3,4);// v[7] = float4(p[0].pos.x - halfS, p[0].pos.y - halfS, p[0].pos.z - halfS , 1.0f);//float4(1,2,3,4);// float4x4 vp = mul(UNITY_MATRIX_MVP, _World2Object); SHADOW_FS_INPUT pIn=p[0]; //back pIn.pos = mul(vp, v[0]); triStream.Append(pIn); pIn.pos = mul(vp, v[2]); triStream.Append(pIn); pIn.pos = mul(vp, v[1]); triStream.Append(pIn); pIn.pos = mul(vp, v[3]); triStream.Append(pIn); triStream.RestartStrip(); //front pIn.pos = mul(vp, v[4]); triStream.Append(pIn); pIn.pos = mul(vp, v[5]); triStream.Append(pIn); pIn.pos = mul(vp, v[6]); triStream.Append(pIn); pIn.pos = mul(vp, v[7]); triStream.Append(pIn); triStream.RestartStrip(); //right pIn.pos = mul(vp, v[0]); triStream.Append(pIn); pIn.pos = mul(vp, v[1]); triStream.Append(pIn); pIn.pos = mul(vp, v[4]); triStream.Append(pIn); pIn.pos = mul(vp, v[5]); triStream.Append(pIn); triStream.RestartStrip(); //left pIn.pos = mul(vp, v[6]); triStream.Append(pIn); pIn.pos = mul(vp, v[7]); triStream.Append(pIn); pIn.pos = mul(vp, v[2]); triStream.Append(pIn); pIn.pos = mul(vp, v[3]); triStream.Append(pIn); triStream.RestartStrip(); //top pIn.pos = mul(vp, v[0]); triStream.Append(pIn); pIn.pos = mul(vp, v[4]); triStream.Append(pIn); pIn.pos = mul(vp, v[2]); triStream.Append(pIn); pIn.pos = mul(vp, v[6]); triStream.Append(pIn); triStream.RestartStrip(); //bottom pIn.pos = mul(vp, v[5]); triStream.Append(pIn); pIn.pos = mul(vp, v[1]); triStream.Append(pIn); pIn.pos = mul(vp, v[7]); triStream.Append(pIn); pIn.pos = mul(vp, v[3]); triStream.Append(pIn); triStream.RestartStrip(); } // Fragment Shader ----------------------------------------------- float4 SHADOW_FS_Main(SHADOW_FS_INPUT input) : COLOR { SHADOW_CASTER_FRAGMENT(input) } ENDCG } } Fallback Off } The same version drawn in the OnRenderObject function doesn't work. In fact, it simply draws black cubes ontop of the original ones. I assume it's due to the render queue being screwed when the "OnRenderObject" function is called compared to normal object rendering.