From 737e70077421023fcebf7a2c177afa032f3a7297 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Mon, 29 Aug 2016 13:10:22 +0200 Subject: [PATCH] Added SSAO pass --- src/CMakeLists.txt | 1 + src/gl/renderer/gl_postprocess.cpp | 106 ++++++++++++++- src/gl/renderer/gl_renderbuffers.cpp | 152 +++++++++++++++++----- src/gl/renderer/gl_renderbuffers.h | 30 +++-- src/gl/renderer/gl_renderer.cpp | 5 + src/gl/renderer/gl_renderer.h | 9 ++ src/gl/scene/gl_scene.cpp | 10 +- src/gl/shaders/gl_ambientshader.cpp | 89 +++++++++++++ src/gl/shaders/gl_ambientshader.h | 39 ++++++ src/gl/stereo3d/gl_stereo3d.cpp | 2 +- wadsrc/static/language.enu | 1 + wadsrc/static/menudef.z | 1 + wadsrc/static/shaders/glsl/lineardepth.fp | 16 +++ wadsrc/static/shaders/glsl/ssao.fp | 117 +++++++++++++++++ 14 files changed, 531 insertions(+), 47 deletions(-) create mode 100644 src/gl/shaders/gl_ambientshader.cpp create mode 100644 src/gl/shaders/gl_ambientshader.h create mode 100644 wadsrc/static/shaders/glsl/lineardepth.fp create mode 100644 wadsrc/static/shaders/glsl/ssao.fp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 03f3372f0..13ef11ec3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1113,6 +1113,7 @@ set( FASTMATH_SOURCES gl/shaders/gl_shaderprogram.cpp gl/shaders/gl_presentshader.cpp gl/shaders/gl_bloomshader.cpp + gl/shaders/gl_ambientshader.cpp gl/shaders/gl_blurshader.cpp gl/shaders/gl_tonemapshader.cpp gl/shaders/gl_lensshader.cpp diff --git a/src/gl/renderer/gl_postprocess.cpp b/src/gl/renderer/gl_postprocess.cpp index 3a83f4caf..c7d0494c8 100644 --- a/src/gl/renderer/gl_postprocess.cpp +++ b/src/gl/renderer/gl_postprocess.cpp @@ -68,6 +68,7 @@ #include "gl/renderer/gl_postprocessstate.h" #include "gl/data/gl_data.h" #include "gl/data/gl_vertexbuffer.h" +#include "gl/shaders/gl_ambientshader.h" #include "gl/shaders/gl_bloomshader.h" #include "gl/shaders/gl_blurshader.h" #include "gl/shaders/gl_tonemapshader.h" @@ -106,6 +107,20 @@ CVAR(Float, gl_lens_k, -0.12f, 0) CVAR(Float, gl_lens_kcube, 0.1f, 0) CVAR(Float, gl_lens_chromatic, 1.12f, 0) +CVAR(Bool, gl_ssao, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, gl_ssao_debug, false, 0) +CVAR(Float, gl_ssao_bias, 0.5f, 0) +CVAR(Float, gl_ssao_radius, 100.0f, 0) +CUSTOM_CVAR(Float, gl_ssao_blur_amount, 6.0f, 0) +{ + if (self < 0.1f) self = 0.1f; +} +CUSTOM_CVAR(Int, gl_ssao_blur_samples, 9, 0) +{ + if (self < 3 || self > 15 || self % 2 == 0) + self = 9; +} + EXTERN_CVAR(Float, vid_brightness) EXTERN_CVAR(Float, vid_contrast) @@ -117,6 +132,95 @@ void FGLRenderer::RenderScreenQuad() GLRenderer->mVBO->RenderArray(GL_TRIANGLE_STRIP, FFlatVertexBuffer::PRESENT_INDEX, 4); } +void FGLRenderer::PostProcessScene() +{ + if (FGLRenderBuffers::IsEnabled()) mBuffers->BlitSceneToTexture(); + AmbientOccludeScene(); + BloomScene(); + TonemapScene(); + LensDistortScene(); +} + +//----------------------------------------------------------------------------- +// +// Adds ambient occlusion to the scene +// +//----------------------------------------------------------------------------- + +void FGLRenderer::AmbientOccludeScene() +{ + if (!gl_ssao || !FGLRenderBuffers::IsEnabled()) + return; + + FGLDebug::PushGroup("AmbientOccludeScene"); + + FGLPostProcessState savedState; + + float bias = gl_ssao_bias; + float aoRadius = gl_ssao_radius; + const float blurAmount = gl_ssao_blur_amount; + int blurSampleCount = gl_ssao_blur_samples; + + //float tanHalfFovy = tan(fovy * (M_PI / 360.0f)); + float tanHalfFovy = 1.0f / 1.33333302f; //gl_RenderState.mProjectionMatrix.get()[5]; + float invFocalLenX = tanHalfFovy * (mBuffers->AmbientWidth / (float)mBuffers->AmbientHeight); + float invFocalLenY = tanHalfFovy; + float nDotVBias = clamp(bias, 0.0f, 1.0f); + float r2 = aoRadius * aoRadius; + + // Calculate linear depth values + glBindFramebuffer(GL_FRAMEBUFFER, mBuffers->AmbientFB0); + glViewport(0, 0, mBuffers->AmbientWidth, mBuffers->AmbientHeight); + mBuffers->BindSceneDepthTexture(0); + mLinearDepthShader->Bind(); + mLinearDepthShader->DepthTexture.Set(0); + mLinearDepthShader->LinearizeDepthA.Set(1.0f / GetZFar() - 1.0f / GetZNear()); + mLinearDepthShader->LinearizeDepthB.Set(MAX(1.0f / GetZNear(), 1.e-8f)); + mLinearDepthShader->InverseDepthRangeA.Set(1.0f); + mLinearDepthShader->InverseDepthRangeB.Set(0.0f); + RenderScreenQuad(); + + // Apply ambient occlusion + glBindFramebuffer(GL_FRAMEBUFFER, mBuffers->AmbientFB1); + glBindTexture(GL_TEXTURE_2D, mBuffers->AmbientTexture0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + mSSAOShader->Bind(); + mSSAOShader->DepthTexture.Set(0); + mSSAOShader->UVToViewA.Set(2.0f * invFocalLenX, -2.0f * invFocalLenY); + mSSAOShader->UVToViewB.Set(-invFocalLenX, invFocalLenY); + mSSAOShader->InvFullResolution.Set(1.0f / mBuffers->AmbientWidth, 1.0f / mBuffers->AmbientHeight); + mSSAOShader->NDotVBias.Set(nDotVBias); + mSSAOShader->NegInvR2.Set(-1.0f / r2); + mSSAOShader->RadiusToScreen.Set(aoRadius * 0.5 / tanHalfFovy * mBuffers->AmbientHeight); + mSSAOShader->AOMultiplier.Set(1.0f / (1.0f - nDotVBias)); + RenderScreenQuad(); + + // Blur SSAO texture + mBlurShader->BlurHorizontal(this, blurAmount, blurSampleCount, mBuffers->AmbientTexture1, mBuffers->AmbientFB0, mBuffers->AmbientWidth, mBuffers->AmbientHeight); + mBlurShader->BlurVertical(this, blurAmount, blurSampleCount, mBuffers->AmbientTexture0, mBuffers->AmbientFB1, mBuffers->AmbientWidth, mBuffers->AmbientHeight); + + // Add SSAO back to scene texture: + mBuffers->BindCurrentFB(); + glViewport(mSceneViewport.left, mSceneViewport.top, mSceneViewport.width, mSceneViewport.height); + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + if (gl_ssao_debug) + glBlendFunc(GL_ONE, GL_ZERO); + else + glBlendFunc(GL_ZERO, GL_SRC_COLOR); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mBuffers->AmbientTexture1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + mBloomCombineShader->Bind(); + mBloomCombineShader->BloomTexture.Set(0); + RenderScreenQuad(); + glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height); + + FGLDebug::PopGroup(); +} + //----------------------------------------------------------------------------- // // Adds bloom contribution to scene texture @@ -126,7 +230,7 @@ void FGLRenderer::RenderScreenQuad() void FGLRenderer::BloomScene() { // Only bloom things if enabled and no special fixed light mode is active - if (!gl_bloom || !FGLRenderBuffers::IsEnabled() || gl_fixedcolormap != CM_DEFAULT) + if (!gl_bloom || !FGLRenderBuffers::IsEnabled() || gl_fixedcolormap != CM_DEFAULT || gl_ssao_debug) return; FGLDebug::PushGroup("BloomScene"); diff --git a/src/gl/renderer/gl_renderbuffers.cpp b/src/gl/renderer/gl_renderbuffers.cpp index fd98522d8..c1976565b 100644 --- a/src/gl/renderer/gl_renderbuffers.cpp +++ b/src/gl/renderer/gl_renderbuffers.cpp @@ -86,15 +86,16 @@ FGLRenderBuffers::~FGLRenderBuffers() ClearScene(); ClearPipeline(); ClearBloom(); + ClearAmbientOcclusion(); } void FGLRenderBuffers::ClearScene() { DeleteFrameBuffer(mSceneFB); - DeleteRenderBuffer(mSceneMultisample); - DeleteRenderBuffer(mSceneDepthStencil); - DeleteRenderBuffer(mSceneDepth); - DeleteRenderBuffer(mSceneStencil); + DeleteRenderBuffer(mSceneMSColor); + DeleteRenderBuffer(mSceneMSDepthStencil); + DeleteRenderBuffer(mSceneMSDepth); + DeleteRenderBuffer(mSceneMSStencil); } void FGLRenderBuffers::ClearPipeline() @@ -104,6 +105,9 @@ void FGLRenderBuffers::ClearPipeline() DeleteFrameBuffer(mPipelineFB[i]); DeleteTexture(mPipelineTexture[i]); } + DeleteTexture(mPipelineDepthStencil); + DeleteTexture(mPipelineDepth); + DeleteTexture(mPipelineStencil); } void FGLRenderBuffers::ClearBloom() @@ -119,6 +123,14 @@ void FGLRenderBuffers::ClearBloom() } } +void FGLRenderBuffers::ClearAmbientOcclusion() +{ + DeleteFrameBuffer(AmbientFB0); + DeleteFrameBuffer(AmbientFB1); + DeleteTexture(AmbientTexture0); + DeleteTexture(AmbientTexture1); +} + void FGLRenderBuffers::DeleteTexture(GLuint &handle) { if (handle != 0) @@ -178,11 +190,12 @@ bool FGLRenderBuffers::Setup(int width, int height, int sceneWidth, int sceneHei } // Bloom bluring buffers need to match the scene to avoid bloom bleeding artifacts - if (mBloomWidth != sceneWidth || mBloomHeight != sceneHeight) + if (mSceneWidth != sceneWidth || mSceneHeight != sceneHeight) { CreateBloom(sceneWidth, sceneHeight); - mBloomWidth = sceneWidth; - mBloomHeight = sceneHeight; + CreateAmbientOcclusion(sceneWidth, sceneHeight); + mSceneWidth = sceneWidth; + mSceneHeight = sceneHeight; } glBindTexture(GL_TEXTURE_2D, textureBinding); @@ -198,8 +211,8 @@ bool FGLRenderBuffers::Setup(int width, int height, int sceneWidth, int sceneHei mWidth = 0; mHeight = 0; mSamples = 0; - mBloomWidth = 0; - mBloomHeight = 0; + mSceneWidth = 0; + mSceneHeight = 0; } return !FailedCreate; @@ -216,18 +229,27 @@ void FGLRenderBuffers::CreateScene(int width, int height, int samples) ClearScene(); if (samples > 1) - mSceneMultisample = CreateRenderBuffer("SceneMultisample", GetHdrFormat(), samples, width, height); - - if ((gl.flags & RFL_NO_DEPTHSTENCIL) != 0) { - mSceneDepth = CreateRenderBuffer("SceneDepth", GL_DEPTH_COMPONENT24, samples, width, height); - mSceneStencil = CreateRenderBuffer("SceneStencil", GL_STENCIL_INDEX8, samples, width, height); - mSceneFB = CreateFrameBuffer("SceneFB", samples > 1 ? mSceneMultisample : mPipelineTexture[0], mSceneDepth, mSceneStencil, samples > 1); + mSceneMSColor = CreateRenderBuffer("SceneMSColor", GetHdrFormat(), samples, width, height); + + if ((gl.flags & RFL_NO_DEPTHSTENCIL) != 0) + { + mSceneMSDepth = CreateRenderBuffer("SceneMSDepth", GL_DEPTH_COMPONENT24, samples, width, height); + mSceneMSStencil = CreateRenderBuffer("SceneMSStencil", GL_STENCIL_INDEX8, samples, width, height); + mSceneFB = CreateFrameBuffer("SceneFB", mSceneMSColor, mSceneMSDepth, mSceneMSStencil, true); + } + else + { + mSceneMSDepthStencil = CreateRenderBuffer("SceneMSDepthStencil", GL_DEPTH24_STENCIL8, samples, width, height); + mSceneFB = CreateFrameBuffer("SceneFB", mSceneMSColor, mSceneMSDepthStencil, true); + } } else { - mSceneDepthStencil = CreateRenderBuffer("SceneDepthStencil", GL_DEPTH24_STENCIL8, samples, width, height); - mSceneFB = CreateFrameBuffer("SceneFB", samples > 1 ? mSceneMultisample : mPipelineTexture[0], mSceneDepthStencil, samples > 1); + if ((gl.flags & RFL_NO_DEPTHSTENCIL) != 0) + mSceneFB = CreateFrameBuffer("SceneFB", mPipelineTexture[0], mPipelineDepth, mPipelineStencil, false); + else + mSceneFB = CreateFrameBuffer("SceneFB", mPipelineTexture[0], mPipelineDepthStencil, false); } } @@ -241,10 +263,23 @@ void FGLRenderBuffers::CreatePipeline(int width, int height) { ClearPipeline(); + if ((gl.flags & RFL_NO_DEPTHSTENCIL) != 0) + { + mPipelineDepth = Create2DTexture("PipelineDepth", GL_DEPTH_COMPONENT24, width, height); + mPipelineStencil = Create2DTexture("PipelineStencil", GL_STENCIL_INDEX8, width, height); + } + else + { + mPipelineDepthStencil = Create2DTexture("PipelineDepthStencil", GL_DEPTH24_STENCIL8, width, height); + } + for (int i = 0; i < NumPipelineTextures; i++) { mPipelineTexture[i] = Create2DTexture("PipelineTexture", GetHdrFormat(), width, height); - mPipelineFB[i] = CreateFrameBuffer("PipelineFB", mPipelineTexture[i]); + if ((gl.flags & RFL_NO_DEPTHSTENCIL) != 0) + mPipelineFB[i] = CreateFrameBuffer("PipelineFB", mPipelineTexture[i], mPipelineDepth, mPipelineStencil, false); + else + mPipelineFB[i] = CreateFrameBuffer("PipelineFB", mPipelineTexture[i], mPipelineDepthStencil, false); } } @@ -280,6 +315,27 @@ void FGLRenderBuffers::CreateBloom(int width, int height) } } +//========================================================================== +// +// Creates ambient occlusion working buffers +// +//========================================================================== + +void FGLRenderBuffers::CreateAmbientOcclusion(int width, int height) +{ + ClearAmbientOcclusion(); + + if (width <= 0 || height <= 0) + return; + + AmbientWidth = width / 2; + AmbientHeight = height / 2; + AmbientTexture0 = Create2DTexture("AmbientTexture0", GetHdrFormat(), AmbientWidth, AmbientHeight); + AmbientTexture1 = Create2DTexture("AmbientTexture1", GetHdrFormat(), AmbientWidth, AmbientHeight); + AmbientFB0 = CreateFrameBuffer("AmbientFB0", AmbientTexture0); + AmbientFB1 = CreateFrameBuffer("AmbientFB1", AmbientTexture1); +} + //========================================================================== // // Fallback support for older OpenGL where RGBA16F might not be available @@ -299,12 +355,24 @@ GLuint FGLRenderBuffers::GetHdrFormat() GLuint FGLRenderBuffers::Create2DTexture(const FString &name, GLuint format, int width, int height) { - GLuint type = (format == GL_RGBA16F) ? GL_FLOAT : GL_UNSIGNED_BYTE; GLuint handle = 0; glGenTextures(1, &handle); glBindTexture(GL_TEXTURE_2D, handle); FGLDebug::LabelObject(GL_TEXTURE, handle, name); - glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, GL_RGBA, type, nullptr); + + GLenum dataformat, datatype; + switch (format) // Special thanks to the designers of OpenGL.. + { + case GL_RGBA8: dataformat = GL_RGBA; datatype = GL_UNSIGNED_BYTE; break; + case GL_RGBA16: dataformat = GL_RGBA; datatype = GL_UNSIGNED_SHORT; break; + case GL_RGBA16F: dataformat = GL_RGBA; datatype = GL_FLOAT; break; + case GL_DEPTH_COMPONENT24: dataformat = GL_DEPTH_COMPONENT; datatype = GL_FLOAT; break; + case GL_STENCIL_INDEX8: dataformat = GL_STENCIL_INDEX; datatype = GL_INT; break; + case GL_DEPTH24_STENCIL8: dataformat = GL_DEPTH_STENCIL; datatype = GL_UNSIGNED_INT_24_8; break; + default: I_FatalError("Unknown format passed to FGLRenderBuffers.Create2DTexture"); + } + + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, dataformat, datatype, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); @@ -359,34 +427,45 @@ GLuint FGLRenderBuffers::CreateFrameBuffer(const FString &name, GLuint colorbuff return handle; } -GLuint FGLRenderBuffers::CreateFrameBuffer(const FString &name, GLuint colorbuffer, GLuint depthstencil, bool colorIsARenderBuffer) +GLuint FGLRenderBuffers::CreateFrameBuffer(const FString &name, GLuint colorbuffer, GLuint depthstencil, bool fromRenderBuffers) { GLuint handle = 0; glGenFramebuffers(1, &handle); glBindFramebuffer(GL_FRAMEBUFFER, handle); FGLDebug::LabelObject(GL_FRAMEBUFFER, handle, name); - if (colorIsARenderBuffer) + if (fromRenderBuffers) + { glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuffer); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthstencil); + } else + { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer, 0); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthstencil); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthstencil, 0); + } if (CheckFrameBufferCompleteness()) ClearFrameBuffer(true, true); return handle; } -GLuint FGLRenderBuffers::CreateFrameBuffer(const FString &name, GLuint colorbuffer, GLuint depth, GLuint stencil, bool colorIsARenderBuffer) +GLuint FGLRenderBuffers::CreateFrameBuffer(const FString &name, GLuint colorbuffer, GLuint depth, GLuint stencil, bool fromRenderBuffers) { GLuint handle = 0; glGenFramebuffers(1, &handle); glBindFramebuffer(GL_FRAMEBUFFER, handle); FGLDebug::LabelObject(GL_FRAMEBUFFER, handle, name); - if (colorIsARenderBuffer) + if (fromRenderBuffers) + { glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuffer); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, stencil); + } else + { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer, 0); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, stencil); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, stencil, 0); + } if (CheckFrameBufferCompleteness()) ClearFrameBuffer(true, true); return handle; @@ -441,7 +520,7 @@ void FGLRenderBuffers::ClearFrameBuffer(bool stencil, bool depth) glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &stencilValue); glGetDoublev(GL_DEPTH_CLEAR_VALUE, &depthValue); glDisable(GL_SCISSOR_TEST); - glClearColor(0.0, 0.0, 0.0, 0.0); + glClearColor(1.0, 0.0, 0.0, 0.0); glClearDepth(0.0); glClearStencil(0); GLenum flags = GL_COLOR_BUFFER_BIT; @@ -471,7 +550,7 @@ void FGLRenderBuffers::BlitSceneToTexture() glBindFramebuffer(GL_READ_FRAMEBUFFER, mSceneFB); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mPipelineFB[mCurrentPipelineTexture]); - glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST); + glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); if ((gl.flags & RFL_INVALIDATE_BUFFER) != 0) { @@ -494,6 +573,21 @@ void FGLRenderBuffers::BindSceneFB() glBindFramebuffer(GL_FRAMEBUFFER, mSceneFB); } +//========================================================================== +// +// Binds the depth texture to the specified texture unit +// +//========================================================================== + +void FGLRenderBuffers::BindSceneDepthTexture(int index) +{ + glActiveTexture(GL_TEXTURE0 + index); + if ((gl.flags & RFL_NO_DEPTHSTENCIL) != 0) + glBindTexture(GL_TEXTURE_2D, mPipelineDepth); + else + glBindTexture(GL_TEXTURE_2D, mPipelineDepthStencil); +} + //========================================================================== // // Binds the current scene/effect/hud texture to the specified texture unit diff --git a/src/gl/renderer/gl_renderbuffers.h b/src/gl/renderer/gl_renderbuffers.h index ee6d8de5e..898193e59 100644 --- a/src/gl/renderer/gl_renderbuffers.h +++ b/src/gl/renderer/gl_renderbuffers.h @@ -23,6 +23,7 @@ public: bool Setup(int width, int height, int sceneWidth, int sceneHeight); void BindSceneFB(); + void BindSceneDepthTexture(int index); void BlitSceneToTexture(); void BindCurrentTexture(int index); @@ -35,6 +36,14 @@ public: enum { NumBloomLevels = 4 }; FGLBloomTextureLevel BloomLevels[NumBloomLevels]; + // Ambient occlusion buffers + GLuint AmbientTexture0 = 0; + GLuint AmbientTexture1 = 0; + GLuint AmbientFB0 = 0; + GLuint AmbientFB1 = 0; + int AmbientWidth = 0; + int AmbientHeight = 0; + static bool IsEnabled(); int GetWidth() const { return mWidth; } @@ -44,15 +53,17 @@ private: void ClearScene(); void ClearPipeline(); void ClearBloom(); + void ClearAmbientOcclusion(); void CreateScene(int width, int height, int samples); void CreatePipeline(int width, int height); void CreateBloom(int width, int height); + void CreateAmbientOcclusion(int width, int height); GLuint Create2DTexture(const FString &name, GLuint format, int width, int height); GLuint CreateRenderBuffer(const FString &name, GLuint format, int width, int height); GLuint CreateRenderBuffer(const FString &name, GLuint format, int samples, int width, int height); GLuint CreateFrameBuffer(const FString &name, GLuint colorbuffer); - GLuint CreateFrameBuffer(const FString &name, GLuint colorbuffer, GLuint depthstencil, bool colorIsARenderBuffer); - GLuint CreateFrameBuffer(const FString &name, GLuint colorbuffer, GLuint depth, GLuint stencil, bool colorIsARenderBuffer); + GLuint CreateFrameBuffer(const FString &name, GLuint colorbuffer, GLuint depthstencil, bool fromRenderBuffers); + GLuint CreateFrameBuffer(const FString &name, GLuint colorbuffer, GLuint depth, GLuint stencil, bool fromRenderBuffers); bool CheckFrameBufferCompleteness(); void ClearFrameBuffer(bool stencil, bool depth); void DeleteTexture(GLuint &handle); @@ -65,22 +76,25 @@ private: int mHeight = 0; int mSamples = 0; int mMaxSamples = 0; - int mBloomWidth = 0; - int mBloomHeight = 0; + int mSceneWidth = 0; + int mSceneHeight = 0; static const int NumPipelineTextures = 2; int mCurrentPipelineTexture = 0; // Buffers for the scene - GLuint mSceneMultisample = 0; - GLuint mSceneDepthStencil = 0; - GLuint mSceneDepth = 0; - GLuint mSceneStencil = 0; + GLuint mSceneMSColor = 0; + GLuint mSceneMSDepthStencil = 0; + GLuint mSceneMSDepth = 0; + GLuint mSceneMSStencil = 0; GLuint mSceneFB = 0; // Effect/HUD buffers GLuint mPipelineTexture[NumPipelineTextures]; GLuint mPipelineFB[NumPipelineTextures]; + GLuint mPipelineDepthStencil = 0; + GLuint mPipelineDepth = 0; + GLuint mPipelineStencil = 0; // Back buffer frame buffer GLuint mOutputFB = 0; diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index c8f2eb224..9e64c13b0 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -64,6 +64,7 @@ #include "gl/data/gl_vertexbuffer.h" #include "gl/scene/gl_drawinfo.h" #include "gl/shaders/gl_shader.h" +#include "gl/shaders/gl_ambientshader.h" #include "gl/shaders/gl_bloomshader.h" #include "gl/shaders/gl_blurshader.h" #include "gl/shaders/gl_tonemapshader.h" @@ -120,6 +121,8 @@ void gl_FlushModels(); void FGLRenderer::Initialize(int width, int height) { mBuffers = new FGLRenderBuffers(); + mLinearDepthShader = new FLinearDepthShader(); + mSSAOShader = new FSSAOShader(); mBloomExtractShader = new FBloomExtractShader(); mBloomCombineShader = new FBloomCombineShader(); mBlurShader = new FBlurShader(); @@ -179,6 +182,8 @@ FGLRenderer::~FGLRenderer() } if (mBuffers) delete mBuffers; if (mPresentShader) delete mPresentShader; + if (mLinearDepthShader) delete mLinearDepthShader; + if (mSSAOShader) delete mSSAOShader; if (mBloomExtractShader) delete mBloomExtractShader; if (mBloomCombineShader) delete mBloomCombineShader; if (mBlurShader) delete mBlurShader; diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 4b663680f..b07870738 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -19,6 +19,8 @@ class FLightBuffer; class FSamplerManager; class DPSprite; class FGLRenderBuffers; +class FLinearDepthShader; +class FSSAOShader; class FBloomExtractShader; class FBloomCombineShader; class FBlurShader; @@ -89,6 +91,8 @@ public: int mOldFBID; FGLRenderBuffers *mBuffers; + FLinearDepthShader *mLinearDepthShader; + FSSAOShader *mSSAOShader; FBloomExtractShader *mBloomExtractShader; FBloomCombineShader *mBloomCombineShader; FBlurShader *mBlurShader; @@ -164,6 +168,8 @@ public: void SetFixedColormap (player_t *player); void WriteSavePic (player_t *player, FILE *file, int width, int height); void EndDrawScene(sector_t * viewsector); + void PostProcessScene(); + void AmbientOccludeScene(); void BloomScene(); void TonemapScene(); void BindTonemapPalette(int texunit); @@ -186,6 +192,9 @@ public: void FillSimplePoly(FTexture *texture, FVector2 *points, int npoints, double originx, double originy, double scalex, double scaley, DAngle rotation, FDynamicColormap *colormap, int lightlevel); + + static float GetZNear() { return 5.f; } + static float GetZFar() { return 65536.f; } }; // Global functions. Make them members of GLRenderer later? diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 08591283c..7537010b3 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -221,7 +221,7 @@ void FGLRenderer::SetProjection(float fov, float ratio, float fovratio) { float fovy = 2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovratio)); - gl_RenderState.mProjectionMatrix.perspective(fovy, ratio, 5.f, 65536.f); + gl_RenderState.mProjectionMatrix.perspective(fovy, ratio, GetZNear(), GetZFar()); } // raw matrix input from stereo 3d modes @@ -855,13 +855,7 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo ProcessScene(toscreen); if (mainview && toscreen) EndDrawScene(retval); // do not call this for camera textures. - if (mainview) - { - if (FGLRenderBuffers::IsEnabled()) mBuffers->BlitSceneToTexture(); - BloomScene(); - TonemapScene(); - LensDistortScene(); - } + if (mainview) PostProcessScene(); mDrawingScene2D = false; eye->TearDown(); } diff --git a/src/gl/shaders/gl_ambientshader.cpp b/src/gl/shaders/gl_ambientshader.cpp new file mode 100644 index 000000000..d9d4a3425 --- /dev/null +++ b/src/gl/shaders/gl_ambientshader.cpp @@ -0,0 +1,89 @@ +/* +** gl_bloomshader.cpp +** Shaders used for screen space ambient occlusion +** +**--------------------------------------------------------------------------- +** Copyright 2016 Magnus Norddahl +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. Full disclosure of the entire project's source code, except for third +** party libraries is mandatory. (NOTE: This clause is non-negotiable!) +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "gl/system/gl_system.h" +#include "files.h" +#include "m_swap.h" +#include "v_video.h" +#include "gl/gl_functions.h" +#include "vectors.h" +#include "gl/system/gl_interface.h" +#include "gl/system/gl_framebuffer.h" +#include "gl/system/gl_cvars.h" +#include "gl/shaders/gl_ambientshader.h" + +void FLinearDepthShader::Bind() +{ + if (!mShader) + { + mShader.Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", 330); + mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/lineardepth.fp", "", 330); + mShader.SetFragDataLocation(0, "FragColor"); + mShader.Link("shaders/glsl/lineardepth"); + mShader.SetAttribLocation(0, "PositionInProjection"); + DepthTexture.Init(mShader, "DepthTexture"); + LinearizeDepthA.Init(mShader, "LinearizeDepthA"); + LinearizeDepthB.Init(mShader, "LinearizeDepthB"); + InverseDepthRangeA.Init(mShader, "InverseDepthRangeA"); + InverseDepthRangeB.Init(mShader, "InverseDepthRangeB"); + } + mShader.Bind(); +} + +void FSSAOShader::Bind() +{ + if (!mShader) + { + mShader.Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", 330); + mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/ssao.fp", "", 330); + mShader.SetFragDataLocation(0, "FragColor"); + mShader.Link("shaders/glsl/ssao"); + mShader.SetAttribLocation(0, "PositionInProjection"); + DepthTexture.Init(mShader, "DepthTexture"); + UVToViewA.Init(mShader, "UVToViewA"); + UVToViewB.Init(mShader, "UVToViewB"); + InvFullResolution.Init(mShader, "InvFullResolution"); + NDotVBias.Init(mShader, "NDotVBias"); + NegInvR2.Init(mShader, "NegInvR2"); + RadiusToScreen.Init(mShader, "RadiusToScreen"); + AOMultiplier.Init(mShader, "AOMultiplier"); + } + mShader.Bind(); +} diff --git a/src/gl/shaders/gl_ambientshader.h b/src/gl/shaders/gl_ambientshader.h new file mode 100644 index 000000000..44f916596 --- /dev/null +++ b/src/gl/shaders/gl_ambientshader.h @@ -0,0 +1,39 @@ +#ifndef __GL_AMBIENTSHADER_H +#define __GL_AMBIENTSHADER_H + +#include "gl_shaderprogram.h" + +class FLinearDepthShader +{ +public: + void Bind(); + + FBufferedUniformSampler DepthTexture; + FBufferedUniform1f LinearizeDepthA; + FBufferedUniform1f LinearizeDepthB; + FBufferedUniform1f InverseDepthRangeA; + FBufferedUniform1f InverseDepthRangeB; + +private: + FShaderProgram mShader; +}; + +class FSSAOShader +{ +public: + void Bind(); + + FBufferedUniformSampler DepthTexture; + FBufferedUniform2f UVToViewA; + FBufferedUniform2f UVToViewB; + FBufferedUniform2f InvFullResolution; + FBufferedUniform1f NDotVBias; + FBufferedUniform1f NegInvR2; + FBufferedUniform1f RadiusToScreen; + FBufferedUniform1f AOMultiplier; + +private: + FShaderProgram mShader; +}; + +#endif \ No newline at end of file diff --git a/src/gl/stereo3d/gl_stereo3d.cpp b/src/gl/stereo3d/gl_stereo3d.cpp index d686a4a43..fa2a20ad4 100644 --- a/src/gl/stereo3d/gl_stereo3d.cpp +++ b/src/gl/stereo3d/gl_stereo3d.cpp @@ -49,7 +49,7 @@ VSMatrix EyePose::GetProjection(float fov, float aspectRatio, float fovRatio) co // Lifted from gl_scene.cpp FGLRenderer::SetProjection() float fovy = (float)(2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovRatio))); - result.perspective(fovy, aspectRatio, 5.f, 65536.f); + result.perspective(fovy, aspectRatio, FGLRenderer::GetZNear(), FGLRenderer::GetZFar()); return result; } diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 010c70565..6c455b94c 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2634,6 +2634,7 @@ GLPREFMNU_MULTISAMPLE = "Multisample"; GLPREFMNU_TONEMAP = "Tonemap Mode"; GLPREFMNU_BLOOM = "Bloom effect"; GLPREFMNU_LENS = "Lens distortion effect"; +GLPREFMNU_SSAO = "Ambient occlusion"; // Option Values OPTVAL_SMART = "Smart"; diff --git a/wadsrc/static/menudef.z b/wadsrc/static/menudef.z index 0d080d709..aae2ae76d 100644 --- a/wadsrc/static/menudef.z +++ b/wadsrc/static/menudef.z @@ -230,4 +230,5 @@ OptionMenu "GLPrefOptions" Option "$GLPREFMNU_TONEMAP", gl_tonemap, "TonemapModes" Option "$GLPREFMNU_BLOOM", gl_bloom, "OnOff" Option "$GLPREFMNU_LENS", gl_lens, "OnOff" + Option "$GLPREFMNU_SSAO", gl_ssao, "OnOff" } diff --git a/wadsrc/static/shaders/glsl/lineardepth.fp b/wadsrc/static/shaders/glsl/lineardepth.fp new file mode 100644 index 000000000..f61bb3995 --- /dev/null +++ b/wadsrc/static/shaders/glsl/lineardepth.fp @@ -0,0 +1,16 @@ + +in vec2 TexCoord; +out vec4 FragColor; + +uniform sampler2D DepthTexture; +uniform float LinearizeDepthA; +uniform float LinearizeDepthB; +uniform float InverseDepthRangeA; +uniform float InverseDepthRangeB; + +void main() +{ + float depth = texture(DepthTexture, TexCoord).x; + float normalizedDepth = clamp(InverseDepthRangeA * depth + InverseDepthRangeB, 0.0, 1.0); + FragColor = vec4(1.0 / (normalizedDepth * LinearizeDepthA + LinearizeDepthB), 0.0, 0.0, 1.0); +} diff --git a/wadsrc/static/shaders/glsl/ssao.fp b/wadsrc/static/shaders/glsl/ssao.fp new file mode 100644 index 000000000..c9f8534d9 --- /dev/null +++ b/wadsrc/static/shaders/glsl/ssao.fp @@ -0,0 +1,117 @@ + +in vec2 TexCoord; +out vec4 FragColor; + +uniform vec2 UVToViewA; +uniform vec2 UVToViewB; +uniform vec2 InvFullResolution; + +uniform float NDotVBias; +uniform float NegInvR2; +uniform float RadiusToScreen; +uniform float AOMultiplier; + +uniform sampler2D DepthTexture; + +#if USE_RANDOM_TEXTURE +uniform sampler2D RandomTexture; +#endif + +#define NUM_DIRECTIONS 8.0 +#define NUM_STEPS 4.0 + +#define PI 3.14159265358979323846 + +// Calculate eye space position for the specified texture coordinate +vec3 FetchViewPos(vec2 uv) +{ + float z = texture(DepthTexture, uv).x; + return vec3((UVToViewA * uv + UVToViewB) * z, z); +} + +vec3 MinDiff(vec3 p, vec3 pr, vec3 pl) +{ + vec3 v1 = pr - p; + vec3 v2 = p - pl; + return (dot(v1, v1) < dot(v2, v2)) ? v1 : v2; +} + +// Reconstruct eye space normal from nearest neighbors +vec3 ReconstructNormal(vec3 p) +{ + vec3 pr = FetchViewPos(TexCoord + vec2(InvFullResolution.x, 0)); + vec3 pl = FetchViewPos(TexCoord + vec2(-InvFullResolution.x, 0)); + vec3 pt = FetchViewPos(TexCoord + vec2(0, InvFullResolution.y)); + vec3 pb = FetchViewPos(TexCoord + vec2(0, -InvFullResolution.y)); + return normalize(cross(MinDiff(p, pr, pl), MinDiff(p, pt, pb))); +} + +// Compute normalized 2D direction +vec2 RotateDirection(vec2 dir, vec2 cossin) +{ + return vec2(dir.x * cossin.x - dir.y * cossin.y, dir.x * cossin.y + dir.y * cossin.x); +} + +vec4 GetJitter() +{ +#if !USE_RANDOM_TEXTURE + return vec4(1,0,1,1); + //vec3 rand = noise3(TexCoord.x + TexCoord.y); + //float angle = 2.0 * PI * rand.x / NUM_DIRECTIONS; + //return vec4(cos(angle), sin(angle), rand.y, rand.z); +#else + #define RANDOM_TEXTURE_WIDTH 4.0 + return texture(RandomTexture, gl_FragCoord.xy / RANDOM_TEXTURE_WIDTH); +#endif +} + +// Calculates the ambient occlusion of a sample +float ComputeSampleAO(vec3 kernelPos, vec3 normal, vec3 samplePos) +{ + vec3 v = samplePos - kernelPos; + float distanceSquare = dot(v, v); + float nDotV = dot(normal, v) * inversesqrt(distanceSquare); + return clamp(nDotV - NDotVBias, 0.0, 1.0) * clamp(distanceSquare * NegInvR2 + 1.0, 0.0, 1.0); +} + +// Calculates the total ambient occlusion for the entire fragment +float ComputeAO(vec3 viewPosition, vec3 viewNormal) +{ + vec4 rand = GetJitter(); + + float radiusPixels = RadiusToScreen / viewPosition.z; + float stepSizePixels = radiusPixels / (NUM_STEPS + 1.0); + + const float directionAngleStep = 2.0 * PI / NUM_DIRECTIONS; + float ao = 0.0; + + for (float directionIndex = 0.0; directionIndex < NUM_DIRECTIONS; ++directionIndex) + { + float angle = directionAngleStep * directionIndex; + + vec2 direction = RotateDirection(vec2(cos(angle), sin(angle)), rand.xy); + float rayPixels = (rand.z * stepSizePixels + 1.0); + + for (float StepIndex = 0.0; StepIndex < NUM_STEPS; ++StepIndex) + { + vec2 sampleUV = round(rayPixels * direction) * InvFullResolution + TexCoord; + vec3 samplePos = FetchViewPos(sampleUV); + ao += ComputeSampleAO(viewPosition, viewNormal, samplePos); + rayPixels += stepSizePixels; + } + } + + ao *= AOMultiplier / (NUM_DIRECTIONS * NUM_STEPS); + return clamp(1.0 - ao * 2.0, 0.0, 1.0); +} + +void main() +{ + vec3 viewPosition = FetchViewPos(TexCoord); + vec3 viewNormal = ReconstructNormal(viewPosition); + float occlusion = ComputeAO(viewPosition, viewNormal); + //FragColor = vec4(viewPosition.x * 0.001 + 0.5, viewPosition.y * 0.001 + 0.5, viewPosition.z * 0.001, 1.0); + //FragColor = vec4(viewNormal.x * 0.5 + 0.5, viewNormal.y * 0.5 + 0.5, viewNormal.z * 0.5 + 0.5, 1.0); + //FragColor = vec4(occlusion, viewPosition.z, 0.0, 1.0); + FragColor = vec4(occlusion, occlusion, occlusion, 1.0); +}