diff --git a/src/gl/renderer/gl_postprocess.cpp b/src/gl/renderer/gl_postprocess.cpp index ac5a6336d..97e2c3be9 100644 --- a/src/gl/renderer/gl_postprocess.cpp +++ b/src/gl/renderer/gl_postprocess.cpp @@ -124,7 +124,7 @@ CVAR(Float, gl_ssao_strength, 0.7, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Int, gl_ssao_debug, 0, 0) CVAR(Float, gl_ssao_bias, 0.5f, 0) CVAR(Float, gl_ssao_radius, 100.0f, 0) -CUSTOM_CVAR(Float, gl_ssao_blur_amount, 4.0f, 0) +CUSTOM_CVAR(Float, gl_ssao_blur_amount, 16.0f, 0) { if (self < 0.1f) self = 0.1f; } @@ -178,6 +178,13 @@ void FGLRenderer::AmbientOccludeScene() 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->AmbientFB0); glViewport(0, 0, mBuffers->AmbientWidth, mBuffers->AmbientHeight); @@ -196,8 +203,8 @@ void FGLRenderer::AmbientOccludeScene() mLinearDepthShader->LinearizeDepthB.Set(MAX(1.0f / GetZNear(), 1.e-8f)); mLinearDepthShader->InverseDepthRangeA.Set(1.0f); mLinearDepthShader->InverseDepthRangeB.Set(0.0f); - mLinearDepthShader->Scale.Set(mBuffers->AmbientWidth * 2.0f / (float)mScreenViewport.width, mBuffers->AmbientHeight * 2.0f / (float)mScreenViewport.height); - mLinearDepthShader->Offset.Set(mSceneViewport.left / (float)mScreenViewport.width, mSceneViewport.top / (float)mScreenViewport.height); + mLinearDepthShader->Scale.Set(sceneScaleX, sceneScaleY); + mLinearDepthShader->Offset.Set(sceneOffsetX, sceneOffsetY); RenderScreenQuad(); // Apply ambient occlusion @@ -206,7 +213,7 @@ void FGLRenderer::AmbientOccludeScene() 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); + 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); @@ -219,44 +226,46 @@ void FGLRenderer::AmbientOccludeScene() mSSAOShader->DepthTexture.Set(0); mSSAOShader->RandomTexture.Set(1); mSSAOShader->NormalTexture.Set(2); - mSSAOShader->UVToViewA.Set(2.0f * invFocalLenX, -2.0f * invFocalLenY); - mSSAOShader->UVToViewB.Set(-invFocalLenX, invFocalLenY); + 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 - 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(); + 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(1.8f); - 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(1.8f); + 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); - if (gl_ssao_debug > 1) - glBlendFunc(GL_ONE, GL_ZERO); - else - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - if (gl_ssao_debug == 1) + 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); @@ -270,8 +279,8 @@ void FGLRenderer::AmbientOccludeScene() mSSAOCombineShader->AODepthTexture.Set(0); mSSAOCombineShader->SceneFogTexture.Set(1); if (gl_multisample > 1) mSSAOCombineShader->SampleCount.Set(gl_multisample); - mSSAOCombineShader->Scale.Set(mBuffers->AmbientWidth * 2.0f / (float)mScreenViewport.width, mBuffers->AmbientHeight * 2.0f / (float)mScreenViewport.height); - mSSAOCombineShader->Offset.Set(mSceneViewport.left / (float)mScreenViewport.width, mSceneViewport.top / (float)mScreenViewport.height); + mSSAOCombineShader->Scale.Set(sceneScaleX, sceneScaleY); + mSSAOCombineShader->Offset.Set(sceneOffsetX, sceneOffsetY); RenderScreenQuad(); FGLDebug::PopGroup(); diff --git a/src/gl/renderer/gl_renderbuffers.cpp b/src/gl/renderer/gl_renderbuffers.cpp index 1a1509d7a..a24596562 100644 --- a/src/gl/renderer/gl_renderbuffers.cpp +++ b/src/gl/renderer/gl_renderbuffers.cpp @@ -59,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); } @@ -151,7 +156,8 @@ void FGLRenderBuffers::ClearAmbientOcclusion() DeleteFrameBuffer(AmbientFB1); DeleteTexture(AmbientTexture0); DeleteTexture(AmbientTexture1); - DeleteTexture(AmbientRandomTexture); + for (int i = 0; i < NumAmbientRandomTextures; i++) + DeleteTexture(AmbientRandomTexture[i]); } void FGLRenderBuffers::DeleteTexture(GLuint &handle) @@ -368,25 +374,31 @@ void FGLRenderBuffers::CreateAmbientOcclusion(int width, int height) AmbientFB0 = CreateFrameBuffer("AmbientFB0", AmbientTexture0); AmbientFB1 = CreateFrameBuffer("AmbientFB1", AmbientTexture1); - int16_t randomValues[16 * 4]; + // 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(-1.0, 1.0); - for (int i = 0; i < 16; i++) + std::uniform_real_distribution distribution(0.0, 1.0); + for (int quality = 0; quality < NumAmbientRandomTextures; quality++) { - double num_directions = 8.0; // Must be same as the define in ssao.fp - double angle = 2.0 * M_PI * distribution(generator) / num_directions; - double x = cos(angle); - double y = sin(angle); - double z = distribution(generator); - double w = distribution(generator); + int16_t randomValues[16 * 4]; - 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); + 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); } - - AmbientRandomTexture = Create2DTexture("AmbientRandomTexture", GL_RGBA16_SNORM, 4, 4, randomValues); } //========================================================================== diff --git a/src/gl/renderer/gl_renderbuffers.h b/src/gl/renderer/gl_renderbuffers.h index 9508f7009..d593b0248 100644 --- a/src/gl/renderer/gl_renderbuffers.h +++ b/src/gl/renderer/gl_renderbuffers.h @@ -65,7 +65,8 @@ public: GLuint AmbientFB1 = 0; int AmbientWidth = 0; int AmbientHeight = 0; - GLuint AmbientRandomTexture = 0; + enum { NumAmbientRandomTextures = 3 }; + GLuint AmbientRandomTexture[NumAmbientRandomTextures]; static bool IsEnabled(); diff --git a/src/gl/shaders/gl_ambientshader.cpp b/src/gl/shaders/gl_ambientshader.cpp index 6348d24cd..0d08e1258 100644 --- a/src/gl/shaders/gl_ambientshader.cpp +++ b/src/gl/shaders/gl_ambientshader.cpp @@ -84,6 +84,8 @@ void FSSAOShader::Bind() RadiusToScreen.Init(*mShader, "RadiusToScreen"); AOMultiplier.Init(*mShader, "AOMultiplier"); AOStrength.Init(*mShader, "AOStrength"); + Scale.Init(*mShader, "Scale"); + Offset.Init(*mShader, "Offset"); mMultisample = multisample; } mShader->Bind(); @@ -91,6 +93,7 @@ void FSSAOShader::Bind() FString FSSAOShader::GetDefines(int mode, bool multisample) { + // Must match quality values in FGLRenderBuffers::CreateAmbientOcclusion int numDirections, numSteps; switch (gl_ssao) { diff --git a/src/gl/shaders/gl_ambientshader.h b/src/gl/shaders/gl_ambientshader.h index 5c68892c3..95dfd6bac 100644 --- a/src/gl/shaders/gl_ambientshader.h +++ b/src/gl/shaders/gl_ambientshader.h @@ -39,6 +39,8 @@ public: FBufferedUniform1f RadiusToScreen; FBufferedUniform1f AOMultiplier; FBufferedUniform1f AOStrength; + FBufferedUniform2f Scale; + FBufferedUniform2f Offset; private: enum Quality diff --git a/wadsrc/static/shaders/glsl/depthblur.fp b/wadsrc/static/shaders/glsl/depthblur.fp index c4f4438d3..7e3dad074 100644 --- a/wadsrc/static/shaders/glsl/depthblur.fp +++ b/wadsrc/static/shaders/glsl/depthblur.fp @@ -7,7 +7,7 @@ uniform float BlurSharpness; uniform vec2 InvFullResolution; uniform float PowExponent; -#define KERNEL_RADIUS 7.0 +#define KERNEL_RADIUS 3.0 float CrossBilateralWeight(float r, float sampleDepth, float centerDepth) { diff --git a/wadsrc/static/shaders/glsl/lineardepth.fp b/wadsrc/static/shaders/glsl/lineardepth.fp index 558738bd9..fa71c9de0 100644 --- a/wadsrc/static/shaders/glsl/lineardepth.fp +++ b/wadsrc/static/shaders/glsl/lineardepth.fp @@ -18,6 +18,12 @@ 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; @@ -28,19 +34,24 @@ void main() ivec2 texSize = textureSize(DepthTexture, 0); #endif - // Use floor here because as we downscale the sampling error has to remain uniform to prevent - // noise in the depth values. - ivec2 ipos = ivec2(max(floor(uv * vec2(texSize) - 0.75), vec2(0.0))); + ivec2 ipos = ivec2(max(uv * vec2(texSize), vec2(0.0))); #if defined(MULTISAMPLE) - float depth = 0.0; - for (int i = 0; i < SampleCount; i++) - depth += texelFetch(ColorTexture, ipos, i).a != 0.0 ? texelFetch(DepthTexture, ipos, i).x : 1.0; - depth /= float(SampleCount); + float depth = normalizeDepth(texelFetch(ColorTexture, ipos, 0).a != 0.0 ? texelFetch(DepthTexture, ipos, 0).x : 1.0); + float sampleIndex = 0.0; + for (int i = 1; i < SampleCount; i++) + { + float hardwareDepth = texelFetch(ColorTexture, ipos, i).a != 0.0 ? texelFetch(DepthTexture, ipos, i).x : 1.0; + float sampleDepth = normalizeDepth(hardwareDepth); + if (sampleDepth < depth) + { + depth = sampleDepth; + sampleIndex = float(i); + } + } + FragColor = vec4(depth, sampleIndex, 0.0, 1.0); #else - float depth = texelFetch(ColorTexture, ipos, 0).a != 0.0 ? texelFetch(DepthTexture, ipos, 0).x : 1.0; + float depth = normalizeDepth(texelFetch(ColorTexture, ipos, 0).a != 0.0 ? texelFetch(DepthTexture, ipos, 0).x : 1.0); + FragColor = vec4(depth, 0.0, 0.0, 1.0); #endif - - float normalizedDepth = clamp(InverseDepthRangeA * depth + InverseDepthRangeB, 0.0, 1.0); - FragColor = vec4(1.0 / (normalizedDepth * LinearizeDepthA + LinearizeDepthB), 0.0, 0.0, 1.0); } diff --git a/wadsrc/static/shaders/glsl/ssao.fp b/wadsrc/static/shaders/glsl/ssao.fp index 758f7dbaa..f895ddc25 100644 --- a/wadsrc/static/shaders/glsl/ssao.fp +++ b/wadsrc/static/shaders/glsl/ssao.fp @@ -13,6 +13,9 @@ uniform float AOMultiplier; uniform float AOStrength; +uniform vec2 Scale; +uniform vec2 Offset; + uniform sampler2D DepthTexture; #if defined(MULTISAMPLE) @@ -27,42 +30,42 @@ uniform sampler2D RandomTexture; #define PI 3.14159265358979323846 -// Calculate eye space position for the specified texture coordinate -vec3 FetchViewPos(vec2 uv) +// Calculate eye space position for the specified texture coordinate and depth +vec3 FetchViewPos(vec2 uv, float z) { - float z = texture(DepthTexture, uv).x; return vec3((UVToViewA * uv + UVToViewB) * z, z); } #if defined(MULTISAMPLE) -vec3 FetchNormal(vec2 uv) +vec3 SampleNormal(vec2 uv, float samplerIndex) { ivec2 texSize = textureSize(NormalTexture); ivec2 ipos = ivec2(uv * vec2(texSize)); - return normalize(texelFetch(NormalTexture, ipos, 0).xyz * 2.0 - 1.0); + return texelFetch(NormalTexture, ipos, int(samplerIndex)).xyz * 2.0 - 1.0; } #else -vec3 FetchNormal(vec2 uv) +vec3 SampleNormal(vec2 uv, float samplerIndex) { - return normalize(texture(NormalTexture, uv).xyz * 2.0 - 1.0); + ivec2 texSize = textureSize(NormalTexture, 0); + ivec2 ipos = ivec2(uv * vec2(texSize)); + return texelFetch(NormalTexture, ipos, 0).xyz * 2.0 - 1.0; } #endif -vec3 MinDiff(vec3 p, vec3 pr, vec3 pl) +// Look up the eye space normal for the specified texture coordinate +vec3 FetchNormal(vec2 uv, float samplerIndex) { - vec3 v1 = pr - p; - vec3 v2 = p - pl; - return (dot(v1, v1) < dot(v2, v2)) ? v1 : v2; -} - -// Reconstruct eye space normal from nearest neighbors -vec3 ReconstructNormal(vec3 p) -{ - vec3 pr = FetchViewPos(TexCoord + vec2(InvFullResolution.x, 0)); - vec3 pl = FetchViewPos(TexCoord + vec2(-InvFullResolution.x, 0)); - vec3 pt = FetchViewPos(TexCoord + vec2(0, InvFullResolution.y)); - vec3 pb = FetchViewPos(TexCoord + vec2(0, -InvFullResolution.y)); - return normalize(cross(MinDiff(p, pr, pl), MinDiff(p, pt, pb))); + vec3 normal = SampleNormal(Offset + uv * Scale, samplerIndex); + if (length(normal) > 0.1) + { + normal = normalize(normal); + normal.z = -normal.z; + return normal; + } + else + { + return vec3(0.0); + } } // Compute normalized 2D direction @@ -113,7 +116,7 @@ float ComputeAO(vec3 viewPosition, vec3 viewNormal) for (float StepIndex = 0.0; StepIndex < NUM_STEPS; ++StepIndex) { vec2 sampleUV = round(rayPixels * direction) * InvFullResolution + TexCoord; - vec3 samplePos = FetchViewPos(sampleUV); + vec3 samplePos = FetchViewPos(sampleUV, texture(DepthTexture, sampleUV).x); ao += ComputeSampleAO(viewPosition, viewNormal, samplePos); rayPixels += stepSizePixels; } @@ -125,9 +128,10 @@ float ComputeAO(vec3 viewPosition, vec3 viewNormal) void main() { - vec3 viewPosition = FetchViewPos(TexCoord); - //vec3 viewNormal = ReconstructNormal(viewPosition); - vec3 viewNormal = FetchNormal(TexCoord); - float occlusion = ComputeAO(viewPosition, viewNormal) * AOStrength + (1.0 - AOStrength); + vec2 depthData = texture(DepthTexture, TexCoord).xy; + vec3 viewPosition = FetchViewPos(TexCoord, depthData.x); + vec3 viewNormal = FetchNormal(TexCoord, depthData.y); + float occlusion = viewNormal != vec3(0.0) ? ComputeAO(viewPosition, viewNormal) * AOStrength + (1.0 - AOStrength) : 1.0; + FragColor = vec4(occlusion, viewPosition.z, 0.0, 1.0); }