void AmbientOcclusionManager::generateNoiseTexture() { unsigned int seed = 0; //no need to generate different random numbers at each start std::uniform_real_distribution<float> randomFloats(0.0, 1.0); std::default_random_engine generator(seed); std::vector<Vector3<float>> ssaoNoise; for (unsigned int i = 0; i < 16; i++) { Vector3<float> noise( randomFloats(generator) * 2.0f - 1.0f, randomFloats(generator) * 2.0f - 1.0f, 0.0f); ssaoNoise.push_back(noise.normalize()); } glBindFramebuffer(GL_FRAMEBUFFER, 0); glGenTextures(1, &noiseTexId); glBindTexture(GL_TEXTURE_2D, noiseTexId); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, noiseTextureSize, noiseTextureSize, 0, GL_RGB, GL_FLOAT, &ssaoNoise[0]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); ShaderManager::instance()->bind(ambientOcclusionShader); int noiseTexLoc = glGetUniformLocation(ambientOcclusionShader, "noiseTex"); glUniform1i(noiseTexLoc, GL_TEXTURE2-GL_TEXTURE0); }
void AmbientOcclusionManager::generateKernelSamples() { unsigned int seed = 0; //no need to generate different random numbers at each start std::uniform_real_distribution<float> randomFloats(0.0, 1.0); std::default_random_engine generator(seed); std::vector<Vector3<float>> ssaoKernel; for (unsigned int i = 0; i < kernelSamples; ++i) { Vector3<float> sample( randomFloats(generator) * 2.0f - 1.0f, randomFloats(generator) * 2.0f - 1.0f, randomFloats(generator)); sample = sample.normalize(); float scale = static_cast<float>(i) / kernelSamples; scale = MathAlgorithm::lerp<float>(0.1f, 1.0f, scale * scale); //use square function to bring most of sample closer to center sample *= scale; ssaoKernel.push_back(sample); } ShaderManager::instance()->bind(ambientOcclusionShader); int samplesLoc = glGetUniformLocation(ambientOcclusionShader, "samples"); glUniform3fv(samplesLoc, ssaoKernel.size(), (const float *)ssaoKernel[0]); #ifdef _DEBUG // exportSVG(std::string(std::getenv("HOME")) + "/ssaoKernel.html", ssaoKernel); #endif }
bool SSAOBuffer::load(unsigned int screenWidth, unsigned int screenHeight) { glGenFramebuffers(2, FBOs); for (unsigned int i = 0; i < 2; ++i) { glBindFramebuffer(GL_FRAMEBUFFER, FBOs[i]); glGenTextures(1, &textures[i]); glBindTexture(GL_TEXTURE_2D, textures[i]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, screenWidth, screenHeight, 0, GL_RGB, GL_FLOAT, NULL); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[i], 0); if (i == 0) { glGenTextures(1, &noiseTexture); std::uniform_real_distribution<GLfloat> randomFloats(0.0f, 1.0f); // generates random floats between 0.0 and 1.0 std::default_random_engine generator; std::vector<glm::vec3> ssaoNoise; for (GLuint i = 0; i < 16; i++) { glm::vec3 noise(randomFloats(generator) * 2.0f - 1.0f, randomFloats(generator) * 2.0f - 1.0f, 0.0f); // rotate around z-axis (in tangent space) ssaoNoise.push_back(noise); } glBindTexture(GL_TEXTURE_2D, noiseTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, 4, 4, 0, GL_RGB, GL_FLOAT, &ssaoNoise[0]); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glBindTexture(GL_TEXTURE_2D, 0); } } glBindFramebuffer(GL_FRAMEBUFFER, 0); if (!Error::checkGL()) return false; return true; }
std::vector<glm::vec3> SSAOScene::generateSampleKernal(int numSamples) { std::vector<glm::vec3> sampleKernal; //Generate a sample within a unit sphere std::uniform_real_distribution<GLfloat> randomFloats(0.0, 1.0); // random floats between 0.0 - 1.0 std::default_random_engine generator; for (GLuint i = 0; i < numSamples; ++i) { glm::vec3 sample( randomFloats(generator) * 2.0 - 1.0, randomFloats(generator) * 2.0 - 1.0, randomFloats(generator) ); sample = glm::normalize(sample); GLfloat scale = GLfloat(i) / numSamples; scale = GlmUtil::lerp(0.1f, 1.0f, scale * scale); sample *= scale; sampleKernal.push_back(sample); } return sampleKernal; }
void Scene::initThisDemo(void) { initCub(cub); initScreenQuad(screenQuatID); std::uniform_real_distribution<GLfloat> randomFloats(0.0, 1.0); // generates random floats between 0.0 and 1.0 std::default_random_engine generator; for (GLuint i = 0; i < 64; ++i) { glm::vec3 sample(randomFloats(generator) * 2.0 - 1.0, randomFloats(generator) * 2.0 - 1.0, randomFloats(generator)); sample = glm::normalize(sample); sample *= randomFloats(generator); GLfloat scale = GLfloat(i) / 64.0; // Scale samples s.t. they're more aligned to center of kernel scale = lerp(0.1f, 1.0f, scale * scale); sample *= scale; ssaoKernel.push_back(sample); } // Noise texture for (GLuint i = 0; i < 16; i++) { glm::vec3 noise(randomFloats(generator) * 2.0 - 1.0, randomFloats(generator) * 2.0 - 1.0, 0.0f); // rotate around z-axis (in tangent space) ssaoNoise.push_back(noise); } glGenTextures(1, &noiseTexture); glBindTexture(GL_TEXTURE_2D, noiseTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, 4, 4, 0, GL_RGB, GL_FLOAT, &ssaoNoise[0]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); }
bool SceneRenderer::loadShaderPrograms(const Camera *camera) { assert(camera); assert(ssaoBuffer); assert(standardVertexShader); assert(geometryVertexShader); assert(skinnedGeometryVertexShader); assert(directionalLightVertexShader); assert(ambientOcclusionVertexShader); assert(blurVertexShader); assert(standardFragmentShader); assert(geometryFragmentShader); assert(directionalLightFragmentShader); assert(ambientOcclusionFragmentShader); assert(blurFragmentShader); if (!Error::checkMemory(geometryShaderProgram = new ShaderProgram())) return false; if (!geometryShaderProgram->load(geometryVertexShader, geometryFragmentShader)) return false; glUseProgram(geometryShaderProgram->getHandle()); glUniformMatrix4fv(geometryShaderProgram->getUniform("projectionMatrix"), 1, GL_FALSE, glm::value_ptr(camera->getProjectionMatrix())); glUniform2f(geometryShaderProgram->getUniform("cameraNearFar"), camera->getNear(), camera->getFar()); if (!Error::checkMemory(skinnedGeometryShaderProgram = new ShaderProgram())) return false; if (!skinnedGeometryShaderProgram->load(skinnedGeometryVertexShader, geometryFragmentShader)) return false; if (!Error::checkMemory(directionalLightShaderProgram = new ShaderProgram())) return false; if (!directionalLightShaderProgram->load(directionalLightVertexShader, directionalLightFragmentShader)) return false; glUseProgram(directionalLightShaderProgram->getHandle()); glUniform2f(directionalLightShaderProgram->getUniform("screenSize"), 1280.0f, 720.0f); glUniform1i(directionalLightShaderProgram->getUniform("inNormal"), 1); glUniform1i(directionalLightShaderProgram->getUniform("inColor"), 2); if (!Error::checkMemory(ambientOcclusionShaderProgram = new ShaderProgram())) return false; if (!ambientOcclusionShaderProgram->load(ambientOcclusionVertexShader, ambientOcclusionFragmentShader)) return false; glUseProgram(ambientOcclusionShaderProgram->getHandle()); glUniform2f(ambientOcclusionShaderProgram->getUniform("screenSize"), 1280.0f, 720.0f); glUniformMatrix4fv(ambientOcclusionShaderProgram->getUniform("projectionMatrix"), 1, GL_FALSE, glm::value_ptr(camera->getProjectionMatrix())); glUniform1i(ambientOcclusionShaderProgram->getUniform("inPosition"), 0); glUniform1i(ambientOcclusionShaderProgram->getUniform("inNormal"), 1); glUniform1i(ambientOcclusionShaderProgram->getUniform("inNoise"), 3); // sample kernel std::uniform_real_distribution<GLfloat> randomFloats(0.0f, 1.0f); std::default_random_engine generator; glm::vec3 samples[64]; for (GLuint i = 0; i < 64; ++i) { glm::vec3 sample(randomFloats(generator) * 2.0f - 1.0f, randomFloats(generator) * 2.0f - 1.0f, randomFloats(generator)); sample = glm::normalize(sample); sample *= randomFloats(generator); GLfloat scale = GLfloat(i) / 64.0f; // scale samples so that they are more aligned to the center of the kernel scale = lerp(0.1f, 1.0f, scale * scale); sample *= scale; samples[i] = sample; } glUniform3fv(ambientOcclusionShaderProgram->getUniform("samples"), 64, glm::value_ptr(samples[0])); if (!Error::checkMemory(blurShaderProgram = new ShaderProgram())) return false; if (!blurShaderProgram->load(blurVertexShader, blurFragmentShader)) return false; glUseProgram(blurShaderProgram->getHandle()); glUniform2f(blurShaderProgram->getUniform("screenSize"), 1280.0f, 720.0f); glUniform1i(blurShaderProgram->getUniform("inAmbientOcclusion"), 0); if (!Error::checkMemory(standardShaderProgram = new ShaderProgram())) return false; if (!standardShaderProgram->load(standardVertexShader, standardFragmentShader)) return false; glUseProgram(standardShaderProgram->getHandle()); glUniform2f(standardShaderProgram->getUniform("screenSize"), 1280.0f, 720.0f); glUniform1i(standardShaderProgram->getUniform("inTexture"), 0); return true; }
SSAOScene::SSAOScene(Context * ctx): CameraScene(ctx), cryModel("assets/models/nanosuit/nanosuit.obj") { FramebufferConfiguration cfg(ctx->getWindowWidth(),ctx->getWindowHeight()); TextureConfig posBfrConfig("positionDepth",GL_RGBA16F,GL_RGBA,GL_FLOAT); posBfrConfig.setTextureFilter(GL_NEAREST); posBfrConfig.setWrapMode(GL_CLAMP_TO_EDGE); TextureAttachment posBfr(posBfrConfig,GL_COLOR_ATTACHMENT0); TextureConfig norBfrConfig("normal",GL_RGB16F,GL_RGB,GL_FLOAT); posBfrConfig.setTextureFilter(GL_NEAREST); TextureAttachment norBfr(norBfrConfig,GL_COLOR_ATTACHMENT1); TextureConfig spec_albedoConfig("color",GL_RGBA,GL_RGBA,GL_FLOAT); posBfrConfig.setTextureFilter(GL_NEAREST); TextureAttachment spec_albedoBfr(spec_albedoConfig,GL_COLOR_ATTACHMENT2); cfg.addTexturebuffer(posBfr); cfg.addTexturebuffer(norBfr); cfg.addTexturebuffer(spec_albedoBfr); cfg.addRenderbuffer(RenderbufferAttachment(GL_DEPTH24_STENCIL8,GL_DEPTH_STENCIL_ATTACHMENT)); gBuffer.init(cfg); FramebufferConfiguration aoConfig(ctx->getWindowWidth(),ctx->getWindowHeight()); TextureConfig aoTexture("occlusion",GL_RED,GL_RGB,GL_FLOAT); aoTexture.setTextureFilter(GL_NEAREST); TextureAttachment ssaoBufferAttachment(aoTexture,GL_COLOR_ATTACHMENT0); aoConfig.addTexturebuffer(ssaoBufferAttachment); ssaoBuffer.init(aoConfig); GL_Logger::LogError("Could not configure ssaoBuffer"); //Blur buffer has same properties as ao buffer. FramebufferConfiguration blurConfiguration(ctx->getWindowWidth(),ctx->getWindowHeight()); TextureConfig blurTexture("occlusion",GL_RED,GL_RGB,GL_FLOAT); blurTexture.setTextureFilter(GL_NEAREST); TextureAttachment blurBufferAttachment(blurTexture,GL_COLOR_ATTACHMENT0); blurConfiguration.addTexturebuffer(blurBufferAttachment); ssaoBlurBuffer.init(blurConfiguration); std::uniform_real_distribution<GLfloat> randomFloats(0.0, 1.0); // random floats between 0.0 - 1.0 std::default_random_engine generator; //Set up a noise texture std::vector<glm::vec3> ssaoNoiseVec; for (GLuint i = 0; i < 16; i++) { glm::vec3 noise( randomFloats(generator) * 2.0 - 1.0, randomFloats(generator) * 2.0 - 1.0, 0.0f); ssaoNoiseVec.push_back(noise); } TextureConfig ssaoConfig("ssaoNoise",GL_RGB16F,GL_RGB,GL_FLOAT); ssaoConfig.setWrapMode(GL_REPEAT); ssaoConfig.setTextureFilter(GL_NEAREST); ssaoNoise.init(ssaoConfig,&ssaoNoiseVec[0],4,4); GL_Logger::LogError("Could not configure ssaoNoise"); deferredGBufferProg = createProgram("SSAO GBuffer fill program"); ssaoProgram = createProgram("SSAO creation program"); postProcessProg = createProgram("Display AO map program"); ssaoBlurProgram = createProgram("SSAO blur program"); finalPassProgram = createProgram("Final Pass Program"); geomPlane.transform.setScale(glm::vec3(20.0)); geomPlane.transform.setPosition(glm::vec3(0,-1,0)); cryModel.transform.setScale(glm::vec3(0.5)); cryModel.transform.setPosition(glm::vec3(0,0,3.5)); cryModel.transform.rotate(-M_PI/2, glm::vec3(1.0,0,0)); }
// The MAIN function, from here we start our application and run our Game loop int main() { // Init GLFW glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", nullptr, nullptr); // Windowed glfwMakeContextCurrent(window); // Set the required callback functions glfwSetKeyCallback(window, key_callback); glfwSetCursorPosCallback(window, mouse_callback); glfwSetScrollCallback(window, scroll_callback); // Options glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); // Initialize GLEW to setup the OpenGL Function pointers glewExperimental = GL_TRUE; glewInit(); glGetError(); // Define the viewport dimensions glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT); // Setup some OpenGL options glEnable(GL_DEPTH_TEST); // Setup and compile our shaders Shader shaderGeometryPass("ssao_geometry.vs", "ssao_geometry.frag"); Shader shaderLightingPass("ssao.vs", "ssao_lighting.frag"); Shader shaderSSAO("ssao.vs", "ssao.frag"); Shader shaderSSAOBlur("ssao.vs", "ssao_blur.frag"); // Set samplers shaderLightingPass.Use(); glUniform1i(glGetUniformLocation(shaderLightingPass.Program, "gPositionDepth"), 0); glUniform1i(glGetUniformLocation(shaderLightingPass.Program, "gNormal"), 1); glUniform1i(glGetUniformLocation(shaderLightingPass.Program, "gAlbedo"), 2); glUniform1i(glGetUniformLocation(shaderLightingPass.Program, "ssao"), 3); shaderSSAO.Use(); glUniform1i(glGetUniformLocation(shaderSSAO.Program, "gPositionDepth"), 0); glUniform1i(glGetUniformLocation(shaderSSAO.Program, "gNormal"), 1); glUniform1i(glGetUniformLocation(shaderSSAO.Program, "texNoise"), 2); // Objects Model nanosuit(FileSystem::getPath("resources/objects/nanosuit/nanosuit.obj").c_str()); // Lights glm::vec3 lightPos = glm::vec3(2.0, 4.0, -2.0); glm::vec3 lightColor = glm::vec3(0.2, 0.2, 0.7); // Set up G-Buffer // 3 textures: // 1. Positions + depth (RGBA) // 2. Color (RGB) // 3. Normals (RGB) GLuint gBuffer; glGenFramebuffers(1, &gBuffer); glBindFramebuffer(GL_FRAMEBUFFER, gBuffer); GLuint gPositionDepth, gNormal, gAlbedo; // - Position + linear depth color buffer glGenTextures(1, &gPositionDepth); glBindTexture(GL_TEXTURE_2D, gPositionDepth); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gPositionDepth, 0); // - Normal color buffer glGenTextures(1, &gNormal); glBindTexture(GL_TEXTURE_2D, gNormal); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, gNormal, 0); // - Albedo color buffer glGenTextures(1, &gAlbedo); glBindTexture(GL_TEXTURE_2D, gAlbedo); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, gAlbedo, 0); // - Tell OpenGL which color attachments we'll use (of this framebuffer) for rendering GLuint attachments[3] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 }; glDrawBuffers(3, attachments); // - Create and attach depth buffer (renderbuffer) GLuint rboDepth; glGenRenderbuffers(1, &rboDepth); glBindRenderbuffer(GL_RENDERBUFFER, rboDepth); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, SCR_WIDTH, SCR_HEIGHT); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboDepth); // - Finally check if framebuffer is complete if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) std::cout << "GBuffer Framebuffer not complete!" << std::endl; // Also create framebuffer to hold SSAO processing stage GLuint ssaoFBO, ssaoBlurFBO; glGenFramebuffers(1, &ssaoFBO); glGenFramebuffers(1, &ssaoBlurFBO); glBindFramebuffer(GL_FRAMEBUFFER, ssaoFBO); GLuint ssaoColorBuffer, ssaoColorBufferBlur; // - SSAO color buffer glGenTextures(1, &ssaoColorBuffer); glBindTexture(GL_TEXTURE_2D, ssaoColorBuffer); glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ssaoColorBuffer, 0); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) std::cout << "SSAO Framebuffer not complete!" << std::endl; // - and blur stage glBindFramebuffer(GL_FRAMEBUFFER, ssaoBlurFBO); glGenTextures(1, &ssaoColorBufferBlur); glBindTexture(GL_TEXTURE_2D, ssaoColorBufferBlur); glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ssaoColorBufferBlur, 0); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) std::cout << "SSAO Blur Framebuffer not complete!" << std::endl; glBindFramebuffer(GL_FRAMEBUFFER, 0); // Sample kernel std::uniform_real_distribution<GLfloat> randomFloats(0.0, 1.0); // generates random floats between 0.0 and 1.0 std::default_random_engine generator; std::vector<glm::vec3> ssaoKernel; for (GLuint i = 0; i < 64; ++i) { glm::vec3 sample(randomFloats(generator) * 2.0 - 1.0, randomFloats(generator) * 2.0 - 1.0, randomFloats(generator)); sample = glm::normalize(sample); sample *= randomFloats(generator); GLfloat scale = GLfloat(i) / 64.0; // Scale samples s.t. they're more aligned to center of kernel scale = lerp(0.1f, 1.0f, scale * scale); sample *= scale; ssaoKernel.push_back(sample); } // Noise texture std::vector<glm::vec3> ssaoNoise; for (GLuint i = 0; i < 16; i++) { glm::vec3 noise(randomFloats(generator) * 2.0 - 1.0, randomFloats(generator) * 2.0 - 1.0, 0.0f); // rotate around z-axis (in tangent space) ssaoNoise.push_back(noise); } GLuint noiseTexture; glGenTextures(1, &noiseTexture); glBindTexture(GL_TEXTURE_2D, noiseTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, 4, 4, 0, GL_RGB, GL_FLOAT, &ssaoNoise[0]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Game loop while (!glfwWindowShouldClose(window)) { // Set frame time GLfloat currentFrame = glfwGetTime(); deltaTime = currentFrame - lastFrame; lastFrame = currentFrame; // Check and call events glfwPollEvents(); Do_Movement(); // 1. Geometry Pass: render scene's geometry/color data into gbuffer glBindFramebuffer(GL_FRAMEBUFFER, gBuffer); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glm::mat4 projection = glm::perspective(camera.Zoom, (GLfloat)SCR_WIDTH / (GLfloat)SCR_HEIGHT, 0.1f, 50.0f); glm::mat4 view = camera.GetViewMatrix(); glm::mat4 model; shaderGeometryPass.Use(); glUniformMatrix4fv(glGetUniformLocation(shaderGeometryPass.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); glUniformMatrix4fv(glGetUniformLocation(shaderGeometryPass.Program, "view"), 1, GL_FALSE, glm::value_ptr(view)); // Floor cube model = glm::translate(model, glm::vec3(0.0, -1.0f, 0.0f)); model = glm::scale(model, glm::vec3(20.0f, 1.0f, 20.0f)); glUniformMatrix4fv(glGetUniformLocation(shaderGeometryPass.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); RenderCube(); // Nanosuit model on the floor model = glm::mat4(); model = glm::translate(model, glm::vec3(0.0f, 0.0f, 5.0)); model = glm::rotate(model, -90.0f, glm::vec3(1.0, 0.0, 0.0)); model = glm::scale(model, glm::vec3(0.5f)); glUniformMatrix4fv(glGetUniformLocation(shaderGeometryPass.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); nanosuit.Draw(shaderGeometryPass); glBindFramebuffer(GL_FRAMEBUFFER, 0); // 2. Create SSAO texture glBindFramebuffer(GL_FRAMEBUFFER, ssaoFBO); glClear(GL_COLOR_BUFFER_BIT); shaderSSAO.Use(); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, gPositionDepth); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, gNormal); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, noiseTexture); // Send kernel + rotation for (GLuint i = 0; i < 64; ++i) glUniform3fv(glGetUniformLocation(shaderSSAO.Program, ("samples[" + std::to_string(i) + "]").c_str()), 1, &ssaoKernel[i][0]); glUniformMatrix4fv(glGetUniformLocation(shaderSSAO.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); RenderQuad(); glBindFramebuffer(GL_FRAMEBUFFER, 0); // 3. Blur SSAO texture to remove noise glBindFramebuffer(GL_FRAMEBUFFER, ssaoBlurFBO); glClear(GL_COLOR_BUFFER_BIT); shaderSSAOBlur.Use(); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, ssaoColorBuffer); RenderQuad(); glBindFramebuffer(GL_FRAMEBUFFER, 0); // 4. Lighting Pass: traditional deferred Blinn-Phong lighting now with added screen-space ambient occlusion glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); shaderLightingPass.Use(); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, gPositionDepth); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, gNormal); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, gAlbedo); glActiveTexture(GL_TEXTURE3); // Add extra SSAO texture to lighting pass glBindTexture(GL_TEXTURE_2D, ssaoColorBufferBlur); // Also send light relevant uniforms glm::vec3 lightPosView = glm::vec3(camera.GetViewMatrix() * glm::vec4(lightPos, 1.0)); glUniform3fv(glGetUniformLocation(shaderLightingPass.Program, "light.Position"), 1, &lightPosView[0]); glUniform3fv(glGetUniformLocation(shaderLightingPass.Program, "light.Color"), 1, &lightColor[0]); // Update attenuation parameters const GLfloat constant = 1.0; // Note that we don't send this to the shader, we assume it is always 1.0 (in our case) const GLfloat linear = 0.09; const GLfloat quadratic = 0.032; glUniform1f(glGetUniformLocation(shaderLightingPass.Program, "light.Linear"), linear); glUniform1f(glGetUniformLocation(shaderLightingPass.Program, "light.Quadratic"), quadratic); glUniform1i(glGetUniformLocation(shaderLightingPass.Program, "draw_mode"), draw_mode); RenderQuad(); // Swap the buffers glfwSwapBuffers(window); } glfwTerminate(); return 0; }