   "Swimming" shadow edges: can they be eliminated?
  To get any kind of decent resolution on a shadow map with a directional light, you have to move the light's position with the camera position (so you're not wasting shadow map texels on geometry that can't be seen).The problem with this approach is that, as you move the camera, the light position moves too and the rasterization of the shadow map is affected, causing the shadow edges to "swim". The higher the resolution of the shadow map, of course, the less obvious this is, but even at very high resolutions it's still perceptible.I wonder if there's a way to avoid this artifact. Theoretically, if the light's POV were rasterized in exactly the same configuration at point P as at point Q - that is to say, at point Q, the shadow map is an exact, texel-perfect duplicate of the shadow map at point P, the only difference being that it's scrolled to a new position - then we could move the light's position only in increments of P-Q. That would let us avoid the "swimming" effect.However, I'm damned if I can find a way to calculate that increment value. I would imagine that the light vector (or its magnitude, perhaps?), the camera vector and the shadow map resolution are critical parameters, but if there exists a formula to derive the value I need from those things, I'm clueless as to what it could be.Any thoughts? I hope my explanation is comprehensible. Basically, if a scene is orthographically projected at point P and rasterized, does there exist a point Q at which the rasterization of the scene will be identical?   
  I just finished doing that myself.  I never found a particularly elegant mathematical solution to it, although one probably exists.  What I did was simply create two vectors that were the light's x and z axes in world space (currently I force y to be 0, which will have to change, but it shouldn't be hard to account for that as well).  Then each time I calculate the focus point of the shadow map, I force it to move from its previous position only along whole increments of those two vectors.  By correctly choosing the magnitude of the vectors it ensures that I'm always moving the shadow map around in whole texels so that one section of world space always maps to exactly one texel (rounding errors excepted of course).I simply do a fairly brute force search to find the nearest point that falls on these vectors from the raw point that gets passed in.  I start at the previous frame's shadow map focus and move both directions along the two vectors (one at a time) until the error (distance to the raw point) is greater than the previous error.  It's not perfectly accurate, but it's easily close enough because I'm already fading the edges of the shadow map into a static much lower res global shadow map, so you can't see if it jumps around a bit.If you're interested I can post a better explanation of this when I get home, possibly complete with diagrams.  Probably the hardest part was figuring out what the magnitude of the vectors needed to be, the rest I implemented in about an hour once it occurred to me to do it this way.   ;  PSM (and in particular LSPSM) with a high enough resolution for the shadow map, can mostly eliminate swimming by avoiding undersampling.   ;Quote:Original post by nemebeanI just finished doing that myself.  I never found a particularly elegant mathematical solution to it, although one probably exists.  What I did was simply create two vectors that were the light's x and z axes in world space (currently I force y to be 0, which will have to change, but it shouldn't be hard to account for that as well).  Then each time I calculate the focus point of the shadow map, I force it to move from its previous position only along whole increments of those two vectors.  By correctly choosing the magnitude of the vectors it ensures that I'm always moving the shadow map around in whole texels so that one section of world space always maps to exactly one texel (rounding errors excepted of course).I think we're more or less on the same wavelength here. This is what I've been trying to do for a few days now. The problem is finding that increment. I've actually managed, through trial and error, to find fairly suitable increments for various specific light directions - I just can't see a consistent mathematical correlation between those values and their respective light directions.I suppose I could just go through all possible light directions (I doubt I'd ever need to rotate the sun in gradations finer than one degree) and manually find a good increment for each, but it'd be a big headache and a horrible hack. In addition, the values I've found aren't quite perfect - the map still "jumps" slightly when the light position changes.Quote:I simply do a fairly brute force search to find the nearest point that falls on these vectors from the raw point that gets passed in.  I start at the previous frame's shadow map focus and move both directions along the two vectors (one at a time) until the error (distance to the raw point) is greater than the previous error.  It's not perfectly accurate, but it's easily close enough because I'm already fading the edges of the shadow map into a static much lower res global shadow map, so you can't see if it jumps around a bit.This may be slightly different from what I've been trying, but I'm not sure. I've simply been rounding the light position to the nearest multiple of the increment. Is that the same as your approach?Incidentally, I'd've thought the shadow map resolution would be a factor in these calculations, though you don't mention it. As the resolution increases, the error must reduce, right?Quote:If you're interested I can post a better explanation of this when I get home, possibly complete with diagrams.  Probably the hardest part was figuring out what the magnitude of the vectors needed to be, the rest I implemented in about an hour once it occurred to me to do it this way.I'd be interested in seeing your approach in more detail. How do you calculate the magnitude of the vectors, and how exactly do you do the "brute force search" you mention?   ;Quote:Original post by SneftelPSM (and in particular LSPSM) with a high enough resolution for the shadow map, can mostly eliminate swimming by avoiding undersampling.So far, I've been reluctant to look too deeply into PSM because of the special cases I keep hearing about. Would it make that much difference with orthographic maps, anyway?   ;  Well, I'll give a better explanation a shot here.  First, finding the vector magnitudes.  Essentially this involves finding the area that is covered by a single pixel of the shadowmap.Let's start with the easy one, the light's X axis.  This one is simply the size of your shadow map in world space (basically right - left of your orthographic projection) divided by the X dimension of your shadowmap.  You're partitioning the area you're rendering to the shadow map into [shadowmap dimensions] chunks, and this will be the size of those chunks.  Note that this one is not affected by the light direction or location.  Let's call this value WorldPerOneShadow because we'll be using it again.Finding the Z axis magnitude is a little more involved.  If we're lucky I'll be able to remember the math to do it.:-)  In reality, it's just solving a triangle, so if you managed high school geometry you can do this (it took me a while to remember my high school geometry, but I digress).  Basically you want to think of the following picture:http://www.nemebean.com/stuff/orthotri.pngEssentially that's looking at the volume of space covered by one texel of your shadowmap from the side.  The value we're looking for is d at the bottom.  That will be the size we need for our z axis vector.  In order to get it, you will need to find the angle between your light's direction vector and the vector parallel to the ground (we can get away with treating all shadow receivers as being flat because of the ortho projection).  Because of the way my frustum culling works, I actually find both the rotation around the x axis and around the y axis to transform the vector (0, 0, -1) to my light's direction vector.  I imagine you could just get the x axis rotation for this purpose though.  Here's the code I use to get both rotations (slightly edited for clarity):Vector3 rots;Vector3 dir = light.GetDir();Vector3 start(0, 0, -1); // Initial view directionVector3 neg;dir *= -1; // Dir is where it's coming from, we need where it's going todir.y = 0;dir.normalize();rots.y = acos(start.dot(dir)) * 180.f / 3.14159265;neg = start.cross(dir);if (neg.y &gt;= 0)  // Cheap trick to check which quadrant we're in   rots.y *= -1;dir = light.GetDir();dir *= -1;GraphicMatrix rotm;rotm.rotatey(rots.y);start.transform(rotm);rots.x = acos(start.dot(dir)) * 180.f / 3.14159265;return rots;The value you need is rots.x.  Hopefully it's obvious enough what everything does.  Now you just need to plug the numbers into the law of sines, which yields sin(rots.x) / WorldPerOneShadow = sin(90) / d.  Therefore d = WorldPerOneShadow / sin(rots.x).  Note that I refer to d as totalsize below.Now, when your light direction happens to fall right on an axis (as in this picture: http://www.nemebean.com/stuff/orthogood.png) you can just round your shadow map focus to be the nearest multiple of these values, but unfortunately you run into trouble when you try to use pretty much anything else, as in this picture: http://www.nemebean.com/stuff/orthoprob.pngIf you think of each of the little squares as mapping to one texel of the shadow map (they don't, but let's pretend), you can see why you can't just round to those values - they don't match the texels in the problematic image.  Because I took that one at a 45 degree angle you could probably find a scaling factor that would work, but consider what happens if you're at a 40 degree angle, and now your texels don't fall on any nice axis-aligned vectors, the value you're allowed to use for x is dependent on the value you use for z, and vice versa.  Hence why I think I wasn't able to come up with a nice elegant mathematical way of doing this.Fortunately, we still know the size a texel covers in world space (our two magnitudes above), so we can just force our shadow map focus locations to fall on coordinates that we can reach using whole multiples of those vectors.  And it turns out I lied, you will need rots.y from above because that's how you get these two vectors.  The following code will get you your axis vectors:Vector3 lightxaxis(0, 0, -1);Vector3 lightzaxis(0, 0, -1);float shadowmapsizeworld = 200;float worldperoneshadow = shadowmapsizeworld / shadowmapsize;rots.x *= PI / 180.f;  // Need it in radiansfloat totalsize = worldperoneshadow / sin(rots.x);// Generate the light's axes in world spaceGraphicMatrix rot;rot.rotatey(rots.y);lightzaxis.transform(rot);rot.rotatey(90);lightxaxis.transform(rot);lightxaxis *= worldperoneshadow;lightzaxis *= totalsize;Once I have those axes, I just do the brute force search for the best location to use:// Find out where we're looking and focus the shadow map on that, a little rough ATMrot.identity();rot.rotatex(player[0].pitch);rot.rotatey(player[0].facing + player[0].rotation);rot.rotatez(player[0].roll);Vector3 look(0, 0, -100);look.transform(rot);static Vector3 oldshadowpos = player[0].pos + look;Vector3 rawlook = player[0].pos + look;Vector3 lookat = oldshadowpos;rawlook.y = lookat.y = 0;float error = lookat.distance2(rawlook);// Just noticed that I never actually use error.  Oh well.:-)float olderror = error;while ((lookat + lightxaxis).distance2(rawlook) &lt; olderror){   lookat += lightxaxis;   olderror = lookat.distance2(rawlook);}while ((lookat - lightxaxis).distance2(rawlook) &lt; olderror){   lookat -= lightxaxis;   olderror = lookat.distance2(rawlook);}while ((lookat + lightzaxis).distance2(rawlook) &lt; olderror){   lookat += lightzaxis;   olderror = lookat.distance2(rawlook);}while ((lookat - lightzaxis).distance2(rawlook) &lt; olderror){   lookat -= lightzaxis;   olderror = lookat.distance2(rawlook);}oldshadowpos = lookat;// Generate our shadow map using the coordinates we just gotGenShadows(lookat);And that's pretty much it.  Your regular shadow mapping code should be fine as long as the coordinates it's looking at are found in this way.So now that I've no doubt put everyone in this thread to sleep, I'll stop and just say that if I wasn't clear enough about anything (which is most likely the case) just ask.:-)   ;  Why that complicated? Just round the shadow map position to the texel size in world space. An pseudo code example, overly verbose// transform base position into light spaceVector3 pos = mLightRotationMatrix * basePosition; // with basePosition being the centre of left lower edge or whatever you use to place the shadow map// determine texel size in light spacefloat texelSize = shadowMapAreaSize.x / float( shadowMapResolution);// in light space y is assumed to be the light direction and x|z the shadow map plane// So we just have to align x and z on texel borderspos.x = floor( pos.x / texelSize) * texelSize;pos.z = floor( pos.z / texelSize) * texelSize;A very rough example but I hope you get the point. It basically achieves the same effect as your code - aligning the shadow map at texel borders to avoid egde flickering. It's just way simpler because it works in light space. If you need the shadow map base position and world size in world space you'll have to transform it back. I also assumed the shadow map size in world space to be square to make the example simpler but you should have no problem extending it to your needs. Just make sure the shadow map area is constant in size over time, it doesn't work else.Extended (working) code snippet on request.Bye, Thomas   ;  So in your example pos.x and pos.z are in light space at the end?  And then you have to transform them back into world space using the LightRotationMatrix inverse before you give them to the shadow map renderer as the focus point of the shadow map?  Or is there some way to directly use the light space coordinates to set up the light modelview matrix?   ;  You can directly build the light view matrix from that. Take the light rotation matrix and apply the translation afterwards so that the light's view matrix first rotates and then translates by the position vector. This way the translation should be in light space already and you can directly use the shadow map position in light space to build the matrix.Transforming the position back to world space is fine as well... the light's rotation can simply be inverted by transposing it, it's just a rotation after all.Bye, Thomas   ;  I think CSM is probably a better solution for real life.  PSM only works in academic papers.   