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

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