From e29974282e0101635e8e4f3de6310cc61e50ebed Mon Sep 17 00:00:00 2001 From: NULL511 Date: Fri, 1 Sep 2023 03:22:40 -0400 Subject: [PATCH] faster ssao --- .../render/renderer/shaders/SSAORenderer.java | 106 +++--------------- core/src/main/resources/shaders/ssao/ao.frag | 99 +++++++++------- .../resources/shaders/ssao/apply-frag.frag | 25 ----- 3 files changed, 73 insertions(+), 157 deletions(-) delete mode 100644 core/src/main/resources/shaders/ssao/apply-frag.frag diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAORenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAORenderer.java index bcddd4e1a..cd7ee6423 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAORenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAORenderer.java @@ -22,7 +22,7 @@ public class SSAORenderer private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); - private static final int MAX_KERNEL_SIZE = 128; + private static final int MAX_KERNEL_SIZE = 32; private static final float[] box_vertices = { -1, -1, 1, -1, @@ -33,21 +33,14 @@ public class SSAORenderer }; - private ShaderProgram ssaoShader; - private ShaderProgram applyShader; + private GLVertexBuffer boxBuffer; private VertexAttribute va; private boolean init = false; private float[] kernel = new float[MAX_KERNEL_SIZE * 3]; - private int width = -1; - private int height = -1; - private int ssaoFramebuffer = -1; - - private int ssaoTexture = -1; - // ssao uniforms private final SsaoShaderUniforms ssaoShaderUniforms = new SsaoShaderUniforms(); private static class SsaoShaderUniforms @@ -61,15 +54,6 @@ public class SSAORenderer public int gDepthMapUniform; } - // apply uniforms - private final ApplyShaderUniforms applyShaderUniforms = new ApplyShaderUniforms(); - private static class ApplyShaderUniforms - { - public int gSSAOMapUniform; - public int gDepthMapUniform; - } - - //=============// // constructor // @@ -79,26 +63,19 @@ public class SSAORenderer public void init() { - if (this.init) - { - return; - } - - + if (this.init) return; this.init = true; + + this.va = VertexAttribute.create(); this.va.bind(); + // Pos this.va.setVertexAttribute(0, 0, VertexAttribute.VertexPointer.addVec2Pointer(false)); this.va.completeAndCheck(Float.BYTES * 2); this.ssaoShader = new ShaderProgram("shaders/normal.vert", "shaders/ssao/ao.frag", "fragColor", new String[]{"vPosition"}); - this.applyShader = new ShaderProgram("shaders/normal.vert", "shaders/ssao/apply-frag.frag", - "fragColor", new String[]{"vPosition"}); - - - // SSAO uniform setup this.ssaoShaderUniforms.gProjUniform = this.ssaoShader.getUniformLocation("gProj"); this.ssaoShaderUniforms.gInvProjUniform = this.ssaoShader.getUniformLocation("gInvProj"); @@ -108,43 +85,12 @@ public class SSAORenderer this.ssaoShaderUniforms.gKernelUniform = this.ssaoShader.getUniformLocation("gKernel"); this.ssaoShaderUniforms.gDepthMapUniform = this.ssaoShader.getUniformLocation("gDepthMap"); - // Apply uniform setup - this.applyShaderUniforms.gSSAOMapUniform = this.applyShader.getUniformLocation("gSSAOMap"); - this.applyShaderUniforms.gDepthMapUniform = this.applyShader.getUniformLocation("gDepthMap"); - - - // Generate kernel this.kernel = genKernel(); // Framebuffer this.createBuffer(); } - - private void createFramebuffer(int width, int height) - { - if (this.ssaoFramebuffer != -1) - { - GL32.glDeleteFramebuffers(this.ssaoFramebuffer); - this.ssaoFramebuffer = -1; - } - if (this.ssaoTexture != -1) - { - GL32.glDeleteTextures(this.ssaoTexture); - this.ssaoTexture = -1; - } - - this.ssaoFramebuffer = GL32.glGenFramebuffers(); - GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.ssaoFramebuffer); - - this.ssaoTexture = GL32.glGenTextures(); - GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.ssaoTexture); - GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RED, width, height, 0, GL32.GL_RED, GL32.GL_FLOAT, (ByteBuffer) null); - GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_NEAREST); - GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_NEAREST); - GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.ssaoTexture, 0); - } - private void createBuffer() { ByteBuffer buffer = ByteBuffer.allocateDirect(box_vertices.length * Float.BYTES); @@ -194,24 +140,20 @@ public class SSAORenderer public void render(float partialTicks) { GLState state = new GLState(); + this.init(); + int width = MC_RENDER.getTargetFrameBufferViewportWidth(); int height = MC_RENDER.getTargetFrameBufferViewportHeight(); - if (this.width != width || this.height != height) - { - this.width = width; - this.height = height; - this.createFramebuffer(width, height); - } - - GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.ssaoFramebuffer); GL32.glViewport(0, 0, width, height); GL32.glDisable(GL32.GL_DEPTH_TEST); - GL32.glDisable(GL32.GL_BLEND); + GL32.glEnable(GL11.GL_BLEND); + GL32.glBlendEquation(GL32.GL_FUNC_ADD); + GL32.glBlendFuncSeparate(GL32.GL_ZERO, GL32.GL_SRC_ALPHA, GL32.GL_ZERO, GL32.GL_ONE); + GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, MC_RENDER.getTargetFrameBuffer()); GL32.glDisable(GL32.GL_SCISSOR_TEST); - Mat4f perspective = Mat4f.perspective( (float) MC_RENDER.getFov(partialTicks), MC_RENDER.getTargetFrameBufferViewportWidth() / (float) MC_RENDER.getTargetFrameBufferViewportHeight(), @@ -221,13 +163,12 @@ public class SSAORenderer Mat4f invertedPerspective = new Mat4f(perspective); invertedPerspective.invert(); - this.ssaoShader.bind(); this.ssaoShader.setUniform(this.ssaoShaderUniforms.gProjUniform, perspective); this.ssaoShader.setUniform(this.ssaoShaderUniforms.gInvProjUniform, invertedPerspective); this.ssaoShader.setUniform(this.ssaoShaderUniforms.gSampleRadUniform, 3.0f); - this.ssaoShader.setUniform(this.ssaoShaderUniforms.gFactorUniform, 0.8f); - this.ssaoShader.setUniform(this.ssaoShaderUniforms.gPowerUniform, 1.0f); + this.ssaoShader.setUniform(this.ssaoShaderUniforms.gFactorUniform, 0.7f); + this.ssaoShader.setUniform(this.ssaoShaderUniforms.gPowerUniform, 1.5f); this.va.bind(); this.va.bindBufferToAllBindingPoint(this.boxBuffer.getId()); @@ -240,30 +181,11 @@ public class SSAORenderer GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, 6); - - this.applyShader.bind(); - - GL32.glEnable(GL11.GL_BLEND); - GL32.glBlendFunc(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA); - GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, MC_RENDER.getTargetFrameBuffer()); - - GL32.glActiveTexture(GL32.GL_TEXTURE0); - GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.ssaoTexture); - GL32.glUniform1i(this.applyShaderUniforms.gSSAOMapUniform, 0); - GL32.glActiveTexture(GL32.GL_TEXTURE1); - GL32.glBindTexture(GL32.GL_TEXTURE_2D, MC_RENDER.getDepthTextureId()); - GL32.glUniform1i(this.applyShaderUniforms.gDepthMapUniform, 1); - - GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, 6); - - state.restore(); } public void free() { this.ssaoShader.free(); - this.applyShader.free(); } - } diff --git a/core/src/main/resources/shaders/ssao/ao.frag b/core/src/main/resources/shaders/ssao/ao.frag index fc48bcc05..32124d957 100644 --- a/core/src/main/resources/shaders/ssao/ao.frag +++ b/core/src/main/resources/shaders/ssao/ao.frag @@ -1,4 +1,5 @@ #version 150 core +#extension GL_ARB_derivative_control : enable in vec2 TexCoord; @@ -11,55 +12,73 @@ uniform float gPower; uniform mat4 gProj; uniform mat4 gInvProj; -const int MAX_KERNEL_SIZE = 128; +const float SAMPLE_BIAS = 0.1; +const int MAX_KERNEL_SIZE = 32; const float INV_MAX_KERNEL_SIZE_F = 1.0 / float(MAX_KERNEL_SIZE); -const vec2 HALF_2 = vec2(0.5); uniform vec3 gKernel[MAX_KERNEL_SIZE]; -vec3 calcViewPosition(vec2 coords) { - float fragmentDepth = texture(gDepthMap, coords).r; +const vec3 MAGIC = vec3(0.06711056, 0.00583715, 52.9829189); +const float PI = 3.1415926538; - vec4 ndc = vec4( - coords.x * 2.0 - 1.0, - coords.y * 2.0 - 1.0, - fragmentDepth * 2.0 - 1.0, - 1.0 - ); - vec4 vs_pos = gInvProj * ndc; - vs_pos.xyz = vs_pos.xyz / vs_pos.w; - return vs_pos.xyz; +float InterleavedGradientNoise(const in vec2 pixel) { + float x = dot(pixel, MAGIC.xy); + return fract(MAGIC.z * fract(x)); } -void main() -{ - vec3 viewPos = calcViewPosition(TexCoord); - vec3 viewNormal = normalize(cross(dFdy(viewPos.xyz), dFdx(viewPos.xyz)) * -1.0); +vec3 calcViewPosition(const in vec3 clipPos) { + vec4 viewPos = gInvProj * vec4(clipPos * 2.0 - 1.0, 1.0); + return viewPos.xyz / viewPos.w; +} - vec3 randomVec = vec3(0.0, -1.0, 0.0); +void main() { + float fragmentDepth = textureLod(gDepthMap, TexCoord, 0).r; + float occlusion = 1.0; + + if (fragmentDepth < 1.0) { + float dither = InterleavedGradientNoise(gl_FragCoord.xy); + vec3 viewPos = calcViewPosition(vec3(TexCoord, fragmentDepth)); + + #ifdef GL_ARB_derivative_control + vec3 viewNormal = normalize(cross(dFdxFine(viewPos.xyz), dFdyFine(viewPos.xyz))); + #else + vec3 viewNormal = normalize(cross(dFdx(viewPos.xyz), dFdy(viewPos.xyz))); + #endif - vec3 tangent = normalize(randomVec - viewNormal * dot(randomVec, viewNormal)); - vec3 bitangent = cross(viewNormal, tangent); - mat3 TBN = mat3(tangent, bitangent, viewNormal); + const vec3 upVec = vec3(0.0, -1.0, 0.0); - float occlusion_factor = 0.0; - for (int i = 0; i < MAX_KERNEL_SIZE; i++) - { - vec3 samplePos = vec3(0.0) + (TBN * gKernel[i]); - samplePos = viewPos + samplePos * gSampleRad; - - vec4 offset = gProj * vec4(samplePos + viewPos, 1.0); - offset.xy /= offset.w; - offset.xy = offset.xy * HALF_2 + HALF_2; - - float geometryDepth = calcViewPosition(offset.xy).z; - - float rangeCheck = smoothstep(0.0, 1.0, gSampleRad / abs(viewPos.z - geometryDepth)); - // the number added to the samplePos.z can be used to reduce noise in the SSAO application at the cost of reducing the overall affect - occlusion_factor += float(geometryDepth >= samplePos.z + 0.1) * rangeCheck; + float angle = dither * (PI * 2.0); + vec3 rotation = vec3(sin(angle), cos(angle), 0.0); + vec3 tangent = normalize(cross(viewNormal, rotation)); + vec3 bitangent = normalize(cross(viewNormal, tangent)); + mat3 TBN = mat3(tangent, bitangent, viewNormal); + + float maxWeight = 0.0; + float occlusion_factor = 0.0; + for (int i = 0; i < MAX_KERNEL_SIZE; i++) { + vec3 samplePos = TBN * gKernel[i]; + samplePos = viewPos + samplePos * gSampleRad; + + vec4 sampleNdcPos = gProj * vec4(samplePos + viewPos, 1.0); + sampleNdcPos = sampleNdcPos / sampleNdcPos.w; + if (any(greaterThanEqual(abs(sampleNdcPos.xy), vec2(1.0)))) continue; + + maxWeight += 1.0; + + vec2 sampleTexPos = sampleNdcPos.xy * 0.5 + 0.5; + float sampleDepth = textureLod(gDepthMap, sampleTexPos, 0).r; + float geometryDepth = calcViewPosition(vec3(sampleTexPos, sampleDepth)).z; + + float rangeCheck = smoothstep(0.0, 1.0, gSampleRad / abs(viewPos.z - geometryDepth)); + // the number added to the samplePos.z can be used to reduce noise in the SSAO application at the cost of reducing the overall affect + occlusion_factor += step(samplePos.z + SAMPLE_BIAS, geometryDepth) * rangeCheck; + } + + float visibility_factor = 1.0 - (occlusion_factor / maxWeight); + occlusion = (1.0 - pow(visibility_factor, gFactor)) * gPower; + occlusion = clamp(1.0 - occlusion, 0.25, 1.0); } - - float visibility_factor = 1.0 - (occlusion_factor / MAX_KERNEL_SIZE); - fragColor = vec4(clamp(1.0 - ((1.0 - pow(visibility_factor, gFactor)) * gPower), 0.1, 1.0)); -} \ No newline at end of file + + fragColor = vec4(vec3(1.0), occlusion); +} diff --git a/core/src/main/resources/shaders/ssao/apply-frag.frag b/core/src/main/resources/shaders/ssao/apply-frag.frag deleted file mode 100644 index 534306c50..000000000 --- a/core/src/main/resources/shaders/ssao/apply-frag.frag +++ /dev/null @@ -1,25 +0,0 @@ -#version 150 core - -in vec2 TexCoord; -in vec2 ViewRay; - -out vec4 fragColor; - -uniform sampler2D gSSAOMap; -uniform sampler2D gDepthMap; - -void main() -{ - float fragmentDepth = texture(gDepthMap, TexCoord).r; - // a fragment depth of "1" means the fragment wasn't drawn to, - // we only want to apply SSAO to LODs, not to the sky outside the LODs - if (fragmentDepth < 1) - { - fragColor = vec4(0.0, 0.0, 0.0, 1-texture(gSSAOMap, TexCoord).r); - } - else - { - // every pixel needs to be set to something, otherwise the pixel may be undefined by some drivers (specifically Intel) - fragColor = vec4(0.0, 0.0, 0.0, 0.0); - } -}