diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b75f3559c..49153f574 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -963,6 +963,7 @@ set( FASTMATH_SOURCES gl/shaders/gl_presentshader.cpp gl/shaders/gl_present3dRowshader.cpp gl/shaders/gl_bloomshader.cpp + gl/shaders/gl_ambientshader.cpp gl/shaders/gl_blurshader.cpp gl/shaders/gl_colormapshader.cpp gl/shaders/gl_tonemapshader.cpp diff --git a/src/gl/renderer/gl_postprocess.cpp b/src/gl/renderer/gl_postprocess.cpp index 6d080434d..f35071854 100644 --- a/src/gl/renderer/gl_postprocess.cpp +++ b/src/gl/renderer/gl_postprocess.cpp @@ -54,6 +54,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,32 @@ CUSTOM_CVAR(Int, gl_fxaa, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) } } +CUSTOM_CVAR(Int, gl_ssao, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (self < 0 || self > 3) + self = 0; +} + +CUSTOM_CVAR(Int, gl_ssao_portals, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (self < 0) + self = 0; +} + +CVAR(Float, gl_ssao_strength, 0.7, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Int, gl_ssao_debug, 0, 0) +CVAR(Float, gl_ssao_bias, 0.2f, 0) +CVAR(Float, gl_ssao_radius, 80.0f, 0) +CUSTOM_CVAR(Float, gl_ssao_blur, 16.0f, 0) +{ + if (self < 0.1f) self = 0.1f; +} + +CUSTOM_CVAR(Float, gl_ssao_exponent, 1.8f, 0) +{ + if (self < 0.1f) self = 0.1f; +} + EXTERN_CVAR(Float, vid_brightness) EXTERN_CVAR(Float, vid_contrast) @@ -117,6 +144,153 @@ void FGLRenderer::RenderScreenQuad() GLRenderer->mVBO->RenderArray(GL_TRIANGLE_STRIP, FFlatVertexBuffer::PRESENT_INDEX, 4); } +void FGLRenderer::PostProcessScene() +{ + mBuffers->BlitSceneToTexture(); + UpdateCameraExposure(); + BloomScene(); + TonemapScene(); + ColormapScene(); + LensDistortScene(); + ApplyFXAA(); +} + +//----------------------------------------------------------------------------- +// +// Adds ambient occlusion to the scene +// +//----------------------------------------------------------------------------- + +void FGLRenderer::AmbientOccludeScene() +{ + FGLDebug::PushGroup("AmbientOccludeScene"); + + FGLPostProcessState savedState; + savedState.SaveTextureBindings(3); + + float bias = gl_ssao_bias; + float aoRadius = gl_ssao_radius; + const float blurAmount = gl_ssao_blur; + float aoStrength = gl_ssao_strength; + + //float tanHalfFovy = tan(fovy * (M_PI / 360.0f)); + float tanHalfFovy = 1.0f / gl_RenderState.mProjectionMatrix.get()[5]; + float invFocalLenX = tanHalfFovy * (mBuffers->GetSceneWidth() / (float)mBuffers->GetSceneHeight()); + float invFocalLenY = tanHalfFovy; + float nDotVBias = clamp(bias, 0.0f, 1.0f); + float r2 = aoRadius * aoRadius; + + float blurSharpness = 1.0f / blurAmount; + + float sceneScaleX = mSceneViewport.width / (float)mScreenViewport.width; + float sceneScaleY = mSceneViewport.height / (float)mScreenViewport.height; + float sceneOffsetX = mSceneViewport.left / (float)mScreenViewport.width; + float sceneOffsetY = mSceneViewport.top / (float)mScreenViewport.height; + + int randomTexture = clamp(gl_ssao - 1, 0, FGLRenderBuffers::NumAmbientRandomTextures - 1); + + // Calculate linear depth values + glBindFramebuffer(GL_FRAMEBUFFER, mBuffers->LinearDepthFB); + glViewport(0, 0, mBuffers->AmbientWidth, mBuffers->AmbientHeight); + mBuffers->BindSceneDepthTexture(0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + mBuffers->BindSceneColorTexture(1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glActiveTexture(GL_TEXTURE0); + mLinearDepthShader->Bind(); + mLinearDepthShader->DepthTexture.Set(0); + mLinearDepthShader->ColorTexture.Set(1); + if (gl_multisample > 1) mLinearDepthShader->SampleIndex.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); + mLinearDepthShader->Scale.Set(sceneScaleX, sceneScaleY); + mLinearDepthShader->Offset.Set(sceneOffsetX, sceneOffsetY); + RenderScreenQuad(); + + // Apply ambient occlusion + glBindFramebuffer(GL_FRAMEBUFFER, mBuffers->AmbientFB1); + glBindTexture(GL_TEXTURE_2D, mBuffers->LinearDepthTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, mBuffers->AmbientRandomTexture[randomTexture]); + 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); + mBuffers->BindSceneNormalTexture(2); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glActiveTexture(GL_TEXTURE0); + mSSAOShader->Bind(); + mSSAOShader->DepthTexture.Set(0); + mSSAOShader->RandomTexture.Set(1); + mSSAOShader->NormalTexture.Set(2); + if (gl_multisample > 1) mSSAOShader->SampleIndex.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)); + mSSAOShader->AOStrength.Set(aoStrength); + mSSAOShader->Scale.Set(sceneScaleX, sceneScaleY); + mSSAOShader->Offset.Set(sceneOffsetX, sceneOffsetY); + RenderScreenQuad(); + + // Blur SSAO texture + if (gl_ssao_debug < 2) + { + glBindFramebuffer(GL_FRAMEBUFFER, mBuffers->AmbientFB0); + glBindTexture(GL_TEXTURE_2D, mBuffers->AmbientTexture1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + mDepthBlurShader->Bind(false); + mDepthBlurShader->BlurSharpness[false].Set(blurSharpness); + mDepthBlurShader->InvFullResolution[false].Set(1.0f / mBuffers->AmbientWidth, 1.0f / mBuffers->AmbientHeight); + RenderScreenQuad(); + + glBindFramebuffer(GL_FRAMEBUFFER, mBuffers->AmbientFB1); + glBindTexture(GL_TEXTURE_2D, mBuffers->AmbientTexture0); + mDepthBlurShader->Bind(true); + mDepthBlurShader->BlurSharpness[true].Set(blurSharpness); + mDepthBlurShader->InvFullResolution[true].Set(1.0f / mBuffers->AmbientWidth, 1.0f / mBuffers->AmbientHeight); + mDepthBlurShader->PowExponent[true].Set(gl_ssao_exponent); + RenderScreenQuad(); + } + + // Add SSAO back to scene texture: + mBuffers->BindSceneFB(false); + glViewport(mSceneViewport.left, mSceneViewport.top, mSceneViewport.width, mSceneViewport.height); + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + if (gl_ssao_debug != 0) + { + glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + } + 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); + mBuffers->BindSceneFogTexture(1); + mSSAOCombineShader->Bind(); + mSSAOCombineShader->AODepthTexture.Set(0); + mSSAOCombineShader->SceneFogTexture.Set(1); + if (gl_multisample > 1) mSSAOCombineShader->SampleCount.Set(gl_multisample); + mSSAOCombineShader->Scale.Set(sceneScaleX, sceneScaleY); + mSSAOCombineShader->Offset.Set(sceneOffsetX, sceneOffsetY); + RenderScreenQuad(); + + FGLDebug::PopGroup(); +} + //----------------------------------------------------------------------------- // // Extracts light average from the scene and updates the camera exposure texture @@ -131,7 +305,7 @@ void FGLRenderer::UpdateCameraExposure() FGLDebug::PushGroup("UpdateCameraExposure"); FGLPostProcessState savedState; - savedState.SaveTextureBinding1(); + savedState.SaveTextureBindings(2); // Extract light level from scene texture: const auto &level0 = mBuffers->ExposureLevels[0]; @@ -198,13 +372,13 @@ void FGLRenderer::UpdateCameraExposure() void FGLRenderer::BloomScene() { // Only bloom things if enabled and no special fixed light mode is active - if (!gl_bloom || gl_fixedcolormap != CM_DEFAULT) + if (!gl_bloom || gl_fixedcolormap != CM_DEFAULT || gl_ssao_debug) return; FGLDebug::PushGroup("BloomScene"); FGLPostProcessState savedState; - savedState.SaveTextureBinding1(); + savedState.SaveTextureBindings(2); const float blurAmount = gl_bloom_amount; int sampleCount = gl_bloom_kernel_size; @@ -293,7 +467,10 @@ void FGLRenderer::TonemapScene() FGLDebug::PushGroup("TonemapScene"); + CreateTonemapPalette(); + FGLPostProcessState savedState; + savedState.SaveTextureBindings(2); mBuffers->BindNextFB(); mBuffers->BindCurrentTexture(0); @@ -302,12 +479,18 @@ void FGLRenderer::TonemapScene() if (mTonemapShader->IsPaletteMode()) { + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, mTonemapPalette->GetTextureHandle(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_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glActiveTexture(GL_TEXTURE0); + mTonemapShader->PaletteLUT.Set(1); - BindTonemapPalette(1); } else { - savedState.SaveTextureBinding1(); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, mBuffers->ExposureTexture); glActiveTexture(GL_TEXTURE0); @@ -321,13 +504,9 @@ void FGLRenderer::TonemapScene() FGLDebug::PopGroup(); } -void FGLRenderer::BindTonemapPalette(int texunit) +void FGLRenderer::CreateTonemapPalette() { - if (mTonemapPalette) - { - mTonemapPalette->Bind(texunit, 0, false); - } - else + if (!mTonemapPalette) { TArray lut; lut.Resize(512 * 512 * 4); @@ -348,14 +527,7 @@ void FGLRenderer::BindTonemapPalette(int texunit) } mTonemapPalette = new FHardwareTexture(512, 512, true); - mTonemapPalette->CreateTexture(&lut[0], 512, 512, texunit, false, 0, "mTonemapPalette"); - - glActiveTexture(GL_TEXTURE0 + texunit); - 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); - glActiveTexture(GL_TEXTURE0); + mTonemapPalette->CreateTexture(&lut[0], 512, 512, 0, false, 0, "mTonemapPalette"); } } @@ -425,7 +597,7 @@ void FGLRenderer::LensDistortScene() 0.0f }; - float aspect = mSceneViewport.width / mSceneViewport.height; + float aspect = mSceneViewport.width / (float)mSceneViewport.height; // Scale factor to keep sampling within the input texture float r2 = aspect * aspect * 0.25 + 0.25f; diff --git a/src/gl/renderer/gl_postprocessstate.cpp b/src/gl/renderer/gl_postprocessstate.cpp index 57b7862f5..fead1435a 100644 --- a/src/gl/renderer/gl_postprocessstate.cpp +++ b/src/gl/renderer/gl_postprocessstate.cpp @@ -45,17 +45,7 @@ FGLPostProcessState::FGLPostProcessState() { glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTex); glActiveTexture(GL_TEXTURE0); - glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding[0]); - glBindTexture(GL_TEXTURE_2D, 0); - if (gl.flags & RFL_SAMPLER_OBJECTS) - { - glGetIntegerv(GL_SAMPLER_BINDING, &samplerBinding[0]); - glBindSampler(0, 0); - glActiveTexture(GL_TEXTURE0 + 1); - glGetIntegerv(GL_SAMPLER_BINDING, &samplerBinding[1]); - glBindSampler(1, 0); - glActiveTexture(GL_TEXTURE0); - } + SaveTextureBindings(1); glGetBooleanv(GL_BLEND, &blendEnabled); glGetBooleanv(GL_SCISSOR_TEST, &scissorEnabled); @@ -75,12 +65,26 @@ FGLPostProcessState::FGLPostProcessState() glDisable(GL_BLEND); } -void FGLPostProcessState::SaveTextureBinding1() +void FGLPostProcessState::SaveTextureBindings(unsigned int numUnits) { - glActiveTexture(GL_TEXTURE1); - glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding[1]); - glBindTexture(GL_TEXTURE_2D, 0); - textureBinding1Saved = true; + while (textureBinding.Size() < numUnits) + { + unsigned int i = textureBinding.Size(); + + GLint texture; + glActiveTexture(GL_TEXTURE0 + i); + glGetIntegerv(GL_TEXTURE_BINDING_2D, &texture); + glBindTexture(GL_TEXTURE_2D, 0); + textureBinding.Push(texture); + + if (gl.flags & RFL_SAMPLER_OBJECTS) + { + GLint sampler; + glGetIntegerv(GL_SAMPLER_BINDING, &sampler); + glBindSampler(i, 0); + samplerBinding.Push(sampler); + } + } glActiveTexture(GL_TEXTURE0); } @@ -117,25 +121,22 @@ FGLPostProcessState::~FGLPostProcessState() glUseProgram(currentProgram); - if (textureBinding1Saved) + // Fully unbind to avoid incomplete texture warnings from Nvidia's driver when gl_debug_level 4 is active + for (unsigned int i = 0; i < textureBinding.Size(); i++) { - glActiveTexture(GL_TEXTURE1); + glActiveTexture(GL_TEXTURE0 + i); glBindTexture(GL_TEXTURE_2D, 0); } - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, 0); - if (gl.flags & RFL_SAMPLER_OBJECTS) + for (unsigned int i = 0; i < samplerBinding.Size(); i++) { - glBindSampler(0, samplerBinding[0]); - glBindSampler(1, samplerBinding[1]); + glBindSampler(i, samplerBinding[i]); } - glBindTexture(GL_TEXTURE_2D, textureBinding[0]); - if (textureBinding1Saved) + for (unsigned int i = 0; i < textureBinding.Size(); i++) { - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, textureBinding[1]); + glActiveTexture(GL_TEXTURE0 + i); + glBindTexture(GL_TEXTURE_2D, textureBinding[i]); } glActiveTexture(activeTex); diff --git a/src/gl/renderer/gl_postprocessstate.h b/src/gl/renderer/gl_postprocessstate.h index bf53aa7de..795a7d4ba 100644 --- a/src/gl/renderer/gl_postprocessstate.h +++ b/src/gl/renderer/gl_postprocessstate.h @@ -14,15 +14,15 @@ public: FGLPostProcessState(); ~FGLPostProcessState(); - void SaveTextureBinding1(); + void SaveTextureBindings(unsigned int numUnits); private: FGLPostProcessState(const FGLPostProcessState &) = delete; FGLPostProcessState &operator=(const FGLPostProcessState &) = delete; GLint activeTex; - GLint textureBinding[2]; - GLint samplerBinding[2]; + TArray textureBinding; + TArray samplerBinding; GLboolean blendEnabled; GLboolean scissorEnabled; GLboolean depthEnabled; @@ -34,7 +34,6 @@ private: GLint blendSrcAlpha; GLint blendDestRgb; GLint blendDestAlpha; - bool textureBinding1Saved = false; }; #endif diff --git a/src/gl/renderer/gl_renderbuffers.cpp b/src/gl/renderer/gl_renderbuffers.cpp index b2471e4b9..b14ee9852 100644 --- a/src/gl/renderer/gl_renderbuffers.cpp +++ b/src/gl/renderer/gl_renderbuffers.cpp @@ -40,6 +40,7 @@ #include "w_wad.h" #include "i_system.h" #include "doomerrors.h" +#include CVAR(Int, gl_multisample, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); CVAR(Bool, gl_renderbuffers, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) @@ -58,6 +59,11 @@ FGLRenderBuffers::FGLRenderBuffers() mPipelineFB[i] = 0; } + for (int i = 0; i < NumAmbientRandomTextures; i++) + { + AmbientRandomTexture[i] = 0; + } + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&mOutputFB); glGetIntegerv(GL_MAX_SAMPLES, &mMaxSamples); } @@ -75,15 +81,27 @@ FGLRenderBuffers::~FGLRenderBuffers() ClearEyeBuffers(); ClearBloom(); ClearExposureLevels(); + ClearAmbientOcclusion(); } void FGLRenderBuffers::ClearScene() { DeleteFrameBuffer(mSceneFB); - DeleteRenderBuffer(mSceneMultisample); - DeleteRenderBuffer(mSceneDepthStencil); - DeleteRenderBuffer(mSceneDepth); - DeleteRenderBuffer(mSceneStencil); + DeleteFrameBuffer(mSceneDataFB); + if (mSceneUsesTextures) + { + DeleteTexture(mSceneMultisample); + DeleteTexture(mSceneFog); + DeleteTexture(mSceneNormal); + DeleteTexture(mSceneDepthStencil); + } + else + { + DeleteRenderBuffer(mSceneMultisample); + DeleteRenderBuffer(mSceneFog); + DeleteRenderBuffer(mSceneNormal); + DeleteRenderBuffer(mSceneDepthStencil); + } } void FGLRenderBuffers::ClearPipeline() @@ -132,6 +150,18 @@ void FGLRenderBuffers::ClearEyeBuffers() mEyeFBs.Clear(); } +void FGLRenderBuffers::ClearAmbientOcclusion() +{ + DeleteFrameBuffer(LinearDepthFB); + DeleteFrameBuffer(AmbientFB0); + DeleteFrameBuffer(AmbientFB1); + DeleteTexture(LinearDepthTexture); + DeleteTexture(AmbientTexture0); + DeleteTexture(AmbientTexture1); + for (int i = 0; i < NumAmbientRandomTextures; i++) + DeleteTexture(AmbientRandomTexture[i]); +} + void FGLRenderBuffers::DeleteTexture(GLuint &handle) { if (handle != 0) @@ -177,6 +207,7 @@ bool FGLRenderBuffers::Setup(int width, int height, int sceneWidth, int sceneHei I_FatalError("Requested invalid render buffer sizes: screen = %dx%d", width, height); int samples = clamp((int)gl_multisample, 0, mMaxSamples); + bool needsSceneTextures = (gl_ssao != 0); GLint activeTex; GLint textureBinding; @@ -184,25 +215,23 @@ bool FGLRenderBuffers::Setup(int width, int height, int sceneWidth, int sceneHei glActiveTexture(GL_TEXTURE0); glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding); - if (width == mWidth && height == mHeight && mSamples != samples) - { - CreateScene(mWidth, mHeight, samples); - mSamples = samples; - } - else if (width != mWidth || height != mHeight) - { + if (width != mWidth || height != mHeight) CreatePipeline(width, height); - CreateScene(width, height, samples); - mWidth = width; - mHeight = height; - mSamples = samples; - } + + if (width != mWidth || height != mHeight || mSamples != samples || mSceneUsesTextures != needsSceneTextures) + CreateScene(width, height, samples, needsSceneTextures); + + mWidth = width; + mHeight = height; + mSamples = samples; + mSceneUsesTextures = needsSceneTextures; // Bloom bluring buffers need to match the scene to avoid bloom bleeding artifacts if (mSceneWidth != sceneWidth || mSceneHeight != sceneHeight) { CreateBloom(sceneWidth, sceneHeight); CreateExposureLevels(sceneWidth, sceneHeight); + CreateAmbientOcclusion(sceneWidth, sceneHeight); mSceneWidth = sceneWidth; mSceneHeight = sceneHeight; } @@ -235,15 +264,46 @@ bool FGLRenderBuffers::Setup(int width, int height, int sceneWidth, int sceneHei // //========================================================================== -void FGLRenderBuffers::CreateScene(int width, int height, int samples) +void FGLRenderBuffers::CreateScene(int width, int height, int samples, bool needsSceneTextures) { ClearScene(); if (samples > 1) - mSceneMultisample = CreateRenderBuffer("SceneMultisample", GL_RGBA16F, samples, width, height); - - mSceneDepthStencil = CreateRenderBuffer("SceneDepthStencil", GL_DEPTH24_STENCIL8, samples, width, height); - mSceneFB = CreateFrameBuffer("SceneFB", samples > 1 ? mSceneMultisample : mPipelineTexture[0], mSceneDepthStencil, samples > 1); + { + if (needsSceneTextures) + { + mSceneMultisample = Create2DMultisampleTexture("SceneMultisample", GL_RGBA16F, width, height, samples, false); + mSceneDepthStencil = Create2DMultisampleTexture("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height, samples, false); + mSceneFog = Create2DMultisampleTexture("SceneFog", GL_RGBA8, width, height, samples, false); + mSceneNormal = Create2DMultisampleTexture("SceneNormal", GL_RGB10_A2, width, height, samples, false); + mSceneFB = CreateFrameBuffer("SceneFB", mSceneMultisample, 0, 0, mSceneDepthStencil, true); + mSceneDataFB = CreateFrameBuffer("SceneGBufferFB", mSceneMultisample, mSceneFog, mSceneNormal, mSceneDepthStencil, true); + } + else + { + mSceneMultisample = CreateRenderBuffer("SceneMultisample", GL_RGBA16F, width, height, samples); + mSceneDepthStencil = CreateRenderBuffer("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height, samples); + mSceneFB = CreateFrameBuffer("SceneFB", mSceneMultisample, mSceneDepthStencil, true); + mSceneDataFB = CreateFrameBuffer("SceneGBufferFB", mSceneMultisample, mSceneDepthStencil, true); + } + } + else + { + if (needsSceneTextures) + { + mSceneDepthStencil = Create2DTexture("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height); + mSceneFog = Create2DTexture("SceneFog", GL_RGBA8, width, height); + mSceneNormal = Create2DTexture("SceneNormal", GL_RGB10_A2, width, height); + mSceneFB = CreateFrameBuffer("SceneFB", mPipelineTexture[0], 0, 0, mSceneDepthStencil, false); + mSceneDataFB = CreateFrameBuffer("SceneGBufferFB", mPipelineTexture[0], mSceneFog, mSceneNormal, mSceneDepthStencil, false); + } + else + { + mSceneDepthStencil = CreateRenderBuffer("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height); + mSceneFB = CreateFrameBuffer("SceneFB", mPipelineTexture[0], mSceneDepthStencil, false); + mSceneDataFB = CreateFrameBuffer("SceneGBufferFB", mPipelineTexture[0], mSceneDepthStencil, false); + } + } } //========================================================================== @@ -278,13 +338,13 @@ void FGLRenderBuffers::CreateBloom(int width, int height) if (width <= 0 || height <= 0) return; - int bloomWidth = MAX(width / 2, 1); - int bloomHeight = MAX(height / 2, 1); + int bloomWidth = (width + 1) / 2; + int bloomHeight = (height + 1) / 2; for (int i = 0; i < NumBloomLevels; i++) { auto &level = BloomLevels[i]; - level.Width = MAX(bloomWidth / 2, 1); - level.Height = MAX(bloomHeight / 2, 1); + level.Width = (bloomWidth + 1) / 2; + level.Height = (bloomHeight + 1) / 2; level.VTexture = Create2DTexture("Bloom.VTexture", GL_RGBA16F, level.Width, level.Height); level.HTexture = Create2DTexture("Bloom.HTexture", GL_RGBA16F, level.Width, level.Height); @@ -296,6 +356,55 @@ 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 + 1) / 2; + AmbientHeight = (height + 1) / 2; + LinearDepthTexture = Create2DTexture("LinearDepthTexture", GL_R32F, AmbientWidth, AmbientHeight); + AmbientTexture0 = Create2DTexture("AmbientTexture0", GL_RG16F, AmbientWidth, AmbientHeight); + AmbientTexture1 = Create2DTexture("AmbientTexture1", GL_RG16F, AmbientWidth, AmbientHeight); + LinearDepthFB = CreateFrameBuffer("LinearDepthFB", LinearDepthTexture); + AmbientFB0 = CreateFrameBuffer("AmbientFB0", AmbientTexture0); + AmbientFB1 = CreateFrameBuffer("AmbientFB1", AmbientTexture1); + + // Must match quality enum in FSSAOShader::GetDefines + double numDirections[NumAmbientRandomTextures] = { 2.0, 4.0, 8.0 }; + + std::mt19937 generator(1337); + std::uniform_real_distribution distribution(0.0, 1.0); + for (int quality = 0; quality < NumAmbientRandomTextures; quality++) + { + int16_t randomValues[16 * 4]; + + for (int i = 0; i < 16; i++) + { + double angle = 2.0 * M_PI * distribution(generator) / numDirections[quality]; + double x = cos(angle); + double y = sin(angle); + double z = distribution(generator); + double w = distribution(generator); + + randomValues[i * 4 + 0] = (int16_t)clamp(x * 32767.0, -32768.0, 32767.0); + randomValues[i * 4 + 1] = (int16_t)clamp(y * 32767.0, -32768.0, 32767.0); + randomValues[i * 4 + 2] = (int16_t)clamp(z * 32767.0, -32768.0, 32767.0); + randomValues[i * 4 + 3] = (int16_t)clamp(w * 32767.0, -32768.0, 32767.0); + } + + AmbientRandomTexture[quality] = Create2DTexture("AmbientRandomTexture", GL_RGBA16_SNORM, 4, 4, randomValues); + } +} + //========================================================================== // // Creates camera exposure level buffers @@ -368,12 +477,31 @@ void FGLRenderBuffers::CreateEyeBuffers(int eye) GLuint FGLRenderBuffers::Create2DTexture(const FString &name, GLuint format, int width, int height, const void *data) { - GLuint type = (format == GL_RGBA16F || format == GL_R32F) ? 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, format != GL_R32F ? GL_RGBA : GL_RED, type, data); + + GLenum dataformat, datatype; + switch (format) + { + 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_RGBA32F: dataformat = GL_RGBA; datatype = GL_FLOAT; break; + case GL_RGBA16_SNORM: dataformat = GL_RGBA; datatype = GL_SHORT; break; + case GL_R32F: dataformat = GL_RED; datatype = GL_FLOAT; break; + case GL_R16F: dataformat = GL_RED; datatype = GL_FLOAT; break; + case GL_RG32F: dataformat = GL_RG; datatype = GL_FLOAT; break; + case GL_RG16F: dataformat = GL_RG; datatype = GL_FLOAT; break; + case GL_RGB10_A2: dataformat = GL_RGBA; datatype = GL_UNSIGNED_INT_10_10_10_2; 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, data); 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); @@ -381,6 +509,17 @@ GLuint FGLRenderBuffers::Create2DTexture(const FString &name, GLuint format, int return handle; } +GLuint FGLRenderBuffers::Create2DMultisampleTexture(const FString &name, GLuint format, int width, int height, int samples, bool fixedSampleLocations) +{ + GLuint handle = 0; + glGenTextures(1, &handle); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, handle); + FGLDebug::LabelObject(GL_TEXTURE, handle, name); + glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, format, width, height, fixedSampleLocations); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); + return handle; +} + //========================================================================== // // Creates a render buffer @@ -397,7 +536,7 @@ GLuint FGLRenderBuffers::CreateRenderBuffer(const FString &name, GLuint format, return handle; } -GLuint FGLRenderBuffers::CreateRenderBuffer(const FString &name, GLuint format, int samples, int width, int height) +GLuint FGLRenderBuffers::CreateRenderBuffer(const FString &name, GLuint format, int width, int height, int samples) { if (samples <= 1) return CreateRenderBuffer(name, format, width, height); @@ -444,18 +583,30 @@ GLuint FGLRenderBuffers::CreateFrameBuffer(const FString &name, GLuint colorbuff return handle; } -GLuint FGLRenderBuffers::CreateFrameBuffer(const FString &name, GLuint colorbuffer, GLuint depth, GLuint stencil, bool colorIsARenderBuffer) +GLuint FGLRenderBuffers::CreateFrameBuffer(const FString &name, GLuint colorbuffer0, GLuint colorbuffer1, GLuint colorbuffer2, GLuint depthstencil, bool multisample) { GLuint handle = 0; glGenFramebuffers(1, &handle); glBindFramebuffer(GL_FRAMEBUFFER, handle); FGLDebug::LabelObject(GL_FRAMEBUFFER, handle, name); - if (colorIsARenderBuffer) - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuffer); + if (multisample) + { + 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); + if (colorbuffer2 != 0) + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D_MULTISAMPLE, colorbuffer2, 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); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, stencil); + { + 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); + if (colorbuffer2 != 0) + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, colorbuffer2, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthstencil, 0); + } if (CheckFrameBufferCompleteness()) ClearFrameBuffer(true, true); return handle; @@ -475,22 +626,23 @@ bool FGLRenderBuffers::CheckFrameBufferCompleteness() FailedCreate = true; -#if 0 - FString error = "glCheckFramebufferStatus failed: "; - switch (result) + if (gl_debug_level > 0) { - default: error.AppendFormat("error code %d", (int)result); break; - case GL_FRAMEBUFFER_UNDEFINED: error << "GL_FRAMEBUFFER_UNDEFINED"; break; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: error << "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; break; - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: error << "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; break; - case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: error << "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"; break; - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: error << "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"; break; - case GL_FRAMEBUFFER_UNSUPPORTED: error << "GL_FRAMEBUFFER_UNSUPPORTED"; break; - case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: error << "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; break; - case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: error << "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS"; break; + FString error = "glCheckFramebufferStatus failed: "; + switch (result) + { + default: error.AppendFormat("error code %d", (int)result); break; + case GL_FRAMEBUFFER_UNDEFINED: error << "GL_FRAMEBUFFER_UNDEFINED"; break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: error << "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: error << "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; break; + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: error << "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"; break; + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: error << "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"; break; + case GL_FRAMEBUFFER_UNSUPPORTED: error << "GL_FRAMEBUFFER_UNSUPPORTED"; break; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: error << "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; break; + case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: error << "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS"; break; + } + Printf("%s\n", error.GetChars()); } - I_FatalError(error); -#endif return false; } @@ -595,9 +747,69 @@ 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); +} + +//========================================================================== +// +// Binds the scene color texture to the specified texture unit +// +//========================================================================== + +void FGLRenderBuffers::BindSceneColorTexture(int index) +{ + glActiveTexture(GL_TEXTURE0 + index); + if (mSamples > 1) + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mSceneMultisample); + else + glBindTexture(GL_TEXTURE_2D, mPipelineTexture[0]); +} + +//========================================================================== +// +// Binds the scene fog data texture to the specified texture unit +// +//========================================================================== + +void FGLRenderBuffers::BindSceneFogTexture(int index) +{ + glActiveTexture(GL_TEXTURE0 + index); + if (mSamples > 1) + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mSceneFog); + else + glBindTexture(GL_TEXTURE_2D, mSceneFog); +} + +//========================================================================== +// +// Binds the scene normal data texture to the specified texture unit +// +//========================================================================== + +void FGLRenderBuffers::BindSceneNormalTexture(int index) +{ + glActiveTexture(GL_TEXTURE0 + index); + if (mSamples > 1) + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mSceneNormal); + else + glBindTexture(GL_TEXTURE_2D, mSceneNormal); +} + +//========================================================================== +// +// Binds the depth texture to the specified texture unit +// +//========================================================================== + +void FGLRenderBuffers::BindSceneDepthTexture(int index) +{ + glActiveTexture(GL_TEXTURE0 + index); + if (mSamples > 1) + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mSceneDepthStencil); + else + glBindTexture(GL_TEXTURE_2D, mSceneDepthStencil); } //========================================================================== diff --git a/src/gl/renderer/gl_renderbuffers.h b/src/gl/renderer/gl_renderbuffers.h index 4477718f4..f2f7b7cb9 100644 --- a/src/gl/renderer/gl_renderbuffers.h +++ b/src/gl/renderer/gl_renderbuffers.h @@ -31,7 +31,11 @@ public: bool Setup(int width, int height, int sceneWidth, int sceneHeight); - void BindSceneFB(); + void BindSceneFB(bool sceneData); + void BindSceneColorTexture(int index); + void BindSceneFogTexture(int index); + void BindSceneNormalTexture(int index); + void BindSceneDepthTexture(int index); void BlitSceneToTexture(); void BindCurrentTexture(int index); @@ -53,28 +57,46 @@ public: GLuint ExposureFB = 0; bool FirstExposureFrame = true; + // Ambient occlusion buffers + GLuint LinearDepthTexture = 0; + GLuint LinearDepthFB = 0; + GLuint AmbientTexture0 = 0; + GLuint AmbientTexture1 = 0; + GLuint AmbientFB0 = 0; + GLuint AmbientFB1 = 0; + int AmbientWidth = 0; + int AmbientHeight = 0; + enum { NumAmbientRandomTextures = 3 }; + GLuint AmbientRandomTexture[NumAmbientRandomTextures]; + static bool IsEnabled(); int GetWidth() const { return mWidth; } int GetHeight() const { return mHeight; } + int GetSceneWidth() const { return mSceneWidth; } + int GetSceneHeight() const { return mSceneHeight; } + private: void ClearScene(); void ClearPipeline(); void ClearEyeBuffers(); void ClearBloom(); void ClearExposureLevels(); - void CreateScene(int width, int height, int samples); + void ClearAmbientOcclusion(); + void CreateScene(int width, int height, int samples, bool needsSceneTextures); void CreatePipeline(int width, int height); void CreateBloom(int width, int height); void CreateExposureLevels(int width, int height); void CreateEyeBuffers(int eye); + void CreateAmbientOcclusion(int width, int height); GLuint Create2DTexture(const FString &name, GLuint format, int width, int height, const void *data = nullptr); + GLuint Create2DMultisampleTexture(const FString &name, GLuint format, int width, int height, int samples, bool fixedSampleLocations); 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 CreateRenderBuffer(const FString &name, GLuint format, int width, int height, int samples); 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 colorbuffer0, GLuint colorbuffer1, GLuint colorbuffer2, GLuint depthstencil, bool multisample); bool CheckFrameBufferCompleteness(); void ClearFrameBuffer(bool stencil, bool depth); void DeleteTexture(GLuint &handle); @@ -94,9 +116,11 @@ private: // Buffers for the scene GLuint mSceneMultisample = 0; GLuint mSceneDepthStencil = 0; - GLuint mSceneDepth = 0; - GLuint mSceneStencil = 0; + GLuint mSceneFog = 0; + GLuint mSceneNormal = 0; GLuint mSceneFB = 0; + GLuint mSceneDataFB = 0; + bool mSceneUsesTextures = false; // Effect/HUD buffers GLuint mPipelineTexture[NumPipelineTextures]; diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 0ffafef49..90687bcb7 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -51,6 +51,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" @@ -118,6 +119,10 @@ FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb) mTonemapPalette = nullptr; mColormapShader = nullptr; mLensShader = nullptr; + mLinearDepthShader = nullptr; + mDepthBlurShader = nullptr; + mSSAOShader = nullptr; + mSSAOCombineShader = nullptr; mFXAAShader = nullptr; mFXAALumaShader = nullptr; } @@ -128,6 +133,10 @@ void gl_FlushModels(); void FGLRenderer::Initialize(int width, int height) { mBuffers = new FGLRenderBuffers(); + mLinearDepthShader = new FLinearDepthShader(); + mDepthBlurShader = new FDepthBlurShader(); + mSSAOShader = new FSSAOShader(); + mSSAOCombineShader = new FSSAOCombineShader(); mBloomExtractShader = new FBloomExtractShader(); mBloomCombineShader = new FBloomCombineShader(); mExposureExtractShader = new FExposureExtractShader(); @@ -196,6 +205,10 @@ FGLRenderer::~FGLRenderer() } if (mBuffers) delete mBuffers; if (mPresentShader) delete mPresentShader; + if (mLinearDepthShader) delete mLinearDepthShader; + if (mDepthBlurShader) delete mDepthBlurShader; + if (mSSAOShader) delete mSSAOShader; + if (mSSAOCombineShader) delete mSSAOCombineShader; if (mPresent3dCheckerShader) delete mPresent3dCheckerShader; if (mPresent3dColumnShader) delete mPresent3dColumnShader; if (mPresent3dRowShader) delete mPresent3dRowShader; @@ -327,7 +340,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/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 6ea1e357a..869b46d2d 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -19,6 +19,10 @@ class FLightBuffer; class FSamplerManager; class DPSprite; class FGLRenderBuffers; +class FLinearDepthShader; +class FDepthBlurShader; +class FSSAOShader; +class FSSAOCombineShader; class FBloomExtractShader; class FBloomCombineShader; class FExposureExtractShader; @@ -98,6 +102,10 @@ public: int mOldFBID; FGLRenderBuffers *mBuffers; + FLinearDepthShader *mLinearDepthShader; + FSSAOShader *mSSAOShader; + FDepthBlurShader *mDepthBlurShader; + FSSAOCombineShader *mSSAOCombineShader; FBloomExtractShader *mBloomExtractShader; FBloomCombineShader *mBloomCombineShader; FExposureExtractShader *mExposureExtractShader; @@ -182,10 +190,12 @@ public: void WriteSavePic (player_t *player, FileWriter *file, int width, int height); void EndDrawScene(sector_t * viewsector); void UpdateCameraExposure(); + void PostProcessScene(); + void AmbientOccludeScene(); void BloomScene(); void TonemapScene(); void ColormapScene(); - void BindTonemapPalette(int texunit); + void CreateTonemapPalette(); void ClearTonemapPalette(); void LensDistortScene(); void ApplyFXAA(); @@ -209,6 +219,9 @@ public: DAngle rotation, FDynamicColormap *colormap, int lightlevel, int bottomclip); int PTM_BestColor (const uint32 *pal_in, int r, int g, int b, int first, int num); + + 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/renderer/gl_renderstate.cpp b/src/gl/renderer/gl_renderstate.cpp index a8c9c5da2..fa8237cca 100644 --- a/src/gl/renderer/gl_renderstate.cpp +++ b/src/gl/renderer/gl_renderstate.cpp @@ -105,6 +105,7 @@ void FRenderState::Reset() mViewMatrix.loadIdentity(); mModelMatrix.loadIdentity(); mTextureMatrix.loadIdentity(); + mPassType = NORMAL_PASS; } //========================================================================== @@ -118,11 +119,11 @@ bool FRenderState::ApplyShader() static const float nulvec[] = { 0.f, 0.f, 0.f, 0.f }; if (mSpecialEffect > EFF_NONE) { - activeShader = GLRenderer->mShaderManager->BindEffect(mSpecialEffect); + activeShader = GLRenderer->mShaderManager->BindEffect(mSpecialEffect, mPassType); } else { - activeShader = GLRenderer->mShaderManager->Get(mTextureEnabled ? mEffectState : 4, mAlphaThreshold >= 0.f); + activeShader = GLRenderer->mShaderManager->Get(mTextureEnabled ? mEffectState : 4, mAlphaThreshold >= 0.f, mPassType); activeShader->Bind(); } @@ -347,7 +348,7 @@ void FRenderState::ApplyMatrices() { if (GLRenderer->mShaderManager != NULL) { - GLRenderer->mShaderManager->ApplyMatrices(&mProjectionMatrix, &mViewMatrix); + GLRenderer->mShaderManager->ApplyMatrices(&mProjectionMatrix, &mViewMatrix, mPassType); } } diff --git a/src/gl/renderer/gl_renderstate.h b/src/gl/renderer/gl_renderstate.h index 6a1856910..76e2c791c 100644 --- a/src/gl/renderer/gl_renderstate.h +++ b/src/gl/renderer/gl_renderstate.h @@ -63,6 +63,13 @@ enum EEffect MAX_EFFECTS }; +enum EPassType +{ + NORMAL_PASS, + GBUFFER_PASS, + MAX_PASS_TYPES +}; + class FRenderState { bool mTextureEnabled; @@ -112,6 +119,9 @@ class FRenderState FShader *activeShader; + EPassType mPassType = NORMAL_PASS; + int mNumDrawBuffers = 1; + bool ApplyShader(); public: @@ -472,6 +482,32 @@ public: return mInterpolationFactor; } + void SetPassType(EPassType passType) + { + mPassType = passType; + } + + EPassType GetPassType() + { + return mPassType; + } + + void EnableDrawBuffers(int count) + { + count = MIN(count, 3); + if (mNumDrawBuffers != count) + { + static GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 }; + glDrawBuffers(count, buffers); + mNumDrawBuffers = count; + } + } + + int GetPassDrawBufferCount() + { + return mPassType == GBUFFER_PASS ? 3 : 1; + } + // Backwards compatibility crap follows void ApplyFixedFunction(); void DrawColormapOverlay(); diff --git a/src/gl/scene/gl_decal.cpp b/src/gl/scene/gl_decal.cpp index 651cb7aa9..7fb680f8e 100644 --- a/src/gl/scene/gl_decal.cpp +++ b/src/gl/scene/gl_decal.cpp @@ -316,6 +316,8 @@ void GLWall::DrawDecal(DBaseDecal *decal) gl_RenderState.SetFog(0,-1); } + gl_RenderState.SetNormal(glseg.Normal()); + FQuadDrawer qd; for (i = 0; i < 4; i++) { diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index 50693d75c..02fd9b452 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -423,14 +423,16 @@ void GLPortal::End(bool usestencil) glDepthFunc(GL_LEQUAL); glDepthRange(0, 1); { - ScopedColorMask colorMask(0, 0, 0, 0); - // glColorMask(0,0,0,0); // no graphics + ScopedColorMask colorMask(0, 0, 0, 1); // mark portal in alpha channel but don't touch color gl_RenderState.SetEffect(EFF_STENCIL); gl_RenderState.EnableTexture(false); + gl_RenderState.BlendFunc(GL_ONE, GL_ZERO); + gl_RenderState.BlendEquation(GL_FUNC_ADD); + gl_RenderState.Apply(); DrawPortalStencil(); gl_RenderState.SetEffect(EFF_NONE); gl_RenderState.EnableTexture(true); - } // glColorMask(1, 1, 1, 1); + } glDepthFunc(GL_LESS); } PortalAll.Unclock(); diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 0cf0fa0a1..5b969d7b9 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -158,7 +158,11 @@ void FGLRenderer::Set3DViewport(bool mainview) { if (mainview && mBuffers->Setup(mScreenViewport.width, mScreenViewport.height, mSceneViewport.width, mSceneViewport.height)) { - mBuffers->BindSceneFB(); + bool useSSAO = (gl_ssao != 0); + mBuffers->BindSceneFB(useSSAO); + gl_RenderState.SetPassType(useSSAO ? GBUFFER_PASS : NORMAL_PASS); + gl_RenderState.EnableDrawBuffers(gl_RenderState.GetPassDrawBufferCount()); + gl_RenderState.Apply(); } // Always clear all buffers with scissor test disabled. @@ -209,7 +213,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 @@ -472,6 +476,23 @@ void FGLRenderer::RenderTranslucent() void FGLRenderer::DrawScene(int drawmode) { static int recursion=0; + static int ssao_portals_available = 0; + + bool applySSAO = false; + if (drawmode == DM_MAINVIEW) + { + ssao_portals_available = gl_ssao_portals; + applySSAO = true; + } + else if (drawmode == DM_OFFSCREEN) + { + ssao_portals_available = 0; + } + else if (ssao_portals_available > 0) + { + applySSAO = true; + ssao_portals_available--; + } if (camera != nullptr) { @@ -491,6 +512,16 @@ void FGLRenderer::DrawScene(int drawmode) RenderScene(recursion); + if (applySSAO && gl_RenderState.GetPassType() == GBUFFER_PASS) + { + gl_RenderState.EnableDrawBuffers(1); + AmbientOccludeScene(); + mBuffers->BindSceneFB(true); + gl_RenderState.EnableDrawBuffers(gl_RenderState.GetPassDrawBufferCount()); + gl_RenderState.Apply(); + gl_RenderState.ApplyMatrices(); + } + // Handle all portals after rendering the opaque objects but before // doing all translucent stuff recursion++; @@ -830,13 +861,7 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo if (mainview && toscreen) EndDrawScene(lviewsector); // do not call this for camera textures. if (mainview && FGLRenderBuffers::IsEnabled()) { - mBuffers->BlitSceneToTexture(); - UpdateCameraExposure(); - BloomScene(); - TonemapScene(); - ColormapScene(); - LensDistortScene(); - ApplyFXAA(); + PostProcessScene(); // This should be done after postprocessing, not before. mBuffers->BindCurrentFB(); diff --git a/src/gl/scene/gl_walls_draw.cpp b/src/gl/scene/gl_walls_draw.cpp index f166a2ce1..81164e434 100644 --- a/src/gl/scene/gl_walls_draw.cpp +++ b/src/gl/scene/gl_walls_draw.cpp @@ -228,6 +228,7 @@ void GLWall::RenderFogBoundary() { int rel = rellight + getExtraLight(); gl_SetFog(lightlevel, rel, &Colormap, false); + gl_RenderState.EnableDrawBuffers(1); gl_RenderState.SetEffect(EFF_FOGBOUNDARY); gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f); glEnable(GL_POLYGON_OFFSET_FILL); @@ -236,6 +237,7 @@ void GLWall::RenderFogBoundary() glPolygonOffset(0.0f, 0.0f); glDisable(GL_POLYGON_OFFSET_FILL); gl_RenderState.SetEffect(EFF_NONE); + gl_RenderState.EnableDrawBuffers(gl_RenderState.GetPassDrawBufferCount()); } else { diff --git a/src/gl/shaders/gl_ambientshader.cpp b/src/gl/shaders/gl_ambientshader.cpp new file mode 100644 index 000000000..effde2496 --- /dev/null +++ b/src/gl/shaders/gl_ambientshader.cpp @@ -0,0 +1,160 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2016 Magnus Norddahl +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + +#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() +{ + bool multisample = (gl_multisample > 1); + if (mMultisample != multisample) + mShader.reset(); + + if (!mShader) + { + mShader = std::make_unique(); + mShader->Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", 330); + mShader->Compile(FShaderProgram::Fragment, "shaders/glsl/lineardepth.fp", multisample ? "#define MULTISAMPLE\n" : "", 330); + mShader->SetFragDataLocation(0, "FragColor"); + mShader->Link("shaders/glsl/lineardepth"); + mShader->SetAttribLocation(0, "PositionInProjection"); + DepthTexture.Init(*mShader, "DepthTexture"); + ColorTexture.Init(*mShader, "ColorTexture"); + SampleIndex.Init(*mShader, "SampleIndex"); + LinearizeDepthA.Init(*mShader, "LinearizeDepthA"); + LinearizeDepthB.Init(*mShader, "LinearizeDepthB"); + InverseDepthRangeA.Init(*mShader, "InverseDepthRangeA"); + InverseDepthRangeB.Init(*mShader, "InverseDepthRangeB"); + Scale.Init(*mShader, "Scale"); + Offset.Init(*mShader, "Offset"); + mMultisample = multisample; + } + mShader->Bind(); +} + +void FSSAOShader::Bind() +{ + bool multisample = (gl_multisample > 1); + if (mCurrentQuality != gl_ssao || mMultisample != multisample) + mShader.reset(); + + if (!mShader) + { + mShader = std::make_unique(); + mShader->Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", 330); + mShader->Compile(FShaderProgram::Fragment, "shaders/glsl/ssao.fp", GetDefines(gl_ssao, multisample), 330); + mShader->SetFragDataLocation(0, "FragColor"); + mShader->Link("shaders/glsl/ssao"); + mShader->SetAttribLocation(0, "PositionInProjection"); + DepthTexture.Init(*mShader, "DepthTexture"); + NormalTexture.Init(*mShader, "NormalTexture"); + RandomTexture.Init(*mShader, "RandomTexture"); + 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"); + AOStrength.Init(*mShader, "AOStrength"); + Scale.Init(*mShader, "Scale"); + Offset.Init(*mShader, "Offset"); + SampleIndex.Init(*mShader, "SampleIndex"); + mMultisample = multisample; + } + mShader->Bind(); +} + +FString FSSAOShader::GetDefines(int mode, bool multisample) +{ + // Must match quality values in FGLRenderBuffers::CreateAmbientOcclusion + int numDirections, numSteps; + switch (gl_ssao) + { + default: + case LowQuality: numDirections = 2; numSteps = 4; break; + case MediumQuality: numDirections = 4; numSteps = 4; break; + case HighQuality: numDirections = 8; numSteps = 4; break; + } + + FString defines; + defines.Format(R"( + #define USE_RANDOM_TEXTURE + #define RANDOM_TEXTURE_WIDTH 4.0 + #define NUM_DIRECTIONS %d.0 + #define NUM_STEPS %d.0 + )", numDirections, numSteps); + + if (multisample) + defines += "\n#define MULTISAMPLE\n"; + return defines; +} + +void FDepthBlurShader::Bind(bool vertical) +{ + auto &shader = mShader[vertical]; + if (!shader) + { + shader.Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", 330); + shader.Compile(FShaderProgram::Fragment, "shaders/glsl/depthblur.fp", vertical ? "#define BLUR_VERTICAL\n" : "#define BLUR_HORIZONTAL\n", 330); + shader.SetFragDataLocation(0, "FragColor"); + shader.Link("shaders/glsl/depthblur"); + shader.SetAttribLocation(0, "PositionInProjection"); + AODepthTexture[vertical].Init(shader, "AODepthTexture"); + BlurSharpness[vertical].Init(shader, "BlurSharpness"); + InvFullResolution[vertical].Init(shader, "InvFullResolution"); + PowExponent[vertical].Init(shader, "PowExponent"); + } + shader.Bind(); +} + +void FSSAOCombineShader::Bind() +{ + bool multisample = (gl_multisample > 1); + if (mMultisample != multisample) + mShader.reset(); + + if (!mShader) + { + mShader = std::make_unique(); + mShader->Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", 330); + mShader->Compile(FShaderProgram::Fragment, "shaders/glsl/ssaocombine.fp", multisample ? "#define MULTISAMPLE\n" : "", 330); + mShader->SetFragDataLocation(0, "FragColor"); + mShader->Link("shaders/glsl/ssaocombine"); + mShader->SetAttribLocation(0, "PositionInProjection"); + AODepthTexture.Init(*mShader, "AODepthTexture"); + SceneFogTexture.Init(*mShader, "SceneFogTexture"); + SampleCount.Init(*mShader, "SampleCount"); + Scale.Init(*mShader, "Scale"); + Offset.Init(*mShader, "Offset"); + mMultisample = multisample; + } + mShader->Bind(); +} diff --git a/src/gl/shaders/gl_ambientshader.h b/src/gl/shaders/gl_ambientshader.h new file mode 100644 index 000000000..d3310c3d6 --- /dev/null +++ b/src/gl/shaders/gl_ambientshader.h @@ -0,0 +1,93 @@ +#ifndef __GL_AMBIENTSHADER_H +#define __GL_AMBIENTSHADER_H + +#include "gl_shaderprogram.h" + +class FLinearDepthShader +{ +public: + void Bind(); + + FBufferedUniformSampler DepthTexture; + FBufferedUniformSampler ColorTexture; + FBufferedUniform1i SampleIndex; + FBufferedUniform1f LinearizeDepthA; + FBufferedUniform1f LinearizeDepthB; + FBufferedUniform1f InverseDepthRangeA; + FBufferedUniform1f InverseDepthRangeB; + FBufferedUniform2f Scale; + FBufferedUniform2f Offset; + +private: + std::unique_ptr mShader; + bool mMultisample = false; +}; + +class FSSAOShader +{ +public: + void Bind(); + + FBufferedUniformSampler DepthTexture; + FBufferedUniformSampler NormalTexture; + FBufferedUniformSampler RandomTexture; + FBufferedUniform2f UVToViewA; + FBufferedUniform2f UVToViewB; + FBufferedUniform2f InvFullResolution; + FBufferedUniform1f NDotVBias; + FBufferedUniform1f NegInvR2; + FBufferedUniform1f RadiusToScreen; + FBufferedUniform1f AOMultiplier; + FBufferedUniform1f AOStrength; + FBufferedUniform2f Scale; + FBufferedUniform2f Offset; + FBufferedUniform1i SampleIndex; + +private: + enum Quality + { + Off, + LowQuality, + MediumQuality, + HighQuality, + NumQualityModes + }; + + FString GetDefines(int mode, bool multisample); + + std::unique_ptr mShader; + Quality mCurrentQuality = Off; + bool mMultisample = false; +}; + +class FDepthBlurShader +{ +public: + void Bind(bool vertical); + + FBufferedUniformSampler AODepthTexture[2]; + FBufferedUniform1f BlurSharpness[2]; + FBufferedUniform2f InvFullResolution[2]; + FBufferedUniform1f PowExponent[2]; + +private: + FShaderProgram mShader[2]; +}; + +class FSSAOCombineShader +{ +public: + void Bind(); + + FBufferedUniformSampler AODepthTexture; + FBufferedUniformSampler SceneFogTexture; + FBufferedUniform1i SampleCount; + FBufferedUniform2f Scale; + FBufferedUniform2f Offset; + +private: + std::unique_ptr mShader; + bool mMultisample = false; +}; + +#endif \ No newline at end of file diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index 8f78e22e4..b7d8335f8 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -181,6 +181,10 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * glBindAttribLocation(hShader, VATTR_VERTEX2, "aVertex2"); glBindAttribLocation(hShader, VATTR_NORMAL, "aNormal"); + glBindFragDataLocation(hShader, 0, "FragColor"); + glBindFragDataLocation(hShader, 1, "FragFog"); + glBindFragDataLocation(hShader, 2, "FragNormal"); + glLinkProgram(hShader); glGetShaderInfoLog(hVertProg, 10000, NULL, buffer); @@ -300,12 +304,13 @@ bool FShader::Bind() // //========================================================================== -FShader *FShaderManager::Compile (const char *ShaderName, const char *ShaderPath, bool usediscard) +FShader *FShaderCollection::Compile (const char *ShaderName, const char *ShaderPath, bool usediscard, EPassType passType) { FString defines; // this can't be in the shader code due to ATI strangeness. if (gl.MaxLights() == 128) defines += "#define MAXLIGHTS128\n"; if (!usediscard) defines += "#define NO_ALPHATEST\n"; + if (passType == GBUFFER_PASS) defines += "#define GBUFFER_PASS\n"; FShader *shader = NULL; try @@ -388,27 +393,75 @@ static const FEffectShader effectshaders[]= { "stencil", "shaders/glsl/main.vp", "shaders/glsl/stencil.fp", NULL, "#define SIMPLE\n#define NO_ALPHATEST\n" }, }; - -//========================================================================== -// -// -// -//========================================================================== - FShaderManager::FShaderManager() { - if (!gl.legacyMode) CompileShaders(); + if (!gl.legacyMode) + { + for (int passType = 0; passType < MAX_PASS_TYPES; passType++) + mPassShaders.Push(new FShaderCollection((EPassType)passType)); + } } -//========================================================================== -// -// -// -//========================================================================== - FShaderManager::~FShaderManager() { - if (!gl.legacyMode) Clean(); + if (!gl.legacyMode) + { + glUseProgram(0); + mActiveShader = NULL; + + for (auto collection : mPassShaders) + delete collection; + } +} + +void FShaderManager::SetActiveShader(FShader *sh) +{ + if (mActiveShader != sh) + { + glUseProgram(sh!= NULL? sh->GetHandle() : 0); + mActiveShader = sh; + } +} + +FShader *FShaderManager::BindEffect(int effect, EPassType passType) +{ + if (passType < mPassShaders.Size()) + return mPassShaders[passType]->BindEffect(effect); + else + return nullptr; +} + +FShader *FShaderManager::Get(unsigned int eff, bool alphateston, EPassType passType) +{ + if (passType < mPassShaders.Size()) + return mPassShaders[passType]->Get(eff, alphateston); + else + return nullptr; +} + +void FShaderManager::ApplyMatrices(VSMatrix *proj, VSMatrix *view, EPassType passType) +{ + if (gl.legacyMode) + { + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(proj->get()); + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(view->get()); + } + else + { + if (passType < mPassShaders.Size()) + mPassShaders[passType]->ApplyMatrices(proj, view); + + if (mActiveShader) + mActiveShader->Bind(); + } +} + +void FShaderManager::ResetFixedColormap() +{ + for (auto &collection : mPassShaders) + collection->ResetFixedColormap(); } //========================================================================== @@ -417,10 +470,30 @@ FShaderManager::~FShaderManager() // //========================================================================== -void FShaderManager::CompileShaders() +FShaderCollection::FShaderCollection(EPassType passType) { - mActiveShader = NULL; + CompileShaders(passType); +} +//========================================================================== +// +// +// +//========================================================================== + +FShaderCollection::~FShaderCollection() +{ + Clean(); +} + +//========================================================================== +// +// +// +//========================================================================== + +void FShaderCollection::CompileShaders(EPassType passType) +{ mTextureEffects.Clear(); mTextureEffectsNAT.Clear(); for (int i = 0; i < MAX_EFFECTS; i++) @@ -430,11 +503,11 @@ void FShaderManager::CompileShaders() for(int i=0;defaultshaders[i].ShaderName != NULL;i++) { - FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, true); + FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, true, passType); mTextureEffects.Push(shc); if (i <= 3) { - FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, false); + FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, false, passType); mTextureEffectsNAT.Push(shc); } } @@ -444,7 +517,7 @@ void FShaderManager::CompileShaders() FString name = ExtractFileBase(usershaders[i]); FName sfn = name; - FShader *shc = Compile(sfn, usershaders[i], true); + FShader *shc = Compile(sfn, usershaders[i], true, passType); mTextureEffects.Push(shc); } @@ -466,11 +539,8 @@ void FShaderManager::CompileShaders() // //========================================================================== -void FShaderManager::Clean() +void FShaderCollection::Clean() { - glUseProgram(0); - mActiveShader = NULL; - for (unsigned int i = 0; i < mTextureEffectsNAT.Size(); i++) { if (mTextureEffectsNAT[i] != NULL) delete mTextureEffectsNAT[i]; @@ -494,7 +564,7 @@ void FShaderManager::Clean() // //========================================================================== -int FShaderManager::Find(const char * shn) +int FShaderCollection::Find(const char * shn) { FName sfn = shn; @@ -508,21 +578,6 @@ int FShaderManager::Find(const char * shn) return -1; } -//========================================================================== -// -// -// -//========================================================================== - -void FShaderManager::SetActiveShader(FShader *sh) -{ - if (mActiveShader != sh) - { - glUseProgram(sh!= NULL? sh->GetHandle() : 0); - mActiveShader = sh; - } -} - //========================================================================== // @@ -530,7 +585,7 @@ void FShaderManager::SetActiveShader(FShader *sh) // //========================================================================== -FShader *FShaderManager::BindEffect(int effect) +FShader *FShaderCollection::BindEffect(int effect) { if (effect >= 0 && effect < MAX_EFFECTS && mEffectShaders[effect] != NULL) { @@ -548,39 +603,28 @@ FShader *FShaderManager::BindEffect(int effect) //========================================================================== EXTERN_CVAR(Int, gl_fuzztype) -void FShaderManager::ApplyMatrices(VSMatrix *proj, VSMatrix *view) +void FShaderCollection::ApplyMatrices(VSMatrix *proj, VSMatrix *view) { - if (gl.legacyMode) - { - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(proj->get()); - glMatrixMode(GL_MODELVIEW); - glLoadMatrixf(view->get()); - } - else - { - VSMatrix norm; - norm.computeNormalMatrix(*view); + VSMatrix norm; + norm.computeNormalMatrix(*view); - for (int i = 0; i < 4; i++) - { - mTextureEffects[i]->ApplyMatrices(proj, view, &norm); - mTextureEffectsNAT[i]->ApplyMatrices(proj, view, &norm); - } - mTextureEffects[4]->ApplyMatrices(proj, view, &norm); - if (gl_fuzztype != 0) - { - mTextureEffects[4 + gl_fuzztype]->ApplyMatrices(proj, view, &norm); - } - for (unsigned i = 12; i < mTextureEffects.Size(); i++) - { - mTextureEffects[i]->ApplyMatrices(proj, view, &norm); - } - for (int i = 0; i < MAX_EFFECTS; i++) - { - mEffectShaders[i]->ApplyMatrices(proj, view, &norm); - } - if (mActiveShader != NULL) mActiveShader->Bind(); + for (int i = 0; i < 4; i++) + { + mTextureEffects[i]->ApplyMatrices(proj, view, &norm); + mTextureEffectsNAT[i]->ApplyMatrices(proj, view, &norm); + } + mTextureEffects[4]->ApplyMatrices(proj, view, &norm); + if (gl_fuzztype != 0) + { + mTextureEffects[4 + gl_fuzztype]->ApplyMatrices(proj, view, &norm); + } + for (unsigned i = 12; i < mTextureEffects.Size(); i++) + { + mTextureEffects[i]->ApplyMatrices(proj, view, &norm); + } + for (int i = 0; i < MAX_EFFECTS; i++) + { + mEffectShaders[i]->ApplyMatrices(proj, view, &norm); } } diff --git a/src/gl/shaders/gl_shader.h b/src/gl/shaders/gl_shader.h index 679ae1172..acdd530aa 100644 --- a/src/gl/shaders/gl_shader.h +++ b/src/gl/shaders/gl_shader.h @@ -37,6 +37,7 @@ enum VATTR_NORMAL = 4 }; +class FShaderCollection; //========================================================================== // @@ -248,7 +249,7 @@ public: class FShader { - friend class FShaderManager; + friend class FShaderCollection; friend class FRenderState; unsigned int hShader; @@ -324,7 +325,6 @@ public: }; - //========================================================================== // // The global shader manager @@ -332,26 +332,40 @@ public: //========================================================================== class FShaderManager { - TArray mTextureEffects; - TArray mTextureEffectsNAT; - FShader *mActiveShader; - FShader *mEffectShaders[MAX_EFFECTS]; - - void Clean(); - void CompileShaders(); - public: FShaderManager(); ~FShaderManager(); - FShader *Compile(const char *ShaderName, const char *ShaderPath, bool usediscard); + + void SetActiveShader(FShader *sh); + FShader *GetActiveShader() const { return mActiveShader; } + + FShader *BindEffect(int effect, EPassType passType); + FShader *Get(unsigned int eff, bool alphateston, EPassType passType); + void ApplyMatrices(VSMatrix *proj, VSMatrix *view, EPassType passType); + + void ResetFixedColormap(); + +private: + FShader *mActiveShader = nullptr; + TArray mPassShaders; +}; + +class FShaderCollection +{ + TArray mTextureEffects; + TArray mTextureEffectsNAT; + FShader *mEffectShaders[MAX_EFFECTS]; + + void Clean(); + void CompileShaders(EPassType passType); + +public: + FShaderCollection(EPassType passType); + ~FShaderCollection(); + FShader *Compile(const char *ShaderName, const char *ShaderPath, bool usediscard, EPassType passType); int Find(const char *mame); FShader *BindEffect(int effect); - void SetActiveShader(FShader *sh); void ApplyMatrices(VSMatrix *proj, VSMatrix *view); - FShader *GetActiveShader() const - { - return mActiveShader; - } void ResetFixedColormap() { diff --git a/src/gl/stereo3d/gl_stereo3d.cpp b/src/gl/stereo3d/gl_stereo3d.cpp index 9403d4751..6b1623ba6 100644 --- a/src/gl/stereo3d/gl_stereo3d.cpp +++ b/src/gl/stereo3d/gl_stereo3d.cpp @@ -41,7 +41,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/src/gl/stereo3d/scoped_color_mask.h b/src/gl/stereo3d/scoped_color_mask.h index 42c02db3a..e55772092 100644 --- a/src/gl/stereo3d/scoped_color_mask.h +++ b/src/gl/stereo3d/scoped_color_mask.h @@ -41,8 +41,10 @@ public: gl_RenderState.GetColorMask(saved[0], saved[1], saved[2], saved[3]); gl_RenderState.SetColorMask(r, g, b, a); gl_RenderState.ApplyColorMask(); + gl_RenderState.EnableDrawBuffers(1); } ~ScopedColorMask() { + gl_RenderState.EnableDrawBuffers(gl_RenderState.GetPassDrawBufferCount()); gl_RenderState.SetColorMask(saved[0], saved[1], saved[2], saved[3]); gl_RenderState.ApplyColorMask(); } diff --git a/src/gl/system/gl_cvars.h b/src/gl/system/gl_cvars.h index 4cda3657d..3b425d261 100644 --- a/src/gl/system/gl_cvars.h +++ b/src/gl/system/gl_cvars.h @@ -50,6 +50,13 @@ 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(Int, gl_ssao) +EXTERN_CVAR(Int, gl_ssao_portals) +EXTERN_CVAR(Float, gl_ssao_strength) +EXTERN_CVAR(Int, 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/src/gl/textures/gl_hwtexture.cpp b/src/gl/textures/gl_hwtexture.cpp index edcda4398..477d1986b 100644 --- a/src/gl/textures/gl_hwtexture.cpp +++ b/src/gl/textures/gl_hwtexture.cpp @@ -374,6 +374,11 @@ unsigned int FHardwareTexture::Bind(int texunit, int translation, bool needmipma return 0; } +unsigned int FHardwareTexture::GetTextureHandle(int translation) +{ + TranslatedTexture *pTex = GetTexID(translation); + return pTex->glTexID; +} void FHardwareTexture::Unbind(int texunit) { diff --git a/src/gl/textures/gl_hwtexture.h b/src/gl/textures/gl_hwtexture.h index 96eff0264..59fc98169 100644 --- a/src/gl/textures/gl_hwtexture.h +++ b/src/gl/textures/gl_hwtexture.h @@ -83,6 +83,7 @@ public: unsigned int Bind(int texunit, int translation, bool needmipmap); unsigned int CreateTexture(unsigned char * buffer, int w, int h, int texunit, bool mipmap, int translation, const FString &name); + unsigned int GetTextureHandle(int translation); void Clean(bool all); void CleanUnused(SpriteHits &usedtranslations); diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 448506727..fc611b3c0 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2171,6 +2171,9 @@ NETMNU_TICBALANCE = "Latency balancing"; // Option Values OPTVAL_OFF = "Off"; OPTVAL_ON = "On"; +OPTVAL_LOW = "Low"; +OPTVAL_MEDIUM = "Medium"; +OPTVAL_HIGH = "High"; OPTVAL_MALE = "Male"; OPTVAL_FEMALE = "Female"; OPTVAL_OTHER = "Other"; @@ -2640,6 +2643,8 @@ GLPREFMNU_MULTISAMPLE = "Multisample"; GLPREFMNU_TONEMAP = "Tonemap Mode"; GLPREFMNU_BLOOM = "Bloom effect"; GLPREFMNU_LENS = "Lens distortion effect"; +GLPREFMNU_SSAO = "Ambient occlusion quality"; +GLPREFMNU_SSAO_PORTALS = "Portals with AO"; GLPREFMNU_FXAA = "FXAA Quality"; // Option Values diff --git a/wadsrc/static/menudef.zz b/wadsrc/static/menudef.zz index 1261286d6..599c28a2d 100644 --- a/wadsrc/static/menudef.zz +++ b/wadsrc/static/menudef.zz @@ -42,6 +42,14 @@ OptionValue "TonemapModes" 5, "$OPTVAL_PALETTE" } +OptionValue "SSAOModes" +{ + 0, "$OPTVAL_OFF" + 1, "$OPTVAL_LOW" + 2, "$OPTVAL_MEDIUM" + 3, "$OPTVAL_HIGH" +} + OptionValue "FXAAQuality" { 0, "$OPTVAL_OFF" @@ -238,5 +246,7 @@ 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, "SSAOModes" + Slider "$GLPREFMNU_SSAO_PORTALS", gl_ssao_portals, 0.0, 4.0, 1.0, 0 Option "$GLPREFMNU_FXAA", gl_fxaa, "FXAAQuality" } diff --git a/wadsrc/static/shaders/glsl/depthblur.fp b/wadsrc/static/shaders/glsl/depthblur.fp new file mode 100644 index 000000000..7e3dad074 --- /dev/null +++ b/wadsrc/static/shaders/glsl/depthblur.fp @@ -0,0 +1,69 @@ + +in vec2 TexCoord; +out vec4 FragColor; + +uniform sampler2D AODepthTexture; +uniform float BlurSharpness; +uniform vec2 InvFullResolution; +uniform float PowExponent; + +#define KERNEL_RADIUS 3.0 + +float CrossBilateralWeight(float r, float sampleDepth, float centerDepth) +{ + const float blurSigma = KERNEL_RADIUS * 0.5; + const float blurFalloff = 1.0 / (2.0 * blurSigma * blurSigma); + + float deltaZ = (sampleDepth - centerDepth) * BlurSharpness; + + return exp2(-r * r * blurFalloff - deltaZ * deltaZ); +} + +void ProcessSample(float ao, float z, float r, float centerDepth, inout float totalAO, inout float totalW) +{ + float w = CrossBilateralWeight(r, z, centerDepth); + totalAO += w * ao; + totalW += w; +} + +void ProcessRadius(vec2 deltaUV, float centerDepth, inout float totalAO, inout float totalW) +{ + for (float r = 1; r <= KERNEL_RADIUS; r += 1.0) + { + vec2 uv = r * deltaUV + TexCoord; + vec2 aoZ = texture(AODepthTexture, uv).xy; + ProcessSample(aoZ.x, aoZ.y, r, centerDepth, totalAO, totalW); + } +} + +vec2 ComputeBlur(vec2 deltaUV) +{ + vec2 aoZ = texture(AODepthTexture, TexCoord).xy; + + float totalAO = aoZ.x; + float totalW = 1.0; + + ProcessRadius(deltaUV, aoZ.y, totalAO, totalW); + ProcessRadius(-deltaUV, aoZ.y, totalAO, totalW); + + return vec2(totalAO / totalW, aoZ.y); +} + +vec2 BlurX() +{ + return ComputeBlur(vec2(InvFullResolution.x, 0.0)); +} + +float BlurY() +{ + return pow(clamp(ComputeBlur(vec2(0.0, InvFullResolution.y)).x, 0.0, 1.0), PowExponent); +} + +void main() +{ +#if defined(BLUR_HORIZONTAL) + FragColor = vec4(BlurX(), 0.0, 1.0); +#else + FragColor = vec4(BlurY(), 0.0, 0.0, 1.0); +#endif +} diff --git a/wadsrc/static/shaders/glsl/lineardepth.fp b/wadsrc/static/shaders/glsl/lineardepth.fp new file mode 100644 index 000000000..3e2b3eb95 --- /dev/null +++ b/wadsrc/static/shaders/glsl/lineardepth.fp @@ -0,0 +1,46 @@ + +in vec2 TexCoord; +out vec4 FragColor; + +#if defined(MULTISAMPLE) +uniform sampler2DMS DepthTexture; +uniform sampler2DMS ColorTexture; +uniform int SampleIndex; +#else +uniform sampler2D DepthTexture; +uniform sampler2D ColorTexture; +#endif + +uniform float LinearizeDepthA; +uniform float LinearizeDepthB; +uniform float InverseDepthRangeA; +uniform float InverseDepthRangeB; +uniform vec2 Scale; +uniform vec2 Offset; + +float normalizeDepth(float depth) +{ + float normalizedDepth = clamp(InverseDepthRangeA * depth + InverseDepthRangeB, 0.0, 1.0); + return 1.0 / (normalizedDepth * LinearizeDepthA + LinearizeDepthB); +} + +void main() +{ + vec2 uv = Offset + TexCoord * Scale; + +#if defined(MULTISAMPLE) + ivec2 texSize = textureSize(DepthTexture); +#else + ivec2 texSize = textureSize(DepthTexture, 0); +#endif + + ivec2 ipos = ivec2(max(uv * vec2(texSize), vec2(0.0))); + +#if defined(MULTISAMPLE) + float depth = normalizeDepth(texelFetch(ColorTexture, ipos, SampleIndex).a != 0.0 ? texelFetch(DepthTexture, ipos, SampleIndex).x : 1.0); +#else + float depth = normalizeDepth(texelFetch(ColorTexture, ipos, 0).a != 0.0 ? texelFetch(DepthTexture, ipos, 0).x : 1.0); +#endif + + FragColor = vec4(depth, 0.0, 0.0, 1.0); +} diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp index 36db31f68..5834dcf5d 100644 --- a/wadsrc/static/shaders/glsl/main.fp +++ b/wadsrc/static/shaders/glsl/main.fp @@ -7,6 +7,10 @@ in vec4 vTexCoord; in vec4 vColor; out vec4 FragColor; +#ifdef GBUFFER_PASS +out vec4 FragFog; +out vec4 FragNormal; +#endif #ifdef SHADER_STORAGE_LIGHTS layout(std430, binding = 1) buffer LightBufferSSO @@ -266,6 +270,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, distance(pixelpos.xyz, uCameraPos.xyz)); + } + fogfactor = exp2 (uFogDensity * fogdist); + + return mix(uFogColor.rgb, vec3(0.0), fogfactor); +} //=========================================================================== // @@ -381,5 +411,9 @@ void main() } } FragColor = frag; +#ifdef GBUFFER_PASS + FragFog = vec4(AmbientOcclusionColor(), 1.0); + FragNormal = vec4(vEyeNormal.xyz * 0.5 + 0.5, 1.0); +#endif } diff --git a/wadsrc/static/shaders/glsl/ssao.fp b/wadsrc/static/shaders/glsl/ssao.fp new file mode 100644 index 000000000..3b2db6005 --- /dev/null +++ b/wadsrc/static/shaders/glsl/ssao.fp @@ -0,0 +1,138 @@ + +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 float AOStrength; + +uniform vec2 Scale; +uniform vec2 Offset; + +uniform sampler2D DepthTexture; + +#if defined(MULTISAMPLE) +uniform sampler2DMS NormalTexture; +uniform int SampleIndex; +#else +uniform sampler2D NormalTexture; +#endif + +#if defined(USE_RANDOM_TEXTURE) +uniform sampler2D RandomTexture; +#endif + +#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); +} + +#if defined(MULTISAMPLE) +vec3 SampleNormal(vec2 uv) +{ + ivec2 texSize = textureSize(NormalTexture); + ivec2 ipos = ivec2(uv * vec2(texSize)); + return texelFetch(NormalTexture, ipos, SampleIndex).xyz * 2.0 - 1.0; +} +#else +vec3 SampleNormal(vec2 uv) +{ + ivec2 texSize = textureSize(NormalTexture, 0); + ivec2 ipos = ivec2(uv * vec2(texSize)); + return texelFetch(NormalTexture, ipos, 0).xyz * 2.0 - 1.0; +} +#endif + +// Look up the eye space normal for the specified texture coordinate +vec3 FetchNormal(vec2 uv) +{ + vec3 normal = SampleNormal(Offset + uv * Scale); + if (length(normal) > 0.1) + { + normal = normalize(normal); + normal.z = -normal.z; + return normal; + } + else + { + return vec3(0.0); + } +} + +// 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 !defined(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 + 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 = FetchNormal(TexCoord); + float occlusion = viewNormal != vec3(0.0) ? ComputeAO(viewPosition, viewNormal) * AOStrength + (1.0 - AOStrength) : 1.0; + + FragColor = vec4(occlusion, viewPosition.z, 0.0, 1.0); +} diff --git a/wadsrc/static/shaders/glsl/ssaocombine.fp b/wadsrc/static/shaders/glsl/ssaocombine.fp new file mode 100644 index 000000000..4ca64421b --- /dev/null +++ b/wadsrc/static/shaders/glsl/ssaocombine.fp @@ -0,0 +1,39 @@ + +in vec2 TexCoord; +out vec4 FragColor; + +uniform sampler2D AODepthTexture; + +#if defined(MULTISAMPLE) +uniform sampler2DMS SceneFogTexture; +uniform int SampleCount; +#else +uniform sampler2D SceneFogTexture; +#endif + +uniform vec2 Scale; +uniform vec2 Offset; + +void main() +{ + vec2 uv = Offset + TexCoord * Scale; + +#if defined(MULTISAMPLE) + ivec2 texSize = textureSize(SceneFogTexture); +#else + ivec2 texSize = textureSize(SceneFogTexture, 0); +#endif + ivec2 ipos = ivec2(uv * vec2(texSize)); + +#if defined(MULTISAMPLE) + vec3 fogColor = vec3(0.0); + for (int i = 0; i < SampleCount; i++) + fogColor += texelFetch(SceneFogTexture, ipos, i).rgb; + fogColor /= float(SampleCount); +#else + vec3 fogColor = texelFetch(SceneFogTexture, ipos, 0).rgb; +#endif + + float attenutation = texture(AODepthTexture, TexCoord).x; + FragColor = vec4(fogColor, 1.0 - attenutation); +} diff --git a/wadsrc/static/shaders/glsl/stencil.fp b/wadsrc/static/shaders/glsl/stencil.fp index d1b8745f6..65f12b405 100644 --- a/wadsrc/static/shaders/glsl/stencil.fp +++ b/wadsrc/static/shaders/glsl/stencil.fp @@ -3,6 +3,6 @@ out vec4 FragColor; void main() { - FragColor = vec4(1.0); + FragColor = vec4(1.0, 1.0, 1.0, 0.0); }