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

Revision 2915, 11.4 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
87ShadowMap::ShadowMap(Light *light, int size, const AxisAlignedBox3 &sceneBox, Camera *cam):
88mSceneBox(sceneBox), mSize(size), mCamera(cam), mLight(light)
89{
90        mFbo = new FrameBufferObject(size, size, FrameBufferObject::DEPTH_32, true);
91        // the diffuse color buffer
92        mFbo->AddColorBuffer(ColorBufferObject::BUFFER_UBYTE, ColorBufferObject::WRAP_CLAMP_TO_EDGE, ColorBufferObject::FILTER_LINEAR, false);
93
94        mShadowCam = new Camera(mSceneBox.Size().x * 0.5f, mSceneBox.Size().y * 0.5f);
95        mShadowCam->SetOrtho(true);
96}
97
98
99ShadowMap::~ShadowMap()
100{
101        DEL_PTR(mFbo);
102        DEL_PTR(mShadowCam);
103}
104
105
106void ShadowMap::DrawPolys()
107{
108        if (!polyhedron) return;
109
110        for (size_t i = 0; i < polyhedron->NumPolygons(); ++ i)
111        {
112                float r = (float)i / polyhedron->NumPolygons();
113                float g = 1;
114                float b = 1;
115
116                glColor3f(r, g, b);
117
118                glBegin(GL_LINE_LOOP);
119
120                Polygon3 *poly = polyhedron->GetPolygons()[i];
121
122                for (size_t j = 0; j < poly->mVertices.size(); ++ j)
123                {
124                        Vector3 v = poly->mVertices[j];
125                        glVertex3d(v.x, v.y, v.z);
126                }
127
128                glEnd();
129        }
130}
131
132
133void ShadowMap::IncludeLightVolume(const Polyhedron &polyhedron,
134                                                                   VertexArray &frustumPoints,
135                                                                   const Vector3 lightDir,
136                                                                   const AxisAlignedBox3 &sceneBox
137                                                                   )
138{
139        // we don't need closed form anymore => just store vertices
140        VertexArray vertices;
141        polyhedron.CollectVertices(vertices);
142
143        // we 'look' at each point and calculate intersections of rays with scene bounding box
144        VertexArray::const_iterator it, it_end = vertices.end();
145
146        for (it = vertices.begin(); it != it_end; ++ it)
147        {
148                Vector3 v  = *it;
149
150                frustumPoints.push_back(v);
151               
152                // hack: get point surely outside of box
153                v -= Magnitude(mSceneBox.Diagonal()) * lightDir;
154
155                SimpleRay ray(v, lightDir);
156
157                float tNear, tFar;
158
159                if (sceneBox.Intersects(ray, tNear, tFar))
160                {
161                        Vector3 newpt = ray.Extrap(tNear);
162                        frustumPoints.push_back(newpt);                 
163                }
164        }
165}
166
167
168bool ShadowMap::CalcLightProjectionLispSM(Matrix4x4 &lightProj)
169{
170        Matrix4x4 lispMtx = IdentityMatrix();
171
172        const float dotProd = DotProd(mCamera->GetDirection(), mShadowCam->GetDirection());
173        const float sinGamma = sqrt(1.0f - dotProd * dotProd);
174
175
176        ///////////////////
177        //-- First step: calc frustum clipped by scene box
178
179        DEL_PTR(polyhedron);
180        polyhedron = CalcClippedFrustum(mSceneBox);
181
182        if (!polyhedron) return false; // something is wrong
183
184        // include the part of the light volume that "sees" the frustum
185        // we only require frustum vertices
186
187        VertexArray frustumPoints;
188        IncludeLightVolume(*polyhedron, frustumPoints, mShadowCam->GetDirection(), mSceneBox);
189
190
191        ///////////////
192        //-- transform points from world view to light view and calculate extremal points
193
194        AxisAlignedBox3 extremalPoints;
195        extremalPoints.Initialize();
196
197        Matrix4x4 lightView;
198        mShadowCam->GetModelViewMatrix(lightView);
199
200        VertexArray::const_iterator it, it_end = frustumPoints.end();
201               
202        for (it = frustumPoints.begin(); it != it_end; ++ it)
203        {
204                Vector3 pt = *it;
205                pt = lightView * pt;
206
207                extremalPoints.Include(pt);
208        }
209
210
211        ///////////////
212        //-- We apply the lispsm algorithm in order to calculate an optimal light projection matrix
213
214        // use lispsm formulas
215        const float fac = 1.0f / sinGamma;
216       
217        const float z_n = fac * mCamera->GetNear();
218
219        // light space y size
220        const float d = fabs(extremalPoints.Max()[1] - extremalPoints.Min()[1]);
221
222        const float z_f = z_n + d * sinGamma;
223
224        // this is the famous n parameter
225        const float n = (z_n + sqrt(z_f * z_n)) /sinGamma;
226        const float f = n + d;
227
228        const Vector3 nearPt = mShadowCam->GetNear() * mShadowCam->GetDirection() + mShadowCam->GetPosition();
229
230        //get the coordinates of the near camera point in light space
231        const Vector3 lsNear = lightView * nearPt;
232
233        //c start has the x and y coordinate of e, the z coord of B.min()
234        const Vector3 startPt = Vector3(lsNear.x, lsNear.y, extremalPoints.Max().z);
235
236        // the new projection center
237        Vector3 projCenter = startPt - Vector3(0, 0, 1) * n;
238
239        //construct a translation that moves to the projection center
240        const Matrix4x4 projectionCenter = TranslationMatrix(-projCenter);
241
242        lightProj = IdentityMatrix();
243
244        //one possibility for a simple perspective transformation matrix
245        //with the two parameters n(near) and f(far) in y direction
246       
247        // a = (f+n)/(f-n); b = -2*f*n/(f-n);
248        // [ 1 0 0 0]
249        // [ 0 a 0 b]
250        // [ 0 0 1 0]
251        // [ 0 1 0 0]
252
253        lightProj.x[1][1] = (f + n) / (f - n);           
254        lightProj.x[1][3] = 1.0f;
255
256        lightProj.x[3][1] = -2.0f * f * n / (f - n);           
257        lightProj.x[3][3] = 0.0f;                               
258
259        cout << "here4\n" << lightProj << endl;
260
261        // transform into OpenGL right handed system
262        Matrix4x4 scale = ScaleMatrix(1.0f, 1.0f, -1.0f);
263
264        lightProj = projectionCenter * scale * lightProj;
265
266        Vector3 pmax = extremalPoints.Max();
267        Vector3 pmin = extremalPoints.Min();
268
269        cout << "min: " << lightProj * pmin << endl;
270        cout << "max: " << lightProj * pmax << endl;
271       
272
273        return true;
274}
275
276
277bool ShadowMap::CalcLightProjection(Matrix4x4 &lightProj)
278{
279        DEL_PTR(polyhedron);
280
281
282        ///////////////////
283        //-- First step: calc frustum clipped by scene box
284
285        polyhedron = CalcClippedFrustum(mSceneBox);
286
287        if (!polyhedron) return false; // something is wrong
288
289        // include the part of the light volume that "sees" the frustum
290        // we only require frustum vertices
291
292        VertexArray frustumPoints;
293        IncludeLightVolume(*polyhedron, frustumPoints, mShadowCam->GetDirection(), mSceneBox);
294
295
296        ///////////////
297        //-- transform points from world view to light view and calculate extremal points
298
299        AxisAlignedBox3 extremalPoints;
300        extremalPoints.Initialize();
301
302        Matrix4x4 lightView;
303        mShadowCam->GetModelViewMatrix(lightView);
304
305        VertexArray::const_iterator it, it_end = frustumPoints.end();
306               
307        for (it = frustumPoints.begin(); it != it_end; ++ it)
308        {
309                Vector3 pt = *it;
310                pt = lightView * pt;
311
312                extremalPoints.Include(pt);
313        }
314
315        // focus projection matrix on the extremal points
316        lightProj = GetFittingProjectionMatrix(extremalPoints);
317
318        return true;
319}
320
321
322Polyhedron *ShadowMap::CalcClippedFrustum(const AxisAlignedBox3 &box) const
323{
324        Vector3 ftl, ftr, fbl, fbr;
325        Vector3 ntl, ntr, nbl, nbr;
326
327        VertexArray sides[6];
328
329        mCamera->ComputePoints(ftl, ftr, fbl, fbr, ntl, ntr, nbl, nbr);
330
331        for (int i = 0; i < 6; ++ i)
332                sides[i].resize(4);
333       
334        // left, right
335        sides[0][0] = ftl; sides[0][1] = fbl; sides[0][2] = nbl; sides[0][3] = ntl;
336        sides[1][0] = fbr; sides[1][1] = ftr; sides[1][2] = ntr; sides[1][3] = nbr;
337        // bottom, top
338        sides[2][0] = fbl; sides[2][1] = fbr; sides[2][2] = nbr; sides[2][3] = nbl;
339        sides[3][0] = ftr; sides[3][1] = ftl; sides[3][2] = ntl; sides[3][3] = ntr;
340        // near, far
341        sides[4][0] = ntr; sides[4][1] = ntl; sides[4][2] = nbl; sides[4][3] = nbr;
342        sides[5][0] = ftl; sides[5][1] = ftr; sides[5][2] = fbr; sides[5][3] = fbl;
343
344        //sides[4][0] = ntl; sides[4][1] = ntr; sides[4][2] = nbr; sides[4][3] = nbl;
345        //sides[5][0] = ftr; sides[5][1] = ftl; sides[5][2] = fbl; sides[5][3] = fbr;
346
347
348
349        //////////
350        //-- compute polyhedron
351
352        PolygonContainer polygons;
353
354        for (int i = 0; i < 6; ++ i)
355        {
356                Polygon3 *poly = new Polygon3(sides[i]);
357                polygons.push_back(poly);
358        }
359
360        Polyhedron *p = new Polyhedron(polygons);
361        Polyhedron *clippedPolyhedron = box.CalcIntersection(*p);
362       
363        DEL_PTR(p);
364       
365
366        return clippedPolyhedron;
367}
368
369
370void ShadowMap::ComputeShadowMap(RenderTraverser *renderer, const Matrix4x4 &projView)
371{
372        const float xlen = Magnitude(mSceneBox.Diagonal() * 0.5f);
373        const float ylen = Magnitude(mSceneBox.Diagonal() * 0.5f);
374       
375        const Vector3 dir = mLight->GetDirection();
376        //const Vector3 dir(0, 0, 1);
377
378        mShadowCam->SetDirection(dir);
379       
380        //cout << "lightdir: " << mShadowCam->GetDirection() << endl;
381
382        // set position so that we can see the whole scene
383        Vector3 pos = mSceneBox.Center();
384
385        //Matrix4x4 camView;
386        //mCamera->GetModelViewMatrix(camView);
387
388        pos -= dir * Magnitude(mSceneBox.Diagonal() * 0.5f);
389        mShadowCam->SetPosition(pos);
390
391        mFbo->Bind();
392       
393        glDrawBuffers(1, mrt);
394
395        glPushAttrib(GL_VIEWPORT_BIT);
396        glViewport(0, 0, mSize, mSize);
397
398        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
399
400        glDisable(GL_LIGHTING);
401        glDisable(GL_TEXTURE_2D);
402        glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
403
404        glPolygonOffset(1.0f, 2000.0f);
405        glEnable(GL_POLYGON_OFFSET_FILL);
406
407        glShadeModel(GL_FLAT);
408        glEnable(GL_DEPTH_TEST);
409
410       
411        // setup projection
412        /*glMatrixMode(GL_PROJECTION);
413        glLoadIdentity();
414        glOrtho(+xlen, -xlen, +ylen, -ylen, 0.0f, Magnitude(mSceneBox.Diagonal()));
415
416        Matrix4x4 dummyMat;
417        glGetFloatv(GL_PROJECTION_MATRIX, (float *)dummyMat.x);
418        cout << "old:\n" << dummyMat << endl;
419        */
420        Matrix4x4 lightView, lightProj;
421
422        mShadowCam->GetModelViewMatrix(lightView);
423
424        //CalcLightProjection(lightProj);
425        CalcLightProjectionLispSM(lightProj);
426
427        glMatrixMode(GL_PROJECTION);
428        glPushMatrix();
429        glLoadMatrixf((float *)lightProj.x);
430
431        cout << "new:\n" << lightProj << endl;
432
433        glMatrixMode(GL_MODELVIEW);
434        glPushMatrix();
435        glLoadIdentity();
436
437        mShadowCam->SetupCameraView();
438
439        mLightProjView = lightView * lightProj;
440
441
442        //////////////
443        //-- compute texture matrix
444        static Matrix4x4 biasMatrix(0.5f, 0.0f, 0.0f, 0.5f,
445                                                                0.0f, 0.5f, 0.0f, 0.5f,
446                                                                0.0f, 0.0f, 0.5f, 0.5f,
447                                                                0.0f, 0.0f, 0.0f, 1.0f);
448
449        mTextureMatrix = mLightProjView * biasMatrix;
450
451
452
453
454        /////////////
455        //-- render scene into shadow map
456
457        renderer->RenderScene();
458
459       
460        glDisable(GL_POLYGON_OFFSET_FILL);
461        glMatrixMode(GL_MODELVIEW);
462        glPopMatrix();
463
464        glMatrixMode(GL_PROJECTION);
465        glPopMatrix();
466
467        glPopAttrib();
468#if 0
469        float *data = new float[mSize * mSize];
470
471        GrabDepthBuffer(data, mFbo->GetDepthTex());
472        ExportDepthBuffer(data, mSize);
473
474        delete [] data;
475       
476        PrintGLerror("shadow map");
477#endif
478        FrameBufferObject::Release();
479}
480
481
482void ShadowMap::GetTextureMatrix(Matrix4x4 &m) const
483{
484        m = mTextureMatrix;
485}
486
487 
488unsigned int ShadowMap::GetShadowTexture() const
489{
490        return mFbo->GetDepthTex();
491}
492
493
494
495} // namespace
Note: See TracBrowser for help on using the repository browser.