<p>I implemented cascaded shadow maps, where shadow maps for each cascade is rendered to one canvas:<a href="https://i.sstatic.net/3kRtI.png" rel="noreferrer"><img src="https://i.sstatic.net/3kRtI.png" alt="enter image description here"></a></p>

<p>The light projections for each cascade are calculated from the bounding spheres of camera sub-frustums:<a href="https://i.sstatic.net/XcPSK.png" rel="noreferrer"><img src="https://i.sstatic.net/XcPSK.png" alt="enter image description here"></a></p>

<p>The code for calculation visibility value:</p>

<pre><code>for (int i = 0; i &lt; 3; i++) {
    v_shadowCoord[i] = u_lightVPshifted[i] * vec4(a_WolrdPosition, 1);
}

highp float zTocompare = v_shadowCoord[iCascade].z - bias;
return texture(u_shadowMap, vec3(v_shadowCoord[iCascade].xy, zTocompare));
</code></pre>

<p>where <code>v_shadowCoord</code> for first cascade are in <code>(0, 1), (0, 0.33)</code> ranges, for second <code>(0, 1), (0.33, 0.66)</code>, third <code>(0, 1), (0.66, 1)</code>.</p>

<p><strong>The problem</strong> is that I get seams between cascades when anisotropic filtering is enabled. Here is the explanation of problem and suggested fix at the bottom of the page: <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ee416307(v=vs.85).aspx" rel="noreferrer">link</a></p>

<p>As I understand, for cascade borders the wrong mipmap is selected - the smallest. Because the texels for two neighboring vertices on scene come from different cascades and therefore screen-space derivatives are large. I tried the suggested fix from the site, still have same seam:</p>

<pre><code>highp vec2 uv = v_shadowCoord[iCascade].xy;
highp vec2 dx = dFdx (uv) / u_cascadeScales[iCascade];
highp vec2 dy = dFdy (uv) / u_cascadeScales[iCascade];
highp float zTocompare = (v_shadowCoord[iCascade].z) - bias;
return textureGradOffset(u_shadowMap, vec3(uv,  zTocompare), dx, dy, ivec2(0, 0));
</code></pre>

<p>I suppose I don't use the right input there. <code>v_shadowCoord</code> I described above, <code>u_cascadeScales</code> are scales of projection sizes in light view space relative to first one: I have <code>(1.0, 14.0, 300.0)</code>. Could you please explain how just scaling the derivatives will help selecting right mipmap and suggest what is wrong in the code? I expect that even with that scaling, values on the border will still be mapped to distant texels and wrong mipmap will be used.</p>

CC BY-SA 3.0
2017-05-23T16:59:55.583Z
Make sure you got clamp (vs wrap) texture address mode enabled when fetching from shadow map.
JarkkoL
CC BY-SA 4.0
2018-09-05T15:57:50.350Z
Besides what Jarkkol said, it's common to have a blend region between cascades. The blend region is more to hide the fact that the shadow map resolution changed, and would be more of a "cover up" of this seam than a proper fix, but FYI in case it helps!
Alan Wolfe
<opengl><shadow><shadow-mapping><mipmap>
Cascaded shadow maps. Seams between cascades
Zheden
# Cascaded shadow maps. Seams between cascades

(Asked by: username_0 on 2017-05-23)

I implemented cascaded shadow maps, where shadow maps for each cascade is rendered to one canvas:[![enter image description here](https://i.sstatic.net/3kRtI.png)](https://i.sstatic.net/3kRtI.png)

The light projections for each cascade are calculated from the bounding spheres of camera sub-frustums:[![enter image description here](https://i.sstatic.net/XcPSK.png)](https://i.sstatic.net/XcPSK.png)

The code for calculation visibility value:

```
for (int i = 0; i < 3; i++) {
    v_shadowCoord[i] = u_lightVPshifted[i] * vec4(a_WolrdPosition, 1);
}

highp float zTocompare = v_shadowCoord[iCascade].z - bias;
return texture(u_shadowMap, vec3(v_shadowCoord[iCascade].xy, zTocompare));

```

where `v_shadowCoord` for first cascade are in `(0, 1), (0, 0.33)` ranges, for second `(0, 1), (0.33, 0.66)`, third `(0, 1), (0.66, 1)`.

**The problem** is that I get seams between cascades when anisotropic filtering is enabled. Here is the explanation of problem and suggested fix at the bottom of the page: [link](https://msdn.microsoft.com/en-us/library/windows/desktop/ee416307(v=vs.85).aspx)

As I understand, for cascade borders the wrong mipmap is selected - the smallest. Because the texels for two neighboring vertices on scene come from different cascades and therefore screen-space derivatives are large. I tried the suggested fix from the site, still have same seam:

```
highp vec2 uv = v_shadowCoord[iCascade].xy;
highp vec2 dx = dFdx (uv) / u_cascadeScales[iCascade];
highp vec2 dy = dFdy (uv) / u_cascadeScales[iCascade];
highp float zTocompare = (v_shadowCoord[iCascade].z) - bias;
return textureGradOffset(u_shadowMap, vec3(uv,  zTocompare), dx, dy, ivec2(0, 0));

```

I suppose I don't use the right input there. `v_shadowCoord` I described above, `u_cascadeScales` are scales of projection sizes in light view space relative to first one: I have `(1.0, 14.0, 300.0)`. Could you please explain how just scaling the derivatives will help selecting right mipmap and suggest what is wrong in the code? I expect that even with that scaling, values on the border will still be mapped to distant texels and wrong mipmap will be used.

**Tags:** opengl, shadow, shadow-mapping, mipmap

> **Comments:**
> **username_1**: Make sure you got clamp (vs wrap) texture address mode enabled when fetching from shadow map.  
> **username_2**: Besides what username_1 said, it's common to have a blend region between cascades. The blend region is more to hide the fact that the shadow map resolution changed, and would be more of a "cover up" of this seam than a proper fix, but FYI in case it helps!  

---

## 0 answers

