source: GTP/trunk/App/Demos/Vis/FriendlyCulling/src/ShadowMapping.cpp @ 2929

Revision 2929, 16.3 KB checked in by mattausch, 16 years ago (diff)
Line 
1#include "ShadowMapping.h"
2#include "FrameBufferObject.h"
3#include "RenderState.h"
4#include "RenderTraverser.h"
5#include "Light.h"
6#include "Polygon3.h"
7#include "Polyhedron.h"
8
9#include <IL/il.h>
10#include <assert.h>
11
12
13using namespace std;
14
15
16namespace CHCDemoEngine
17{
18
19
20static Polyhedron *polyhedron = NULL;
21static Polyhedron *lightPoly = NULL;
22
23
24static void PrintGLerror(char *msg)
25{
26        GLenum errCode;
27        const GLubyte *errStr;
28       
29        if ((errCode = glGetError()) != GL_NO_ERROR)
30        {
31                errStr = gluErrorString(errCode);
32                fprintf(stderr,"OpenGL ERROR: %s: %s\n", errStr, msg);
33        }
34}
35
36
37
38static CGprogram sCgShadowProgram;
39static CGparameter sShadowParam;
40
41
42static void GrabDepthBuffer(float *data, GLuint depthTexture)
43{
44        glEnable(GL_TEXTURE_2D);
45        glBindTexture(GL_TEXTURE_2D, depthTexture);
46
47        glGetTexImage(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, GL_FLOAT, data);
48
49        glBindTexture(GL_TEXTURE_2D, 0);
50        glDisable(GL_TEXTURE_2D);
51}
52
53
54static void ExportDepthBuffer(float *data, int size)
55{
56        ilInit();
57        assert(ilGetError() == IL_NO_ERROR);
58
59        ILstring filename = ILstring("shadow.tga");
60        ilRegisterType(IL_FLOAT);
61
62        const int depth = 1;
63        const int bpp = 1;
64
65        if (!ilTexImage(size, size, depth, bpp, IL_LUMINANCE, IL_FLOAT, data))
66        {
67                cerr << "IL error " << ilGetError() << endl;
68       
69                ilShutDown();
70                assert(ilGetError() == IL_NO_ERROR);
71
72                return;
73        }
74
75        if (!ilSaveImage(filename))
76        {
77                cerr << "TGA write error " << ilGetError() << endl;
78        }
79
80        ilShutDown();
81        assert(ilGetError() == IL_NO_ERROR);
82
83        cout << "exported depth buffer" << endl;
84}
85
86
87
88static AxisAlignedBox3 GetExtremalPoints(const Matrix4x4 &m,
89                                                                                 const VertexArray &pts)
90{
91        AxisAlignedBox3 extremalPoints;
92        extremalPoints.Initialize();
93
94        VertexArray::const_iterator it, it_end = pts.end();
95               
96        for (it = pts.begin(); it != it_end; ++ it)
97        {
98                Vector3 pt = *it;
99                pt = m * pt;
100
101                extremalPoints.Include(pt);
102        }
103
104        return extremalPoints;
105}
106
107
108ShadowMap::ShadowMap(Light *light, int size, const AxisAlignedBox3 &sceneBox, Camera *cam):
109mSceneBox(sceneBox), mSize(size), mCamera(cam), mLight(light)
110{
111        mFbo = new FrameBufferObject(size, size, FrameBufferObject::DEPTH_32, true);
112        // the diffuse color buffer
113        mFbo->AddColorBuffer(ColorBufferObject::BUFFER_UBYTE, ColorBufferObject::WRAP_CLAMP_TO_EDGE, ColorBufferObject::FILTER_LINEAR, false);
114        //mFbo->AddColorBuffer(ColorBufferObject::BUFFER_FLOAT_32, ColorBufferObject::WRAP_CLAMP_TO_EDGE, ColorBufferObject::FILTER_LINEAR, false);
115
116        mShadowCam = new Camera(mSize, mSize);//mSceneBox.Size().x * 0.5f, mSceneBox.Size().y * 0.5f);
117        mShadowCam->SetOrtho(true);
118}
119
120
121ShadowMap::~ShadowMap()
122{
123        DEL_PTR(mFbo);
124        DEL_PTR(mShadowCam);
125}
126
127
128void ShadowMap::DrawPoly(Polyhedron *poly, const Vector3 &color)
129{
130        if (!poly) return;
131
132        for (size_t i = 0; i < poly->NumPolygons(); ++ i)
133        {
134                glColor3f(color.x, color.y, color.z);
135
136                glBegin(GL_LINE_LOOP);
137
138                Polygon3 *p = poly->GetPolygons()[i];
139
140                for (size_t j = 0; j < p->mVertices.size(); ++ j)
141                {
142                        Vector3 v = p->mVertices[j];
143                        glVertex3d(v.x, v.y, v.z);
144                }
145
146                glEnd();
147        }
148}
149
150
151void ShadowMap::DrawPolys()
152{
153        DrawPoly(lightPoly, Vector3(1, 0, 1));
154        DrawPoly(polyhedron, Vector3(0, 1, 0));
155}
156
157
158float ShadowMap::ComputeN(const AxisAlignedBox3 &extremalPoints) const
159{
160        const float n = mCamera->GetNear();
161       
162        const float d = fabs(extremalPoints.Max()[2] - extremalPoints.Min()[2]);
163       
164        const float dotProd = DotProd(mCamera->GetDirection(), mShadowCam->GetDirection());
165        const float sinGamma = sin(fabs(acos(dotProd)));
166
167        return (n + sqrt(n * (n + d * sinGamma))) /  sinGamma;
168}
169
170
171Matrix4x4 ShadowMap::CalcLispSMTransform(const Matrix4x4 &lightSpace,
172                                                                                 const AxisAlignedBox3 &extremalPoints,
173                                                                                 const VertexArray &body
174                                                                                 )
175{
176        //return IdentityMatrix();
177
178        AxisAlignedBox3 bounds_ls = GetExtremalPoints(lightSpace, body);
179
180        ///////////////
181        //-- We apply the lispsm algorithm in order to calculate an optimal light projection matrix
182        //-- first find the free parameter values n, and P (the projection center), and the projection depth
183
184        //const float n = 1e6f;
185        const float n = 1e3f;
186        //const float n = ComputeN(bounds_ls) * 100;
187
188        cout << "n: " << n << endl;
189
190        const Vector3 nearPt = GetNearCameraPointE(body);
191        const float dummy = 0;
192        //get the coordinates of the near camera point in light space
193        const Vector3 lsNear = lightSpace * nearPt;
194
195        //c start has the x and y coordinate of e,  the z coord of the near plane of the light volume
196        const Vector3 startPt = Vector3(lsNear.x, lsNear.y, bounds_ls.Max().z);
197
198        cout << "mx: " <<  bounds_ls.Max() << endl;
199        cout << "mn: " << bounds_ls.Min() << endl;
200
201        // the new projection center
202        Vector3 projCenter = startPt + Vector3::UNIT_Z() * n;
203
204        cout <<"start: " << startPt << " " << projCenter << " " << Distance(lightSpace * mCamera->GetPosition(), startPt) << endl;
205
206        //construct a translation that moves to the projection center
207        const Matrix4x4 projectionCenter = TranslationMatrix(-projCenter);
208
209        // light space y size
210        const float d = fabs(bounds_ls.Max()[2] - bounds_ls.Min()[2])+dummy;
211
212        const float dy = fabs(bounds_ls.Max()[1] - bounds_ls.Min()[1]);
213        const float dx = fabs(bounds_ls.Max()[0] - bounds_ls.Min()[0]);
214
215        cout << "d: " << d << " dy: " << dy << " dx: " << dx << endl;
216
217       
218
219        //////////
220        //-- now apply these values to construct the perspective lispsm matrix
221
222        Matrix4x4 matLispSM;
223       
224        matLispSM = GetFrustum(-1.0, 1.0, -1.0, 1.0, n+dummy, n + d);
225
226        //cout << "lispsm\n" << matLispSM << endl;
227
228        // translate to the projection center
229        matLispSM = projectionCenter * matLispSM;
230
231        //cout << "new\n" << matLispSM << endl;
232
233        // transform into OpenGL right handed system
234        Matrix4x4 refl = ScaleMatrix(1.0f, 1.0f, -1.0f);
235        matLispSM *= refl;
236       
237        return matLispSM;
238}
239
240#if 0
241Vector3 ShadowMap::GetNearCameraPointE(const VertexArray &pts) const
242{
243        float maxDist = -1e25f;
244        Vector3 nearest = Vector3::ZERO();
245
246        Matrix4x4 eyeView;
247        mCamera->GetModelViewMatrix(eyeView);
248
249        VertexArray newPts;
250        polyhedron->CollectVertices(newPts);
251       
252        //the LVS volume is always in front of the camera
253        VertexArray::const_iterator it, it_end = pts.end();     
254
255        for (it = pts.begin(); it != it_end; ++ it)
256        {
257                Vector3 pt = *it;
258                Vector3 ptE = eyeView * pt;
259//cout<<"i"<< pt.z;
260                if (ptE.z > 0) cerr <<"should not happen " << ptE.z << endl;
261                else
262                if (ptE.z > maxDist)
263                {
264                        cout << " d " << ptE.z;
265       
266                        maxDist = ptE.z;
267                        nearest = pt;
268                }
269        }
270
271        //      Invert(eyeView); return eyeView * nearest;
272        return nearest;
273}
274
275#else
276
277Vector3 ShadowMap::GetNearCameraPointE(const VertexArray &pts) const
278{
279        VertexArray newPts;
280        polyhedron->CollectVertices(newPts);
281
282        Vector3 nearest = Vector3::ZERO();
283        float minDist = 1e25f;
284
285        const Vector3 camPos = mCamera->GetPosition();
286
287        VertexArray::const_iterator it, it_end = newPts.end();
288
289        for (it = newPts.begin(); it != it_end; ++ it)
290        {
291                Vector3 pt = *it;
292
293                const float dist = SqrDistance(pt, camPos);
294
295                if (dist < minDist)
296                {
297                        minDist = dist;
298                        nearest = pt;
299                }
300        }
301
302        return nearest;
303}
304
305#endif
306
307Vector3 ShadowMap::GetProjViewDir(const Matrix4x4 &lightSpace, const VertexArray &pts) const
308{
309        //get the point in the LVS volume that is nearest to the camera
310        const Vector3 e = GetNearCameraPointE(pts);
311
312        //construct edge to transform into light-space
313        const Vector3 b = e + mCamera->GetDirection();
314        //transform to light-space
315        const Vector3 e_lp = lightSpace * e;
316        const Vector3 b_lp = lightSpace * b;
317
318        Vector3 projDir(b_lp - e_lp);
319
320        Matrix4x4 dummy = lightSpace;
321        Invert(dummy);
322        Vector3 dummyVec = dummy * e_lp;
323        Vector3 dummyVec2 = dummy * b_lp;
324
325        //projDir.z = -projDir.z;
326
327        cout << "dummy: " << Normalize(dummyVec2 - dummyVec) << endl;
328        //project the view direction into the shadow map plane
329        projDir.y = 0.0;
330
331        return Normalize(projDir);
332        //return projDir;
333}
334
335
336bool ShadowMap::CalcLightProjection(Matrix4x4 &lightProj)
337{
338        ///////////////////
339        //-- First step: calc frustum clipped by scene box
340
341        DEL_PTR(polyhedron);
342        polyhedron = CalcClippedFrustum(mSceneBox);
343
344        if (!polyhedron) return false; // something is wrong
345
346        // include the part of the light volume that "sees" the frustum
347        // we only require frustum vertices
348
349        VertexArray frustumPoints;
350        IncludeLightVolume(*polyhedron, frustumPoints, mShadowCam->GetDirection(), mSceneBox);
351
352
353        ///////////////
354        //-- transform points from world view to light view and calculate extremal points
355
356        Matrix4x4 lightView;
357        mShadowCam->GetModelViewMatrix(lightView);
358
359        const AxisAlignedBox3 extremalPoints = GetExtremalPoints(lightView, frustumPoints);
360
361        // we use directional lights, so the projection can be set to identity
362        lightProj = IdentityMatrix();
363
364        // switch coordinate system to that used in the lispsm algorithm for calculations
365        Matrix4x4 transform2LispSM = ZeroMatrix();
366
367        transform2LispSM.x[0][0] =  1.0f;
368        transform2LispSM.x[1][2] = -1.0f; // y => -z
369        transform2LispSM.x[2][1] =  1.0f; // z => y
370        transform2LispSM.x[3][3] =  1.0f;
371
372        //switch to the lightspace used in the article
373        lightProj = lightProj * transform2LispSM;
374
375        const Vector3 projViewDir = GetProjViewDir(lightView * lightProj, frustumPoints);
376
377
378        cout << "projViewDir: " << projViewDir << " orig " << mCamera->GetDirection() << endl;
379
380        //do Light Space Perspective shadow mapping
381        //rotate the lightspace so that the projected light view always points upwards
382        //calculate a frame matrix that uses the projViewDir[lightspace] as up vector
383        //look(from position, into the direction of the projected direction, with unchanged up-vector)
384        const Matrix4x4 frame = LookAt(Vector3::ZERO(), projViewDir, Vector3::UNIT_Y());
385
386        cout << "frame\n " << frame << endl;
387        lightProj = lightProj * frame;
388
389        cout << "here9\n" << lightProj << endl;
390
391        const Matrix4x4 matLispSM =
392                CalcLispSMTransform(lightView * lightProj, extremalPoints, frustumPoints);
393
394        Vector3 mydummy = projViewDir;//Vector3::UNIT_Z();
395        //cout << "transformed unit z vector: " << Normalize(lightView * lightProj * mydummy) << endl;
396        cout << "transformed unit z vector: " << Normalize(frame * mydummy) << endl;
397
398        lightProj = lightProj * matLispSM;
399
400        // change back to GL coordinate system
401        Matrix4x4 transformToGL = ZeroMatrix();
402       
403        transformToGL.x[0][0] =  1.0f;
404        transformToGL.x[1][2] =  1.0f; // z => y
405        transformToGL.x[2][1] = -1.0f; // y => -z
406        transformToGL.x[3][3] =  1.0f;
407
408        lightProj = lightProj * transformToGL;
409        //cout << "here4 \n" << lightProj << endl;
410
411        AxisAlignedBox3 lightPts = GetExtremalPoints(lightView * lightProj, frustumPoints);
412
413        //cout << "ma2: " << lightPts.Max() << endl;
414        //cout << "mi2: " << lightPts.Min() << endl;
415
416        // focus projection matrix on the extremal points => scale to unit cube
417        Matrix4x4 scaleTranslate = GetFittingProjectionMatrix(lightPts);
418        lightProj *= scaleTranslate;
419
420        cout << "max: " << lightProj * extremalPoints.Max() << endl;
421        cout << "min: " << lightProj * extremalPoints.Min() << endl;
422
423        // we have to flip the signs in order to tranform to opengl right handed system
424        Matrix4x4 refl = ScaleMatrix(1, 1, -1);
425        lightProj *= refl;
426       
427        return true;
428}
429
430
431Polyhedron *ShadowMap::CalcClippedFrustum(const AxisAlignedBox3 &box) const
432{
433        Vector3 ftl, ftr, fbl, fbr;
434        Vector3 ntl, ntr, nbl, nbr;
435
436        VertexArray sides[6];
437
438        mCamera->ComputePoints(ftl, ftr, fbl, fbr, ntl, ntr, nbl, nbr);
439
440        for (int i = 0; i < 6; ++ i)
441                sides[i].resize(4);
442       
443        // left, right
444        sides[0][0] = ftl; sides[0][1] = fbl; sides[0][2] = nbl; sides[0][3] = ntl;
445        sides[1][0] = fbr; sides[1][1] = ftr; sides[1][2] = ntr; sides[1][3] = nbr;
446        // bottom, top
447        sides[2][0] = fbl; sides[2][1] = fbr; sides[2][2] = nbr; sides[2][3] = nbl;
448        sides[3][0] = ftr; sides[3][1] = ftl; sides[3][2] = ntl; sides[3][3] = ntr;
449        // near, far
450        sides[4][0] = ntr; sides[4][1] = ntl; sides[4][2] = nbl; sides[4][3] = nbr;
451        sides[5][0] = ftl; sides[5][1] = ftr; sides[5][2] = fbr; sides[5][3] = fbl;
452
453        //////////
454        //-- compute polyhedron
455
456        PolygonContainer polygons;
457
458        for (int i = 0; i < 6; ++ i)
459        {
460                Polygon3 *poly = new Polygon3(sides[i]);
461                polygons.push_back(poly);
462        }
463
464        Polyhedron *p = new Polyhedron(polygons);
465        Polyhedron *clippedPolyhedron = box.CalcIntersection(*p);
466       
467        DEL_PTR(p);
468       
469
470        return clippedPolyhedron;
471}
472
473
474void ShadowMap::ComputeShadowMap(RenderTraverser *renderer, const Matrix4x4 &projView)
475{
476        const float xlen = Magnitude(mSceneBox.Diagonal() * 0.5f);
477        const float ylen = Magnitude(mSceneBox.Diagonal() * 0.5f);
478       
479        //const Vector3 dir = mLight->GetDirection();
480        const Vector3 dir(0, 0, -1);
481
482        mShadowCam->SetDirection(dir);
483       
484        //cout << "lightdir: " << mShadowCam->GetDirection() << endl;
485
486        // set position so that we can see the whole scene
487        Vector3 pos = mSceneBox.Center();
488
489        //Matrix4x4 camView;
490        //mCamera->GetModelViewMatrix(camView);
491
492        pos -= dir * Magnitude(mSceneBox.Diagonal() * 0.5f);
493        mShadowCam->SetPosition(pos);
494
495        mFbo->Bind();
496       
497        glDrawBuffers(1, mrt);
498
499        glPushAttrib(GL_VIEWPORT_BIT);
500        glViewport(0, 0, mSize, mSize);
501
502        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
503
504        //glEnable(GL_LIGHTING);
505        //glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
506        glDisable(GL_LIGHTING);
507        glDisable(GL_TEXTURE_2D);
508        glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
509
510        glPolygonOffset(1.0f, 2000.0f);
511        glEnable(GL_POLYGON_OFFSET_FILL);
512
513        glShadeModel(GL_FLAT);
514        glEnable(GL_DEPTH_TEST);
515
516        // setup projection
517        /*glMatrixMode(GL_PROJECTION);
518        glLoadIdentity();
519        glOrtho(+xlen, -xlen, +ylen, -ylen, 0.0f, Magnitude(mSceneBox.Diagonal()));
520
521        Matrix4x4 dummyMat;
522        glGetFloatv(GL_PROJECTION_MATRIX, (float *)dummyMat.x);
523        cout << "old:\n" << dummyMat << endl;
524        */
525
526        Matrix4x4 lightView, lightProj;
527
528        mShadowCam->GetModelViewMatrix(lightView);
529        CalcLightProjection(lightProj);
530
531        glMatrixMode(GL_PROJECTION);
532        glPushMatrix();
533        glLoadMatrixf((float *)lightProj.x);
534
535        mLightProjView = lightView * lightProj;
536
537        Frustum frustum(mLightProjView);
538        //Frustum frustum(projView);
539
540        DEL_PTR(lightPoly);
541
542        vector<Plane3> clipPlanes;
543
544        for (int i = 0; i < 6; ++ i)
545        {
546                frustum.mClipPlanes[i].mNormal *= -1;
547                frustum.mClipPlanes[i].mD *= -1;
548
549                clipPlanes.push_back(frustum.mClipPlanes[i]);
550        }
551
552        DEL_PTR(lightPoly);
553        lightPoly = Polyhedron::CreatePolyhedron(clipPlanes, mSceneBox);
554
555        //cout << "new:\n" << lightProj << endl;
556
557        glMatrixMode(GL_MODELVIEW);
558        glPushMatrix();
559        glLoadIdentity();
560
561        mShadowCam->SetupCameraView();
562
563
564        //////////////
565        //-- compute texture matrix
566
567        static Matrix4x4 biasMatrix(0.5f, 0.0f, 0.0f, 0.5f,
568                                                                0.0f, 0.5f, 0.0f, 0.5f,
569                                                                0.0f, 0.0f, 0.5f, 0.5f,
570                                                                0.0f, 0.0f, 0.0f, 1.0f);
571
572        mTextureMatrix = mLightProjView * biasMatrix;
573
574
575
576
577        /////////////
578        //-- render scene into shadow map
579
580        renderer->RenderScene();
581
582       
583        glDisable(GL_POLYGON_OFFSET_FILL);
584        glMatrixMode(GL_MODELVIEW);
585        glPopMatrix();
586
587        glMatrixMode(GL_PROJECTION);
588        glPopMatrix();
589
590        glPopAttrib();
591
592       
593        glEnable(GL_LIGHTING);
594        glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
595
596#if 0
597        float *data = new float[mSize * mSize];
598
599        GrabDepthBuffer(data, mFbo->GetDepthTex());
600        ExportDepthBuffer(data, mSize);
601
602        delete [] data;
603       
604        PrintGLerror("shadow map");
605#endif
606        FrameBufferObject::Release();
607}
608
609
610void ShadowMap::GetTextureMatrix(Matrix4x4 &m) const
611{
612        m = mTextureMatrix;
613}
614
615 
616unsigned int ShadowMap::GetDepthTexture() const
617{
618        return mFbo->GetDepthTex();
619}
620
621unsigned int ShadowMap::GetShadowColorTexture() const
622{
623        return mFbo->GetColorBuffer(0)->GetTexture();
624       
625}
626
627
628void ShadowMap::IncludeLightVolume(const Polyhedron &polyhedron,
629                                                                   VertexArray &frustumPoints,
630                                                                   const Vector3 lightDir,
631                                                                   const AxisAlignedBox3 &sceneBox
632                                                                   )
633{
634        // we don't need closed form anymore => just store vertices
635        VertexArray vertices;
636        polyhedron.CollectVertices(vertices);
637
638        // we 'look' at each point and calculate intersections of rays with scene bounding box
639        VertexArray::const_iterator it, it_end = vertices.end();
640
641        for (it = vertices.begin(); it != it_end; ++ it)
642        {
643                Vector3 v  = *it;
644
645                frustumPoints.push_back(v);
646               
647                // hack: get point surely outside of box
648                v -= Magnitude(mSceneBox.Diagonal()) * lightDir;
649
650                SimpleRay ray(v, lightDir);
651
652                float tNear, tFar;
653
654                if (sceneBox.Intersects(ray, tNear, tFar))
655                {
656                        Vector3 newpt = ray.Extrap(tNear);
657                        frustumPoints.push_back(newpt);                 
658                }
659        }
660}
661
662
663} // namespace
Note: See TracBrowser for help on using the repository browser.