source: GTP/trunk/App/Demos/Vis/FriendlyCulling/src/SsaoShader.cpp @ 2871

Revision 2871, 16.6 KB checked in by mattausch, 16 years ago (diff)

working with 50 frames

Line 
1#include "SsaoShader.h"
2#include "FrameBufferObject.h"
3#include "RenderState.h"
4#include "SampleGenerator.h"
5#include "Vector3.h"
6#include "Camera.h"
7
8
9using namespace std;
10
11static GLenum mymrt[] = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_COLOR_ATTACHMENT2_EXT};
12
13namespace CHCDemoEngine
14{
15
16// number of ssao samples
17#define NUM_SAMPLES 16
18
19
20static CGprogram sCgSsaoProgram = NULL;
21static CGprogram sCgDeferredProgram2 = NULL;
22static CGprogram sCgAntiAliasingProgram = NULL;
23static CGprogram sCgCombineProgram = NULL;
24
25static CGparameter sColorsTexCombineParam;
26static CGparameter sSsaoTexCombineParam;
27
28static CGparameter sColorsTexDeferredParam;
29static CGparameter sPositionsTexDeferredParam;
30static CGparameter sNormalsTexDeferredParam;
31
32static CGparameter sColorsTexParam;
33static CGparameter sPositionsTexParam;
34static CGparameter sNormalsTexParam;
35
36
37static CGparameter sOldModelViewProjMatrixParam;
38static CGparameter sMaxDepthParam;
39static CGparameter sSamplesParam;
40static CGparameter sOldTexParam;
41static CGparameter sNoiseTexParam;
42static CGparameter sNoiseMultiplierParam;
43static CGparameter sExpFactorParam;
44
45static CGparameter sColorsTexAntiAliasingParam;
46static CGparameter sNormalsTexAntiAliasingParam;
47
48
49static GLuint noiseTex;
50
51// ssao random spherical samples
52static Sample2 samples[NUM_SAMPLES];
53
54
55static void PrintGLerror(char *msg)
56{
57        GLenum errCode;
58        const GLubyte *errStr;
59       
60        if ((errCode = glGetError()) != GL_NO_ERROR)
61        {
62                errStr = gluErrorString(errCode);
63                fprintf(stderr,"OpenGL ERROR: %s: %s\n", errStr, msg);
64        }
65}
66
67
68/** Generate poisson disc distributed sample points on the unit disc
69*/
70static void GenerateSamples()
71{
72        static PoissonDiscSampleGenerator poisson(NUM_SAMPLES, 1.0f);
73        poisson.Generate((Sample2 *)samples);
74}
75
76
77SsaoShader::SsaoShader(int w, int h, Camera *cam, float scaleFactor):
78mWidth(w), mHeight(h),
79mCamera(cam),
80mScaleFactor(scaleFactor)
81{
82       
83        ///////////
84        //-- the flip-flop fbos
85
86        mNewFbo = new FrameBufferObject(w, h, FrameBufferObject::DEPTH_NONE);
87        // the diffuse color buffer
88        mNewFbo->AddColorBuffer(ColorBufferObject::BUFFER_FLOAT_32, ColorBufferObject::WRAP_CLAMP_TO_EDGE, ColorBufferObject::FILTER_LINEAR, false);
89        mNewFbo->AddColorBuffer(ColorBufferObject::BUFFER_FLOAT_32, ColorBufferObject::WRAP_CLAMP_TO_EDGE, ColorBufferObject::FILTER_LINEAR, false);
90       
91        mOldFbo = new FrameBufferObject(w, h, FrameBufferObject::DEPTH_NONE);
92        // the diffuse color buffer
93        mOldFbo->AddColorBuffer(ColorBufferObject::BUFFER_FLOAT_32, ColorBufferObject::WRAP_CLAMP_TO_EDGE, ColorBufferObject::FILTER_LINEAR, false);
94        mOldFbo->AddColorBuffer(ColorBufferObject::BUFFER_FLOAT_32, ColorBufferObject::WRAP_CLAMP_TO_EDGE, ColorBufferObject::FILTER_LINEAR, false);
95       
96        mFbo3 = new FrameBufferObject(w, h, FrameBufferObject::DEPTH_NONE);
97        // the diffuse color buffer
98        mFbo3->AddColorBuffer(ColorBufferObject::BUFFER_FLOAT_32, ColorBufferObject::WRAP_CLAMP_TO_EDGE, ColorBufferObject::FILTER_LINEAR, false);
99               
100        mFbo4 = new FrameBufferObject(w, h, FrameBufferObject::DEPTH_NONE);
101        // the diffuse color buffer
102        mFbo4->AddColorBuffer(ColorBufferObject::BUFFER_FLOAT_32, ColorBufferObject::WRAP_CLAMP_TO_EDGE, ColorBufferObject::FILTER_LINEAR, false);
103
104        // create noise texture for ssao
105        CreateNoiseTex2D();
106}
107
108
109SsaoShader::~SsaoShader()
110{
111        if (sCgSsaoProgram)
112                cgDestroyProgram(sCgSsaoProgram);
113
114        DEL_PTR(mNewFbo);
115        DEL_PTR(mOldFbo);
116
117        glDeleteTextures(1, &noiseTex);
118}
119
120
121void SsaoShader::Init(CGcontext context)
122{       
123        sCgDeferredProgram2 =
124                cgCreateProgramFromFile(context,
125                                                                CG_SOURCE,
126                                                                "src/shaders/deferred.cg",
127                                                                RenderState::sCgFragmentProfile,
128                                                                "main2",
129                                                                NULL);
130
131        if (sCgDeferredProgram2 != NULL)
132        {
133                cgGLLoadProgram(sCgDeferredProgram2);
134
135                // we need size of texture for scaling
136                sPositionsTexDeferredParam = cgGetNamedParameter(sCgDeferredProgram2, "positions"); 
137                sColorsTexDeferredParam = cgGetNamedParameter(sCgDeferredProgram2, "colors"); 
138                sNormalsTexDeferredParam = cgGetNamedParameter(sCgDeferredProgram2, "normals");
139        }
140        else
141                cerr << "deferred program failed to load" << endl;
142
143
144        /*sCgCombineProgram =
145                cgCreateProgramFromFile(context,
146                                                                CG_SOURCE,
147                                                                "src/shaders/ssao.cg",
148                                                                RenderState::sCgFragmentProfile,
149                                                                "combined",
150                                                                NULL);
151
152        if (sCgCombineProgram != NULL)
153        {
154                cgGLLoadProgram(sCgCombineProgram);
155
156                // we need size of texture for scaling
157                sColorsTexCombineParam = cgGetNamedParameter(sCgCombineProgram, "colors"); 
158                sSsaoTexCombineParam = cgGetNamedParameter(sCgCombineProgram, "ssaoTex");
159        }
160        else
161                cerr << "combined program failed to load" << endl;
162*/
163
164        ///////////////
165
166        sCgSsaoProgram =
167                cgCreateProgramFromFile(context,
168                                                                CG_SOURCE,
169                                                                "src/shaders/ssao.cg",
170                                                                RenderState::sCgFragmentProfile,
171                                                                "main",
172                                                                NULL);
173
174        if (sCgSsaoProgram != NULL)
175        {
176                cgGLLoadProgram(sCgSsaoProgram);
177
178                // we need size of texture for scaling
179                sPositionsTexParam = cgGetNamedParameter(sCgSsaoProgram, "positions"); 
180                sColorsTexParam = cgGetNamedParameter(sCgSsaoProgram, "colors"); 
181                sNormalsTexParam = cgGetNamedParameter(sCgSsaoProgram, "normals"); 
182                sNoiseTexParam = cgGetNamedParameter(sCgSsaoProgram, "noiseTexture");
183                sNoiseMultiplierParam = cgGetNamedParameter(sCgSsaoProgram, "noiseMultiplier");
184               
185                sOldModelViewProjMatrixParam = cgGetNamedParameter(sCgSsaoProgram, "oldModelViewProj");
186                sMaxDepthParam = cgGetNamedParameter(sCgSsaoProgram, "maxDepth");
187                sExpFactorParam = cgGetNamedParameter(sCgSsaoProgram, "expFactor");
188
189                sSamplesParam = cgGetNamedParameter(sCgSsaoProgram, "samples");
190                sOldTexParam = cgGetNamedParameter(sCgSsaoProgram, "oldTex"); 
191
192                // generate samples for ssao kernel
193                GenerateSamples();
194                cgGLSetParameterArray2f(sSamplesParam, 0, NUM_SAMPLES, (const float *)samples);
195        }
196        else
197                cerr << "ssao program failed to load" << endl;
198
199        sCgAntiAliasingProgram =
200                cgCreateProgramFromFile(context,
201                                                                CG_SOURCE,
202                                                                "src/shaders/antialiasing.cg",
203                                                                RenderState::sCgFragmentProfile,
204                                                                "main",
205                                                                NULL);
206
207        if (sCgAntiAliasingProgram != NULL)
208        {
209                cgGLLoadProgram(sCgAntiAliasingProgram);
210
211                sColorsTexAntiAliasingParam = cgGetNamedParameter(sCgAntiAliasingProgram, "colors"); 
212                sNormalsTexAntiAliasingParam = cgGetNamedParameter(sCgAntiAliasingProgram, "normals");
213        }
214        else
215                cerr << "antialiasing program failed to load" << endl;
216
217
218        PrintGLerror("init");
219}
220
221
222void SsaoShader::Render(FrameBufferObject *fbo,
223                                                const Matrix4x4 &oldProjViewMatrix,
224                                                float expFactor)
225{
226       
227        // switch roles of old and new fbo
228        // the algorihm uses two input fbos, where the one
229        // contais the color buffer from the last frame,
230        // the other one will be written
231        swap(mNewFbo, mOldFbo);
232
233        cgGLSetMatrixParameterfc(sOldModelViewProjMatrixParam, (const float *)oldProjViewMatrix.x);
234
235        glPushAttrib(GL_VIEWPORT_BIT);
236        glViewport(0, 0, mWidth, mHeight);
237
238        FrameBufferObject::Release();
239
240        glDrawBuffers(1, mymrt);
241
242        cgGLEnableProfile(RenderState::sCgFragmentProfile);
243
244        glDisable(GL_ALPHA_TEST);
245        glDisable(GL_TEXTURE_2D);
246        glDisable(GL_LIGHTING);
247
248        glMatrixMode(GL_PROJECTION);
249        glPushMatrix();
250        glLoadIdentity();
251
252        glMatrixMode(GL_MODELVIEW);
253        glPushMatrix();
254        glLoadIdentity();
255
256        const float offs = 0.5f;
257        glOrtho(-offs, offs, -offs, offs, 0, 1);
258
259        FirstPass(fbo);
260        ComputeSsao(fbo, expFactor);
261        //Combine(fbo);
262        AntiAliasing(fbo);
263
264        glEnable(GL_LIGHTING);
265        glDisable(GL_TEXTURE_2D);
266
267        glMatrixMode(GL_PROJECTION);
268        glPopMatrix();
269
270        glMatrixMode(GL_MODELVIEW);
271        glPopMatrix();
272
273        glPopAttrib();
274
275        cgGLDisableProfile(RenderState::sCgFragmentProfile);
276}
277
278
279void SsaoShader::ComputeSsao(FrameBufferObject *fbo, float expFactor)
280{
281        GLuint colorsTex = mFbo3->GetColorBuffer(0)->GetTexture();
282        GLuint positionsTex = fbo->GetColorBuffer(1)->GetTexture();
283        GLuint normalsTex = fbo->GetColorBuffer(2)->GetTexture();
284
285        if (1)
286        {
287                // generate mip map levels for position texture
288                glBindTexture(GL_TEXTURE_2D, positionsTex);
289                glGenerateMipmapEXT(GL_TEXTURE_2D);
290        }
291
292
293        // read the second buffer, write to the first buffer
294        mNewFbo->Bind();
295        glDrawBuffers(2, mymrt);
296
297        GLuint oldTex = mOldFbo->GetColorBuffer(0)->GetTexture();
298
299        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
300
301        cgGLEnableProfile(RenderState::sCgFragmentProfile);
302        cgGLBindProgram(sCgSsaoProgram);
303
304        cgGLSetTextureParameter(sPositionsTexParam, positionsTex);
305        cgGLEnableTextureParameter(sPositionsTexParam);
306
307        cgGLSetTextureParameter(sColorsTexParam, colorsTex);
308        cgGLEnableTextureParameter(sColorsTexParam);
309
310        cgGLSetTextureParameter(sNormalsTexParam, normalsTex);
311        cgGLEnableTextureParameter(sNormalsTexParam);
312
313        cgGLSetTextureParameter(sNoiseTexParam, noiseTex);
314        cgGLEnableTextureParameter(sNoiseTexParam);
315
316        cgGLSetTextureParameter(sOldTexParam, oldTex);
317        cgGLEnableTextureParameter(sOldTexParam);
318
319        cgGLSetParameter1f(sNoiseMultiplierParam, RandomValue(3.0f, 17.0f));
320       
321        cgGLSetParameter1f(sMaxDepthParam, mScaleFactor);
322        cgGLSetParameter1f(sExpFactorParam, expFactor);
323
324
325        //GenerateSamples();
326        cgGLSetParameterArray2f(sSamplesParam, 0, NUM_SAMPLES, (const float *)samples);
327
328        Vector3 tl, tr, bl, br;
329        ComputeViewVectors(tl, tr, bl, br);
330
331        glColor3f(1.0f, 1.0f, 1.0f);
332
333        glBegin(GL_QUADS);
334
335        // note: slightly larger texture hides ambient occlusion error on border but costs resolution
336        //const float new_offs = 0.55f;
337        const float new_offs = 0.5f;
338       
339        glColor3f(bl.x, bl.y, bl.z); glTexCoord2f(0, 0); glVertex3f(-new_offs, -new_offs, -0.5f);
340        glColor3f(br.x, br.y, br.z); glTexCoord2f(1, 0); glVertex3f( new_offs, -new_offs, -0.5f);
341        glColor3f(tr.x, tr.y, tr.z); glTexCoord2f(1, 1); glVertex3f( new_offs,  new_offs, -0.5f);
342        glColor3f(tl.x, tl.y, tl.z); glTexCoord2f(0, 1); glVertex3f(-new_offs,  new_offs, -0.5f);
343
344        glEnd();
345
346        cgGLDisableTextureParameter(sColorsTexParam);
347        cgGLDisableTextureParameter(sPositionsTexParam);
348        cgGLDisableTextureParameter(sNormalsTexParam);
349        cgGLDisableTextureParameter(sNoiseTexParam);
350        cgGLDisableTextureParameter(sOldTexParam);
351
352        FrameBufferObject::Release();
353
354        PrintGLerror("ssao first pass");
355}
356
357
358void SsaoShader::ComputeViewVectors(Vector3 &tl, Vector3 &tr, Vector3 &bl, Vector3 &br)
359{
360        Vector3 ftl, ftr, fbl, fbr, ntl, ntr, nbl, nbr;
361
362        mCamera->ComputePoints(ftl, ftr, fbl, fbr, ntl, ntr, nbl, nbr);
363
364#if 1 // matT: debug this!!
365       
366        bl = -Normalize(nbl - fbl);
367        br = -Normalize(nbr - fbr);
368        tl = -Normalize(ntl - ftl);
369        tr = -Normalize(ntr - ftr);
370
371#else // just take camera direction
372       
373        bl = -Normalize(mCamera->GetDirection());
374        br = -Normalize(mCamera->GetDirection());
375        tl = -Normalize(mCamera->GetDirection());
376        tr = -Normalize(mCamera->GetDirection());
377
378#endif
379
380        // normalize to 0 .. 1
381        bl = bl * 0.5f + 0.5f;
382        br = br * 0.5f + 0.5f;
383        tl = tl * 0.5f + 0.5f;
384        tr = tr * 0.5f + 0.5f;
385}
386
387
388void SsaoShader::CreateNoiseTex2D()
389{
390        //GLubyte *randomNormals = new GLubyte[mWidth * mHeight * 3];
391        float *randomNormals = new float[mWidth * mHeight * 3];
392
393        for (int i = 0; i < mWidth * mHeight * 3; i += 3)
394        {
395                // create random samples on a circle
396                const float rx = RandomValue(0, 1);
397                const float theta = 2.0f * acos(sqrt(1.0f - rx));
398
399                //randomNormals[i + 0] = (GLubyte)((cos(theta) * 0.5f + 0.5f) * 255.0f);
400                //randomNormals[i + 1] = (GLubyte)((sin(theta) * 0.5f + 0.5f) * 255.0f);
401                randomNormals[i + 0] = cos(theta);
402                randomNormals[i + 1] = sin(theta);
403                randomNormals[i + 2] = 0;
404        }
405
406        glEnable(GL_TEXTURE_2D);
407        glGenTextures(1, &noiseTex);
408        glBindTexture(GL_TEXTURE_2D, noiseTex);
409               
410        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
411        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
412        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
413        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
414
415        //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, mWidth, mHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, randomNormals);
416        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, mWidth, mHeight, 0, GL_RGB, GL_FLOAT, randomNormals);
417
418        glBindTexture(GL_TEXTURE_2D, 0);
419        glDisable(GL_TEXTURE_2D);
420
421        delete [] randomNormals;
422
423        cout << "created noise texture" << endl;
424
425        PrintGLerror("noisetexture");
426}
427
428
429
430static void SetVertex(float x, float y, float x_offs, float y_offs)
431{
432        glMultiTexCoord2fARB(GL_TEXTURE0_ARB, x, y); // center
433        glMultiTexCoord2fARB(GL_TEXTURE1_ARB, x - x_offs, y + y_offs); // left top
434        glMultiTexCoord2fARB(GL_TEXTURE2_ARB, x + x_offs, y - y_offs); // right bottom
435        glMultiTexCoord2fARB(GL_TEXTURE3_ARB, x + x_offs, y + y_offs); // right top
436        glMultiTexCoord2fARB(GL_TEXTURE4_ARB, x - x_offs, y - y_offs); // left bottom
437
438        glMultiTexCoord4fARB(GL_TEXTURE5_ARB, x - x_offs, y, x + x_offs, y); // left right
439        glMultiTexCoord4fARB(GL_TEXTURE6_ARB, x, y + y_offs, x, y - y_offs); // top bottom
440
441        glVertex3f(x - 0.5f, y - 0.5f, -0.5f);
442}
443
444
445void SsaoShader::AntiAliasing(FrameBufferObject *fbo)
446{
447        //GLuint colorsTex = mFbo4->GetColorBuffer(0)->GetTexture();
448        GLuint colorsTex = mNewFbo->GetColorBuffer(1)->GetTexture();
449        GLuint normalsTex = fbo->GetColorBuffer(2)->GetTexture();
450       
451        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
452
453        cgGLEnableProfile(RenderState::sCgFragmentProfile);
454        cgGLBindProgram(sCgAntiAliasingProgram);
455       
456        cgGLSetTextureParameter(sColorsTexAntiAliasingParam, colorsTex);
457        cgGLEnableTextureParameter(sColorsTexAntiAliasingParam);
458
459        cgGLSetTextureParameter(sNormalsTexAntiAliasingParam, normalsTex);
460        cgGLEnableTextureParameter(sNormalsTexAntiAliasingParam);
461
462        glColor3f(1.0f, 1.0f, 1.0f);
463
464        float offs2 = 0.5f;
465
466        glBegin(GL_QUADS);
467
468        // the neighbouring texels
469        float x_offs = 1.0f / mWidth;
470        float y_offs = 1.0f / mHeight;
471
472        SetVertex(0, 0, x_offs, y_offs);
473        SetVertex(1, 0, x_offs, y_offs);
474        SetVertex(1, 1, x_offs, y_offs);
475        SetVertex(0, 1, x_offs, y_offs);
476
477        glEnd();
478
479        cgGLDisableTextureParameter(sColorsTexAntiAliasingParam);
480        cgGLDisableTextureParameter(sNormalsTexAntiAliasingParam);
481
482        PrintGLerror("antialiasing");
483}
484
485
486void SsaoShader::FirstPass(FrameBufferObject *fbo)
487{
488        GLuint colorsTex = fbo->GetColorBuffer(0)->GetTexture();
489        GLuint positionsTex = fbo->GetColorBuffer(1)->GetTexture();
490        GLuint normalsTex = fbo->GetColorBuffer(2)->GetTexture();
491
492        mFbo3->Bind();
493        //mNewFbo->Bind();
494
495        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
496       
497        glDrawBuffers(1, mymrt);
498
499        cgGLEnableProfile(RenderState::sCgFragmentProfile);
500
501        cgGLBindProgram(sCgDeferredProgram2);
502
503        cgGLSetTextureParameter(sColorsTexDeferredParam, colorsTex);
504        cgGLEnableTextureParameter(sColorsTexDeferredParam);
505
506        cgGLSetTextureParameter(sPositionsTexDeferredParam, positionsTex);
507        cgGLEnableTextureParameter(sPositionsTexDeferredParam);
508
509        cgGLSetTextureParameter(sNormalsTexDeferredParam, normalsTex);
510        cgGLEnableTextureParameter(sNormalsTexDeferredParam);
511       
512        glColor3f(1.0f, 1.0f, 1.0f);
513
514        const float offs = 0.5f;
515
516        glBegin(GL_QUADS);
517
518        glTexCoord2f(0, 0); glVertex3f(-offs, -offs, -0.5f);
519        glTexCoord2f(1, 0); glVertex3f( offs, -offs, -0.5f);
520        glTexCoord2f(1, 1); glVertex3f( offs,  offs, -0.5f);
521        glTexCoord2f(0, 1); glVertex3f(-offs,  offs, -0.5f);
522
523        glEnd();
524
525        cgGLDisableTextureParameter(sColorsTexDeferredParam);
526        cgGLDisableTextureParameter(sPositionsTexDeferredParam);
527        cgGLDisableTextureParameter(sNormalsTexDeferredParam);
528
529        cgGLDisableProfile(RenderState::sCgFragmentProfile);
530
531        FrameBufferObject::Release();
532
533        PrintGLerror("deferred shading");
534}
535
536
537void SsaoShader::Combine(FrameBufferObject *fbo)
538{
539        GLuint colorsTex = mFbo3->GetColorBuffer(0)->GetTexture();
540        GLuint ssaoTex = mNewFbo->GetColorBuffer(0)->GetTexture();
541
542        mFbo4->Bind();
543        //mNewFbo->Bind();
544
545        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
546       
547        glDrawBuffers(1, mymrt);
548
549        cgGLEnableProfile(RenderState::sCgFragmentProfile);
550
551        cgGLBindProgram(sCgCombineProgram);
552
553        cgGLSetTextureParameter(sColorsTexCombineParam, colorsTex);
554        cgGLEnableTextureParameter(sColorsTexCombineParam);
555
556        cgGLSetTextureParameter(sSsaoTexCombineParam, ssaoTex);
557        cgGLEnableTextureParameter(sSsaoTexCombineParam);
558
559        glColor3f(1.0f, 1.0f, 1.0f);
560
561        const float offs = 0.5f;
562
563        glBegin(GL_QUADS);
564
565        glTexCoord2f(0, 0); glVertex3f(-offs, -offs, -0.5f);
566        glTexCoord2f(1, 0); glVertex3f( offs, -offs, -0.5f);
567        glTexCoord2f(1, 1); glVertex3f( offs,  offs, -0.5f);
568        glTexCoord2f(0, 1); glVertex3f(-offs,  offs, -0.5f);
569
570        glEnd();
571
572        cgGLDisableTextureParameter(sColorsTexCombineParam);
573        cgGLDisableTextureParameter(sSsaoTexCombineParam);
574
575        cgGLDisableProfile(RenderState::sCgFragmentProfile);
576
577        FrameBufferObject::Release();
578
579        PrintGLerror("deferred shading");
580}
581
582
583} // namespace
Note: See TracBrowser for help on using the repository browser.