0
\$\begingroup\$

So about a week ago I decided to make my own shadow mapping technique in Unity based on my understanding of the whole thing. The entire experience was somewhat successful. I learned a lot and I get to correct a lot of things such as the MVP matrix and the math behind it. One thing though didn't work as planned and I don't know why.

First lets talk about what worked. I wrote a replacement shader that will show the depth of every object based on the Light Position/perspective. Therefore I made a custom Light GameObject that act as a camera/light with that replacement shader attached to it, and I record everything the Light sees into a render texture.

enter image description here

Then I made a second Camera which is basically a child of the main camera, and sees what the main camera see with the exception of the replacement Shader that I attached previously to the Light gameobject. again I recorded everything from the camera to a render texture as well.

enter image description here

After that, I wrote a basic Shader that does a basic light calculation and make a the shadow comparison between the two textures, and determines whether a fragment in a shadow or light. Also, I added a shadow Bias as well to eliminate any artifacts.

            struct appdata {
            float4 position : POSITION;
            float3 normal : NORMAL;
            float2 uv : TEXCOORD0;
        };

        struct v2f {
            float4 position : SV_POSITION;
            float2 uv : TEXCOORD0;
            float3 normal : TEXCOORD1;
            float3 worldPos : TEXCOORD2;

        };

        float4 _Tint;
        sampler2D _MainTex;
        sampler2D  SahdowMap_Light, SahdowMap_View;
        float4 _MainTex_ST;
        float4x4 WorldToCamera, WorldToLight;
        float3 _LightDir1;


        v2f VertexProgram(appdata v) {
            v2f i;
            i.position = UnityObjectToClipPos(v.position);
            i.worldPos = mul(unity_ObjectToWorld, v.position);
            i.normal = UnityObjectToWorldNormal(v.normal);
            i.uv = TRANSFORM_TEX(v.uv, _MainTex);
            return i;
        }


        float Shadow_Cal(v2f i, sampler2D DepthMap,float4x4 space) {

            float4 projected2 = mul(space, float4(i.worldPos, 1.0f));
            float2 uv = (-projected2.xy / projected2.w) * 0.5 + 0.5;
            float Shadow = tex2D(DepthMap, uv).r;

            if (uv.y > 1.0)
            Shadow = 0.0;

            return Shadow;
        }

        float4 FragmentProgram (v2f i) : SV_TARGET {

            float LightDepth = Shadow_Cal(i, SahdowMap_Light, WorldToLight);
            float ViewDepth = Shadow_Cal(i, SahdowMap_View, WorldToCamera);
            float bias = max(0.05 * (1.0 - dot(i.normal, _LightDir1)), 0.015);
            float Shadow = LightDepth + bias < ViewDepth ? 0 : 1;


            float4 Texture = tex2D(_MainTex, i.uv);
            float3 lightpos = _LightDir1 - i.worldPos;
            return saturate(dot(lightpos, i.normal)) *_Tint * Texture * Shadow;
        }

enter image description here

The problem is when the Light is on perspective, this works perfectly fine as shown above, but when I switch the light camera to Orthographic this happen:

enter image description here

I tried many different things to fix it but I failed. I would love your insight in this whole thing, your thought, solution or even a suggestions.

You are free to download the whole project as a package here Download

Special thanks to DMGregory.

\$\endgroup\$
  • \$\begingroup\$ Can you describe the specific symptoms of "going absolutely crazy"? You may also find you'll get answers faster if you put your main problem in the title and at the intro to your post "I need help correcting this specific artifact with an orthographic shadow map" - that way a user can judge when just skimming posts whether they might have useful expertise on the matter, encouraging them to click in & read deeper. \$\endgroup\$ – DMGregory Jan 10 at 18:11
  • \$\begingroup\$ Thanks again DMGregory for the advise. I have changed the title, and I added a picture to descriptive the problem. \$\endgroup\$ – The Oathman Jan 10 at 18:39
0
\$\begingroup\$

Ok I have figured it out. This is what the fragment shader should look like:

            float4 FragmentProgram (v2f i) : SV_TARGET {

            // transform a point from world space to uv coord to sample the depth map from the light perspective 
            float4 projected = mul(WorldToLight, float4(i.worldPos, 1.0f));
            float2 uv_Light = (projected.xy / projected.w) * 0.5 + 0.5;  
            float LightDepth = tex2D(SahdowMap_Light, uv_Light).r;
            if (uv_Light.y > 1.0)
            LightDepth = 1.0;


            // transform a point from world space to uv coord to sample the depth map from the view perspective
            float4 projected2 = mul(WorldToCamera, float4(i.worldPos, 1.0f));
            float2 uv_View = (-projected2.xy / projected2.w) * 0.5 + 0.5;
            float ViewDepth = tex2D(SahdowMap_View, uv_View).r;


            float bias = max(0.05 * (1.0 - dot(i.normal, _LightDir1)), 0.005);
            float Shadow = LightDepth + bias < ViewDepth ? 0 : 1;


            float4 Texture = tex2D(_MainTex, i.uv);
            float3 lightpos = _LightDir1 - i.worldPos;
            return saturate(dot(lightpos, i.normal)) *_Tint * Texture * Shadow;
        }

it turns out if the Light camera mode is on Orthographic, uv_Light should be

                float2 uv_Light = (projected.xy / projected.w) * 0.5 + 0.5;  

but in a case we are using perspective, uv_Light should look like this:

                float2 uv_Light = (-projected.xy / projected.w) * 0.5 + 0.5;  

I'm in the process of trying to figure out the exact reason behind this.

Spotlight Shadow enter image description here

Directional Light shadow enter image description here

\$\endgroup\$

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.