diff --git a/src/gl/renderer/gl_postprocess.cpp b/src/gl/renderer/gl_postprocess.cpp index 8b8afd52e..b3d125922 100644 --- a/src/gl/renderer/gl_postprocess.cpp +++ b/src/gl/renderer/gl_postprocess.cpp @@ -138,9 +138,6 @@ void FGLRenderer::PostProcessScene() void FGLRenderer::AmbientOccludeScene() { - if (!gl_ssao || !FGLRenderBuffers::IsEnabled()) - return; - FGLDebug::PushGroup("AmbientOccludeScene"); FGLPostProcessState savedState; @@ -227,20 +224,27 @@ void FGLRenderer::AmbientOccludeScene() RenderScreenQuad(); // Add SSAO back to scene texture: - mBuffers->BindSceneFB(); + mBuffers->BindSceneFB(false); + GLenum buffers[] = { GL_COLOR_ATTACHMENT0 }; + glDrawBuffers(1, buffers); 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); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 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); - mSSAOCombineShader->Bind(); - mSSAOCombineShader->AODepthTexture.Set(0); + mBuffers->BindSceneDataTexture(1); + mSSAOCombineShader->Bind(multisample); + mSSAOCombineShader->AODepthTexture[multisample].Set(0); + mSSAOCombineShader->SceneDataTexture[multisample].Set(1); + if (multisample) mSSAOCombineShader->SampleCount[multisample].Set(gl_multisample); + mSSAOCombineShader->Scale[multisample].Set(mBuffers->AmbientWidth * 2.0f / (float)mScreenViewport.width, mBuffers->AmbientHeight * 2.0f / (float)mScreenViewport.height); + mSSAOCombineShader->Offset[multisample].Set(mSceneViewport.left / (float)mScreenViewport.width, mSceneViewport.top / (float)mScreenViewport.height); RenderScreenQuad(); FGLDebug::PopGroup(); diff --git a/src/gl/renderer/gl_renderbuffers.cpp b/src/gl/renderer/gl_renderbuffers.cpp index b52f2a0b6..54d597c4d 100644 --- a/src/gl/renderer/gl_renderbuffers.cpp +++ b/src/gl/renderer/gl_renderbuffers.cpp @@ -82,7 +82,9 @@ FGLRenderBuffers::~FGLRenderBuffers() void FGLRenderBuffers::ClearScene() { DeleteFrameBuffer(mSceneFB); + DeleteFrameBuffer(mSceneDataFB); DeleteTexture(mSceneMultisample); + DeleteTexture(mSceneData); DeleteTexture(mSceneDepthStencil); } @@ -253,13 +255,16 @@ void FGLRenderBuffers::CreateScene(int width, int height, int samples) { mSceneMultisample = Create2DMultisampleTexture("SceneMultisample", GL_RGBA16F, width, height, samples, false); mSceneDepthStencil = Create2DMultisampleTexture("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height, samples, false); + mSceneData = Create2DMultisampleTexture("SceneSSAOData", GL_RGBA8, width, height, samples, false); } else { mSceneDepthStencil = Create2DTexture("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height); + mSceneData = Create2DTexture("SceneSSAOData", GL_RGBA8, width, height); } - mSceneFB = CreateFrameBuffer("SceneFB", samples > 1 ? mSceneMultisample : mPipelineTexture[0], mSceneDepthStencil, samples > 1); + mSceneFB = CreateFrameBuffer("SceneFB", samples > 1 ? mSceneMultisample : mPipelineTexture[0], 0, mSceneDepthStencil, samples > 1); + mSceneDataFB = CreateFrameBuffer("SSAOSceneFB", samples > 1 ? mSceneMultisample : mPipelineTexture[0], mSceneData, mSceneDepthStencil, samples > 1); } //========================================================================== @@ -512,7 +517,7 @@ GLuint FGLRenderBuffers::CreateFrameBuffer(const FString &name, GLuint colorbuff return handle; } -GLuint FGLRenderBuffers::CreateFrameBuffer(const FString &name, GLuint colorbuffer, GLuint depthstencil, bool multisample) +GLuint FGLRenderBuffers::CreateFrameBuffer(const FString &name, GLuint colorbuffer0, GLuint colorbuffer1, GLuint depthstencil, bool multisample) { GLuint handle = 0; glGenFramebuffers(1, &handle); @@ -520,12 +525,16 @@ GLuint FGLRenderBuffers::CreateFrameBuffer(const FString &name, GLuint colorbuff FGLDebug::LabelObject(GL_FRAMEBUFFER, handle, name); if (multisample) { - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, colorbuffer, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, colorbuffer0, 0); + if (colorbuffer1 != 0) + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D_MULTISAMPLE, colorbuffer1, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, depthstencil, 0); } else { - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer0, 0); + if (colorbuffer1 != 0) + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, colorbuffer1, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthstencil, 0); } if (CheckFrameBufferCompleteness()) @@ -668,9 +677,9 @@ void FGLRenderBuffers::BindEyeFB(int eye, bool readBuffer) // //========================================================================== -void FGLRenderBuffers::BindSceneFB() +void FGLRenderBuffers::BindSceneFB(bool sceneData) { - glBindFramebuffer(GL_FRAMEBUFFER, mSceneFB); + glBindFramebuffer(GL_FRAMEBUFFER, sceneData ? mSceneDataFB : mSceneFB); } //========================================================================== @@ -688,6 +697,21 @@ void FGLRenderBuffers::BindSceneColorTexture(int index) glBindTexture(GL_TEXTURE_2D, mPipelineTexture[0]); } +//========================================================================== +// +// Binds the scene data texture to the specified texture unit +// +//========================================================================== + +void FGLRenderBuffers::BindSceneDataTexture(int index) +{ + glActiveTexture(GL_TEXTURE0 + index); + if (mSamples > 1) + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mSceneData); + else + glBindTexture(GL_TEXTURE_2D, mSceneData); +} + //========================================================================== // // Binds the depth texture to the specified texture unit diff --git a/src/gl/renderer/gl_renderbuffers.h b/src/gl/renderer/gl_renderbuffers.h index 19a2979f2..080593881 100644 --- a/src/gl/renderer/gl_renderbuffers.h +++ b/src/gl/renderer/gl_renderbuffers.h @@ -31,8 +31,9 @@ public: bool Setup(int width, int height, int sceneWidth, int sceneHeight); - void BindSceneFB(); + void BindSceneFB(bool sceneData); void BindSceneColorTexture(int index); + void BindSceneDataTexture(int index); void BindSceneDepthTexture(int index); void BlitSceneToTexture(); @@ -90,7 +91,7 @@ private: 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 multisample); + GLuint CreateFrameBuffer(const FString &name, GLuint colorbuffer0, GLuint colorbuffer1, GLuint depthstencil, bool multisample); bool CheckFrameBufferCompleteness(); void ClearFrameBuffer(bool stencil, bool depth); void DeleteTexture(GLuint &handle); @@ -110,7 +111,9 @@ private: // Buffers for the scene GLuint mSceneMultisample = 0; GLuint mSceneDepthStencil = 0; + GLuint mSceneData = 0; GLuint mSceneFB = 0; + GLuint mSceneDataFB = 0; // Effect/HUD buffers GLuint mPipelineTexture[NumPipelineTextures]; diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index db2be0d9d..a02d27852 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -320,7 +320,7 @@ void FGLRenderer::Begin2D() if (mBuffers->Setup(mScreenViewport.width, mScreenViewport.height, mSceneViewport.width, mSceneViewport.height)) { if (mDrawingScene2D) - mBuffers->BindSceneFB(); + mBuffers->BindSceneFB(false); else mBuffers->BindCurrentFB(); } diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 2659e8ea2..596f2c22b 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -157,7 +157,11 @@ void FGLRenderer::Set3DViewport(bool mainview) { if (mainview && mBuffers->Setup(mScreenViewport.width, mScreenViewport.height, mSceneViewport.width, mSceneViewport.height)) { - mBuffers->BindSceneFB(); + mBuffers->BindSceneFB(gl_ssao); + GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; + glDrawBuffers(gl_ssao ? 2 : 1, buffers); + gl_RenderState.SetPassType(gl_ssao ? GBUFFER_PASS : NORMAL_PASS); + gl_RenderState.Apply(); } // Always clear all buffers with scissor test disabled. @@ -490,7 +494,13 @@ void FGLRenderer::DrawScene(int drawmode) RenderScene(recursion); - AmbientOccludeScene(); + bool applySSAO = gl_ssao && FGLRenderBuffers::IsEnabled() && drawmode != DM_PORTAL; + if (applySSAO) + { + AmbientOccludeScene(); + gl_RenderState.SetPassType(GBUFFER_PASS); + gl_RenderState.Apply(); + } // Handle all portals after rendering the opaque objects but before // doing all translucent stuff @@ -498,6 +508,15 @@ void FGLRenderer::DrawScene(int drawmode) GLPortal::EndFrame(); recursion--; RenderTranslucent(); + + if (applySSAO) + { + mBuffers->BindSceneFB(true); + GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; + glDrawBuffers(2, buffers); + gl_RenderState.SetPassType(NORMAL_PASS); + gl_RenderState.Apply(); + } } diff --git a/src/gl/shaders/gl_ambientshader.cpp b/src/gl/shaders/gl_ambientshader.cpp index 3fbd03434..4fa5b0e01 100644 --- a/src/gl/shaders/gl_ambientshader.cpp +++ b/src/gl/shaders/gl_ambientshader.cpp @@ -120,16 +120,21 @@ void FDepthBlurShader::Bind(bool vertical) shader.Bind(); } -void FSSAOCombineShader::Bind() +void FSSAOCombineShader::Bind(bool multisample) { - if (!mShader) + auto &shader = mShader[multisample]; + if (!shader) { - mShader.Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", 330); - mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/ssaocombine.fp", "", 330); - mShader.SetFragDataLocation(0, "FragColor"); - mShader.Link("shaders/glsl/ssaocombine"); - mShader.SetAttribLocation(0, "PositionInProjection"); - AODepthTexture.Init(mShader, "AODepthTexture"); + shader.Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", 330); + shader.Compile(FShaderProgram::Fragment, "shaders/glsl/ssaocombine.fp", multisample ? "#define MULTISAMPLE\n" : "", 330); + shader.SetFragDataLocation(0, "FragColor"); + shader.Link("shaders/glsl/ssaocombine"); + shader.SetAttribLocation(0, "PositionInProjection"); + AODepthTexture[multisample].Init(shader, "AODepthTexture"); + SceneDataTexture[multisample].Init(shader, "SceneDataTexture"); + SampleCount[multisample].Init(shader, "SampleCount"); + Scale[multisample].Init(shader, "Scale"); + Offset[multisample].Init(shader, "Offset"); } - mShader.Bind(); + shader.Bind(); } diff --git a/src/gl/shaders/gl_ambientshader.h b/src/gl/shaders/gl_ambientshader.h index 9c97791ba..f36b10625 100644 --- a/src/gl/shaders/gl_ambientshader.h +++ b/src/gl/shaders/gl_ambientshader.h @@ -59,12 +59,16 @@ private: class FSSAOCombineShader { public: - void Bind(); + void Bind(bool multisample); - FBufferedUniformSampler AODepthTexture; + FBufferedUniformSampler AODepthTexture[2]; + FBufferedUniformSampler SceneDataTexture[2]; + FBufferedUniform1i SampleCount[2]; + FBufferedUniform2f Scale[2]; + FBufferedUniform2f Offset[2]; private: - FShaderProgram mShader; + FShaderProgram mShader[2]; }; #endif \ No newline at end of file diff --git a/src/gl/system/gl_cvars.h b/src/gl/system/gl_cvars.h index f6dc61472..c1bc5c2fe 100644 --- a/src/gl/system/gl_cvars.h +++ b/src/gl/system/gl_cvars.h @@ -51,6 +51,12 @@ EXTERN_CVAR(Bool, gl_lens) EXTERN_CVAR(Float, gl_lens_k) EXTERN_CVAR(Float, gl_lens_kcube) EXTERN_CVAR(Float, gl_lens_chromatic) +EXTERN_CVAR(Bool, gl_ssao) +EXTERN_CVAR(Float, gl_ssao_strength) +EXTERN_CVAR(Bool, gl_ssao_debug) +EXTERN_CVAR(Float, gl_ssao_bias) +EXTERN_CVAR(Float, gl_ssao_radius) +EXTERN_CVAR(Float, gl_ssao_blur_amount) EXTERN_CVAR(Int, gl_debug_level) EXTERN_CVAR(Bool, gl_debug_breakpoint) diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp index 0b66f0569..cdd6ffe3f 100644 --- a/wadsrc/static/shaders/glsl/main.fp +++ b/wadsrc/static/shaders/glsl/main.fp @@ -315,6 +315,32 @@ vec4 applyFog(vec4 frag, float fogfactor) return vec4(mix(uFogColor.rgb, frag.rgb, fogfactor), frag.a); } +//=========================================================================== +// +// The color of the fragment if it is fully occluded by ambient lighting +// +//=========================================================================== + +vec3 AmbientOcclusionColor() +{ + float fogdist; + float fogfactor; + + // + // calculate fog factor + // + if (uFogEnabled == -1) + { + fogdist = pixelpos.w; + } + else + { + fogdist = max(16.0, length(pixelpos.xyz)); + } + fogfactor = exp2 (uFogDensity * fogdist); + + return mix(uFogColor.rgb, vec3(0.0), fogfactor); +} //=========================================================================== // @@ -437,7 +463,7 @@ void main() } FragColor = frag; #ifdef GBUFFER_PASS - FragData = vec4(uFogColor.rgb, 1.0); + FragData = vec4(AmbientOcclusionColor(), 1.0); #endif } diff --git a/wadsrc/static/shaders/glsl/ssaocombine.fp b/wadsrc/static/shaders/glsl/ssaocombine.fp index 21fdff102..7cd096276 100644 --- a/wadsrc/static/shaders/glsl/ssaocombine.fp +++ b/wadsrc/static/shaders/glsl/ssaocombine.fp @@ -4,8 +4,35 @@ out vec4 FragColor; uniform sampler2D AODepthTexture; +#if defined(MULTISAMPLE) +uniform sampler2DMS SceneDataTexture; +uniform int SampleCount; +#else +uniform sampler2D SceneDataTexture; +#endif + +uniform vec2 Scale; +uniform vec2 Offset; + void main() { + vec2 uv = Offset + TexCoord * Scale; +#if defined(MULTISAMPLE) + ivec2 texSize = textureSize(SceneDataTexture); +#else + ivec2 texSize = textureSize(SceneDataTexture, 0); +#endif + ivec2 ipos = ivec2(max(floor(uv * vec2(texSize) - 0.75), vec2(0.0))); + +#if defined(MULTISAMPLE) + vec3 fogColor = vec3(0.0); + for (int i = 0; i < SampleCount; i++) + fogColor += texelFetch(SceneDataTexture, ipos, i).rgb; + fogColor /= float(SampleCount); +#else + vec3 fogColor = texelFetch(SceneDataTexture, ipos, 0).rgb; +#endif + float attenutation = texture(AODepthTexture, TexCoord).x; - FragColor = vec4(attenutation, attenutation, attenutation, 0.0); + FragColor = vec4(fogColor, 1.0 - attenutation); }