#include "DeferredRenderer.h" #include "FrameBufferObject.h" #include "RenderState.h" #include "SampleGenerator.h" #include "Vector3.h" #include "Camera.h" #include "shaderenv.h" #include "Halton.h" #include "ShadowMapping.h" #include "Light.h" #include "ResourceManager.h" #include #include #ifdef _CRT_SET #define _CRTDBG_MAP_ALLOC #include #include // redefine new operator #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) #define new DEBUG_NEW #endif using namespace std; static void startil() { ilInit(); assert(ilGetError() == IL_NO_ERROR); } static void stopil() { ilShutDown(); assert(ilGetError() == IL_NO_ERROR); } namespace CHCDemoEngine { static ShaderProgram *sCgSsaoProgram = NULL; static ShaderProgram *sCgGiProgram = NULL; static ShaderProgram *sCgDeferredProgram = NULL; static ShaderProgram *sCgAntiAliasingProgram = NULL; static ShaderProgram *sCgDeferredShadowProgram = NULL; static ShaderProgram *sCgCombineSsaoProgram = NULL; static ShaderProgram *sCgCombineIllumProgram = NULL; static ShaderProgram *sCgLogLumProgram = NULL; static ShaderProgram *sCgToneProgram = NULL; static ShaderProgram *sCgDownSampleProgram = NULL; ShaderContainer DeferredRenderer::sShaders; static GLuint noiseTex = 0; // ssao random spherical samples static Sample2 samples2[NUM_SAMPLES]; // number of pcf tabs Sample2 pcfSamples[NUM_PCF_TABS]; int DeferredRenderer::colorBufferIdx = 0; static float GaussianDistribution(float x, float y, float rho) { float g = 1.0f / sqrtf(2.0f * M_PI * rho * rho); g *= expf( -(x*x + y*y) / (2.0f * rho * rho)); return g; } static void PrintGLerror(char *msg) { GLenum errCode; const GLubyte *errStr; if ((errCode = glGetError()) != GL_NO_ERROR) { errStr = gluErrorString(errCode); fprintf(stderr,"OpenGL ERROR: %s: %s\n", errStr, msg); } } static void ComputeSampleOffsets(float *sampleOffsets, int w, int h) { /* const float xoffs = 0.5f / w; const float yoffs = 0.5f / h; sampleOffsets[0] = xoffs; sampleOffsets[1] = yoffs; sampleOffsets[2] = xoffs; sampleOffsets[3] = -yoffs; sampleOffsets[4] = -xoffs; sampleOffsets[5] = -yoffs; sampleOffsets[6] = -xoffs; sampleOffsets[7] = yoffs; */ //const float xoffs = .5f / w; //const float yoffs = .5f / h; const float xoffs = 1.0f / w; const float yoffs = 1.0f / h; int idx = 0; for (int x = -1; x <= 1; ++ x) { for (int y = -1; y <= 1; ++ y) { sampleOffsets[idx + 0] = x * xoffs; sampleOffsets[idx + 1] = y * yoffs; idx += 2; } } } void DeferredRenderer::DrawQuad(ShaderProgram *p) { Vector3 tl, tr, bl, br; ComputeViewVectors(tl, tr, bl, br); p->Bind(); // note: slightly larger texture could hide ambient occlusion error on border but costs resolution const float offs = 0.5f; glBegin(GL_QUADS); glTexCoord2f(0, 0); glMultiTexCoord3fARB(GL_TEXTURE1_ARB, bl.x, bl.y, bl.z); glVertex3f(-offs, -offs, -0.5f); glTexCoord2f(1, 0); glMultiTexCoord3fARB(GL_TEXTURE1_ARB, br.x, br.y, br.z); glVertex3f( offs, -offs, -0.5f); glTexCoord2f(1, 1); glMultiTexCoord3fARB(GL_TEXTURE1_ARB, tr.x, tr.y, tr.z); glVertex3f( offs, offs, -0.5f); glTexCoord2f(0, 1); glMultiTexCoord3fARB(GL_TEXTURE1_ARB, tl.x, tl.y, tl.z); glVertex3f(-offs, offs, -0.5f); glEnd(); } /** Generate poisson disc distributed sample points on the unit disc */ static void GenerateSamples(int sampling) { switch (sampling) { case DeferredRenderer::SAMPLING_POISSON: { PoissonDiscSampleGenerator2 poisson(NUM_SAMPLES, 1.0f); poisson.Generate((float *)samples2); } break; case DeferredRenderer::SAMPLING_QUADRATIC: { QuadraticDiscSampleGenerator2 g(NUM_SAMPLES, 1.0f); g.Generate((float *)samples2); } break; default: // SAMPLING_DEFAULT { RandomSampleGenerator2 g(NUM_SAMPLES, 1.0f); g.Generate((float *)samples2); } } } static void CreateNoiseTex2D(int w, int h) { //GLubyte *randomNormals = new GLubyte[mWidth * mHeight * 3]; float *randomNormals = new float[w * h * 3]; static HaltonSequence halton; float r[2]; for (int i = 0; i < w * h * 3; i += 3) { // create random samples on a circle r[0] = RandomValue(0, 1); //halton.GetNext(1, r); const float theta = 2.0f * acos(sqrt(1.0f - r[0])); randomNormals[i + 0] = cos(theta); randomNormals[i + 1] = sin(theta); randomNormals[i + 2] = 0; } glEnable(GL_TEXTURE_2D); glGenTextures(1, &noiseTex); glBindTexture(GL_TEXTURE_2D, noiseTex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, w, h, 0, GL_RGB, GL_FLOAT, randomNormals); glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); delete [] randomNormals; cout << "created noise texture" << endl; PrintGLerror("noisetexture"); } static void InitBuffer(FrameBufferObject *fbo, int index) { // read the second buffer, write to the first buffer fbo->Bind(); glDrawBuffers(1, mrt + index); //glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnd(); FrameBufferObject::Release(); } DeferredRenderer::DeferredRenderer(int w, int h, Camera *cam, float scaleFactor): mWidth(w), mHeight(h), mCamera(cam), mUseTemporalCoherence(true), mRegenerateSamples(true), mSamplingMethod(SAMPLING_POISSON), mShadingMethod(DEFAULT), mIllumFboIndex(0) { // create noise texture for ssao CreateNoiseTex2D(w, h); /////////// //-- the flip-flop fbos mIllumFbo = new FrameBufferObject(w / 2, h / 2, FrameBufferObject::DEPTH_NONE); mFBOs.push_back(mIllumFbo); for (int i = 0; i < 4; ++ i) { mIllumFbo->AddColorBuffer(ColorBufferObject::RGB_FLOAT_32, ColorBufferObject::WRAP_CLAMP_TO_EDGE, ColorBufferObject::FILTER_LINEAR); InitBuffer(mIllumFbo, i); } mDownSampleFbo = new FrameBufferObject(w / 2, h / 2, FrameBufferObject::DEPTH_NONE); mDownSampleFbo->AddColorBuffer(ColorBufferObject::RGBA_FLOAT_32, ColorBufferObject::WRAP_CLAMP_TO_EDGE, ColorBufferObject::FILTER_LINEAR); mDownSampleFbo->AddColorBuffer(ColorBufferObject::RGB_FLOAT_16, ColorBufferObject::WRAP_CLAMP_TO_EDGE, ColorBufferObject::FILTER_LINEAR); mFBOs.push_back(mDownSampleFbo); InitCg(); } DeferredRenderer::~DeferredRenderer() { CLEAR_CONTAINER(mFBOs); glDeleteTextures(1, &noiseTex); } void DeferredRenderer::SetUseTemporalCoherence(bool temporal) { mUseTemporalCoherence = temporal; } void DeferredRenderer::InitCg() { ResourceManager *rm = ResourceManager::GetSingleton(); sCgDeferredProgram = rm->CreateFragmentProgram("deferred", "main"); sCgDeferredShadowProgram = rm->CreateFragmentProgram("deferred", "main_shadow"); sCgSsaoProgram = rm->CreateFragmentProgram("ssao", "main"); sCgGiProgram = rm->CreateFragmentProgram("globillum", "main"); sCgCombineIllumProgram = rm->CreateFragmentProgram("globillum", "combine"); sCgCombineSsaoProgram = rm->CreateFragmentProgram("ssao", "combine"); sCgAntiAliasingProgram = rm->CreateFragmentProgram("antialiasing", "main"); sCgToneProgram = rm->CreateFragmentProgram("tonemap", "ToneMap"); sCgDownSampleProgram = rm->CreateFragmentProgram("tonemap", "DownSample"); sCgToneProgram = rm->CreateFragmentProgram("tonemap", "ToneMap"); sCgLogLumProgram = rm->CreateFragmentProgram("tonemap", "CalcAvgLogLum"); int i; i = 0; sCgSsaoProgram->AddParameter("colors", i ++); sCgSsaoProgram->AddParameter("normals", i ++); sCgSsaoProgram->AddParameter("oldTex", i ++); sCgSsaoProgram->AddParameter("noise", i ++); sCgSsaoProgram->AddParameter("eyePos", i ++); sCgSsaoProgram->AddParameter("temporalCoherence", i ++); sCgSsaoProgram->AddParameter("samples", i ++); sCgSsaoProgram->AddParameter("bl", i ++); sCgSsaoProgram->AddParameter("br", i ++); sCgSsaoProgram->AddParameter("tl", i ++); sCgSsaoProgram->AddParameter("tr", i ++); sCgSsaoProgram->AddParameter("modelViewProj", i ++); sCgSsaoProgram->AddParameter("oldModelViewProj", i ++); i = 0; sCgGiProgram->AddParameter("colors", i ++); sCgGiProgram->AddParameter("normals", i ++); sCgGiProgram->AddParameter("noise", i ++); sCgGiProgram->AddParameter("oldSsaoTex", i ++); sCgGiProgram->AddParameter("oldIllumTex", i ++); sCgGiProgram->AddParameter("eyePos", i ++); sCgGiProgram->AddParameter("temporalCoherence", i ++); sCgGiProgram->AddParameter("samples", i ++); sCgGiProgram->AddParameter("bl", i ++); sCgGiProgram->AddParameter("br", i ++); sCgGiProgram->AddParameter("tl", i ++); sCgGiProgram->AddParameter("tr", i ++); sCgGiProgram->AddParameter("oldModelViewProj", i ++); sCgGiProgram->AddParameter("modelViewProj", i ++); sCgAntiAliasingProgram->AddParameter("colors", 0); sCgAntiAliasingProgram->AddParameter("normals", 1); sCgDeferredProgram->AddParameter("colors", 0); sCgDeferredProgram->AddParameter("normals", 1); sCgDeferredProgram->AddParameter("lightDir", 2); sCgLogLumProgram->AddParameter("colors", 0); sCgToneProgram->AddParameter("colors", 0); sCgToneProgram->AddParameter("imageKey", 1); sCgToneProgram->AddParameter("whiteLum", 2); sCgToneProgram->AddParameter("middleGrey", 3); sCgDownSampleProgram->AddParameter("colors", 0); sCgDownSampleProgram->AddParameter("downSampleOffs", 1); sCgDeferredShadowProgram->AddParameter("colors", 0); sCgDeferredShadowProgram->AddParameter("normals", 1); sCgDeferredShadowProgram->AddParameter("shadowMap", 2); sCgDeferredShadowProgram->AddParameter("noise", 3); sCgDeferredShadowProgram->AddParameter("shadowMatrix", 4); sCgDeferredShadowProgram->AddParameter("sampleWidth", 5); sCgDeferredShadowProgram->AddParameter("lightDir", 6); sCgDeferredShadowProgram->AddParameter("eyePos", 7); sCgDeferredShadowProgram->AddParameter("samples", 8); sCgDeferredShadowProgram->AddParameter("weights", 9); sCgCombineIllumProgram->AddParameter("colors", 0); sCgCombineIllumProgram->AddParameter("ssaoTex", 1); sCgCombineIllumProgram->AddParameter("illumTex", 2); sCgCombineSsaoProgram->AddParameter("colors", 0); sCgCombineSsaoProgram->AddParameter("ssaoTex", 1); sCgCombineSsaoProgram->AddParameter("filterOffs", 2); sCgCombineSsaoProgram->AddParameter("filterWeights", 3); float filterWeights[NUM_PCF_TABS]; PoissonDiscSampleGenerator2 poisson(NUM_PCF_TABS, 1.0f); poisson.Generate((float *)pcfSamples); for (int i = 0; i < NUM_PCF_TABS; ++ i) { filterWeights[i] = GaussianDistribution(pcfSamples[i].x, pcfSamples[i].y, 1.0f); } sCgDeferredShadowProgram->SetArray2f(8, (float *)pcfSamples, NUM_PCF_TABS); sCgDeferredShadowProgram->SetArray1f(9, (float *)filterWeights, NUM_PCF_TABS); PrintGLerror("init"); } void DeferredRenderer::Render(FrameBufferObject *fbo, const Matrix4x4 &oldProjViewMatrix, const Matrix4x4 &projViewMatrix, float tempCohFactor, DirectionalLight *light, bool useToneMapping, ShadowMap *shadowMap ) { // switch roles of old and new fbo // the algorihm uses two input fbos, where the one // contais the color buffer from the last frame, // the other one will be written mIllumFboIndex = 2 - mIllumFboIndex; ResourceManager::GetSingleton()->EnableFragmentProfile(); glDisable(GL_ALPHA_TEST); glDisable(GL_TEXTURE_2D); glDisable(GL_LIGHTING); glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); glPushAttrib(GL_VIEWPORT_BIT); glViewport(0, 0, mWidth, mHeight); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); const float offs = 0.5f; glOrtho(-offs, offs, -offs, offs, 0, 1); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); if (shadowMap) FirstPassShadow(fbo, light, shadowMap); else FirstPass(fbo, light); switch (mShadingMethod) { case SSAO: // downsample fbo buffers for colors, normals DownSample(fbo, colorBufferIdx, mDownSampleFbo, 0); DownSample(fbo, 1, mDownSampleFbo, 1); ComputeSsao(fbo, tempCohFactor, projViewMatrix, oldProjViewMatrix); CombineSsao(fbo); break; case GI: // downsample fbo buffers for colors, normals DownSample(fbo, colorBufferIdx, mDownSampleFbo, 0); DownSample(fbo, 1, mDownSampleFbo, 1); ComputeGlobIllum(fbo, tempCohFactor, projViewMatrix, oldProjViewMatrix); CombineIllum(fbo); break; default: // DEFAULT // do nothing: standard deferred shading break; } if (useToneMapping) { float imageKey, whiteLum, middleGrey; ComputeToneParameters(fbo, light, imageKey, whiteLum, middleGrey); ToneMap(fbo, imageKey, whiteLum, middleGrey); } AntiAliasing(fbo, light); glEnable(GL_LIGHTING); glDisable(GL_TEXTURE_2D); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glPopAttrib(); FrameBufferObject::Release(); ResourceManager::GetSingleton()->DisableFragmentProfile(); } void DeferredRenderer::ComputeSsao(FrameBufferObject *fbo, float tempCohFactor, const Matrix4x4 &projViewMatrix, const Matrix4x4 &oldProjViewMatrix ) { #if 0 GLuint colorsTex = fbo->GetColorBuffer(colorBufferIdx)->GetTexture(); GLuint normalsTex = fbo->GetColorBuffer(1)->GetTexture(); #else GLuint colorsTex = mDownSampleFbo->GetColorBuffer(0)->GetTexture(); GLuint normalsTex = mDownSampleFbo->GetColorBuffer(1)->GetTexture(); #endif GLuint oldTex = mIllumFbo->GetColorBuffer(2 - mIllumFboIndex)->GetTexture(); glPushAttrib(GL_VIEWPORT_BIT); glViewport(0, 0, mIllumFbo->GetWidth(), mIllumFbo->GetHeight()); // read the second buffer, write to the first buffer mIllumFbo->Bind(); glDrawBuffers(1, mrt + mIllumFboIndex); sCgSsaoProgram->SetTexture(0, colorsTex); sCgSsaoProgram->SetTexture(1, normalsTex); sCgSsaoProgram->SetTexture(2, oldTex); sCgSsaoProgram->SetTexture(3, noiseTex); const Vector3 pos = mCamera->GetPosition(); sCgSsaoProgram->SetValue3f(4, pos.x, pos.y, pos.z); sCgSsaoProgram->SetValue1f(5, (mUseTemporalCoherence && !mRegenerateSamples) ? tempCohFactor : 0); if (mUseTemporalCoherence || mRegenerateSamples) { mRegenerateSamples = false; // q: should we generate new samples or only rotate the old ones? // in the first case, the sample patterns look nicer, but the kernel // needs longer to converge GenerateSamples(mSamplingMethod); sCgSsaoProgram->SetArray2f(6, (float *)samples2, NUM_SAMPLES); } Vector3 tl, tr, bl, br; ComputeViewVectors(tl, tr, bl, br); sCgSsaoProgram->SetValue3f(7, bl.x, bl.y, bl.z); sCgSsaoProgram->SetValue3f(8, br.x, br.y, br.z); sCgSsaoProgram->SetValue3f(9, tl.x, tl.y, tl.z); sCgSsaoProgram->SetValue3f(10, tr.x, tr.y, tr.z); sCgSsaoProgram->SetMatrix(11, projViewMatrix); sCgSsaoProgram->SetMatrix(12, oldProjViewMatrix); DrawQuad(sCgSsaoProgram); glPopAttrib(); PrintGLerror("ssao first pass"); } void DeferredRenderer::ComputeViewVectors(Vector3 &tl, Vector3 &tr, Vector3 &bl, Vector3 &br) { Vector3 ftl, ftr, fbl, fbr, ntl, ntr, nbl, nbr; mCamera->ComputePoints(ftl, ftr, fbl, fbr, ntl, ntr, nbl, nbr); bl = Normalize(nbl - fbl); br = Normalize(nbr - fbr); tl = Normalize(ntl - ftl); tr = Normalize(ntr - ftr); } static void SetVertex(float x, float y, float x_offs, float y_offs) { glMultiTexCoord2fARB(GL_TEXTURE0_ARB, x, y); // center glMultiTexCoord2fARB(GL_TEXTURE1_ARB, x - x_offs, y + y_offs); // left top glMultiTexCoord2fARB(GL_TEXTURE2_ARB, x + x_offs, y - y_offs); // right bottom glMultiTexCoord2fARB(GL_TEXTURE3_ARB, x + x_offs, y + y_offs); // right top glMultiTexCoord2fARB(GL_TEXTURE4_ARB, x - x_offs, y - y_offs); // left bottom glMultiTexCoord4fARB(GL_TEXTURE5_ARB, x - x_offs, y, x + x_offs, y); // left right glMultiTexCoord4fARB(GL_TEXTURE6_ARB, x, y + y_offs, x, y - y_offs); // top bottom glVertex3f(x - 0.5f, y - 0.5f, -0.5f); } void DeferredRenderer::AntiAliasing(FrameBufferObject *fbo, DirectionalLight *light) { FrameBufferObject::Release(); ColorBufferObject *colorBuffer = fbo->GetColorBuffer(colorBufferIdx); GLuint colorsTex = colorBuffer->GetTexture(); GLuint normalsTex = fbo->GetColorBuffer(2)->GetTexture(); sCgAntiAliasingProgram->SetTexture(0, colorsTex); sCgAntiAliasingProgram->SetTexture(1, normalsTex); sCgAntiAliasingProgram->Bind(); glColor3f(1.0f, 1.0f, 1.0f); glBegin(GL_QUADS); // the neighbouring texels float x_offs = 1.0f / mWidth; float y_offs = 1.0f / mHeight; SetVertex(0, 0, x_offs, y_offs); SetVertex(1, 0, x_offs, y_offs); SetVertex(1, 1, x_offs, y_offs); SetVertex(0, 1, x_offs, y_offs); glEnd(); PrintGLerror("antialiasing"); } void DeferredRenderer::FirstPass(FrameBufferObject *fbo, DirectionalLight *light) { GLuint colorsTex = fbo->GetColorBuffer(colorBufferIdx)->GetTexture(); GLuint normalsTex = fbo->GetColorBuffer(1)->GetTexture(); GLuint positionsTex = fbo->GetColorBuffer(2)->GetTexture(); fbo->Bind(); colorBufferIdx = 3 - colorBufferIdx; glDrawBuffers(1, mrt + colorBufferIdx); const Vector3 lightDir = -light->GetDirection(); sCgDeferredProgram->SetTexture(0, colorsTex); sCgDeferredProgram->SetTexture(1, normalsTex); sCgDeferredProgram->SetValue3f(2, lightDir.x, lightDir.y, lightDir.z); DrawQuad(sCgDeferredProgram); PrintGLerror("deferred shading"); } void DeferredRenderer::ComputeGlobIllum(FrameBufferObject *fbo, float tempCohFactor, const Matrix4x4 &projViewMatrix, const Matrix4x4 &oldProjViewMatrix) { #if 0 GLuint colorsTex = fbo->GetColorBuffer(colorBufferIdx)->GetTexture(); GLuint normalsTex = fbo->GetColorBuffer(1)->GetTexture(); #else GLuint colorsTex = mDownSampleFbo->GetColorBuffer(0)->GetTexture(); GLuint normalsTex = mDownSampleFbo->GetColorBuffer(1)->GetTexture(); #endif glPushAttrib(GL_VIEWPORT_BIT); glViewport(0, 0, mIllumFbo->GetWidth(), mIllumFbo->GetHeight()); // read the second buffer, write to the first buffer mIllumFbo->Bind(); glDrawBuffers(2, mrt + mIllumFboIndex); GLuint oldSsaoTex = mIllumFbo->GetColorBuffer(2 - mIllumFboIndex)->GetTexture(); GLuint oldIllumTex = mIllumFbo->GetColorBuffer(2 - mIllumFboIndex + 1)->GetTexture(); sCgGiProgram->SetTexture(0, colorsTex); sCgGiProgram->SetTexture(1, normalsTex); sCgGiProgram->SetTexture(2, noiseTex); sCgGiProgram->SetTexture(3, oldSsaoTex); sCgGiProgram->SetTexture(4, oldIllumTex); const Vector3 pos = mCamera->GetPosition(); sCgGiProgram->SetValue3f(5, pos.x, pos.y, pos.z); sCgGiProgram->SetValue1f(6, (mUseTemporalCoherence && !mRegenerateSamples) ? tempCohFactor : 0); if (mUseTemporalCoherence || mRegenerateSamples) { mRegenerateSamples = false; // q: should we generate new samples or only rotate the old ones? // in the first case, the sample patterns look nicer, but the kernel // needs longer to converge GenerateSamples(mSamplingMethod); sCgGiProgram->SetArray2f(7, (float *)samples2, NUM_SAMPLES); } Vector3 tl, tr, bl, br; ComputeViewVectors(tl, tr, bl, br); sCgGiProgram->SetValue3f(8, bl.x, bl.y, bl.z); sCgGiProgram->SetValue3f(9, br.x, br.y, br.z); sCgGiProgram->SetValue3f(10, tl.x, tl.y, tl.z); sCgGiProgram->SetValue3f(11, tr.x, tr.y, tr.z); sCgGiProgram->SetMatrix(12, oldProjViewMatrix); sCgGiProgram->SetMatrix(13, projViewMatrix); DrawQuad(sCgGiProgram); glPopAttrib(); PrintGLerror("globillum first pass"); } void DeferredRenderer::CombineIllum(FrameBufferObject *fbo) { GLuint colorsTex = fbo->GetColorBuffer(colorBufferIdx)->GetTexture(); GLuint ssaoTex = mIllumFbo->GetColorBuffer(mIllumFboIndex)->GetTexture(); GLuint illumTex = mIllumFbo->GetColorBuffer(mIllumFboIndex + 1)->GetTexture(); fbo->Bind(); // overwrite old color texture colorBufferIdx = 3 - colorBufferIdx; glDrawBuffers(1, mrt + colorBufferIdx); sCgCombineIllumProgram->SetTexture(0, colorsTex); sCgCombineIllumProgram->SetTexture(1, ssaoTex); sCgCombineIllumProgram->SetTexture(2, illumTex); DrawQuad(sCgCombineIllumProgram); PrintGLerror("combine"); } void DeferredRenderer::CombineSsao(FrameBufferObject *fbo) { fbo->Bind(); GLuint colorsTex = fbo->GetColorBuffer(colorBufferIdx)->GetTexture(); GLuint positionsTex = fbo->GetColorBuffer(2)->GetTexture(); GLuint ssaoTex = mIllumFbo->GetColorBuffer(mIllumFboIndex)->GetTexture(); // overwrite old color texture colorBufferIdx = 3 - colorBufferIdx; glDrawBuffers(1, mrt + colorBufferIdx); float filterOffsets[NUM_DOWNSAMPLES * 2]; float filterWeights[NUM_DOWNSAMPLES]; PoissonDiscSampleGenerator2 poisson(NUM_DOWNSAMPLES, 1.0f); poisson.Generate((float *)filterOffsets); const float xoffs = 2.0f / fbo->GetWidth(); const float yoffs = 2.0f / fbo->GetHeight(); for (int i = 0; i < NUM_DOWNSAMPLES; ++ i) { float x = filterOffsets[2 * i + 0]; float y = filterOffsets[2 * i + 1]; filterOffsets[2 * i + 0] *= xoffs; filterOffsets[2 * i + 1] *= yoffs; filterWeights[i] = GaussianDistribution(x, y, 1.0f); } sCgCombineSsaoProgram->SetTexture(0, colorsTex); sCgCombineSsaoProgram->SetTexture(1, ssaoTex); sCgCombineSsaoProgram->SetArray2f(2, (float *)filterOffsets, NUM_DOWNSAMPLES); sCgCombineSsaoProgram->SetArray1f(3, (float *)filterWeights, NUM_DOWNSAMPLES); DrawQuad(sCgCombineSsaoProgram); PrintGLerror("combine ssao"); } void DeferredRenderer::FirstPassShadow(FrameBufferObject *fbo, DirectionalLight *light, ShadowMap *shadowMap) { fbo->Bind(); GLuint colorsTex = fbo->GetColorBuffer(colorBufferIdx)->GetTexture(); GLuint normalsTex = fbo->GetColorBuffer(1)->GetTexture(); GLuint positionsTex = fbo->GetColorBuffer(2)->GetTexture(); GLuint shadowTex = shadowMap->GetDepthTexture(); Matrix4x4 shadowMatrix; shadowMap->GetTextureMatrix(shadowMatrix); colorBufferIdx = 3 - colorBufferIdx; glDrawBuffers(1, mrt + colorBufferIdx); sCgDeferredShadowProgram->SetTexture(0, colorsTex); sCgDeferredShadowProgram->SetTexture(1, normalsTex); sCgDeferredShadowProgram->SetTexture(2, shadowTex); sCgDeferredShadowProgram->SetTexture(3, noiseTex); sCgDeferredShadowProgram->SetMatrix(4, shadowMatrix); sCgDeferredShadowProgram->SetValue1f(5, 2.0f / shadowMap->GetSize()); const Vector3 lightDir = -light->GetDirection(); sCgDeferredShadowProgram->SetValue3f(6, lightDir.x, lightDir.y, lightDir.z); const Vector3 pos = mCamera->GetPosition(); sCgDeferredShadowProgram->SetValue3f(7, pos.x, pos.y, pos.z); DrawQuad(sCgDeferredShadowProgram); PrintGLerror("deferred shading + shadows"); } void DeferredRenderer::SetSamplingMethod(SAMPLING_METHOD s) { if (s != mSamplingMethod) { mSamplingMethod = s; mRegenerateSamples = true; } } void DeferredRenderer::SetShadingMethod(SHADING_METHOD s) { if (s != mShadingMethod) { mShadingMethod = s; mRegenerateSamples = true; } } void DeferredRenderer::ComputeToneParameters(FrameBufferObject *fbo, DirectionalLight *light, float &imageKey, float &whiteLum, float &middleGrey) { // hack: estimate value where sky burns out whiteLum = log(WHITE_LUMINANCE); //////////////////// //-- linear interpolate brightness key depending on the current sun position const float minKey = 0.09f; const float maxKey = 0.36f; const float lightIntensity = DotProd(-light->GetDirection(), Vector3::UNIT_Z()); middleGrey = lightIntensity * maxKey + (1.0f - lightIntensity) * minKey; ////////// //-- compute avg loglum ColorBufferObject *colorBuffer = fbo->GetColorBuffer(colorBufferIdx); GLuint colorsTex = colorBuffer->GetTexture(); fbo->Bind(); colorBufferIdx = 3 - colorBufferIdx; glDrawBuffers(1, mrt + colorBufferIdx); sCgLogLumProgram->SetTexture(0, colorsTex); DrawQuad(sCgLogLumProgram); PrintGLerror("ToneMapParams"); /////////////////// //-- compute avg loglum in scene using mipmapping glBindTexture(GL_TEXTURE_2D, fbo->GetColorBuffer(colorBufferIdx)->GetTexture()); glGenerateMipmapEXT(GL_TEXTURE_2D); } static void ExportData(float *data, int w, int h) { startil(); cout << "w: " << w << " h: " << h << endl; ILstring filename = ILstring("downsample2.jpg"); ilRegisterType(IL_FLOAT); const int depth = 1; const int bpp = 4; if (!ilTexImage(w, h, depth, bpp, IL_RGBA, IL_FLOAT, data)) { cerr << "IL error " << ilGetError() << endl; stopil(); return; } if (!ilSaveImage(filename)) { cerr << "TGA write error " << ilGetError() << endl; } stopil(); } void DeferredRenderer::DownSample(FrameBufferObject *fbo, int bufferIdx, FrameBufferObject *downSampleFbo, int downSampleBufferIdx) { ColorBufferObject *colorBuffer = fbo->GetColorBuffer(bufferIdx); GLuint colorsTex = colorBuffer->GetTexture(); glPushAttrib(GL_VIEWPORT_BIT); glViewport(0, 0, downSampleFbo->GetWidth(), downSampleFbo->GetHeight()); sCgDownSampleProgram->SetTexture(0, colorsTex); float downSampleOffsets[NUM_DOWNSAMPLES * 2]; ComputeSampleOffsets(downSampleOffsets, fbo->GetWidth(), fbo->GetHeight()); sCgDownSampleProgram->SetArray2f(1, (float *)downSampleOffsets, NUM_DOWNSAMPLES); mDownSampleFbo->Bind(); glDrawBuffers(1, mrt + downSampleBufferIdx); DrawQuad(sCgDownSampleProgram); glPopAttrib(); PrintGLerror("downsample"); } void DeferredRenderer::ToneMap(FrameBufferObject *fbo, float imageKey, float whiteLum, float middleGrey) { ColorBufferObject *colorBuffer = fbo->GetColorBuffer(colorBufferIdx); GLuint colorsTex = colorBuffer->GetTexture(); fbo->Bind(); colorBufferIdx = 3 - colorBufferIdx; glDrawBuffers(1, mrt + colorBufferIdx); sCgToneProgram->SetTexture(0, colorsTex); sCgToneProgram->SetValue1f(1, imageKey); sCgToneProgram->SetValue1f(2, whiteLum); sCgToneProgram->SetValue1f(3, middleGrey); DrawQuad(sCgToneProgram); PrintGLerror("ToneMap"); } } // namespace