diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGraphicsConfig.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGraphicsConfig.java index 97c500fa0..b083c4eb8 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGraphicsConfig.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGraphicsConfig.java @@ -86,6 +86,18 @@ public interface IDhApiGraphicsConfig extends IDhApiConfigGroup IDhApiConfigValue ambientOcclusion(); + IDhApiConfigValue ambientOcclusion_SampleCount(); + + IDhApiConfigValue ambientOcclusion_Radius(); + + IDhApiConfigValue ambientOcclusion_Strength(); + + IDhApiConfigValue ambientOcclusion_Bias(); + + IDhApiConfigValue ambientOcclusion_MinLight(); + + IDhApiConfigValue ambientOcclusion_BlurRadius(); + IDhApiConfigValue transparency(); /** Defines what blocks won't be rendered as LODs. */ diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGraphicsConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGraphicsConfig.java index 81c7cc547..3e7943751 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGraphicsConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGraphicsConfig.java @@ -84,6 +84,30 @@ public class DhApiGraphicsConfig implements IDhApiGraphicsConfig public IDhApiConfigValue ambientOcclusion() { return new DhApiConfigValue(Config.Client.Advanced.Graphics.Quality.ssao); } + @Override + public IDhApiConfigValue ambientOcclusion_SampleCount() + { return new DhApiConfigValue(Config.Client.Advanced.Graphics.Quality.ssaoSampleCount); } + + @Override + public IDhApiConfigValue ambientOcclusion_Radius() + { return new DhApiConfigValue(Config.Client.Advanced.Graphics.Quality.ssaoRadius); } + + @Override + public IDhApiConfigValue ambientOcclusion_Strength() + { return new DhApiConfigValue(Config.Client.Advanced.Graphics.Quality.ssaoStrength); } + + @Override + public IDhApiConfigValue ambientOcclusion_Bias() + { return new DhApiConfigValue(Config.Client.Advanced.Graphics.Quality.ssaoBias); } + + @Override + public IDhApiConfigValue ambientOcclusion_MinLight() + { return new DhApiConfigValue(Config.Client.Advanced.Graphics.Quality.ssaoMinLight); } + + @Override + public IDhApiConfigValue ambientOcclusion_BlurRadius() + { return new DhApiConfigValue(Config.Client.Advanced.Graphics.Quality.ssaoBlurRadius); } + @Override public IDhApiConfigValue transparency() { return new DhApiConfigValue(Config.Client.Advanced.Graphics.Quality.transparency); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 5398f5f1a..6d6ddece6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -179,6 +179,36 @@ public class Config .comment("Enable Screen Space Ambient Occlusion") .build(); + public static ConfigEntry ssaoSampleCount = new ConfigEntry.Builder() + .set(6) + .comment("Number of samples to use for Screen Space Ambient Occlusion") + .build(); + + public static ConfigEntry ssaoRadius = new ConfigEntry.Builder() + .set(4.0) + .comment("Radius of Screen Space Ambient Occlusion effect in blocks") + .build(); + + public static ConfigEntry ssaoStrength = new ConfigEntry.Builder() + .set(0.2) + .comment("Strength of Screen Space Ambient Occlusion effect") + .build(); + + public static ConfigEntry ssaoBias = new ConfigEntry.Builder() + .set(0.02) + .comment("Bias of Screen Space Ambient Occlusion effect") + .build(); + + public static ConfigEntry ssaoMinLight = new ConfigEntry.Builder() + .set(0.25) + .comment("Minimum brightness of Screen Space Ambient Occlusion effect") + .build(); + + public static ConfigEntry ssaoBlurRadius = new ConfigEntry.Builder() + .set(2) + .comment("Radius in pixels of Screen Space Ambient Occlusion blurring") + .build(); + public static ConfigEntry horizontalQuality = new ConfigEntry.Builder() .set(EHorizontalQuality.MEDIUM) .comment("" diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/RenderQualityPresetConfigEventHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/RenderQualityPresetConfigEventHandler.java index e0cd426bc..bc8808689 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/RenderQualityPresetConfigEventHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/RenderQualityPresetConfigEventHandler.java @@ -85,7 +85,7 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE this.put(EQualityPreset.HIGH, true); this.put(EQualityPreset.EXTREME, true); }}); - + //==============// 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 c10efa6a3..926a2e8b1 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 @@ -20,6 +20,7 @@ package com.seibel.distanthorizons.core.render.renderer.shaders; import com.seibel.distanthorizons.api.enums.config.EGpuUploadMethod; +import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.render.glObject.GLState; import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer; @@ -41,7 +42,6 @@ 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 float[] box_vertices = { -1, -1, 1, -1, @@ -52,15 +52,13 @@ 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; @@ -73,10 +71,11 @@ public class SSAORenderer { public int gProjUniform; public int gInvProjUniform; - public int gSampleRadUniform; - public int gFactorUniform; - public int gPowerUniform; - public int gKernelUniform; + public int gSampleCountUniform; + public int gRadiusUniform; + public int gStrengthUniform; + public int gMinLightUniform; + public int gBiasUniform; public int gDepthMapUniform; } @@ -86,10 +85,13 @@ public class SSAORenderer { public int gSSAOMapUniform; public int gDepthMapUniform; + public int gViewSizeUniform; + public int gBlurRadiusUniform; + public int gNearUniform; + public int gFarUniform; } - //=============// // constructor // //=============// @@ -98,43 +100,39 @@ 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", + this.applyShader = new ShaderProgram("shaders/normal.vert", "shaders/ssao/apply.frag", "fragColor", new String[]{"vPosition"}); - - // SSAO uniform setup this.ssaoShaderUniforms.gProjUniform = this.ssaoShader.getUniformLocation("gProj"); this.ssaoShaderUniforms.gInvProjUniform = this.ssaoShader.getUniformLocation("gInvProj"); - this.ssaoShaderUniforms.gSampleRadUniform = this.ssaoShader.getUniformLocation("gSampleRad"); - this.ssaoShaderUniforms.gFactorUniform = this.ssaoShader.getUniformLocation("gFactor"); - this.ssaoShaderUniforms.gPowerUniform = this.ssaoShader.getUniformLocation("gPower"); - this.ssaoShaderUniforms.gKernelUniform = this.ssaoShader.getUniformLocation("gKernel"); + this.ssaoShaderUniforms.gSampleCountUniform = this.ssaoShader.getUniformLocation("gSampleCount"); + this.ssaoShaderUniforms.gRadiusUniform = this.ssaoShader.getUniformLocation("gRadius"); + this.ssaoShaderUniforms.gStrengthUniform = this.ssaoShader.getUniformLocation("gStrength"); + this.ssaoShaderUniforms.gMinLightUniform = this.ssaoShader.getUniformLocation("gMinLight"); + this.ssaoShaderUniforms.gBiasUniform = this.ssaoShader.getUniformLocation("gBias"); this.ssaoShaderUniforms.gDepthMapUniform = this.ssaoShader.getUniformLocation("gDepthMap"); // Apply uniform setup this.applyShaderUniforms.gSSAOMapUniform = this.applyShader.getUniformLocation("gSSAOMap"); this.applyShaderUniforms.gDepthMapUniform = this.applyShader.getUniformLocation("gDepthMap"); + this.applyShaderUniforms.gViewSizeUniform = tryGetUniformLocation(this.applyShader, "gViewSize"); + this.applyShaderUniforms.gBlurRadiusUniform = tryGetUniformLocation(this.applyShader, "gBlurRadius"); + this.applyShaderUniforms.gNearUniform = tryGetUniformLocation(this.applyShader, "gNear"); + this.applyShaderUniforms.gFarUniform = tryGetUniformLocation(this.applyShader, "gFar"); - - - // Generate kernel - this.kernel = genKernel(); // Framebuffer this.createBuffer(); } @@ -158,53 +156,24 @@ public class SSAORenderer 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.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_R16F, width, height, 0, GL32.GL_RED, GL32.GL_HALF_FLOAT, (ByteBuffer) null); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR); 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); buffer.order(ByteOrder.nativeOrder()); buffer.asFloatBuffer().put(box_vertices); buffer.rewind(); + this.boxBuffer = new GLVertexBuffer(false); this.boxBuffer.bind(); this.boxBuffer.uploadBuffer(buffer, box_vertices.length, EGpuUploadMethod.DATA, box_vertices.length * Float.BYTES); } - private static float[] genKernel() - { - float[] kernel = new float[MAX_KERNEL_SIZE * 3]; - for (int i = 0; i < MAX_KERNEL_SIZE; i++) - { - float sampleX = (float) (Math.random() * 2.0 - 1.0); - float sampleY = (float) (Math.random() * 2.0 - 1.0); - float sampleZ = (float) Math.random(); - - - // Normalize - float magnitude = (float) Math.sqrt(Math.pow(sampleX, 2) + Math.pow(sampleY, 2) + Math.pow(sampleZ, 2)); - sampleX /= magnitude; - sampleY /= magnitude; - sampleZ /= magnitude; - - float scale = i / (float) MAX_KERNEL_SIZE; - float interpolatedScale = (float) (0.1 + (scale * scale) * (0.9)); - - sampleX *= interpolatedScale; - sampleY *= interpolatedScale; - sampleZ *= interpolatedScale; - kernel[i * 3] = sampleX; - kernel[i * 3 + 1] = sampleY; - kernel[i * 3 + 2] = sampleZ; - } - return kernel; - } - - //========// // render // @@ -213,7 +182,9 @@ public class SSAORenderer public void render(float partialTicks) { GLState state = new GLState(); + this.init(); + int width = MC_RENDER.getTargetFrameBufferViewportWidth(); int height = MC_RENDER.getTargetFrameBufferViewportHeight(); @@ -226,27 +197,36 @@ public class SSAORenderer 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.glDisable(GL32.GL_SCISSOR_TEST); + GL32.glDisable(GL32.GL_DEPTH_TEST); + GL32.glDisable(GL11.GL_BLEND); + float near = RenderUtil.getNearClipPlaneDistanceInBlocks(partialTicks); + float far = (float) ((RenderUtil.getFarClipPlaneDistanceInBlocks() + LodUtil.REGION_WIDTH) * Math.sqrt(2)); Mat4f perspective = Mat4f.perspective( (float) MC_RENDER.getFov(partialTicks), - MC_RENDER.getTargetFrameBufferViewportWidth() / (float) MC_RENDER.getTargetFrameBufferViewportHeight(), - RenderUtil.getNearClipPlaneDistanceInBlocks(partialTicks), - (float) ((RenderUtil.getFarClipPlaneDistanceInBlocks() + LodUtil.REGION_WIDTH) * Math.sqrt(2))); + width / (float) height, + near, far); Mat4f invertedPerspective = new Mat4f(perspective); invertedPerspective.invert(); + int sampleCount = Config.Client.Advanced.Graphics.Quality.ssaoSampleCount.get(); + int blurRadius = Config.Client.Advanced.Graphics.Quality.ssaoBlurRadius.get(); + float radius = Config.Client.Advanced.Graphics.Quality.ssaoRadius.get().floatValue(); + float strength = Config.Client.Advanced.Graphics.Quality.ssaoStrength.get().floatValue(); + float minLight = Config.Client.Advanced.Graphics.Quality.ssaoMinLight.get().floatValue(); + float bias = Config.Client.Advanced.Graphics.Quality.ssaoBias.get().floatValue(); 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.gSampleCountUniform, sampleCount); + this.ssaoShader.setUniform(this.ssaoShaderUniforms.gRadiusUniform, radius); + this.ssaoShader.setUniform(this.ssaoShaderUniforms.gStrengthUniform, strength); + this.ssaoShader.setUniform(this.ssaoShaderUniforms.gMinLightUniform, minLight); + this.ssaoShader.setUniform(this.ssaoShaderUniforms.gBiasUniform, bias); this.va.bind(); this.va.bindBufferToAllBindingPoint(this.boxBuffer.getId()); @@ -254,35 +234,52 @@ public class SSAORenderer GL32.glActiveTexture(GL32.GL_TEXTURE0); GL32.glBindTexture(GL32.GL_TEXTURE_2D, MC_RENDER.getDepthTextureId()); - GL32.glUniform3fv(this.ssaoShaderUniforms.gKernelUniform, this.kernel); GL32.glUniform1i(this.ssaoShaderUniforms.gDepthMapUniform, 0); 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.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.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.glUniform1i(this.applyShaderUniforms.gBlurRadiusUniform, blurRadius); + + if (this.applyShaderUniforms.gViewSizeUniform >= 0) + GL32.glUniform2f(this.applyShaderUniforms.gViewSizeUniform, width, height); + + if (this.applyShaderUniforms.gNearUniform >= 0) + GL32.glUniform1f(this.applyShaderUniforms.gNearUniform, near); + + if (this.applyShaderUniforms.gFarUniform >= 0) + GL32.glUniform1f(this.applyShaderUniforms.gFarUniform, far); GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, 6); - state.restore(); } + private int tryGetUniformLocation(ShaderProgram shader, String uniformName) + { + try { + return shader.getUniformLocation(uniformName); + } + catch (RuntimeException error) { + return -1; + } + } + public void free() { this.ssaoShader.free(); this.applyShader.free(); } - } diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index da6229faa..a0f378a23 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -98,6 +98,18 @@ "SSAO", "distanthorizons.config.client.advanced.graphics.quality.ssao.@tooltip": "Screen Space Ambient Occlusion adds depth to the lighting of blocks.", + "distanthorizons.config.client.advanced.graphics.quality.ssaoSampleCount": + "SSAO Sample Count", + "distanthorizons.config.client.advanced.graphics.quality.ssaoRadius": + "SSAO Radius", + "distanthorizons.config.client.advanced.graphics.quality.ssaoStrength": + "SSAO Strength", + "distanthorizons.config.client.advanced.graphics.quality.ssaoMinLight": + "SSAO Min Light", + "distanthorizons.config.client.advanced.graphics.quality.ssaoBias": + "SSAO Bias", + "distanthorizons.config.client.advanced.graphics.quality.ssaoBlurRadius": + "SSAO Blur Radius", "distanthorizons.config.client.advanced.graphics.quality.horizontalQuality": "Horizontal Quality", "distanthorizons.config.client.advanced.graphics.quality.horizontalQuality.@tooltip": diff --git a/core/src/main/resources/shaders/flat_shaded.frag b/core/src/main/resources/shaders/flat_shaded.frag index 28109a009..edcddbcca 100644 --- a/core/src/main/resources/shaders/flat_shaded.frag +++ b/core/src/main/resources/shaders/flat_shaded.frag @@ -2,7 +2,7 @@ in vec4 vertexColor; in vec3 vertexWorldPos; -in float vertexYPos; +//in float vertexYPos; in vec4 vPos; out vec4 fragColor; @@ -15,50 +15,19 @@ uniform float noiseIntensity; uniform int noiseDropoff; -// method definitions - -float fade(float value, float start, float end) { - return (clamp(value, start, end) - start) / (end - start); -} - // The random functions for diffrent dimentions float rand(float co) { return fract(sin(co*(91.3458)) * 47453.5453); } -float rand(vec2 co){ return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); } -float rand(vec3 co){ return rand(co.xy+rand(co.z)); } +float rand(vec2 co) { return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); } +float rand(vec3 co) { return rand(co.xy + rand(co.z)); } // Puts steps in a float // EG. setting stepSize to 4 then this would be the result of this function // In: 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, ..., 1.1, 1.2, 1.3 // Out: 0.0, 0.0, 0.0, 0.25, 0.25, 0.5, 0.5, ..., 1.0, 1.0, 1.25 -float quantize(float val, int stepSize) { - return floor(val*stepSize)/stepSize; +vec3 quantize(vec3 val, int stepSize) { + return floor(val * stepSize) / stepSize; } -// The modulus function dosnt exist in GLSL so I made my own -// To speed up the mod function, this only accepts full numbers for y -float mod(float x, int y) { - return x - y * floor(x/y); -} - - -// Some HSV functions I stole somewhere online -vec3 RGB2HSV(vec3 c) { - vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); - vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); - - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); -} - -vec3 HSV2RGB(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); -} - - /** * Fragment Shader @@ -71,66 +40,31 @@ void main() { fragColor = vertexColor; - // TODO: Move into its own function instead of in an if statement if (noiseEnabled) { - vec3 vertexNormal = normalize(cross(dFdx(vPos.xyz), dFdy(vPos.xyz))); + vec3 vertexNormal = normalize(cross(dFdy(vPos.xyz), dFdx(vPos.xyz))); // This bit of code is required to fix the vertex position problem cus of floats in the verted world position varuable - vec3 fixedVPos = vPos.xyz - vertexNormal * 0.001; + vec3 fixedVPos = vPos.xyz + vertexNormal * 0.001; float noiseAmplification = noiseIntensity * 0.01; - noiseAmplification = (-1.0 * pow(2.0*((fragColor.x + fragColor.y + fragColor.z) / 3.0) - 1.0, 2.0) + 1.0) * noiseAmplification; // Lessen the effect on depending on how dark the object is, equasion for this is -(2x-1)^{2}+1 - noiseAmplification *= fragColor.w; // The effect would lessen on transparent objects + float lum = (fragColor.r + fragColor.g + fragColor.b) / 3.0; + noiseAmplification = (1.0 - pow(lum * 2.0 - 1.0, 2.0)) * noiseAmplification; // Lessen the effect on depending on how dark the object is, equasion for this is -(2x-1)^{2}+1 + noiseAmplification *= fragColor.a; // The effect would lessen on transparent objects // Random value for each position - float randomValue = rand(vec3( - quantize(fixedVPos.x, noiseSteps), - quantize(fixedVPos.y, noiseSteps), - quantize(fixedVPos.z, noiseSteps) - )) - * 2.0 * noiseAmplification - noiseAmplification; - - + float randomValue = rand(quantize(fixedVPos, noiseSteps)) + * 2.0 * noiseAmplification - noiseAmplification; + // Modifies the color // A value of 0 on the randomValue will result in the original color, while a value of 1 will result in a fully bright color vec3 newCol = fragColor.rgb + (1.0 - fragColor.rgb) * randomValue; + newCol = clamp(newCol, 0.0, 1.0); + + if (noiseDropoff != 0) { + float distF = min(length(vertexWorldPos) / noiseDropoff, 1.0); + newCol = mix(newCol, fragColor.rgb, distF); // The further away it gets, the less noise gets applied + } - // Clamps it and turns it back into a vec4 - if (noiseDropoff == 0) - fragColor = vec4(clamp(newCol.rgb, 0.0, 1.0), fragColor.w); - else - fragColor = mix( - vec4(clamp(newCol.rgb, 0.0, 1.0), fragColor.w), fragColor, - min(length(vertexWorldPos) / noiseDropoff, 1.0) // The further away it gets, the less noise gets applied - ); - - // For testing - // if (fragColor.r != 69420.) { - // fragColor = vec4( - // mod(fixedVPos.x, 1), - // mod(fixedVPos.y, 1), - // mod(fixedVPos.z, 1), - // fragColor.w); - // } + fragColor.rgb = newCol; } } - - - -// Are these still needed? -float linearFog(float x, float fogStart, float fogLength, float fogMin, float fogRange) { - x = clamp((x-fogStart)/fogLength, 0.0, 1.0); - return fogMin + fogRange * x; -} - -float exponentialFog(float x, float fogStart, float fogLength, -float fogMin, float fogRange, float fogDensity) { - x = max((x-fogStart)/fogLength, 0.0) * fogDensity; - return fogMin + fogRange - fogRange/exp(x); -} - -float exponentialSquaredFog(float x, float fogStart, float fogLength, -float fogMin, float fogRange, float fogDensity) { - x = max((x-fogStart)/fogLength, 0.0) * fogDensity; - return fogMin + fogRange - fogRange/exp(x*x); -} \ No newline at end of file diff --git a/core/src/main/resources/shaders/fog/fog.frag b/core/src/main/resources/shaders/fog/fog.frag index 8e9f0767a..04eb3f7c1 100644 --- a/core/src/main/resources/shaders/fog/fog.frag +++ b/core/src/main/resources/shaders/fog/fog.frag @@ -42,23 +42,14 @@ float mixFogThickness(float near, float far, float height); // ========================================================= -// Puts steps in a float -// EG. setting stepSize to 4 then this would be the result of this function -// In: 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, ..., 1.1, 1.2, 1.3 -// Out: 0.0, 0.0, 0.0, 0.25, 0.25, 0.5, 0.5, ..., 1.0, 1.0, 1.25 -float quantize(float val, int stepSize) { - return floor(val*stepSize)/stepSize; +const vec3 MAGIC = vec3(0.06711056, 0.00583715, 52.9829189); + +float InterleavedGradientNoise(const in vec2 pixel) { + float x = dot(pixel, MAGIC.xy); + return fract(MAGIC.z * fract(x)); } -// The modulus function dosnt exist in GLSL so I made my own -// To speed up the mod function, this only accepts full numbers for y -float mod(float x, int y) { - return x - y * floor(x/y); -} - - -vec3 calcViewPosition(float fragmentDepth) -{ +vec3 calcViewPosition(float fragmentDepth) { vec4 ndc = vec4(TexCoord.xy, fragmentDepth, 1.0); ndc.xyz = ndc.xyz * 2.0 - 1.0; @@ -76,13 +67,12 @@ void main() { float vertexYPos = 100.0f; float fragmentDepth = texture(gDepthMap, TexCoord).r; + fragColor = vec4(fogColor.rgb, 0.0); // a fragment depth of "1" means the fragment wasn't drawn to, // we only want to apply Fog to LODs, not to the sky outside the LODs - if (fragmentDepth < 1) - { - if (fullFogMode == 0) - { + if (fragmentDepth < 1.0) { + if (fullFogMode == 0) { // render fog based on distance from the camera vec3 vertexWorldPos = calcViewPosition(fragmentDepth); @@ -94,17 +84,16 @@ void main() float farFogThickness = getFarFogThickness(farDist); float heightFogThickness = getHeightFogThickness(heightDist); float mixedFogThickness = mixFogThickness(nearFogThickness, farFogThickness, heightFogThickness); - mixedFogThickness = clamp(mixedFogThickness, 0.0, 1.0); + fragColor.a = clamp(mixedFogThickness, 0.0, 1.0); - fragColor = vec4(fogColor.rgb, mixedFogThickness); + float dither = InterleavedGradientNoise(gl_FragCoord.xy) - 0.5; + fragColor.a += dither / 255.0; } - else if (fullFogMode == 1) - { + else if (fullFogMode == 1) { // render everything with the fog color - fragColor = vec4(fogColor.rgb, 1.0); + fragColor.a = 1.0; } - else - { + else { // test code. // this can be fired by manually changing the fullFogMode to a (normally) @@ -112,19 +101,13 @@ void main() // a uniform we don't have to worry about GLSL optimizing away different // options when testing, causing a bunch of headaches if we just want to render the screen red. - float depthValue = texture(gDepthMap, TexCoord).r; - fragColor = vec4(vec3(depthValue), 1.0); // Convert depth value to grayscale color + float depthValue = textureLod(gDepthMap, TexCoord, 0).r; + fragColor.rgb = vec3(depthValue); // Convert depth value to grayscale color + fragColor.a = 1.0; } } - 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); - } } - - // Are these still needed? float linearFog(float x, float fogStart, float fogLength, float fogMin, float fogRange) { x = clamp((x-fogStart)/fogLength, 0.0, 1.0); @@ -132,13 +115,15 @@ float linearFog(float x, float fogStart, float fogLength, float fogMin, float fo } float exponentialFog(float x, float fogStart, float fogLength, -float fogMin, float fogRange, float fogDensity) { + float fogMin, float fogRange, float fogDensity) +{ x = max((x-fogStart)/fogLength, 0.0) * fogDensity; return fogMin + fogRange - fogRange/exp(x); } float exponentialSquaredFog(float x, float fogStart, float fogLength, -float fogMin, float fogRange, float fogDensity) { + float fogMin, float fogRange, float fogDensity) +{ x = max((x-fogStart)/fogLength, 0.0) * fogDensity; return fogMin + fogRange - fogRange/exp(x*x); -} \ No newline at end of file +} diff --git a/core/src/main/resources/shaders/noise/noise.frag b/core/src/main/resources/shaders/noise/noise.frag index 9ae668cbe..1156311fc 100644 --- a/core/src/main/resources/shaders/noise/noise.frag +++ b/core/src/main/resources/shaders/noise/noise.frag @@ -30,14 +30,6 @@ vec3 quantize(vec3 val, int stepSize) { return floor(val*stepSize)/stepSize; } -// The modulus function dosnt exist in GLSL so I made my own -// To speed up the mod function, this only accepts full numbers for y -float mod(float x, int y) { - return x - y * floor(x/y); -} - - - /** * Fragment shader for adding noise to lods. diff --git a/core/src/main/resources/shaders/ssao/ao.frag b/core/src/main/resources/shaders/ssao/ao.frag index fc48bcc05..149736efb 100644 --- a/core/src/main/resources/shaders/ssao/ao.frag +++ b/core/src/main/resources/shaders/ssao/ao.frag @@ -1,65 +1,110 @@ #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; uniform sampler2D gDepthMap; -uniform float gSampleRad; -uniform float gFactor; -uniform float gPower; -uniform mat4 gProj; +uniform int gSampleCount; +uniform float gRadius; +uniform float gStrength; +uniform float gMinLight; +uniform float gBias; uniform mat4 gInvProj; +uniform mat4 gProj; -const int MAX_KERNEL_SIZE = 128; -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]; +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 calcViewPosition(vec2 coords) { - float fragmentDepth = texture(gDepthMap, coords).r; - 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; +vec3 unproject(vec4 pos) { + return pos.xyz / pos.w; } -void main() -{ - vec3 viewPos = calcViewPosition(TexCoord); - vec3 viewNormal = normalize(cross(dFdy(viewPos.xyz), dFdx(viewPos.xyz)) * -1.0); +float InterleavedGradientNoise(const in vec2 pixel) { + float x = dot(pixel, MAGIC.xy); + return fract(MAGIC.z * fract(x)); +} - vec3 randomVec = vec3(0.0, -1.0, 0.0); +vec3 calcViewPosition(const in vec3 clipPos) { + vec4 viewPos = gInvProj * vec4(clipPos * 2.0 - 1.0, 1.0); + return viewPos.xyz / viewPos.w; +} - vec3 tangent = normalize(randomVec - viewNormal * dot(randomVec, viewNormal)); - vec3 bitangent = cross(viewNormal, tangent); - mat3 TBN = mat3(tangent, bitangent, viewNormal); - 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 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 = gRadius / gSampleCount; + + vec2 offset; + + float ao = 0.0; + int sampleCount = 0; + float radius = rStep; + for (int i = 0; i < clamp(gSampleCount, 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(gProj * vec4(sampleViewPos, 1.0)) * 0.5 + 0.5; + sampleClipPos = saturate(sampleClipPos); + + float sampleClipDepth = textureLod(gDepthMap, sampleClipPos.xy, 0.0).r; + if (sampleClipDepth >= 1.0 - EPSILON) continue; + + sampleClipPos.z = sampleClipDepth; + sampleViewPos = unproject(gInvProj * 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) - gBias, 0.0); + float aoF = 1.0 - saturate(sampleDist / gRadius); + ao += sampleNoLm * aoF; + sampleCount++; } - 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 + ao /= max(sampleCount, 1); + ao = smoothstep(0.0, gStrength, ao); + + return ao * (1.0 - gMinLight); +} + + +void main() { + float fragmentDepth = textureLod(gDepthMap, TexCoord, 0).r; + float occlusion = 0.0; + + // Do not apply to sky + if (fragmentDepth < 1.0) { + vec3 viewPos = calcViewPosition(vec3(TexCoord, fragmentDepth)); + + #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); + } + + fragColor = vec4(vec3(1.0 - occlusion), 1.0); +} 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); - } -} diff --git a/core/src/main/resources/shaders/ssao/apply.frag b/core/src/main/resources/shaders/ssao/apply.frag new file mode 100644 index 000000000..44026c96c --- /dev/null +++ b/core/src/main/resources/shaders/ssao/apply.frag @@ -0,0 +1,75 @@ +#version 150 core + +in vec2 TexCoord; + +out vec4 fragColor; + +uniform sampler2D gSSAOMap; +uniform sampler2D gDepthMap; +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(gSSAOMap, sampleTex, 0).r; + float sampleDepth = textureLod(gDepthMap, 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(gDepthMap, 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(gSSAOMap, ivec2(gl_FragCoord.xy), 0).r; + } + } +}