From 84c212a78040457ca2fb88c998dc95c4758cb1f1 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Mon, 2 Mar 2026 07:45:25 -0600 Subject: [PATCH] add SSAO --- .../core/render/renderer/McLodRenderer.java | 10 +- .../render/IMcSsaoRenderer.java | 32 ++++ .../distanthorizons/shaders/ssao/ao.fsh | 137 ++++++++++++++++++ .../distanthorizons/shaders/ssao/apply.fsh | 79 ++++++++++ .../shaders/ssao/quad_apply.vsh | 15 ++ 5 files changed, 267 insertions(+), 6 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/render/IMcSsaoRenderer.java create mode 100644 core/src/main/resources/assets/distanthorizons/shaders/ssao/ao.fsh create mode 100644 core/src/main/resources/assets/distanthorizons/shaders/ssao/apply.fsh create mode 100644 core/src/main/resources/assets/distanthorizons/shaders/ssao/quad_apply.vsh diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/McLodRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/McLodRenderer.java index 9059baaa0..fd95c07cf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/McLodRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/McLodRenderer.java @@ -33,10 +33,7 @@ import com.seibel.distanthorizons.core.util.math.Vec3d; import com.seibel.distanthorizons.core.util.objects.SortedArraySet; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.render.IMcGenericRenderer; -import com.seibel.distanthorizons.core.wrapperInterfaces.render.IMcLodRenderer; -import com.seibel.distanthorizons.core.wrapperInterfaces.render.IMcTestRenderer; -import com.seibel.distanthorizons.core.wrapperInterfaces.render.IVertexBufferWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.*; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import com.seibel.distanthorizons.core.util.math.Vec3f; @@ -150,6 +147,7 @@ public class McLodRenderer } IMcLodRenderer lodRenderer = SingletonInjector.INSTANCE.get(IMcLodRenderer.class); + IMcSsaoRenderer ssaoRenderer = SingletonInjector.INSTANCE.get(IMcSsaoRenderer.class); @@ -183,8 +181,8 @@ public class McLodRenderer // SSAO if (Config.Client.Advanced.Graphics.Ssao.enableSsao.get()) { - //profiler.popPush("LOD SSAO"); - //SSAORenderer.INSTANCE.render(new Mat4f(renderParams.dhProjectionMatrix), renderParams.partialTicks); + profiler.popPush("LOD SSAO"); + ssaoRenderer.render(renderParams.dhProjectionMatrix); } // custom objects without SSAO diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/render/IMcSsaoRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/render/IMcSsaoRenderer.java new file mode 100644 index 000000000..a3931299d --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/render/IMcSsaoRenderer.java @@ -0,0 +1,32 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * 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, version 3. + * + * 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 . + */ + +package com.seibel.distanthorizons.core.wrapperInterfaces.render; + +import com.seibel.distanthorizons.api.objects.math.DhApiMat4f; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; +import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; + +public interface IMcSsaoRenderer extends IBindable +{ + + void render(DhApiMat4f dhProjectionMatrix); + +} diff --git a/core/src/main/resources/assets/distanthorizons/shaders/ssao/ao.fsh b/core/src/main/resources/assets/distanthorizons/shaders/ssao/ao.fsh new file mode 100644 index 000000000..517523342 --- /dev/null +++ b/core/src/main/resources/assets/distanthorizons/shaders/ssao/ao.fsh @@ -0,0 +1,137 @@ +#version 150 core +#extension GL_ARB_derivative_control : enable + +#define SAMPLE_MAX 64 + +#define saturate(x) (clamp((x), 0.0, 1.0)) + +in vec2 TexCoord; + +out vec4 fragColor; + + +layout (std140) uniform fragUniformBlock +{ + int uSampleCount; + + float uRadius; + float uStrength; + float uMinLight; + float uBias; + float uFadeDistanceInBlocks; + + mat4 uInvProj; + mat4 uProj; +}; + +uniform sampler2D uDhDepthTexture; + +const float EPSILON = 1.e-6; +const float GOLDEN_ANGLE = 2.39996323; +const vec3 MAGIC = vec3(0.06711056, 0.00583715, 52.9829189); +const float PI = 3.1415926538; +const float TAU = PI * 2.0; + + +vec3 unproject(vec4 pos) +{ + return pos.xyz / pos.w; +} + +float InterleavedGradientNoise(const in vec2 pixel) +{ + float x = dot(pixel, MAGIC.xy); + return fract(MAGIC.z * fract(x)); +} + +vec3 calcViewPosition(const in vec3 clipPos) +{ + vec4 viewPos = uInvProj * vec4(clipPos * 2.0 - 1.0, 1.0); + return viewPos.xyz / viewPos.w; +} + +float GetSpiralOcclusion(const in vec2 uv, const in vec3 viewPos, const in vec3 viewNormal) +{ + float dither = InterleavedGradientNoise(gl_FragCoord.xy); + float rotatePhase = dither * TAU; + float rStep = uRadius / uSampleCount; + + vec2 offset; + + float ao = 0.0; + int sampleCount = 0; + float radius = rStep; + for (int i = 0; i < clamp(uSampleCount, 1, SAMPLE_MAX); i++) { + vec2 offset = vec2( + sin(rotatePhase), + cos(rotatePhase) + ) * radius; + + radius += rStep; + rotatePhase += GOLDEN_ANGLE; + + vec3 sampleViewPos = viewPos + vec3(offset, -0.1); + vec3 sampleClipPos = unproject(uProj * vec4(sampleViewPos, 1.0)) * 0.5 + 0.5; + sampleClipPos = saturate(sampleClipPos); + + float sampleClipDepth = textureLod(uDhDepthTexture, sampleClipPos.xy, 0.0).r; + if (sampleClipDepth >= 1.0 - EPSILON) continue; + + sampleClipPos.z = sampleClipDepth; + sampleViewPos = unproject(uInvProj * vec4(sampleClipPos * 2.0 - 1.0, 1.0)); + + vec3 diff = sampleViewPos - viewPos; + float sampleDist = length(diff); + vec3 sampleNormal = diff / sampleDist; + + float sampleNoLm = max(dot(viewNormal, sampleNormal) - uBias, 0.0); + float aoF = 1.0 - saturate(sampleDist / uRadius); + ao += sampleNoLm * aoF; + sampleCount++; + } + + ao /= max(sampleCount, 1); + ao = smoothstep(0.0, uStrength, ao); + + return ao * (1.0 - uMinLight); +} + + +void main() +{ + float fragmentDepth = textureLod(uDhDepthTexture, TexCoord, 0).r; + float occlusion = 0.0; + + // Do not apply to sky + if (fragmentDepth < 1.0) + { + vec3 viewPos = calcViewPosition(vec3(TexCoord, fragmentDepth)); + + // fading is done to prevent banding/noise + // at super far distance + float distanceFromCamera = length(viewPos); + float fadeDistance = uFadeDistanceInBlocks; + if (distanceFromCamera < fadeDistance) + { + #ifdef GL_ARB_derivative_control + // Get higher precision derivatives when available + vec3 viewNormal = cross(dFdxFine(viewPos.xyz), dFdyFine(viewPos.xyz)); + #else + vec3 viewNormal = cross(dFdx(viewPos.xyz), dFdy(viewPos.xyz)); + #endif + + viewNormal = normalize(viewNormal); + occlusion = GetSpiralOcclusion(TexCoord, viewPos, viewNormal); + + // linearly fade with distance + occlusion *= (fadeDistance - distanceFromCamera) / fadeDistance; + } + else + { + // we're out of range, no need to do any SSAO calculations + occlusion = 0.0; + } + } + + fragColor = vec4(vec3(1.0 - occlusion), 1.0); +} diff --git a/core/src/main/resources/assets/distanthorizons/shaders/ssao/apply.fsh b/core/src/main/resources/assets/distanthorizons/shaders/ssao/apply.fsh new file mode 100644 index 000000000..dc48356f3 --- /dev/null +++ b/core/src/main/resources/assets/distanthorizons/shaders/ssao/apply.fsh @@ -0,0 +1,79 @@ +#version 150 core + +in vec2 TexCoord; + +out vec4 fragColor; + +uniform sampler2D uSsaoColorTexture; +uniform sampler2D uDhDepthTexture; + +uniform vec2 gViewSize; +uniform int gBlurRadius; +uniform float gNear; +uniform float gFar; + + +float linearizeDepth(const in float depth) { + return (gNear * gFar) / (depth * (gNear - gFar) + gFar); +} + +float Gaussian(const in float sigma, const in float x) { + return exp(-(x*x) / (2.0 * (sigma*sigma))); +} + +float BilateralGaussianBlur(const in vec2 texcoord, const in float linearDepth, const in float g_sigmaV) { + float g_sigmaX = 1.6; + float g_sigmaY = 1.6; + + int radius = clamp(gBlurRadius, 1, 3); + + vec2 pixelSize = 1.0 / gViewSize; + + float accum = 0.0; + float total = 0.0; + for (int iy = -radius; iy <= radius; iy++) { + float fy = Gaussian(g_sigmaY, iy); + + for (int ix = -radius; ix <= radius; ix++) { + float fx = Gaussian(g_sigmaX, ix); + + vec2 sampleTex = texcoord + ivec2(ix, iy) * pixelSize; + float sampleValue = textureLod(uSsaoColorTexture, sampleTex, 0).r; + float sampleDepth = textureLod(uDhDepthTexture, sampleTex, 0).r; + float sampleLinearDepth = linearizeDepth(sampleDepth); + + float depthDiff = abs(sampleLinearDepth - linearDepth); + float fv = Gaussian(g_sigmaV, depthDiff); + + float weight = fx*fy*fv; + accum += weight * sampleValue; + total += weight; + } + } + + if (total <= 1.e-4) return 1.0; + return accum / total; +} + + +void main() +{ + fragColor = vec4(1.0); + + float fragmentDepth = textureLod(uDhDepthTexture, TexCoord, 0).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) + { + if (gBlurRadius > 0) + { + float fragmentDepthLinear = linearizeDepth(fragmentDepth); + fragColor.a = BilateralGaussianBlur(TexCoord, fragmentDepthLinear, 1.6); + } + else + { + fragColor.a = texelFetch(uSsaoColorTexture, ivec2(gl_FragCoord.xy), 0).r; + } + } +} diff --git a/core/src/main/resources/assets/distanthorizons/shaders/ssao/quad_apply.vsh b/core/src/main/resources/assets/distanthorizons/shaders/ssao/quad_apply.vsh new file mode 100644 index 000000000..3f614c123 --- /dev/null +++ b/core/src/main/resources/assets/distanthorizons/shaders/ssao/quad_apply.vsh @@ -0,0 +1,15 @@ +#version 150 core + +in vec2 vPosition; + +out vec2 TexCoord; + +/** + * This is specifically used by application shaders. + * IE post process or pixel transfer shaders, anything that is rendered using a single rectangle. + */ +void main() +{ + gl_Position = vec4(vPosition, 1.0, 1.0); + TexCoord = vPosition.xy * 0.5 + 0.5; +} \ No newline at end of file