{"id":20400364,"url":"https://github.com/fschutt/moment-shadow-mapping","last_synced_at":"2026-03-09T15:03:37.662Z","repository":{"id":90270356,"uuid":"125820753","full_name":"fschutt/moment-shadow-mapping","owner":"fschutt","description":"Moment shadow mapping walkthrough (description only)","archived":false,"fork":false,"pushed_at":"2018-03-19T07:51:50.000Z","size":6,"stargazers_count":29,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-14T07:18:21.608Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fschutt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-03-19T07:49:29.000Z","updated_at":"2025-08-20T08:44:34.000Z","dependencies_parsed_at":null,"dependency_job_id":"538fe0aa-b2cb-49fb-9a70-5b1bf8e88ddb","html_url":"https://github.com/fschutt/moment-shadow-mapping","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/fschutt/moment-shadow-mapping","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschutt%2Fmoment-shadow-mapping","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschutt%2Fmoment-shadow-mapping/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschutt%2Fmoment-shadow-mapping/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschutt%2Fmoment-shadow-mapping/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fschutt","download_url":"https://codeload.github.com/fschutt/moment-shadow-mapping/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschutt%2Fmoment-shadow-mapping/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30299873,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-09T14:33:48.460Z","status":"ssl_error","status_checked_at":"2026-03-09T14:33:48.027Z","response_time":61,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-15T04:39:54.304Z","updated_at":"2026-03-09T15:03:37.643Z","avatar_url":"https://github.com/fschutt.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Moment shadow mapping\n\n- Idea: Store various exponentials of the depth in a texture,\n  and apply a gaussian blur to each one of them. Then recombine \n  them back together.\n- Goal: prevent light leaking + shadow acne by using not one, but four\n  exponents of the depth, combined in various ways\n- RGBA texture = 4 channels for storing the z-depth from the lights POV:\n- See https://www.gdcvault.com/play/1023808/Rendering-Antialiased-Shadows-with-Moment\n\n```\nR: ((-3/2) * z) + (2 * (z ^ 3)) + (1/2)\nG: 4 * ((z ^ 2) - (z ^ 4))\nB: (sqrt(3) * (((1/2) * z) - ((2/9) * z ^ 3))) + (1/2)\nA: (1/2) * ((z ^ 2) + (z ^ 4))\n```\n\n# Implementation\n\n1. Render the scene from the lights view to a multisampled depth buffer (do multisampling here)\n2. Compute the RGBA texture (R16G16B16A16 normalized uint, 1024², no mipmaps):\n\n```\nR   =    [[    1.5    ], [    0.0    ], [   -2.0    ], [    0.0    ]]   *   [ z ]   +   [0.5]\nG   =    [[    0.0    ], [    4.0    ], [    0.0    ], [   -4.0    ]]   *   [z^2]   +   [0.0]\nB   =    [[ sqrt(3)/2 ], [    0.0    ], [-sqrt(12)/9], [    0.0    ]]   *   [z^3]   +   [0.5]\nA   =    [[    0.0    ], [    0.5    ], [    0.0    ], [    0.5    ]]   *   [z^4]   +   [0.0]\n```\n\n3. 2-pass gaussian filter over the RGBA texture\n4. In view-space, shade the final pixel by sampling the RGBA texture and reversing the matrix:\n\n```\nb.x   =    [[  -(1/3)   ], [    0.0    ], [    sqrt(3)   ], [   0.0    ]]   *   [[r]   -   [0.5]]\nb.y   =    [[    0.0    ], [   0.125   ], [      0.0     ], [   1.0    ]]   *   [[g]   -   [0.0]]\nb.z   =    [[  -0.75    ], [    0.0    ], [0.75 * sqrt(3)], [   0.0    ]]   *   [[b]   -   [0.5]]\nb.w   =    [[    0.0    ], [  -0.125   ], [      0.0     ], [   1.0    ]]   *   [[a]   -   [0.0]]\n```\n\n5. Invalidate rounding errors by multiplying:\n    \n```\na = 0.15;\n//  a = 6 ⋅ pow(10, −5); // 4 * 16 bit ???\nb' = 1.0 − (a ⋅ b) + (a ⋅ pow(vec4(0.0, 0.63, 0.0, 0.63), T))\n```\n\n6. Put the final b value through the following function\n\nOriginal HLSL: \n\n```hlsl\nfloat ComputeMSMShadowIntensity(float4 b,float FragmentDepth) {\n    float L32D22=mad(-b[0],b[1],b[2]);\n    float D22=mad(-b[0],b[0], b[1]);\n    float SquaredDepthVariance=mad(-b[1],b[1], b[3]);\n    float D33D22=dot(float2(SquaredDepthVariance,-L32D22), float2(D22, L32D22));\n    float InvD22=1.0f/D22;\n    float L32=L32D22*InvD22;\n\n    float3 z;\n    z[0]=FragmentDepth;\n    float3 c=float3(1.0f,z[0],z[0]*z[0]);\n    c[1]-=b.x;\n    c[2]-=b.y+L32*c[1];\n    c[1]*=InvD22;\n    c[2]*=D22/D33D22;\n    c[1]-=L32*c[2];\n    c[0]-=dot(c.yz,b.xy);\n    float InvC2=1.0f/c[2];\n    float p=c[1]*InvC2;\n    float q=c[0]*InvC2;\n    float r=sqrt((p*p*0.25f)-q);\n    z[1]=-p*0.5f-r;\n    z[2]=-p*0.5f+r;\n    float4 Switch=\n    (z[2]\u003cz[0])?float4(z[1],z[0],1.0f,1.0f):(\n    (z[1]\u003cz[0])?float4(z[0],z[1],0.0f,1.0f):float4(0.0f,0.0f,0.0f,0.0f));\n    float Quotient=(Switch[0]*z[2]-b[0]*(Switch[0]+z[2])+b[1])/((z[2]-Switch[1])*(z[0]-z[1]));\n    return saturate(Switch[2]+Switch[3]*Quotient);\n}\n```\n\nGLSL port, comment out the fma() calls for OpenGL 4:\n\n```glsl\n/// Computes the Moment-shadow intensity for the fragment depth\n/// \n/// - b is RGBA value that you got from sampling the texture and \n///   putting it through the reverse matrix.\n/// - fragment_depth is the sample point at which you want to calculate\n///   the shadow\n/// - The return value is the shadow value\nfloat compute_msm_shadow_intensity(vec4 b, float fragment_depth) {\n    \n    // OpenGL 4 only - fma has higher precision:\n    // float l32_d22 = fma(-b.x, b.y, b.z); // a * b + c\n    // float d22 = fma(-b.x, b.x, b.y);     // a * b + c\n    // float squared_depth_variance = fma(-b.x, b.y, b.z); // a * b + c\n    \n    float l32_d22 = -b.x * b.y + b.z;\n    float d22 = -b.x *  b.x + b.y;\n    float squared_depth_variance = -b.x * b.y + b.z;\n    \n    float d33_d22 = dot(vec2(squared_depth_variance, -l32_d22), vec2(d22, l32_d22));\n    float inv_d22 = 1.0 - d22;\n    float l32 = l32_d22 * inv_d22;\n\n    float z_zero = fragment_depth;\n    vec3 c = vec3(1.0, z_zero - b.x, z_zero * z_zero);\n    c.z -= b.y + l32 * c.y;\n    c.y *= inv_d22;\n    c.z *= d22 / d33_d22;\n    c.y -= l32 * c.z;\n    c.x -= dot(c.yz, b.xy);\n    \n    float inv_c2 = 1.0 / c.z;\n    float p = c.y * inv_c2;\n    float q = c.x * inv_c2;\n    float r = sqrt((p * p * 0.25) - q);\n\n    float z_one = -p * 0.5 - r;\n    float z_two = -p * 0.5 + r;\n    \n    vec4 switch_msm;\n    if (z_two \u003c z_zero) {\n        switch_msm = vec4(z_one, z_zero, 1.0, 1.0);\n    } else {\n        if (z_one \u003c z_zero) {\n            switch_msm = vec4(z_zero, z_one, 0.0, 1.0);\n        } else {\n            switch_msm = vec4(0.0);\n        }\n    }\n    \n    float quotient = (switch_msm.x * z_two - b.x * (switch_msm.x + z_two + b.y)) / ((z_two - switch_msm.y) * (z_zero - z_one));\n    return clamp(switch_msm.y + switch_msm.z * quotient ,0.0,1.0);\n}\n```\n\n7. Multiply every value in the b vector with 0.98 to get rid of the remaining light leaks\n8. You're done!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffschutt%2Fmoment-shadow-mapping","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffschutt%2Fmoment-shadow-mapping","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffschutt%2Fmoment-shadow-mapping/lists"}