From 225c2df8df18a241aed931c81dd260ec55d44f8b Mon Sep 17 00:00:00 2001 From: James Seibel Date: Tue, 3 Mar 2026 07:47:58 -0600 Subject: [PATCH] add fog --- .../core/render/renderer/McLodRenderer.java | 14 +- .../render/renderer/shaders/FogShader.java | 4 +- .../render/IMcFogRenderer.java | 30 ++ .../distanthorizons/shaders/fog/apply.fsh | 27 ++ .../distanthorizons/shaders/fog/fog.fsh | 299 ++++++++++++++++++ .../shaders/fog/quad_apply.vsh | 15 + 6 files changed, 381 insertions(+), 8 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/render/IMcFogRenderer.java create mode 100644 core/src/main/resources/assets/distanthorizons/shaders/fog/apply.fsh create mode 100644 core/src/main/resources/assets/distanthorizons/shaders/fog/fog.fsh create mode 100644 core/src/main/resources/assets/distanthorizons/shaders/fog/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 fd95c07cf..701b42ca9 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 @@ -29,6 +29,7 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.render.DhApiRenderProxy; import com.seibel.distanthorizons.core.render.RenderBufferHandler; import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; +import com.seibel.distanthorizons.core.util.math.Mat4f; import com.seibel.distanthorizons.core.util.math.Vec3d; import com.seibel.distanthorizons.core.util.objects.SortedArraySet; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; @@ -148,6 +149,7 @@ public class McLodRenderer IMcLodRenderer lodRenderer = SingletonInjector.INSTANCE.get(IMcLodRenderer.class); IMcSsaoRenderer ssaoRenderer = SingletonInjector.INSTANCE.get(IMcSsaoRenderer.class); + IMcFogRenderer fogRenderer = SingletonInjector.INSTANCE.get(IMcFogRenderer.class); @@ -214,12 +216,12 @@ public class McLodRenderer // this is done to fix issues with: underwater fog, blindness effect, etc. || renderParams.vanillaFogEnabled) { - //profiler.popPush("LOD Fog"); - // - //Mat4f combinedMatrix = new Mat4f(renderParams.dhProjectionMatrix); - //combinedMatrix.multiply(renderParams.dhModelViewMatrix); - // - //FogRenderer.INSTANCE.render(combinedMatrix, renderParams.partialTicks); + profiler.popPush("LOD Fog"); + + Mat4f combinedMatrix = new Mat4f(renderParams.dhProjectionMatrix); + combinedMatrix.multiply(renderParams.dhModelViewMatrix); + + fogRenderer.render(combinedMatrix, renderParams.partialTicks); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java index 3f18283fe..3bc83af45 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java @@ -243,9 +243,9 @@ public class FogShader extends AbstractShaderRenderer return fogColor; } - public void setProjectionMatrix(Mat4f projectionMatrix) + public void setProjectionMatrix(Mat4f modelViewProjectionMatrix) { - this.inverseMvmProjMatrix = new Mat4f(projectionMatrix); + this.inverseMvmProjMatrix = new Mat4f(modelViewProjectionMatrix); this.inverseMvmProjMatrix.invert(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/render/IMcFogRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/render/IMcFogRenderer.java new file mode 100644 index 000000000..05a33cde9 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/render/IMcFogRenderer.java @@ -0,0 +1,30 @@ +/* + * 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.coreapi.interfaces.dependencyInjection.IBindable; + +public interface IMcFogRenderer extends IBindable +{ + + void render(DhApiMat4f modelViewProjectionMatrix, float partialTicks); + +} diff --git a/core/src/main/resources/assets/distanthorizons/shaders/fog/apply.fsh b/core/src/main/resources/assets/distanthorizons/shaders/fog/apply.fsh new file mode 100644 index 000000000..c9316b8ee --- /dev/null +++ b/core/src/main/resources/assets/distanthorizons/shaders/fog/apply.fsh @@ -0,0 +1,27 @@ +#version 150 core + +in vec2 TexCoord; + +out vec4 fragColor; + +uniform sampler2D uColorTexture; +uniform sampler2D uDhDepthTexture; + + +/** + * Fog application shader + * + * This merges the rendered fog onto DH's rendered LODs + */ +void main() +{ + fragColor = vec4(0.0); + + // a fragment depth of "1" means the fragment wasn't drawn to, + // only update fragments that were drawn to + float fragmentDepth = textureLod(uDhDepthTexture, TexCoord, 0).r; + if (fragmentDepth != 1) + { + fragColor = texture(uColorTexture, TexCoord); + } +} diff --git a/core/src/main/resources/assets/distanthorizons/shaders/fog/fog.fsh b/core/src/main/resources/assets/distanthorizons/shaders/fog/fog.fsh new file mode 100644 index 000000000..9b0614970 --- /dev/null +++ b/core/src/main/resources/assets/distanthorizons/shaders/fog/fog.fsh @@ -0,0 +1,299 @@ +#version 150 core + +in vec2 TexCoord; + +out vec4 fragColor; + +layout (std140) uniform fragUniformBlock +{ + // fog uniforms + vec4 uFogColor; + float uFogScale; + float uFogVerticalScale; + int uFogDebugMode; + int uFogFalloffType; + + // fog config + float uFarFogStart; + float uFarFogLength; + float uFarFogMin; + float uFarFogRange; + float uFarFogDensity; + + // height fog config + float uHeightFogStart; + float uHeightFogLength; + float uHeightFogMin; + float uHeightFogRange; + float uHeightFogDensity; + + // ??? + bool uHeightFogEnabled; + int uHeightFogFalloffType; + bool uHeightBasedOnCamera; + float uHeightFogBaseHeight; + bool uHeightFogAppliesUp; + bool uHeightFogAppliesDown; + bool uUseSphericalFog; + int uHeightFogMixingMode; + float uCameraBlockYPos; + + // inverted model view matrix and projection matrix + mat4 uInvMvmProj; +}; + +uniform sampler2D uDhDepthTexture; + + + +//====================// +// method definitions // +//====================// + +vec3 calcViewPosition(float fragmentDepth); + +float getFarFogThickness(float dist); +float getHeightFogThickness(float dist); +float calculateHeightFogDepth(float worldYPos); +float mixFogThickness(float far, float height); + +float linearFog(float worldDist, float fogStart, float fogLength, float fogMin, float fogRange); +float exponentialFog(float x, float fogStart, float fogLength, float fogMin, float fogRange, float fogDensity); +float exponentialSquaredFog(float x, float fogStart, float fogLength, float fogMin, float fogRange, float fogDensity); + + + +//======// +// main // +//======// + +/** + * Fragment shader for fog. + * This should be run last so it applies above other affects like Ambient Occlusioning + */ +void main() +{ + float fragmentDepth = texture(uDhDepthTexture, TexCoord).r; + fragColor = vec4(uFogColor.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.0) + { + int fogDebugMode = uFogDebugMode; + if (fogDebugMode == 0) + { + // render fog based on distance from the camera + vec3 vertexWorldPos = calcViewPosition(fragmentDepth); + + float horizontalWorldDistance = length(vertexWorldPos.xz) * uFogScale; + float worldDistance = length(vertexWorldPos.xyz) * uFogScale; + float activeDistance = uUseSphericalFog ? worldDistance : horizontalWorldDistance; + + + // far fog + float farFogThickness = getFarFogThickness(activeDistance); + + // height fog + float heightFogDepth = calculateHeightFogDepth(vertexWorldPos.y); + float heightFogThickness = getHeightFogThickness(heightFogDepth); + + // combined fog + float mixedFogThickness = mixFogThickness(farFogThickness, heightFogThickness); + fragColor.a = clamp(mixedFogThickness, 0.0, 1.0); + } + else if (fogDebugMode == 1) + { + // test code + + // render everything with the fog color + fragColor.a = 1.0; + } + else + { + // test code. + + // this can be fired by manually changing the fullFogMode to a (normally) + // invalid value (like 7). + // By having a separate if statement defined by + // 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 = textureLod(uDhDepthTexture, TexCoord, 0).r; + fragColor.rgb = vec3(depthValue); // Convert depth value to grayscale color + fragColor.a = 1.0; + } + } +} + + + +//================// +// helper methods // +//================// + +vec3 calcViewPosition(float fragmentDepth) +{ + vec4 ndc = vec4(TexCoord.xy, fragmentDepth, 1.0); + ndc.xyz = ndc.xyz * 2.0 - 1.0; + + vec4 eyeCoord = uInvMvmProj * ndc; + return eyeCoord.xyz / eyeCoord.w; +} + + + +//=========// +// far fog // +//=========// + +float getFarFogThickness(float dist) +{ + if (uFogFalloffType == 0) // LINEAR + { + return linearFog(dist, uFarFogStart, uFarFogLength, uFarFogMin, uFarFogRange); + } + else if (uFogFalloffType == 1) // EXPONENTIAL + { + return exponentialFog(dist, uFarFogStart, uFarFogLength, uFarFogMin, uFarFogRange, uFarFogDensity); + } + else // EXPONENTIAL_SQUARED + { + return exponentialSquaredFog(dist, uFarFogStart, uFarFogLength, uFarFogMin, uFarFogRange, uFarFogDensity); + } +} + +float getHeightFogThickness(float dist) +{ + if (!uHeightFogEnabled) + { + return 0.0; + } + + if (uHeightFogFalloffType == 0) // LINEAR + { + return linearFog(dist, uHeightFogStart, uHeightFogLength, uHeightFogMin, uHeightFogRange); + } + else if (uHeightFogFalloffType == 1) // EXPONENTIAL + { + return exponentialFog(dist, uHeightFogStart, uHeightFogLength, uHeightFogMin, uHeightFogRange, uHeightFogDensity); + } + else // EXPONENTIAL_SQUARED + { + return exponentialSquaredFog(dist, uHeightFogStart, uHeightFogLength, uHeightFogMin, uHeightFogRange, uHeightFogDensity); + } +} + +float linearFog(float worldDist, float fogStart, float fogLength, float fogMin, float fogRange) +{ + worldDist = (worldDist - fogStart) / fogLength; + worldDist = clamp(worldDist, 0.0, 1.0); + return fogMin + fogRange * worldDist; +} + +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); +} + + + +//============// +// height fog // +//============// + +/** 1 = full fog, 0 = no fog */ +float calculateHeightFogDepth(float worldYPos) +{ + // worldYPos -65 - 384 + + + //worldYPos = worldYPos * -1; // negative, fog below height; positive, fog above height + //return worldYPos * uFogVerticalScale; // "* uFogVerticalScale" is done to convert world position to a percent of the world height; + + if (!uHeightFogEnabled) + { + // ignore the height + return 0.0; + } + + + if (!uHeightBasedOnCamera) + { + worldYPos -= (uHeightFogBaseHeight - uCameraBlockYPos); + } + + + if (uHeightFogAppliesDown && uHeightFogAppliesUp) + { + return abs(worldYPos) * uFogVerticalScale; + } + else if (uHeightFogAppliesDown) + { + // apploy fog below given height + return -worldYPos * uFogVerticalScale; + } + else if (uHeightFogAppliesUp) + { + // apply fog above given height + return worldYPos * uFogVerticalScale; + } + else + { + // shouldn't happen, + return 0.0; + } + +} + +float mixFogThickness(float far, float height) +{ + switch (uHeightFogMixingMode) + { + case 0: // BASIC + case 1: // IGNORE_HEIGHT + return far; + + case 2: // MAX + return max(far, height); + + case 3: // ADDITION + return (far + height); + + case 4: // MULTIPLY + return far * height; + + case 5: // INVERSE_MULTIPLY + return (1.0 - (1.0-far)*(1.0-height)); + + case 6: // LIMITED_ADDITION + return (far + max(far, height)); + + case 7: // MULTIPLY_ADDITION + return (far + far*height); + + case 8: // INVERSE_MULTIPLY_ADDITION + return (far + 1.0 - (1.0-far)*(1.0-height)); + + case 9: // AVERAGE + return (far*0.5 + height*0.5); + } + + // shouldn't happen, but default to BASIC / IGNORE_HEIGHT + // if an invalid option is selected + return far; +} + + + diff --git a/core/src/main/resources/assets/distanthorizons/shaders/fog/quad_apply.vsh b/core/src/main/resources/assets/distanthorizons/shaders/fog/quad_apply.vsh new file mode 100644 index 000000000..3f614c123 --- /dev/null +++ b/core/src/main/resources/assets/distanthorizons/shaders/fog/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