/* Copyright (c) 2016 yvt This file is part of OpenSpades. OpenSpades is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenSpades is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenSpades. If not, see . */ #include #include #include "GLProfiler.h" #include "GLProgram.h" #include "GLProgramAttribute.h" #include "GLProgramUniform.h" #include "GLQuadRenderer.h" #include "GLRenderer.h" #include "GLSSAOFilter.h" #include "GLImage.h" #include "IGLDevice.h" #include #include namespace spades { namespace draw { GLSSAOFilter::GLSSAOFilter(GLRenderer *renderer) : renderer(renderer), settings(renderer->GetSettings()) { ssaoProgram = renderer->RegisterProgram("Shaders/PostFilters/SSAO.program"); bilateralProgram = renderer->RegisterProgram("Shaders/PostFilters/BilateralFilter.program"); ditherPattern = static_cast(renderer->RegisterImage("Gfx/DitherPattern4x4.png")); } GLColorBuffer GLSSAOFilter::GenerateRawSSAOImage(int width, int height) { SPADES_MARK_FUNCTION(); IGLDevice *dev = renderer->GetGLDevice(); GLQuadRenderer qr(dev); GLColorBuffer output = renderer->GetFramebufferManager()->CreateBufferHandle(width, height, 1); { GLProgram *program = ssaoProgram; static GLProgramAttribute positionAttribute("positionAttribute"); static GLProgramUniform depthTexture("depthTexture"); static GLProgramUniform ditherTexture("ditherTexture"); static GLProgramUniform texCoordRange("texCoordRange"); static GLProgramUniform zNearFar("zNearFar"); static GLProgramUniform pixelShift("pixelShift"); static GLProgramUniform fieldOfView("fieldOfView"); static GLProgramUniform sampleOffsetScale("sampleOffsetScale"); positionAttribute(program); depthTexture(program); ditherTexture(program); texCoordRange(program); zNearFar(program); pixelShift(program); fieldOfView(program); sampleOffsetScale(program); program->Use(); const client::SceneDefinition &def = renderer->GetSceneDef(); zNearFar.SetValue(def.zNear, def.zFar); fieldOfView.SetValue(std::tan(def.fovX * 0.5f), std::tan(def.fovY * 0.5f)); pixelShift.SetValue(1.f / (float)width, 1.f / (float)height); float kernelSize = std::max(1.0f, std::min(width, height) * 0.0018f); sampleOffsetScale.SetValue(kernelSize / (float)width, kernelSize / (float)height); if (width < dev->ScreenWidth()) { // 2x downsampling texCoordRange.SetValue(0.25f / width, 0.25f / height, 1.f, 1.f); } else { texCoordRange.SetValue(0.f, 0.f, 1.f, 1.f); } dev->ActiveTexture(0); depthTexture.SetValue(0); dev->BindTexture(IGLDevice::Texture2D, renderer->GetFramebufferManager()->GetDepthTexture()); dev->ActiveTexture(1); ditherTexture.SetValue(1); ditherPattern->Bind(IGLDevice::Texture2D); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter, IGLDevice::Nearest); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter, IGLDevice::Nearest); dev->BindFramebuffer(IGLDevice::Framebuffer, output.GetFramebuffer()); dev->Viewport(0, 0, width, height); qr.SetCoordAttributeIndex(positionAttribute()); qr.Draw(); dev->ActiveTexture(0); dev->BindTexture(IGLDevice::Texture2D, 0); } return output; } GLColorBuffer GLSSAOFilter::ApplyBilateralFilter(GLColorBuffer tex, bool direction, int width, int height) { SPADES_MARK_FUNCTION(); // do gaussian blur GLProgram *program = bilateralProgram; IGLDevice *dev = renderer->GetGLDevice(); GLQuadRenderer qr(dev); int w = width == -1 ? tex.GetWidth() : width; int h = height == -1 ? tex.GetHeight() : height; static GLProgramAttribute positionAttribute("positionAttribute"); static GLProgramUniform inputTexture("inputTexture"); static GLProgramUniform depthTexture("depthTexture"); static GLProgramUniform texCoordRange("texCoordRange"); static GLProgramUniform unitShift("unitShift"); static GLProgramUniform zNearFar("zNearFar"); static GLProgramUniform isUpsampling("isUpsampling"); static GLProgramUniform pixelShift("pixelShift"); program->Use(); positionAttribute(program); inputTexture(program); depthTexture(program); texCoordRange(program); unitShift(program); zNearFar(program); isUpsampling(program); pixelShift(program); inputTexture.SetValue(0); dev->ActiveTexture(0); dev->BindTexture(IGLDevice::Texture2D, tex.GetTexture()); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter, IGLDevice::Nearest); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter, IGLDevice::Nearest); depthTexture.SetValue(1); dev->ActiveTexture(1); dev->BindTexture(IGLDevice::Texture2D, renderer->GetFramebufferManager()->GetDepthTexture()); texCoordRange.SetValue(0.f, 0.f, 1.f, 1.f); unitShift.SetValue(direction ? 1.f / (float)width : 0.0f, direction ? 0.0f : 1.f / (float)height); pixelShift.SetValue(1.f / (float)width, 1.f / (float)height, (float)width, (float)height); isUpsampling.SetValue(width > tex.GetWidth() ? 1 : 0); const client::SceneDefinition &def = renderer->GetSceneDef(); zNearFar.SetValue(def.zNear, def.zFar); qr.SetCoordAttributeIndex(positionAttribute()); GLColorBuffer buf2 = renderer->GetFramebufferManager()->CreateBufferHandle(w, h, 1); dev->Viewport(0, 0, w, h); dev->BindFramebuffer(IGLDevice::Framebuffer, buf2.GetFramebuffer()); qr.Draw(); dev->ActiveTexture(0); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter, IGLDevice::Linear); dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter, IGLDevice::Linear); return buf2; } GLColorBuffer GLSSAOFilter::Filter() { SPADES_MARK_FUNCTION(); IGLDevice *dev = renderer->GetGLDevice(); int width = dev->ScreenWidth(); int height = dev->ScreenHeight(); dev->Enable(IGLDevice::Blend, false); bool useLowQualitySSAO = renderer->IsRenderingMirror() || renderer->GetSettings().r_ssao >= 2; GLColorBuffer ssao = useLowQualitySSAO ? GenerateRawSSAOImage((width + 1) / 2, (height + 1) / 2) : GenerateRawSSAOImage(width, height); ssao = ApplyBilateralFilter(ssao, false, width, height); ssao = ApplyBilateralFilter(ssao, true, width, height); if (!renderer->IsRenderingMirror()) { ssao = ApplyBilateralFilter(ssao, false, width, height); ssao = ApplyBilateralFilter(ssao, true, width, height); } return ssao; } } }