#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 "ShaderManager.h" #include "Texture.h" #include #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 *sCgFilterSsaoProgram = NULL; static ShaderProgram *sCgCombineIllumProgram = NULL; static ShaderProgram *sCgLogLumProgram = NULL; static ShaderProgram *sCgToneProgram = NULL; static ShaderProgram *sCgDownSampleProgram = NULL; static ShaderProgram *sCgScaleDepthProgram = NULL; static ShaderProgram *sCgPrepareSsaoProgram = NULL; static ShaderProgram *sCgLenseFlareProgram = NULL; static ShaderProgram *sCgDOFProgram = NULL; static GLuint noiseTex2D = 0; static GLuint noiseTex1D = 0; // ssao random spherical samples //static Sample2 samples2[NUM_SAMPLES]; #define NUM_PRECOMPUTED_SAMPLES 2001 static Sample2 samples2[NUM_PRECOMPUTED_SAMPLES]; // pcf samples static Sample2 pcfSamples[NUM_PCF_TABS]; // dof samples static Sample2 dofSamples[NUM_DOF_TABS]; static Texture *sHaloTex[5]; int DeferredRenderer::colorBufferIdx = 0; /** Helper method that computes the view vectors in the corners of the current view frustum. */ static void ComputeViewVectors(PerspectiveCamera *cam, Vector3 &bl, Vector3 &br, Vector3 &tl, Vector3 &tr) { Vector3 ftl, ftr, fbl, fbr, ntl, ntr, nbl, nbr; cam->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 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 Sample2 UnitTest(float x, float y, int wi, int he) { Sample2 s; s.x = float(floor(x * (float)wi - 0.5f) + 1.0f) / (float)wi; s.y = float(floor(y * (float)he - 0.5f) + 1.0f) / (float)he; return s; } static void ComputeSampleOffsets(float *sampleOffsets, int imageW, int imageH, float width, int samples) { const float xoffs = width / (float)imageW; const float yoffs = width / (float)imageH; const int numSamples = (int)sqrt((float)samples); const int startSamples = -numSamples / 2; const int endSamples = numSamples + startSamples - 1; //cout << startSamples << " " << endSamples << endl; int idx = 0; for (int x = startSamples; x <= endSamples; ++ x) { for (int y = startSamples; y <= endSamples; ++ y) { sampleOffsets[idx + 0] = (float)x * xoffs; sampleOffsets[idx + 1] = (float)y * yoffs; idx += 2; } } } void DeferredRenderer::FlipFbos(FrameBufferObject *fbo) { fbo->Bind(); colorBufferIdx = 3 - colorBufferIdx; glDrawBuffers(1, mrt + colorBufferIdx); } void DeferredRenderer::DrawQuad(ShaderProgram *p) { if (p) p->Bind(); // interpolate the view vector Vector3 bl = mCornersView[0]; Vector3 br = mCornersView[1]; Vector3 tl = mCornersView[2]; Vector3 tr = mCornersView[3]; // note: slightly larger texture could hide ambient occlusion error on border but costs resolution glBegin(GL_QUADS); glTexCoord2f(0, 0); glMultiTexCoord3fARB(GL_TEXTURE1_ARB, bl.x, bl.y, bl.z); glVertex2f( .0f, .0f); glTexCoord2f(1, 0); glMultiTexCoord3fARB(GL_TEXTURE1_ARB, br.x, br.y, br.z); glVertex2f(1.0f, .0f); glTexCoord2f(1, 1); glMultiTexCoord3fARB(GL_TEXTURE1_ARB, tr.x, tr.y, tr.z); glVertex2f(1.0f, 1.0f); glTexCoord2f(0, 1); glMultiTexCoord3fARB(GL_TEXTURE1_ARB, tl.x, tl.y, tl.z); glVertex2f( .0f, 1.0f); glEnd(); } /** Generate poisson disc distributed sample points on the unit disc */ static void GenerateSamples(int sampling) { switch (sampling) { case DeferredRenderer::SAMPLING_POISSON: { static PoissonDiscSampleGenerator2D poisson(NUM_SAMPLES, 1.0f); poisson.Generate((float *)samples2); } break; case DeferredRenderer::SAMPLING_QUADRATIC: { static QuadraticDiscSampleGenerator2D g(NUM_PRECOMPUTED_SAMPLES, 1.0f); //static QuadraticDiscSampleGenerator2D g(NUM_SAMPLES, 1.0f); g.Generate((float *)samples2); } break; default: // SAMPLING_DEFAULT { static RandomSampleGenerator2D g(NUM_SAMPLES, 1.0f); g.Generate((float *)samples2); } } } static void CreateNoiseTex2D(int w, int h) { //GLubyte *randomNormals = new GLubyte[mWidth * mHeight * 3]; Vector3 *randomNormals = new Vector3[w * h]; for (int i = 0; i < w * h; ++ i) { // create random samples on a circle const float r = RandomValue(0, 1); const float theta = 2.0f * acos(sqrt(1.0f - r)); //randomNormals[i] = Vector3(cos(theta), sin(theta), 0); randomNormals[i] = Vector3(RandomValue(-M_PI, M_PI), 0, 0); //Normalize(randomNormals[i]); } glEnable(GL_TEXTURE_2D); glGenTextures(1, &noiseTex2D); glBindTexture(GL_TEXTURE_2D, noiseTex2D); //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_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 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, (float *)randomNormals); glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); delete [] randomNormals; cout << "created noise texture" << endl; PrintGLerror("noisetexture"); } static void PrepareLenseFlare() { string textures[] = {"flare4.tga", "lens2.jpg", "lens3.jpg", "lens4.jpg", "lens1.jpg"}; // todo: delete halo textures for (int i = 0; i < 5; ++ i) { sHaloTex[i] = new Texture(model_path + textures[i]); sHaloTex[i]->SetBoundaryModeS(Texture::BORDER); sHaloTex[i]->SetBoundaryModeT(Texture::BORDER); sHaloTex[i]->Create(); } cout << "prepared lense flare textures" << endl; PrintGLerror("prepare lense flare"); } DeferredRenderer::DeferredRenderer(int w, int h, PerspectiveCamera *cam, bool ssaoUseFullResolution): mWidth(w), mHeight(h), mCamera(cam), mUseTemporalCoherence(true), mRegenerateSamples(true), mSamplingMethod(SAMPLING_POISSON), mShadingMethod(DEFAULT), mIllumFboIndex(0), mSortSamples(true), mKernelRadius(1e-8f), mSampleIntensity(0.2f), mSunVisiblePixels(0), mSavedFrameNumber(-1), mSavedFrameSuffix(""), mMaxDistance(1e6f), mTempCohFactor(.0f), mUseToneMapping(false), mUseAntiAliasing(false), mUseDepthOfField(false), mSsaoUseFullResolution(ssaoUseFullResolution) { /////////// //-- the flip-flop fbos int downSampledWidth, downSampledHeight; if (mSsaoUseFullResolution) { downSampledWidth = w; downSampledHeight = h; cout << "using full resolution ssao" << endl; } else { downSampledWidth = w / 2; downSampledHeight = h / 2; cout << "using half resolution ssao" << endl; } ///////// //-- the illumination fbo is either half size or full size mIllumFbo = new FrameBufferObject(downSampledWidth, downSampledHeight, FrameBufferObject::DEPTH_NONE); mFBOs.push_back(mIllumFbo); for (int i = 0; i < 4; ++ i) { mIllumFbo->AddColorBuffer(ColorBufferObject::RGBA_FLOAT_32, ColorBufferObject::WRAP_CLAMP_TO_EDGE, ColorBufferObject::FILTER_LINEAR); FrameBufferObject::InitBuffer(mIllumFbo, i); } /////////////// //-- the downsampled ssao + color bleeding textures: //-- as GI is mostly low frequency, we can use lower resolution to improve performance mDownSampleFbo = new FrameBufferObject(downSampledWidth, downSampledHeight, FrameBufferObject::DEPTH_NONE); // the downsampled color + depth buffer mDownSampleFbo->AddColorBuffer(ColorBufferObject::RGBA_FLOAT_32, ColorBufferObject::WRAP_CLAMP_TO_EDGE, ColorBufferObject::FILTER_LINEAR); // downsample buffer for the normal texture mDownSampleFbo->AddColorBuffer(ColorBufferObject::RGB_FLOAT_16, ColorBufferObject::WRAP_CLAMP_TO_EDGE, ColorBufferObject::FILTER_LINEAR); for (int i = 0; i < 2; ++ i) { FrameBufferObject::InitBuffer(mDownSampleFbo, i); } mFBOs.push_back(mDownSampleFbo); ///////// //-- temporal buffer mTempFbo = new FrameBufferObject(mWidth, mHeight, FrameBufferObject::DEPTH_NONE); mFBOs.push_back(mTempFbo); mTempFbo->AddColorBuffer(ColorBufferObject::RGBA_FLOAT_32, ColorBufferObject::WRAP_CLAMP_TO_EDGE, ColorBufferObject::FILTER_LINEAR); FrameBufferObject::InitBuffer(mTempFbo, 0); // create noise texture for ssao // for performance reasons we use a smaller texture and repeat it over the screen CreateNoiseTex2D(mIllumFbo->GetWidth() / 4, mIllumFbo->GetWidth() / 4); mProjViewMatrix = IdentityMatrix(); mOldProjViewMatrix = IdentityMatrix(); for (int i = 0; i < 4; ++ i) { mCornersView[i] = mOldCornersView[i] = Vector3::UNIT_X(); } mEyePos = mOldEyePos = Vector3::ZERO(); PrepareLenseFlare(); InitCg(); } DeferredRenderer::~DeferredRenderer() { CLEAR_CONTAINER(mFBOs); glDeleteTextures(1, &noiseTex2D); glDeleteTextures(1, &noiseTex1D); } void DeferredRenderer::InitCg() { ShaderManager *sm = ShaderManager::GetSingleton(); sCgDeferredProgram = sm->CreateFragmentProgram("deferred", "main", "DeferredFrag"); sCgDeferredShadowProgram = sm->CreateFragmentProgram("deferred", "main_shadow", "DeferredFragShadow"); sCgSsaoProgram = sm->CreateFragmentProgram("ssao", "main", "SsaoFrag"); sCgGiProgram = sm->CreateFragmentProgram("globillum", "main", "GiFrag"); sCgCombineIllumProgram = sm->CreateFragmentProgram("globillum", "combine", "CombineGi"); if (mSsaoUseFullResolution) { sCgFilterSsaoProgram = sm->CreateFragmentProgram("combineSsaoSep", "FilterSsaoFullRes", "FilterSsao"); } else { sCgFilterSsaoProgram = sm->CreateFragmentProgram("combineSsaoSep", "FilterSsaoHalfRes", "FilterSsao"); } sCgCombineSsaoProgram = sm->CreateFragmentProgram("combineSsaoSep", "CombineSsao", "CombineSsao"); sCgAntiAliasingProgram = sm->CreateFragmentProgram("antialiasing", "main", "AntiAliasing"); sCgToneProgram = sm->CreateFragmentProgram("tonemap", "ToneMap", "ToneMap"); sCgDownSampleProgram = sm->CreateFragmentProgram("deferred", "Output", "Output"); sCgScaleDepthProgram = sm->CreateFragmentProgram("deferred", "ScaleDepth", "ScaleDepth"); sCgLogLumProgram = sm->CreateFragmentProgram("tonemap", "CalcAvgLogLum", "AvgLogLum"); sCgPrepareSsaoProgram = sm->CreateFragmentProgram("deferred", "PrepareSsao", "PrepareSsao"); sCgLenseFlareProgram = sm->CreateFragmentProgram("lenseFlare", "LenseFlare", "LenseFlare"); sCgDOFProgram = sm->CreateFragmentProgram("depthOfField", "DepthOfField", "DepthOfField"); /////////////////// //-- initialize program parameters string ssaoParams[] = {"colors", "normals", "oldTex", "noiseTex", "temporalCoherence", "samples", "bl", "br", "tl", "tr", "modelViewProj", "oldModelViewProj", "oldEyePos", "oldbl", "oldbr", "oldtl", "oldtr", "attribsTex", "kernelRadius", "sampleIntensity"}; sCgSsaoProgram->AddParameters(ssaoParams, 0, 20); string giParams[] = {"colors", "normals", "noiseTex", "oldSsaoTex", "oldIllumTex", "temporalCoherence", "samples", "bl", "br", "tl", "tr", "oldModelViewProj", "modelViewProj"}; sCgGiProgram->AddParameters(giParams, 0, 13); string toneParams[] = {"colors", "imageKey", "whiteLum", "middleGrey"}; sCgToneProgram->AddParameters(toneParams, 0, 4); //////////////// string deferredShadowParams[] = {"colors", "normals", "shadowMap", "noiseTex", "shadowMatrix", "sampleWidth", "lightDir", "eyePos", "samples", "weights"}; sCgDeferredShadowProgram->AddParameters(deferredShadowParams, 0, 10); //////////////// string combineIllumParams[] = {"colorsTex", "ssaoTex", "illumTex"}; sCgCombineIllumProgram->AddParameters(combineIllumParams, 0, 3); //////////////// string combineSsaoParams[] = {"colorsTex", "ssaoTex", "bl", "br", "tl", "tr", "res"}; //sCgCombineSsaoProgram->AddParameters(combineSsaoParams, 0, 13); sCgCombineSsaoProgram->AddParameters(combineSsaoParams, 0, 7); //////////////// string filterSsaoParams[] = {"colorsTex", "ssaoTex", "bl", "br", "tl", "tr", "res"}; sCgFilterSsaoProgram->AddParameters(filterSsaoParams, 0, 7); ////////////// string deferredParams[] = {"colors", "normals", "lightDir"}; sCgDeferredProgram->AddParameters(deferredParams, 0, 3); /////////////////// string aaParams[] = {"colors", "normals", "offsets"}; sCgAntiAliasingProgram->AddParameters(aaParams, 0, 3); ///////////////////// string downSampleParams[] = {"colors"}; sCgDownSampleProgram->AddParameters(downSampleParams, 0, 1); ///////////////////// string scaleDepthParams[] = {"colors"}; sCgScaleDepthProgram->AddParameters(scaleDepthParams, 0, 1); //////////// sCgLogLumProgram->AddParameter("colors", 0); //////////////// string prepareSsaoParams[] = {"colorsTex", "normalsTex", "diffVals", "oldTex", "oldEyePos", "modelViewProj", "oldModelViewProj", "oldbl", "oldbr", "oldtl", "oldtr"}; sCgPrepareSsaoProgram->AddParameters(prepareSsaoParams, 0, 11); //////////////// string lenseFlareParams[] = {"colorsTex", "flareTex1", "flareTex2", "flareTex3", "flareTex4", "flareTex5", "vectorToLight", "distanceToLight", "sunVisiblePixels"}; sCgLenseFlareProgram->AddParameters(lenseFlareParams, 0, 9); //////////////// string dofParams[] = {"colorsTex", "filterOffs", "sceneRange", "zFocus"}; sCgDOFProgram->AddParameters(dofParams, 0, 4); ///////// //-- pcf tabs for shadowing float filterWeights[NUM_PCF_TABS]; PoissonDiscSampleGenerator2D poisson2(NUM_PCF_TABS, 1.0f); poisson2.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); ///////// //-- pcf tabs for depth of field // todo matt: it is stupid to put num samples and width of kernel into constructor => change this!!! PoissonDiscSampleGenerator2D poisson3(NUM_DOF_TABS, 1.0f); poisson3.Generate((float *)dofSamples); for (int i = 0; i < NUM_DOF_TABS; ++ i) { dofSamples[i].x *= 1.0f / mWidth; dofSamples[i].y *= 1.0f / mHeight; } //float dofWeights[NUM_PCF_TABS]; //sCgDOFProgram->SetArray1f(2, (float *)dofWeights, NUM_DOF_TABS); PrintGLerror("init"); } void DeferredRenderer::Render(FrameBufferObject *fbo, DirectionalLight *light, ShadowMap *shadowMap ) { InitFrame(); if (shadowMap) FirstPassShadow(fbo, light, shadowMap); else FirstPass(fbo, light); if (mShadingMethod != 0) { PrepareSsao(fbo); // downsample fbo buffers } // q: use antialiasing before or after ssao? //if (useAntiAliasing) AntiAliasing(fbo, light); switch (mShadingMethod) { case SSAO: ComputeSsao(fbo, mTempCohFactor); FilterSsao(fbo); CombineSsao(fbo); break; case GI: ComputeGlobIllum(fbo, mTempCohFactor); CombineIllum(fbo); break; default: // do nothing: standard deferred shading break; } /// depth of field if (mUseDepthOfField) { DepthOfField(fbo); } if (mUseToneMapping) { float imageKey, whiteLum, middleGrey; ComputeToneParameters(fbo, light, imageKey, whiteLum, middleGrey); ToneMap(fbo, imageKey, whiteLum, middleGrey); } /// compute lense flare LenseFlare(fbo, light); const bool saveFrame = (mSavedFrameNumber != -1); const bool displayAfterAA = !saveFrame; // multisampling is difficult / costly with deferred shading // at least do some edge blurring if (mUseAntiAliasing) AntiAliasing(fbo, light, displayAfterAA); /// store the current frame if (saveFrame) SaveFrame(fbo); // if it hasn't been done yet => just output the latest buffer if (!mUseAntiAliasing || !displayAfterAA) Output(fbo); glEnable(GL_LIGHTING); glDisable(GL_TEXTURE_2D); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); // viewport glPopAttrib(); FrameBufferObject::Release(); ShaderManager::GetSingleton()->DisableFragmentProfile(); } static inline float SqrMag(const Sample2 &s) { return (s.x * s.x + s.y * s.y); } static inline float SqrDist(const Sample2 &a, const Sample2 &b) { float x = a.x - b.x; float y = a.y - b.y; return x * x + y * y; } static inline bool lt(const Sample2 &a, const Sample2 &b) { return SqrMag(a) < SqrMag(b); } void DeferredRenderer::SortSamples() { static Sample2 tempSamples[NUM_SAMPLES]; static bool checked[NUM_SAMPLES]; for (int i = 0; i < NUM_SAMPLES; ++ i) checked[i] = false; Sample2 currentSample; currentSample.x = 0; currentSample.y = 0; int ns = 0; // the next sample index for (int i = 0; i < NUM_SAMPLES; ++ i) { float minLen = 1e20f; for (int j = 0; j < NUM_SAMPLES; ++ j) { if (checked[j]) continue; Sample2 s = samples2[j]; const float len = SqrDist(s, currentSample); if (len < minLen) { minLen = len; ns = j; } } tempSamples[i] = samples2[ns]; currentSample = samples2[ns]; checked[ns] = true; } for (int i = 0; i < NUM_SAMPLES; ++ i) samples2[i] = tempSamples[i]; } void DeferredRenderer::ComputeSsao(FrameBufferObject *fbo, float tempCohFactor) { GLuint colorsTex, normalsTex, attribsTex; if (0) { colorsTex = fbo->GetColorBuffer(colorBufferIdx)->GetTexture(); normalsTex = fbo->GetColorBuffer(1)->GetTexture(); } else { normalsTex = mDownSampleFbo->GetColorBuffer(1)->GetTexture(); colorsTex = mDownSampleFbo->GetColorBuffer(0)->GetTexture(); } attribsTex = fbo->GetColorBuffer(2)->GetTexture(); // flip flop between illumination buffers 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); int i = 0; sCgSsaoProgram->SetTexture(i ++, colorsTex); sCgSsaoProgram->SetTexture(i ++, normalsTex); sCgSsaoProgram->SetTexture(i ++, oldTex); sCgSsaoProgram->SetTexture(i ++, noiseTex2D); sCgSsaoProgram->SetValue1f(i ++, (mUseTemporalCoherence && !mRegenerateSamples) ? tempCohFactor : 0); static int currentPos = 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 //if (currentPos + NUM_SAMPLES >= NUM_PRECOMPUTED_SAMPLES) { currentPos = 0; GenerateSamples(mSamplingMethod); //} //if (mSortSamples) { SortSamples(); } //sCgSsaoProgram->SetArray2f(i, (float *)samples2 + currentPos, NUM_SAMPLES); sCgSsaoProgram->SetArray2f(i, (float *)samples2 + currentPos, NUM_PRECOMPUTED_SAMPLES); currentPos += NUM_SAMPLES; } ++ i; for (int j = 0; j < 4; ++ j, ++ i) sCgSsaoProgram->SetValue3f(i, mCornersView[j].x, mCornersView[j].y, mCornersView[j].z); sCgSsaoProgram->SetMatrix(i ++, mProjViewMatrix); sCgSsaoProgram->SetMatrix(i ++, mOldProjViewMatrix); Vector3 de; de.x = mOldEyePos.x - mEyePos.x; de.y = mOldEyePos.y - mEyePos.y; de.z = mOldEyePos.z - mEyePos.z; sCgSsaoProgram->SetValue3f(i ++, de.x, de.y, de.z); for (int j = 0; j < 4; ++ j, ++ i) { sCgSsaoProgram->SetValue3f(i, mOldCornersView[j].x, mOldCornersView[j].y, mOldCornersView[j].z); } sCgSsaoProgram->SetTexture(i ++, attribsTex); sCgSsaoProgram->SetValue1f(i ++, mKernelRadius); sCgSsaoProgram->SetValue1f(i ++, mSampleIntensity * mKernelRadius); DrawQuad(sCgSsaoProgram); glPopAttrib(); PrintGLerror("ssao first pass"); } 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); glVertex2f(x, y); } void DeferredRenderer::AntiAliasing(FrameBufferObject *fbo, DirectionalLight *light, bool displayFrame) { ColorBufferObject *colorBuffer = fbo->GetColorBuffer(colorBufferIdx); GLuint colorsTex = colorBuffer->GetTexture(); GLuint normalsTex = fbo->GetColorBuffer(1)->GetTexture(); // read the second buffer, write to the first buffer if (!displayFrame) FlipFbos(fbo); else // end of the pipeline => just draw image to screen FrameBufferObject::Release(); // the neighbouring texels float xOffs = 1.0f / fbo->GetWidth(); float yOffs = 1.0f / fbo->GetHeight(); sCgAntiAliasingProgram->SetTexture(0, colorsTex); sCgAntiAliasingProgram->SetTexture(1, normalsTex); float offsets[16]; int i = 0; offsets[i] = -xOffs; offsets[i + 1] = yOffs; i += 2; // left top offsets[i] = xOffs; offsets[i + 1] = -yOffs; i += 2; // right bottom offsets[i] = xOffs; offsets[i + 1] = yOffs; i += 2; // right top offsets[i] = -xOffs; offsets[i + 1] = -yOffs; i += 2; // left bottom offsets[i] = -xOffs; offsets[i + 1] = .0f; i += 2; // left offsets[i] = xOffs; offsets[i + 1] = .0f; i += 2; // right offsets[i] = .0f; offsets[i + 1] = yOffs; i += 2; // top offsets[i] = .0f; offsets[i + 1] = -yOffs; i += 2; // bottom sCgAntiAliasingProgram->SetArray2f(2, offsets, 8); DrawQuad(sCgAntiAliasingProgram); PrintGLerror("antialiasing"); } void DeferredRenderer::FirstPass(FrameBufferObject *fbo, DirectionalLight *light) { GLuint colorsTex = fbo->GetColorBuffer(colorBufferIdx)->GetTexture(); GLuint normalsTex = fbo->GetColorBuffer(1)->GetTexture(); FlipFbos(fbo); 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) { #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(); int i = 0; sCgGiProgram->SetTexture(i ++, colorsTex); sCgGiProgram->SetTexture(i ++, normalsTex); sCgGiProgram->SetTexture(i ++, noiseTex2D); sCgGiProgram->SetTexture(i ++, oldSsaoTex); sCgGiProgram->SetTexture(i ++, oldIllumTex); sCgGiProgram->SetValue1f(i ++, (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(i ++, (float *)samples2, NUM_SAMPLES); } Vector3 bl = mCornersView[0]; Vector3 br = mCornersView[1]; Vector3 tl = mCornersView[2]; Vector3 tr = mCornersView[3]; sCgGiProgram->SetValue3f(i ++, bl.x, bl.y, bl.z); sCgGiProgram->SetValue3f(i ++, br.x, br.y, br.z); sCgGiProgram->SetValue3f(i ++, tl.x, tl.y, tl.z); sCgGiProgram->SetValue3f(i ++, tr.x, tr.y, tr.z); sCgGiProgram->SetMatrix(i ++, mOldProjViewMatrix); sCgGiProgram->SetMatrix(i ++, mProjViewMatrix); 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(); FlipFbos(fbo); sCgCombineIllumProgram->SetTexture(0, colorsTex); sCgCombineIllumProgram->SetTexture(1, ssaoTex); sCgCombineIllumProgram->SetTexture(2, illumTex); DrawQuad(sCgCombineIllumProgram); PrintGLerror("combine"); } void DeferredRenderer::FilterSsao(FrameBufferObject *fbo) { GLuint colorsTex = fbo->GetColorBuffer(colorBufferIdx)->GetTexture(); GLuint normalsTex = fbo->GetColorBuffer(1)->GetTexture(); GLuint ssaoTex = mIllumFbo->GetColorBuffer(mIllumFboIndex)->GetTexture(); //mIllumFbo->Bind(); //glDrawBuffers(1, mrt + mIllumFboIndex + 1); mTempFbo->Bind(); glDrawBuffers(1, mrt); int i = 0; sCgFilterSsaoProgram->SetTexture(i ++, colorsTex); sCgFilterSsaoProgram->SetTexture(i ++, ssaoTex); for (int j = 0; j < 4; ++ j, ++ i) { sCgFilterSsaoProgram->SetValue3f(i, mCornersView[j].x, mCornersView[j].y, mCornersView[j].z); } sCgFilterSsaoProgram->SetValue2f(i ++, (float)mWidth, (float)mHeight); DrawQuad(sCgFilterSsaoProgram); PrintGLerror("combine ssao"); } void DeferredRenderer::CombineSsao(FrameBufferObject *fbo) { GLuint colorsTex = fbo->GetColorBuffer(colorBufferIdx)->GetTexture(); GLuint normalsTex = fbo->GetColorBuffer(1)->GetTexture(); //GLuint ssaoTex = mIllumFbo->GetColorBuffer(mIllumFboIndex + 1)->GetTexture(); GLuint ssaoTex = mTempFbo->GetColorBuffer(0)->GetTexture(); FlipFbos(fbo); int i = 0; sCgCombineSsaoProgram->SetTexture(i ++, colorsTex); sCgCombineSsaoProgram->SetTexture(i ++, ssaoTex); for (int j = 0; j < 4; ++ j, ++ i) { sCgCombineSsaoProgram->SetValue3f(i, mCornersView[j].x, mCornersView[j].y, mCornersView[j].z); } sCgCombineSsaoProgram->SetValue2f(i ++, mWidth, mHeight); DrawQuad(sCgCombineSsaoProgram); PrintGLerror("combine ssao"); } void DeferredRenderer::FirstPassShadow(FrameBufferObject *fbo, DirectionalLight *light, ShadowMap *shadowMap) { GLuint colorsTex = fbo->GetColorBuffer(colorBufferIdx)->GetTexture(); GLuint normalsTex = fbo->GetColorBuffer(1)->GetTexture(); GLuint shadowTex = shadowMap->GetDepthTexture(); Matrix4x4 shadowMatrix; shadowMap->GetTextureMatrix(shadowMatrix); FlipFbos(fbo); sCgDeferredShadowProgram->SetTexture(0, colorsTex); sCgDeferredShadowProgram->SetTexture(1, normalsTex); sCgDeferredShadowProgram->SetTexture(2, shadowTex); sCgDeferredShadowProgram->SetTexture(3, noiseTex2D); 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); sCgDeferredShadowProgram->SetValue3f(7, mEyePos.x, mEyePos.y, mEyePos.z); DrawQuad(sCgDeferredShadowProgram); PrintGLerror("deferred shading + shadows"); } 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(); FlipFbos(fbo); 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::PrepareSsao(FrameBufferObject *fbo) { GLuint colorsTex = fbo->GetColorBuffer(colorBufferIdx)->GetTexture(); GLuint normalsTex = fbo->GetColorBuffer(1)->GetTexture(); GLuint diffVals = fbo->GetColorBuffer(2)->GetTexture(); // flip flop between illumination buffers GLuint oldTex = mIllumFbo->GetColorBuffer(2 - mIllumFboIndex)->GetTexture(); int i = 0; sCgPrepareSsaoProgram->SetTexture(i ++, colorsTex); sCgPrepareSsaoProgram->SetTexture(i ++, normalsTex); sCgPrepareSsaoProgram->SetTexture(i ++, diffVals); sCgPrepareSsaoProgram->SetTexture(i ++, oldTex); Vector3 de; de.x = mOldEyePos.x - mEyePos.x; de.y = mOldEyePos.y - mEyePos.y; de.z = mOldEyePos.z - mEyePos.z; sCgPrepareSsaoProgram->SetValue3f(i ++, de.x, de.y, de.z); sCgPrepareSsaoProgram->SetMatrix(i ++, mProjViewMatrix); sCgPrepareSsaoProgram->SetMatrix(i ++, mOldProjViewMatrix); for (int j = 0; j < 4; ++ j, ++ i) sCgPrepareSsaoProgram->SetValue3f(i, mOldCornersView[j].x, mOldCornersView[j].y, mOldCornersView[j].z); glPushAttrib(GL_VIEWPORT_BIT); glViewport(0, 0, mDownSampleFbo->GetWidth(), mDownSampleFbo->GetHeight()); mDownSampleFbo->Bind(); // prepare downsampled depth and normal texture for ssao glDrawBuffers(2, mrt); DrawQuad(sCgPrepareSsaoProgram); glPopAttrib(); PrintGLerror("prepareSsao"); } void DeferredRenderer::DownSample(FrameBufferObject *fbo, int bufferIdx, FrameBufferObject *downSampleFbo, int downSampleBufferIdx, ShaderProgram *program) { ColorBufferObject *buffer = fbo->GetColorBuffer(bufferIdx); GLuint tex = buffer->GetTexture(); glPushAttrib(GL_VIEWPORT_BIT); glViewport(0, 0, downSampleFbo->GetWidth(), downSampleFbo->GetHeight()); downSampleFbo->Bind(); program->SetTexture(0, tex); glDrawBuffers(1, mrt + downSampleBufferIdx); DrawQuad(program); glPopAttrib(); PrintGLerror("downsample"); } void DeferredRenderer::ToneMap(FrameBufferObject *fbo, float imageKey, float whiteLum, float middleGrey) { ColorBufferObject *colorBuffer = fbo->GetColorBuffer(colorBufferIdx); GLuint colorsTex = colorBuffer->GetTexture(); //FrameBufferObject::Release(); FlipFbos(fbo); sCgToneProgram->SetTexture(0, colorsTex); sCgToneProgram->SetValue1f(1, imageKey); sCgToneProgram->SetValue1f(2, whiteLum); sCgToneProgram->SetValue1f(3, middleGrey); DrawQuad(sCgToneProgram); PrintGLerror("ToneMap"); } void DeferredRenderer::Output(FrameBufferObject *fbo) { glPushAttrib(GL_VIEWPORT_BIT); glViewport(0, 0, fbo->GetWidth(), fbo->GetHeight()); ColorBufferObject *colorBuffer = fbo->GetColorBuffer(colorBufferIdx); GLuint colorsTex = colorBuffer->GetTexture(); sCgDownSampleProgram->SetTexture(0, colorsTex); FrameBufferObject::Release(); DrawQuad(sCgDownSampleProgram); PrintGLerror("output"); } void DeferredRenderer::InitFrame() { for (int i = 0; i < 4; ++ i) mOldCornersView[i] = mCornersView[i]; mOldProjViewMatrix = mProjViewMatrix; mOldEyePos = mEyePos; mEyePos = mCamera->GetPosition(); // hack: temporarily change far to improve precision const float oldFar = mCamera->GetFar(); const float oldNear = mCamera->GetNear(); Matrix4x4 matViewing, matProjection; /////////////////// // use view orientation as we assume that the eye point is always in the center // of our coordinate system, this way we have the highest precision near the eye point mCamera->GetViewOrientationMatrix(matViewing); mCamera->GetProjectionMatrix(matProjection); mProjViewMatrix = matViewing * matProjection; ComputeViewVectors(mCamera, mCornersView[0], mCornersView[1], mCornersView[2], mCornersView[3]); // 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; // enable fragment shading ShaderManager::GetSingleton()->EnableFragmentProfile(); glDisable(GL_ALPHA_TEST); glDisable(GL_TEXTURE_2D); glDisable(GL_LIGHTING); glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); glPolygonMode(GL_FRONT, GL_FILL); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(0, 1, 0, 1); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glPushAttrib(GL_VIEWPORT_BIT); glViewport(0, 0, mWidth, mHeight); // revert to old far and near plane mCamera->SetFar(oldFar); mCamera->SetNear(oldNear); } void DeferredRenderer::LenseFlare(FrameBufferObject *fbo, DirectionalLight *light ) { // light source visible? if (!mSunVisiblePixels) return; // the sun is a large distance in the reverse light direction // => extrapolate light pos const Vector3 lightPos = light->GetDirection() * -1e3f; float w; Vector3 projLightPos = mProjViewMatrix.Transform(w, lightPos, 1.0f); projLightPos /= w; projLightPos = projLightPos * 0.5f + Vector3(0.5f); // vector to light from screen center in texture space Vector3 vectorToLight = projLightPos - Vector3(0.5f); vectorToLight.z = .0f; const float distanceToLight = Magnitude(vectorToLight); vectorToLight /= distanceToLight; //cout << "dist " << distanceToLight << " v " << vectorToLight << endl; ColorBufferObject *colorBuffer = fbo->GetColorBuffer(colorBufferIdx); GLuint colorsTex = colorBuffer->GetTexture(); FlipFbos(fbo); int i = 0; sCgLenseFlareProgram->SetTexture(i ++, colorsTex); sCgLenseFlareProgram->SetTexture(i ++, sHaloTex[0]->GetId()); sCgLenseFlareProgram->SetTexture(i ++, sHaloTex[1]->GetId()); sCgLenseFlareProgram->SetTexture(i ++, sHaloTex[2]->GetId()); sCgLenseFlareProgram->SetTexture(i ++, sHaloTex[3]->GetId()); sCgLenseFlareProgram->SetTexture(i ++, sHaloTex[4]->GetId()); sCgLenseFlareProgram->SetValue2f(i ++, vectorToLight.x, vectorToLight.y); sCgLenseFlareProgram->SetValue1f(i ++, distanceToLight); sCgLenseFlareProgram->SetValue1f(i ++, (float)mSunVisiblePixels); DrawQuad(sCgLenseFlareProgram); PrintGLerror("LenseFlare"); } void DeferredRenderer::DepthOfField(FrameBufferObject *fbo) { ColorBufferObject *colorBuffer = fbo->GetColorBuffer(colorBufferIdx); GLuint colorsTex = colorBuffer->GetTexture(); FlipFbos(fbo); int i = 0; const float zFocus = 7.0f; sCgDOFProgram->SetTexture(i ++, colorsTex); sCgDOFProgram->SetArray2f(i ++, (float *)dofSamples, NUM_DOF_TABS); sCgDOFProgram->SetValue1f(i ++, min(mCamera->GetFar(), mMaxDistance) - mCamera->GetNear()); sCgDOFProgram->SetValue1f(i ++, zFocus); DrawQuad(sCgDOFProgram); PrintGLerror("LenseFlare"); } void DeferredRenderer::SaveFrame(FrameBufferObject *fbo) { ColorBufferObject *colorBuffer = fbo->GetColorBuffer(colorBufferIdx); GLuint colorsTex = colorBuffer->GetTexture(); GLubyte *data = new GLubyte[mWidth * mHeight * 4]; // grab texture data glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, colorsTex); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); ///////////////// startil(); static char imageName[200]; sprintf(imageName, "%s_%05d.tga", mSavedFrameSuffix.c_str(), mSavedFrameNumber); ILstring fileName = ILstring(imageName); ilRegisterType(IL_FLOAT); const int depth = 1; const int bpp = 4; if (!ilTexImage(mWidth, mHeight, depth, bpp, IL_RGBA, IL_UNSIGNED_BYTE, data)) { cerr << "IL error " << ilGetError() << endl; stopil(); return; } ilEnable(IL_FILE_OVERWRITE); if (!ilSaveImage(fileName)) { cerr << "TGA write error " << ilGetError() << endl; } delete [] data; stopil(); PrintGLerror("Store frame"); } void DeferredRenderer::SetUseTemporalCoherence(bool temporal) { mUseTemporalCoherence = temporal; } 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; } } #if TODO void DeferredRenderer::SetNumSamples(int numSamples) { mNumSamples = numSamples; } #endif void DeferredRenderer::SetSampleIntensity(float sampleIntensity) { mSampleIntensity = sampleIntensity; mRegenerateSamples = true; } void DeferredRenderer::SetKernelRadius(float kernelRadius) { mKernelRadius = kernelRadius; mRegenerateSamples = true; } /*void DeferredRenderer::SetSortSamples(bool sortSamples) { mSortSamples = sortSamples; }*/ void DeferredRenderer::SetSunVisiblePixels(int pixels) { mSunVisiblePixels = pixels; } void DeferredRenderer::SetMaxDistance(float maxDist) { mMaxDistance = maxDist; } void DeferredRenderer::SetUseToneMapping(bool toneMapping) { mUseToneMapping = toneMapping; } void DeferredRenderer::SetUseAntiAliasing(bool antiAliasing) { mUseAntiAliasing = antiAliasing; } void DeferredRenderer::SetUseDepthOfField(bool dof) { mUseDepthOfField = dof; } void DeferredRenderer::SetTemporalCoherenceFactorForSsao(float factor) { mTempCohFactor = factor; } void DeferredRenderer::SetSaveFrame(const string &suffix, int frameNumber) { mSavedFrameSuffix = suffix; mSavedFrameNumber = frameNumber; } } // namespace