Original Post
I came up with this procedure while working on my portfolio. It occured to me that there is no reason shadowmapping should not offer 100% correct shadows in *appearance* because in theory we should be able to map every shadowmap pixel to a screen pixel, or in other words, give objects that are bigger on screen more shadowmap space. I came up with the idea I present here, then dropped it in favor of light space PSM because it is slower than LiSPSM, but the main drawback of any kind of PSM is that those procedures revert to naive shadowmapping when the light is behind the viewer. For large scenes this means the same problems as with naive shadowmapping (for instance, an outdoor scene with a setting sun, etc). So anyway.. moving on.. my idea is to simply alter the shadowmap texture coordinates based on a vertex's distance from the user viewpoint. The disadvantage of this scheme is that it involves a few more operations per vertex that result in around 3%-5% slower vertex throughput per shadowmap than PSM (or LiSPSM) but the advantage is that shadows near the user are always increased in detail, even if the view direction coincides with the light direction. My procedure involves (for directional lights, since they need the most help): 1) Optimize the light frustrum to fit the view frustrum as close as possible on the 4 sides, and to enclose all possible casters by setting the near plane just in front of the first caster in the scene. 2) Pass in the transformed user view location to the vertex shader. 3) Within the shader, scale each vertex based on its distance from the transformed view origin. This effectively biases the vertices based on their distance from the user *as projected onto the shadowmap plane*. The equation I use is similar to the z transform (since the goal is to mimic the size falloff that the z transform produces in scene geometry): _shift.x = oPos.x - g_frustrumCenter.x; _shift.y = oPos.y - g_frustrumCenter.y; _weight = g_shadowDistanceWeight / (g_shadowDistanceWeight - 1); if(_shift.x >= 0) { oPos.x = (1 - g_frustrumCenter.x)*(_weight - _weight / (_shift.x * (g_shadowDistanceWeight - 1) + 1)); } else { oPos.x = (1 + g_frustrumCenter.x)*(-_weight - _weight / (_shift.x * (g_shadowDistanceWeight - 1) - 1)); } if(_shift.y >= 0) { oPos.y = (1 - g_frustrumCenter.y)*(_weight - _weight / (_shift.y * (g_shadowDistanceWeight - 1) + 1)); } else { oPos.y = (1 + g_frustrumCenter.y)*(-_weight - _weight / (_shift.y * (g_shadowDistanceWeight - 1) - 1)); } oPos.x += g_frustrumCenter.x; oPos.y += g_frustrumCenter.y; Here oPos is the vertex location (shadowmap tex coord) ,_shift is a temporary variable, and g_frustrumCenter is the transformed user viewpoint. g_shadowDistanceWeight is the "far plane" in the z transform equation, with the "near plane" always being set to 1 for simplicity. That is, if this weight is set to a high number (say, 100), the biasing effect will be very high. If it is set to 2, no change in coordinates takes place. This shader fragment essentially does this: a) Translates the vertex so that the user viewpoint would be at (0,0). b) Scales the vertex by the weight - 1, then adds 1. This puts all vertex values between 1 and the "far plane" value. c) Applies the z transform type of scaling to the vertices. d) Scales the vertex back to the maximum possible coordinate it could have had. e) Translates the vertex by putting the view location back where it was. If anyone has a better idea on how to do this, please tell me, because as you can see this shader fragment doesn't move as fast as I would like it to.