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

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