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

Revision 2923, 13.8 KB checked in by mattausch, 16 years ago (diff)

son schas

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