diff --git a/src/client/java/su/divan2000/veila/client/render/PostProcessingManager.java b/src/client/java/su/divan2000/veila/client/render/PostProcessingManager.java index 1dd88ea..0dd3353 100644 --- a/src/client/java/su/divan2000/veila/client/render/PostProcessingManager.java +++ b/src/client/java/su/divan2000/veila/client/render/PostProcessingManager.java @@ -48,6 +48,8 @@ public class PostProcessingManager { private static int rm_inverseProjectionLocation; private static int rm_depthSamplerLocation; private static int rm_fogVolumeLocation; + private static int rm_prevFogVolumeLocation; + private static int rm_fogInterpolationLocation; private static int rm_skyLightVolumeLocation; private static int rm_blockLightVolumeLocation; private static int rm_fogOriginLocation; @@ -106,6 +108,8 @@ public class PostProcessingManager { rm_inverseProjectionLocation = raymarchProgram.uniform("InverseProjection"); rm_depthSamplerLocation = raymarchProgram.uniform("DepthSampler"); rm_fogVolumeLocation = raymarchProgram.uniform("FogVolume"); + rm_prevFogVolumeLocation = raymarchProgram.uniform("PrevFogVolume"); + rm_fogInterpolationLocation = raymarchProgram.uniform("FogInterpolation"); rm_skyLightVolumeLocation = raymarchProgram.uniform("SkyLightVolume"); rm_blockLightVolumeLocation = raymarchProgram.uniform("BlockLightVolume"); rm_fogOriginLocation = raymarchProgram.uniform("FogOrigin"); @@ -280,14 +284,18 @@ public class PostProcessingManager { GL11.glBindTexture(GL12.GL_TEXTURE_3D, FogSimulationManager.getCurrentDensityTexture()); GL20.glUniform1i(rm_fogVolumeLocation, 1); - // Light Volume GL13.glActiveTexture(GL13.GL_TEXTURE2); - GL11.glBindTexture(GL12.GL_TEXTURE_3D, FogLightVolume.getSkyLightTexture()); - GL20.glUniform1i(rm_skyLightVolumeLocation, 2); + GL11.glBindTexture(GL12.GL_TEXTURE_3D, FogSimulationManager.getPreviousDensityTexture()); + GL20.glUniform1i(rm_prevFogVolumeLocation, 2); + // Light Volume GL13.glActiveTexture(GL13.GL_TEXTURE3); + GL11.glBindTexture(GL12.GL_TEXTURE_3D, FogLightVolume.getSkyLightTexture()); + GL20.glUniform1i(rm_skyLightVolumeLocation, 3); + + GL13.glActiveTexture(GL13.GL_TEXTURE4); GL11.glBindTexture(GL12.GL_TEXTURE_3D, FogLightVolume.getBlockLightTexture()); - GL20.glUniform1i(rm_blockLightVolumeLocation, 3); + GL20.glUniform1i(rm_blockLightVolumeLocation, 4); GL20.glUniform2i(rm_fogOriginLocation, FogSystem.originChunkX() * FogWorldVolume.CHUNK_SIZE, @@ -314,6 +322,7 @@ public class PostProcessingManager { // Цвет тумана GL20.glUniform3f(rm_fogColorLocation, fogR, fogG, fogB); + GL20.glUniform1f(rm_fogInterpolationLocation, FogSimulationManager.getFogInterpolationAlpha()); // Яркость неба GL20.glUniform1f(rm_skyBrightnessLocation, client.world.getSkyBrightness(tickDelta)); diff --git a/src/client/java/su/divan2000/veila/client/render/fog/FogDensityTextures.java b/src/client/java/su/divan2000/veila/client/render/fog/FogDensityTextures.java index 352cede..742a944 100644 --- a/src/client/java/su/divan2000/veila/client/render/fog/FogDensityTextures.java +++ b/src/client/java/su/divan2000/veila/client/render/fog/FogDensityTextures.java @@ -14,7 +14,11 @@ public final class FogDensityTextures { private static int textureA = -1; private static int textureB = -1; - private static int readIndex = 0; + private static int textureC = -1; + private static int currentIndex = 0; + private static int previousIndex = 0; + private static int writeIndex = 1; + private static boolean hasPreviousState = false; // PBO для быстрой очистки регионов (half float нули) private static int clearPBO = -1; @@ -54,6 +58,11 @@ public final class FogDensityTextures { textureA = createDensityTexture(); textureB = createDensityTexture(); + textureC = createDensityTexture(); + currentIndex = 0; + previousIndex = 0; + writeIndex = 1; + hasPreviousState = false; } private static int createDensityTexture() { @@ -87,15 +96,45 @@ public final class FogDensityTextures { } public static void swap() { - readIndex = 1 - readIndex; + previousIndex = currentIndex; + currentIndex = writeIndex; + + // Выбираем свободный буфер для следующей записи + writeIndex = 0; + while (writeIndex == currentIndex || writeIndex == previousIndex) { + writeIndex++; + } + + hasPreviousState = true; } public static int getReadTexture() { - return readIndex == 0 ? textureA : textureB; + return indexToTexture(currentIndex); + } + + public static int getPreviousTexture() { + return indexToTexture(previousIndex); } public static int getWriteTexture() { - return readIndex == 0 ? textureB : textureA; + return indexToTexture(writeIndex); + } + + public static boolean hasPreviousState() { + return hasPreviousState; + } + + private static int indexToTexture(int index) { + switch (index) { + case 0: + return textureA; + case 1: + return textureB; + case 2: + return textureC; + default: + throw new IllegalStateException("Unexpected fog density texture index: " + index); + } } /** @@ -108,6 +147,7 @@ public final class FogDensityTextures { ) { clearRegionsInTexture(textureA, physX, physZ, widthChunks, depthChunks, count); clearRegionsInTexture(textureB, physX, physZ, widthChunks, depthChunks, count); + clearRegionsInTexture(textureC, physX, physZ, widthChunks, depthChunks, count); } private static void clearRegionsInTexture( diff --git a/src/client/java/su/divan2000/veila/client/render/fog/FogSimulationManager.java b/src/client/java/su/divan2000/veila/client/render/fog/FogSimulationManager.java index 9887580..8f20aba 100644 --- a/src/client/java/su/divan2000/veila/client/render/fog/FogSimulationManager.java +++ b/src/client/java/su/divan2000/veila/client/render/fog/FogSimulationManager.java @@ -7,8 +7,9 @@ public final class FogSimulationManager { private static boolean initialized = false; private static long lastSimulationTime = 0; + private static long lastSwapTimeNs = 0; - // Симулируем каждые 100 мс (10 раз в секунду) + // Симулируем каждые 300 мс (~3.3 раз в секунду) private static final long SIMULATION_INTERVAL_NS = 300_000_000L; private static final long TARGET_STEP_DURATION_NS = 300_000_000L; private static final long FRAME_TIME_WINDOW_NS = 100_000_000L; @@ -119,6 +120,7 @@ public final class FogSimulationManager { long stepDuration = System.nanoTime() - simulationStartedAtNs; recordCompletedStep(stepDuration); FogDensityTextures.swap(); + lastSwapTimeNs = System.nanoTime(); simulationActive = false; } } @@ -231,6 +233,25 @@ public final class FogSimulationManager { return FogDensityTextures.getReadTexture(); } + public static int getPreviousDensityTexture() { + return FogDensityTextures.getPreviousTexture(); + } + + public static float getFogInterpolationAlpha() { + if (!FogDensityTextures.hasPreviousState()) { + return 1.0f; + } + + long currentTime = System.nanoTime(); + long elapsed = currentTime - lastSwapTimeNs; + if (elapsed <= 0) { + return 0.0f; + } + + float alpha = (float) elapsed / (float) SIMULATION_INTERVAL_NS; + return Math.min(alpha, 1.0f); + } + public static void setGlobalEmissionMultiplier(float value) { globalEmissionMultiplier = value; } diff --git a/src/client/resources/assets/veila/shaders/fog_raymarching.frag b/src/client/resources/assets/veila/shaders/fog_raymarching.frag index fc7caba..8acf6e4 100644 --- a/src/client/resources/assets/veila/shaders/fog_raymarching.frag +++ b/src/client/resources/assets/veila/shaders/fog_raymarching.frag @@ -2,6 +2,7 @@ uniform sampler2D DepthSampler; uniform sampler3D FogVolume; +uniform sampler3D PrevFogVolume; uniform sampler3D SkyLightVolume; uniform sampler3D BlockLightVolume; @@ -11,6 +12,7 @@ uniform mat4 InverseView; uniform vec3 FogColor; uniform vec3 CameraPosition; +uniform float FogInterpolation; uniform float SkyBrightness; uniform ivec2 FogOrigin; @@ -97,7 +99,9 @@ float density(vec3 worldPos) { vec3 uv = worldToUV(worldPos); if (uv.x < 0.0) return 0.0; - return texture(FogVolume, uv).r; + float currentDensity = texture(FogVolume, uv).r; + float prevDensity = texture(PrevFogVolume, uv).r; + return mix(prevDensity, currentDensity, FogInterpolation); } vec2 light(vec3 worldPos)