24.03.2003:
first working implementation of perspective shadow maps. shadows of far away objects are heavily distorted and are popping noticeable when moving around.
30.03.2003:
i run a view tests with dynamical generated standard shadow maps (ssm). the cause for the ugly popping artifacts of the psm texture is a general problem of dynamically generated view point dependent textures: if the view point changes, the dynamic texture chances and the rasterisation chances too (hence hardly any popping at rotations, only at translations). the projection of the texture increases the rasterisation differences. the solution is to use high resolution dynamic textures or take better usage of the resolution of the dynamic texture. role of thumb: projected texel <= pixel -> no artifacts
04.04.2003:
psm have enormous problems with scenes with a high depth range. a near/far = 1/100 results in unsatisfactory resolution of far away shadows. this favors fly by scenarios, but disfavors walking scenarios.

10.04.2003:
another jet unsolved problem is the vanishing of the shadow map if you turn to certain angles to the light source direction.
15.04.2003:
the cause for the vanishing of the shadows is caused by the inversion of the light source mentioned in the paper. if the inversion is correctly implemented the vanishing should stop.
17.04.2003:
i've problems implementing the inversion of the light source. i invert the light source (-light source direction) and i invert the depth map test, but the results are not correct.
18.04.2003:
the solution for the problem: i've got to invert polygon offset too.
05.06.2003:
i have problems with the objects which intersect the light view frustum. these project black stripes outside the light frustum. i tried to use a texture border, but this is not supported in a p-buffer.
06.06.2003:
glViewport and glScissor are not functioning properly used with a p-buffer. if the resolution is different from the values used in the two commands a software rendering ( Detonator 30.82: @ 1 FPS) or access violation(Detonator 43.45).
12.06.2003:
to avoid the blackout of all things intersecting the frustum (caused by GL_CLAMP_TO_EDGE or GL_CLAMP) of the light source (and the stripes behind the intersecting border of the light frustum), i now use a stencil buffer inside the p-buffer to make a border of “white”, so no intersecting object causes black stripes outside the light view frustum. the performance penalty is marginal.
30.06.2003:
to make the shadows more realistic i now use nvparse and a register combiner program to calculate the texture unit blending.
18.-20.09.2003:
all light source parameters are stored in a text file. additionally i have created a scene file, which contains the information which scene is currently active.
22.-25.09.2003:
view frustum-culling is working correctly. ability of culling by general frustums is implemented too. (general frustums are lists of planes. each plane can be active or inactive).
25.-26.09.2003:
new register combiner program for shadow and light calculations to use fog.
{
const0 = (0.6,0.6,0.6,1);
rgb {
discard = const0;
discard = tex1;
spare0 = sum();
}
}
{
rgb {
spare0 = tex0*spare0;
}
alpha {
spare0 = tex0.a*col0.a;
}
}
final_product = spare0*col0;
out.rgb = lerp(fog.a,final_product,fog.rgb)+col1;
out.a = spare0;
01.-03.10.2003:
dynamic adaptation of the near-clipping-plane increases quality enormous. the paper hasn't emphasized the importance of this fact. they have read out the z-buffer to get the nearest z-Value, but this approach is not practical. i use a geometrical solution: i use my hierarchical AABB representation of my scene graph to calculate the distances of each AABB to the camera position. i just need to consider the AABBs inside the view frustum and can use enclosing AABBs to reject object-groups as early as possible. this step results in a near Z value which is most of the time much bigger than a static near Z tends to be. In the Level2 scene this results in a near Z of 10 when you are walking, in contrast to 1 (static). this results in enough psm resolution for far away shadow-caster to. one problem remains: if you are standing directly in front of a shadow-caster and you can see a far away shadow-caster too, you have poor psm resolution for the far away shadow-caster. to restrict the near Z to be above 0 i have created a static minimal near Z, which is defined in the light source datafile. for level2 this value is 0.5, for city 1 and for wood 1.

03.10.2003:
Thatcher Ulrich (http://tulrich.com) publishes a rater pessimistic text:
<...So the big hassle with PSM is that it only really works in some restricted cases, where the light is coming from the sides or above/below, and so occluders aren't casting big nasty shadows towards the viewpoint, or from behind the viewpoint into the scene, etc. Which makes it useful for some very specific applications (e.g. it's OK for character shadows in a 3rd-person follow-cam game), but practically useless for general-purpose full-scene shadowing...> (http://tulrich.com/geekstuff/psm_notes2.html)
that's great :-((
04.10.2003:
the use of multiple shadow maps, layered in depth could increase depth quality. Additionally they could provide filtering capabilities if overlapping is allowed. this would burn one texture unit for one shadow map. A mixture of one perspective shadow map for the near terrain and one uniform shadow map for the far off terrain is not working to well.
07.-09.10.2003:
double perspective shadow maps are working better than expected. they solve my problem with the lack of accuracy for scenes with deep shadows. each frame a test far/near >= 4 is performed. in this case i use two psms: one for near to far/4 and another one for far/4 to far. results look quit pleasing.
10.10.2003:
now i use two register combiner programs: one for the case with one psm and another one for the case with two psms to include filtering together the shadow maps.
{
const0 = (0.6,0.6,0.6,1);
rgb {
discard = const0;
discard = tex1*tex2;
spare0 = sum();
}
}
{
rgb {
spare0 = tex0*spare0;
}
alpha {
spare0 = tex0.a*col0.a;
}
}
final_product = spare0*col0;
out.rgb = lerp(fog.a,final_product,fog.rgb)+col1;
out.a = spare0;


11.10.2002:
a linear z coordinate distribution(as in the case of a w-buffer) could help to increase the psm resolution for certain neuralgic cases. at least the second perspective projection (pl) should be calculated with a non hyperbolic z-distribution. however the calculation does not fit into a 4x4 matrix, without tinkering with the perspective division. this could be solved with a vertex program.
13.-15.10.2003:
(Directional Light) I try to solve the problem of the shadow-caster from outside the view frustum, but with a shadow in the view frustum.
1. solution: i extrude my AABBs in the direction of the light source. this results in a 6-sided prism with infinite height. i just need to intersect this prism with the viewing frustum. if the intersection result equals zero than the object has no visible shadow. in the other case the view frustum must be shifted back to contain this object.
2. solution: i extrude the viewing frustum in the direction of the light source. this also results in a 6-sided prism, but with a bottom cup 3-planes in most cases.

sadly a lot of cases are quit unpleasant: when the light comes from behind, i have no simple rule to create a prism. this one

or that one

-> if I use the view frustum method several different cases must be handled.
16.10.2003:
i try to implement the first solution strategy. first i have to calculate the silhouette of the projected bounding box (a adaptation of http://www.acm.org/jgt/papers/SchmalstiegTobler99 gives good results). 2 results can occur:
the general case (3 visible sides) 6 visible edges

or the special case of a light direction in main axe direction (1 visible side) 4 visible edges


or
in projected main axe direction (2 visible sides) 4 visible edges

To
calculate the intersection of the view frustum and the projected BB i
use projective geometry. i view the scene from the light source,
which gives us an orthogonal projection for directional light
sources. so the intersection calculation is just a 2 dimensional
intersection calculation of two 6-sided (or one 6-sided, one 4-sided,
or 2 4-sided) polygons. note that in every case the two polygons are
convex.
17.10.2003:
created unit Tester to test AABox::computeSilhouette for correctness.
18.10.2003:
created test cases for AABox::computeSilhouette.
20.10.2003:
i have to calculate the silhouette of the view frustum. therefor i program a general Frustum::computeSilhouette. but at first i have to deduce if the AABB is in front of the view frustum. from the silhouette calculation i get a 3-dimensional set of points. after the projection into the light space i check if the maximum z-vertex of the silhouette of the AABB is before the minimal z-vertex of the view frustum (in case of the light coordinate system looking down the -Z axe)
21.10.2003:
new computer :)
27.10.2003:
a general Frustum::computeSilhouette is not efficient. i'm programming a Manipulator:: calcViewFrustumSilhouette function. this function has a similar form as AABox::computeSilhouette. I start with the test cases for Manipulator::calcViewFrustumSilhouette.
28.-29.10.2003:
continue the test cases.
30.10.2003:
another preprocessing step proves to be convenient: to check if the AABB can possible be between the light source and the the view frustum. before the AABB silhouette calculation i test each of the 8 edges of the AABB if at least one of them lies in front of one of the visible planes of the view frustum. This planes are already known, because of the silhouette calculation of the view frustum. For instance with probable 3 visible view frustum planes this results in 24(3*8) plane equation evaluations.
03.10.2003:
now i check hierarchical for each AABB: is the AABB between ,light and view frustum. If it is than i project the AABB silhouette to 2D and check for a polygon-polygon intersection with the projected view frustum. If this intersection exists than the object inside the AABB is a shadow caster from outside the frustum. so if this is a leaf move the frustum back until the AABB is inside the frustum, or if it's a parent node, traverse on. if one of the tests fails, stop traversing this node further.
needed data:
is Between: boolean inFrontOfVisiblePlanes(visible view frustum planes, AABB);
[project to 2D: 4 or 6 Vector2 project2D(viewDir, AABB);]
exists-2D-polygon-polygon-intersection: boolean(view frustum polygon, AABB polygon);
[Real moveBack(view Frustum planes,AABB camera space point wise)]

moveBack:
AABox points to camera space; calculate P'
for each of the five planes; P' is just the in -z-axis direction
shifted version of P( P'=(p.x,p.y,(d-n.x*x-n.y*y)/n.z); calculate the
difference between P.z and P'.z(p.z-p'.z); consider only positive
values; take the biggest of the five differences. this step is only
for leaf nodes needed.
04.11.2003:
projection to 2D with a rotation of the light direction vector, to the z-axe, like glLookAt. I generate a normal to the light direction vector dir: up= (dir.y -dir.x 0) and a left vector left=unitCross(dir,up). With this Coordinate system i transform each 3D point to a 2D point. Note: a rotation doesn't change the convex state of a polygon.
05.11.2003:
check if a convex polygon-polygon intersection occurs; so long i've only found algorithms to compute the intersection points, like Joseph O'Rourke's (in Computational Geometry in C). i will try the simple approach: a separating line: for each line of both polygons i check if all points of the other polygon lie on one side. If this is the case the line is a separating line and no intersection can occur(see gamasutra: Crashing into the New Year: Colliding in the Third Dimension (http://www.gamedev.net/reference/articles/article1115.asp). my polygon::separable function assumes the Polygon is in clockwise winding specified(to test such things as cw or ccw ordering or convex, concave see: http://debian.fmi.uni-sofia.bg/~sergei/cgsr/docs/clockwise.htm), so i can use the polygon::leftofLine to check if a polygon is outside of a polygon-border-segment.
06-07.11.2003:
test cases polygon::separable; possible failure point: manipulator::computeSilhouette works in a right handed coordinate-system(like the GL coordinate system), so the cw results are cw in GL, but AABox::computeSilhouette works in a left handed coordinate system so the cw results are ccw in GL, so if you use AABox::computeSilhouette with GL reverse the processing order of the vertices for a cw result in a right handed coordinate system(like in GL)
07.11.2003:
Another critical note about psm was published in the internet, on the page for Trapezoidal Shadow Maps http://www.comp.nus.edu.sg/~tants/tsm0.91/psm-writeup.html
10.11.2003:
i'm starting to implement the moveBack functions.
12.11.2003:
my moveBack function moves too far back. i hope this is not a general flaw in my approach. now i'm searching for an implementation error.
14.11.2003:
the problem is a flaw in my moveBack approach: i try to move back till all the vertices of the AABB are inside the view frustum(red AABB).

this
is not required. in numerous cases this is a waste. we need only to
assure that the red part of the AABB is inside the view frustum.


Just
the part of the AABB which can throw a shadow has to be included. the
difference between the coarse scheme
implemented by me and the more precise solution is most noticeable
with big AABBs with small overlaps of
projected view frustum and projected AABB.
possible solutions:
more accurate bounding volumes like hierarchical AABB: subdivide big AABBs into smaller AABBs, so the results are more accurate
geometrical intersection calculation on the actual object or on the bounding volume
introduce some kind of overlapping importance: objects far away or just a little bit overlapping are ignored
objects which would result in a big move back value are ignored
16.11.2003:
another preprocessing step: due to the problems with the moveBack value, i just calculate a moveBack value for light directions outside the view frustum border vectors(red lines). If the light vector is between the view frustum border vectors facing towards the eye, all shadows are already inside the view frustum volume, so no moveBack is needed.

17.11.2003:
i try to implement a geometrical intersection calculation on the bounding volume(AABB). a practicable solution calculates the 2D intersection points (2D convex polygon-polygon intersection http://www.integis.ch/documents/ISem_Opprecht_Overlay_2002-02-28.pdf oder siehe auch 05.11.2003) and projects them back to 3D. these points are now included into the view frustum with the normal moveBack algorithm.


18.11.2003:
i've adapted O'Rourkes implementation of his algorithm(O(n*m)) (Computational Geometry in C (ftp://cs.smith.edu/pub/compgeom/SecondEdition/Ccode2.tar.gz)) to work with floating-point points (not integer points) and to produce a polygon as output (not postscript strings) and ported it to C++. additionally i've created a visualization tool for showing me the polygons as a help in generating the test cases.

19.11.2003:
after implementing test cases i've found an “error” in o'rourke's code. a type of special cases is not implemented and my AABox and view frustum polygon intersection is often of this type.

20.11.2003:
i read the paper of Toussaint's algorithm (O(n*m))(http://cgm.cs.mcgill.ca/~godfried/publications/convex.intersection.pdf). the paper suggests it has a higher overhead as o'rourke's, which is a problem for my low complex polygons(4-6 points). (see also: http://cgm.cs.mcgill.ca/~orm/mergech.html or http://www-cgrl.cs.mcgill.ca/~godfried/research/calipers.html or http://www-cgrl.cs.mcgill.ca/~godfried/teaching/cg-projects/97/Plante/CompGeomProject-EPlante)
21.11.2003:
i've found that with ccw polygons o'rourke's code produces correct results. i will therefor use the adapted implementation of his algorithm.
22.11.2003:
i've to change the code further to handle completely enclosed polygons. I've decided to leaf the intersection code as a mathematical intersection, so enclosed polygons are handled outside.
23.11.2003:
i've included the code. The results are better than the simple approach, but are still bad. i tried to handle simple objects (for example < 32 vertices) separate vertex wise not with their bounding box, to gain more exact results, but a simple inside outside test for vertices leads to wrong results, because the real intersections are likely to occur between the vertices, especially with simple objects with big vertex distances. for example in the following graphic the red points are the correct intersection points, but this approach only returns the green point. in this case no shadow would be created at all.

24.11.2003:
most noticeable shadow clipping problems are with shadows near my position.
if the top points of the AABB in the light space are farther away from me than the bottom points they cannot cast a a shadow on to me.

26.11.2003:
if you cast a ray from your position in inverted light direction and you intersect with a AABB than the AABB casts a shadow on to your position(your position is inside the shadow volume). the same is true for every other point. If you take a few (~20) sample points in the view frustum near the eye point and you test for ray AABB intersection you have a good estimate for shadow casting objects with their shadows near the observer (ray caster method).
27.11.2003:
the before mentioned rays are like shadow rays in a ray caster. take the backside intersection point(green) as an estimate point to be included into the moved back view frustum volume.

29.11.2003:
the correct intersection point would be the backside intersection point of the nearest (to the light source) AABB(magenta), but this would cause a greater move back, so hopefully the shadow of the nearest object (to the sample point) will result in a sufficient move back(green), without clipping artifacts.

02.12.2003:
i've found an error in my move back algorithm
//for each intersection point in camera space calc moveBack
Real dist(0.0);
for(uint i = 0; i < interP.size(); i++) {
const Vector3& p = interP[i];
//for left, right, bottom, top, near plane
for(uint j = 0; j < 5; j++) {
const Geometry::Plane& plane = getPlaneUnChecked(j);
Real planeZ = plane.getZ(p.x,p.y);
Real dist2(p.z-planeZ);
if(dist2 > dist) {
dist = dist2;
}
}
}
return dist;
interP points are now in world space but the plane equations from the frustum are in world space, so the correct algorithm is:
//for each intersection point in world space calc moveBack
Real dist(0.0);
const Vector3& dir = getPlaneUnChecked(5).getN(); //near plane n vector is view dir (normalized from Plane)
for(uint i = 0; i < interP.size(); i++) {
const Vector3& a = interP[i];
//for left, right, bottom, top, near plane
for(uint j = 0; j < 5; j++) {
const Geometry::Plane& plane = getPlaneUnChecked(j);
const Vector3& n = plane.getN();
Real denom(n.dot(dir));
if(denom == 0.0f) {
continue;
}
Real t((plane.getD()-n.dot(a))/denom);
if(t <= 0.0f) {
continue;
}
//if |dir| == 1 (normalized)
if(t > dist) {
dist = t;
}
}
}
return dist;
math: i calculate the intersection between each view frustum plane and the intersection points(a) ray casted in view direction(dir), because the move back value is in view direction.
plane equation: n.x*x + n.y*y + n.z*z = d
ray parameter form: p(x) = a.x + t*dir.x; p(y) = a.y + t*dir.y; p(z) = a.z + t*dir.z;
combined:
n.x*(a.x + t*dir.x) + n.y*(a.y + t*dir.y) + n.z*(a.z + t*dir.z) = d ->
n.x*a.x + t*n.x*dir.x + n.y*a.y + t*n.y*dir.y + n.z*a.z + t*n.z*dir.z = d ->
t* (n.x*dir.x + n.y*dir.y + n.z*dir.z) = d - (n.x*a.x + n.y*a.y + n.z*a.z) ->
t* (n dot dir) = d – (n dot a) ->
t = (d – n dot a) / ( n dot dir)
if dir is normalized parameter t equals the distance between a (t == 0) and the intersection point p with the plane ( t == (d – n dot a) / ( n dot dir) )
04.12.2003:
all move back methods decrease the quality of the shadow map further. so it should be possible to avoid move back completely and incorporate the clipped shadow map parts somehow. underneath a moveBack of 7.5 is shown.

05.12.2003:
engine directory structur cleanup: partition into engine directory and applications; divide helper into seperate tool header files and data structures.
06.12.2003:
i've tried to use auto mipmap generation for depth textures:
void DepthPBuffer::init(const bool stencil) {
...
// request mipmap support
iattributes[++niattribs] = WGL_MIPMAP_TEXTURE_ARB;
iattributes[++niattribs] = GL_TRUE;
// Create the p-buffer.
hDepthPBuffer = wglCreatePbufferARB(wnd_hdc,format,width,height,iattributes);
and
Shadow::Shadow(...) {
...
glTexParameteri(GL_TEXTURE_2D,GL_GENERATE_MIPMAP_SGIS,GL_TRUE);
}
but this results in software rendering (Detonator 45.23 @ 0.9 Frames/sec), because on GeForce just GL_RGB8, GL_RGBA8 and GL_RGB5 are hardware accelerated and no visible gain in quality. so i just use it for normal textures(like GL_TEXTURE_2D) if supported.
07.12.2003:
if move back needed use a 3rd shadow map(standard shadow map) from near plane backwards. the blue frustum is the view frustum and the red box is the view frustum for a standard shadow map. note that the view frustum is the bounding box of the shadow casters behind the observers point of view.

08.12.2003:
for a more exact result the standard shadow map view volume should incorporate just the extreme shadow casting points of the shadow casters behind the point of view of the observer(green and blue points).

the
green points are generated from the 2D intersection calculation in
the light space, but the blue point is only traceable with a 3D
intersection calculation.
09.12.2003:
the before mentioned blue point is not on a extreme position in the xy-plane (openGL coordinate system), but on the z direction. it is the nearest point and therefor needed for the near plane calculation. in standard shadow maps and in this setting (with the casters behind the point of view, this should not generate a view with great z-depth) the z-precision is most likely no limiting factor, so it is sufficient to use a point of the AABB for the near clipping plane calculation(magenta point).

10.12.2003:
cleanup of the DirectionalLight class;
11.12.2003:
cleanup of all Light related classes (Light, DirectionalLight, Shadow)
14.12.2003:
memory leak search :( glutMainLoop exit causes the program to terminate in a unordered fashion, to delete all my engine objects i had chosen an atexit(terminationHandler) approach, but automatic object deletion doesn't work in this case, which now and then causes some auto variables not to be deleted...
16.12.2003:
separation of shadow code from light source code.
17.12.2003:
further cleanup of engine: possible SmartPointer replaced by references; a view bugs in data structures merged. ManipulatorHeadingTilt accessable from Renderer.
18.12.2003:
shadow code splitted in hierarchical moduls.
04.01.2004:
ssm with optimal bounding box.
05.01.2004:
seperate traversal classes.
06.-08.01.2004:
cleanup of engine
10.01.2004:
double ssm shadow (one for near 10, one for further away), but problems with filtering. I need two disjunct AABB or the shadow maps are overlapping, which results in nasty artefacts on the near shadow map.

11.01.2004:
when i rotate into the light direction i can choose my up vector freely, so i decided to use one who is perpendicular to the view direction and the light direction:
(lx,ly,lz) dot (vx,vy,vz) = 0 doesn't work
(lx,ly,lz) dot (vx,y,vz) = 0 so i used this form and calculated y
lx*vx+ly*y+lz*vz = 0 -> y = (-lz*vz-lx*vx)/ly in code this leads to the up vector:
const Vector3& up = Vector3(viewDir.x,(-lightDir.z*viewDir.z-lightDir.x*viewDir.x)/lightDir.y,viewDir.z);
which leads to this render result(as seen from the light source)

12.01.2004:
with this setting i can increase the resolution in x-direction of the lower part of the image easyly (some kind of shearing ?!)

13.01.2004:
i need a matrix like
[1 0 0 0]
[0 0 0 1]
[0 0 1 0]
[0 1 0 0]
to use the perspective division to make a mapping of
[ x y z 1] -Matrix-> [x 1 z y] -perpsective division-> [x/y 1/y z/y 1]
more exact:
i'm in the orthogonal space after the light view transformation and the light orthogonal matrix,
so all my x,y and z coordinates are ranging from [-1,1] to use a division a must get rid of the singularity at 0.
so first i need to scale and translate my y coordinate:
[1 0 0 0]
[0 a 0 b]
[0 0 1 0]
[0 0 0 1]
to map from [-1,1] to [n,f]
y' = a*y+b use the mapping prerequisites -> n = a*-1+b -> a=b-n and f=(b-n)*f+b -> b = (f+n)/2 -> a = (f-n)/2
->
[1 0 0 0]
[0 (f-n)/2 0 (f+n)/2]
[0 0 1 0]
[0 0 0 1]
after this step i can use a perspective projection matrix for the y coordinate:
[1 0 0 0]
[0 (f+n)/(f-n) 0 -2*f*n/(f-n)]
[0 0 1 0]
[0 1 0 0]
after all this trouble i have a very narrow stripe in the middle of the screen, so i now can scale the whole thing in x direction with a standard scaleMatrix(x=f,y=1,z=1) to make it fit the screen.
with n=1 and f=100 this leads to (as seen from the light source)

code:
const Vector3& up = Vector3(viewDir.x,(-lightDir.z*viewDir.z-lightDir.x*viewDir.x)/lightDir.y,viewDir.z);
//stay at center but choose a valid eye point
shadow.mtxLightView.rotateAt(lightDir,up);
//take the world coordinates of the view frustum and transform them to light space
Vector3x8 v;
pmv.calcViewFrustumWorldCoord(v);
for(uint i = 0; i < 8; i++) {
v[i] = shadow.mtxLightView.mulIgnoreHomogen(v[i]);
}
//make a AABB box out of the light space view frustum coordinates
AABox eyeBox(v[0],v[0]);
for(i = 1; i < 8; i++) {
eyeBox.expandToContain(v[i]);
}
const Vector3& min = eyeBox.getMin();
const Vector3& max = eyeBox.getMax();
const Real f = 100;
const Real n = 1;
//setup an orthogonal projection with the borders of the AABB of the view frustum coordinates
Matrix4 ort = Matrix4().ortho(min.x,max.x,min.y,max.y,min.z,max.z);
Matrix4 scaleTransY = Matrix4::IDENTITY;
scaleTransY.a22 = (f-n)/2;
scaleTransY.a24 = (f+n)/2;
Matrix4 shearY = Matrix4::ZERO;
shearY.a11 = 1;
shearY.a22 = (f+n)/(f-n);
shearY.a24 = -2*f*n/(f-n);
shearY.a33 = 1;
shearY.a42 = 1;
Matrix4& l = (shadow.mtxLightProjection = Matrix4::IDENTITY);
l *= Matrix4().setScaleMatrix(f,1,1);
l *= shearY;
l *= scaleTransY;
l *= ort;
14.01.2004:
works fine if the light frustum is centered as in the previous pictures, but what is in this case:

the
view point escapes after scaling out of the screen :(
easyly solved: just after the orthogonal projection use a translation to guarantie that the view point stays in the middle:
position = shadow.mtxLightView.mulIgnoreHomogen(position);
position = ort.mulIgnoreHomogen(position);
Matrix4 scaleTransY = Matrix4::IDENTITY;
scaleTransY.a22 = (f-n)/2;
scaleTransY.a24 = (f+n)/2;
Matrix4 shearY = Matrix4::ZERO;
shearY.a11 = 1;
shearY.a22 = (f+n)/(f-n);
shearY.a24 = -2*f*n/(f-n);
shearY.a33 = 1;
shearY.a42 = 1;
Matrix4& l = (shadow.mtxLightProjection = Matrix4::IDENTITY);
l *= Matrix4().setScaleMatrix(f,1,1);
l *= shearY;
l *= scaleTransY;
l *= Matrix4().setTranslationMatrix(Vector3(-position.x,0,0));
l *= ort;
the big advantage of this method in reference to perspective shadow maps is: it is numerical stable; most problem are easyly solved in 2D no post-perspective space to worry, no singularitys.
15.01.2004:
implementation of the new shadow mapping approach
17.01.2004:
introduce general traversal -> traversal cleanup;
19.01.2004:
new traversal find; optimize “shear” shadow
21.01.2004:
new traversal intersection; optimize “shear” shadow
22.01.2004:
NearestLeafTraversal, NearestLeafInFrustumTraversal; optimize “shear” shadow
30.01.2004:
replaced all intersection with IntersectionTraversal;
02.02.2004:
lsosm: in certain situations it is possible for x-clipping to occur.

note
the clippped area of the view frustum in the upper left screen. with
an overestimated view frustum, as often the case, this is hardly
noticable. but it is possible to occur. a qick solution to the
problem is:
//try to stay in the middle bottom position
const Matrix4 transStayMiddleX = Matrix4().setTranslationMatrix(Vector3(-position.x,0,0));
//all together
lp = shearY*scaleTransY*transStayMiddleX*lp;
//scalefactor in x direction to fill shadow map
Real scaleX = f;
//if you want everything seen inside the shadow map than you have to check:
//but this leads to quality degredation in some cases (looking up or down)
//check: for each shadowPolygon: -1 < shadowPolygon.x/shadowPolygon.y*f < 1 else x-clipping occures
for(uint i = 0; i < vfls.size(); i++) {
const Vector3 a(lp.mulHomogen(vfls[i]));
if(abs((a.x/a.y)*f) > 1) {
const Real newScaleX = abs(a.y/a.x);
if(scaleX > newScaleX) {
scaleX = newScaleX;
}
}
}
lp = Matrix4().setScaleMatrix(scaleX,1,1)*lp;
this solution limits the scale factor.
04.02.2004:
the before mentioned method introduces quality degredation when looking up or down. the main cause for this is pictured here:

the view frustum, as seen from the light source, has unsymmetrical borderlines. favorable would be a constellation with equal angels on both sides, because this would lead to a symmetrical expansion of the view frustum and therefor the scale factor minimisation would result in greater results and the degredation would be less noticeable.
07.02.2004:
this is achived through an additional rotation(around the z-axis) of the light space before the orthogonal projection. the rotation vector needed for this rotation can be extracted from the silhouette of the view frustum. first we need to find the line segments connecting the near plane points with the far plane points (2 line segments). from these we extract the direction vectors (near to far plane point). a normalized vector addition results in the rotation vector.

12.02.2004:
implemented orthoCornerPoints, which treats the z information not as distances, but as a coordinate. this solves “strange” clipping errors.
U2Shadow with correct zMax (nearest z value); various array types with hierarchie implemented;
13.02.2004:
cleanup of shadows; DoubleShadow error was because of to early r.viewFrustumCulling = oldstate; IntersectionShadow which calculates the intersection of the view frustum volume and the shadow caster volume;
14.02.2004:
LIFOShadow border vector centering implemented;