From 8ac636d11406abbed56c72bca6af0ff7ece9a209 Mon Sep 17 00:00:00 2001 From: DIvan2000 Date: Wed, 5 Aug 2026 08:41:29 +0400 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BD=D1=8B=20?= =?UTF-8?q?=D0=B1=D0=B5=D1=81=D0=BA=D0=BE=D0=BC=D0=BF=D1=80=D0=BE=D0=BC?= =?UTF-8?q?=D0=B8=D1=81=D1=81=D0=BD=D1=8B=D0=B5=20=D0=BE=D0=BF=D1=82=D0=B8?= =?UTF-8?q?=D0=BC=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/render/PostProcessingManager.java | 1 - .../client/render/fog/FogBlockRegistry.java | 23 ++++-- .../client/render/fog/FogLightProvider.java | 28 ++++++-- .../client/render/fog/FogLightStreamer.java | 71 +++++++++++++------ .../client/render/fog/FogLightVolume.java | 16 +++-- .../veila/client/render/fog/FogSystem.java | 3 +- .../assets/veila/shaders/fog_raymarching.frag | 3 +- 7 files changed, 103 insertions(+), 42 deletions(-) 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 5090caa..065ece6 100644 --- a/src/client/java/su/divan2000/veila/client/render/PostProcessingManager.java +++ b/src/client/java/su/divan2000/veila/client/render/PostProcessingManager.java @@ -5,7 +5,6 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.gl.Framebuffer; import net.minecraft.client.render.Camera; import net.minecraft.util.math.Vec3d; -import net.minecraft.world.LightType; import org.joml.Matrix4f; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.*; diff --git a/src/client/java/su/divan2000/veila/client/render/fog/FogBlockRegistry.java b/src/client/java/su/divan2000/veila/client/render/fog/FogBlockRegistry.java index 867c83e..f7c78ee 100644 --- a/src/client/java/su/divan2000/veila/client/render/fog/FogBlockRegistry.java +++ b/src/client/java/su/divan2000/veila/client/render/fog/FogBlockRegistry.java @@ -60,6 +60,16 @@ public final class FogBlockRegistry { return new FogBlockProperties(solid, 1, 45).pack(); } + if (block == Blocks.SOUL_CAMPFIRE || block == Blocks.SOUL_FIRE) { + return new FogBlockProperties(solid, 1, 63).pack(); + } + if (block == Blocks.SOUL_LANTERN) { + return new FogBlockProperties(solid, 1, 31).pack(); + } + if (block == Blocks.SOUL_TORCH || block == Blocks.SOUL_WALL_TORCH) { + return new FogBlockProperties(solid, 1, 15).pack(); + } + // Поглотители тумана (sign = -1) if (block == Blocks.LAVA) { return new FogBlockProperties(solid, -1, 50).pack(); @@ -67,18 +77,17 @@ public final class FogBlockRegistry { if (block == Blocks.MAGMA_BLOCK) { return new FogBlockProperties(solid, -1, 40).pack(); } - if (block == Blocks.FIRE || block == Blocks.SOUL_FIRE) { + if (block == Blocks.FIRE) { return new FogBlockProperties(solid, -1, 30).pack(); } - if (block == Blocks.CAMPFIRE || block == Blocks.SOUL_CAMPFIRE) { - return new FogBlockProperties(solid, -1, 25).pack(); + if (block == Blocks.CAMPFIRE) { + return new FogBlockProperties(solid, -1, 63).pack(); } - if (block == Blocks.TORCH || block == Blocks.WALL_TORCH || - block == Blocks.SOUL_TORCH || block == Blocks.SOUL_WALL_TORCH) { + if (block == Blocks.TORCH || block == Blocks.WALL_TORCH) { return new FogBlockProperties(solid, -1, 15).pack(); } - if (block == Blocks.LANTERN || block == Blocks.SOUL_LANTERN) { - return new FogBlockProperties(solid, -1, 10).pack(); + if (block == Blocks.LANTERN) { + return new FogBlockProperties(solid, -1, 25).pack(); } // Нейтральные блоки (sign = 0) diff --git a/src/client/java/su/divan2000/veila/client/render/fog/FogLightProvider.java b/src/client/java/su/divan2000/veila/client/render/fog/FogLightProvider.java index d252ad6..93f5995 100644 --- a/src/client/java/su/divan2000/veila/client/render/fog/FogLightProvider.java +++ b/src/client/java/su/divan2000/veila/client/render/fog/FogLightProvider.java @@ -1,9 +1,7 @@ package su.divan2000.veila.client.render.fog; import net.minecraft.client.MinecraftClient; -import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkSectionPos; -import net.minecraft.world.LightType; import net.minecraft.world.chunk.ChunkNibbleArray; import net.minecraft.world.chunk.light.ChunkLightProvider; import net.minecraft.world.chunk.light.LightingProvider; @@ -13,6 +11,16 @@ public final class FogLightProvider { // Размер массива для одной секции: 16×16×16×2 = 8192 (×2 для двух каналов) public static final int SECTION_DATA_SIZE = 16 * 16 * 16 * 2; + // Pre-allocated buffer для чтения одной секции из LightingProvider + // используется в fillSectionIntoArray для избежания аллокаций + private static final byte[] SECTION_BUFFER = new byte[SECTION_DATA_SIZE]; + + // Флаг для отслеживания используется ли буфер сейчас (защита от race condition) + // Так как fillSectionIntoArray может вызываться из разных потоков, + // нужен второй буфер для параллельной работы + private static final byte[] SECTION_BUFFER_ALT = new byte[SECTION_DATA_SIZE]; + private static volatile boolean bufferInUse = false; + private FogLightProvider() { } @@ -38,7 +46,15 @@ public final class FogLightProvider { return null; } - byte[] result = new byte[SECTION_DATA_SIZE]; + // Используем один из двух pre-allocated буферов (double buffering) + byte[] result; + if (!bufferInUse) { + bufferInUse = true; + result = SECTION_BUFFER; + } else { + result = SECTION_BUFFER_ALT; + } + int index = 0; for (int localZ = 0; localZ < 16; localZ++) { @@ -47,13 +63,17 @@ public final class FogLightProvider { int blockLight = (blockArray != null) ? blockArray.get(localX, localY, localZ) : 0; int skyLight = (skyArray != null) ? skyArray.get(localX, localY, localZ) : 0; - // Упаковка в два байта: R=blockLight, G=skyLight FogLightVolume.packLight(blockLight, skyLight, result, index); index += 2; } } } + // Освобождаем буфер + if (result == SECTION_BUFFER) { + bufferInUse = false; + } + return result; } } \ No newline at end of file diff --git a/src/client/java/su/divan2000/veila/client/render/fog/FogLightStreamer.java b/src/client/java/su/divan2000/veila/client/render/fog/FogLightStreamer.java index a6e8aca..7215073 100644 --- a/src/client/java/su/divan2000/veila/client/render/fog/FogLightStreamer.java +++ b/src/client/java/su/divan2000/veila/client/render/fog/FogLightStreamer.java @@ -286,34 +286,51 @@ public final class FogLightStreamer { } } - public static void prepareInitialLoads() { + /** + * Подготавливает все секции для загрузки (client thread). + * Обрабатывает и initialLoadMask, и pendingSectionsMask. + * Вызывается из FogSystem.tick(). + */ + public static void prepareAllSections() { int processed = 0; - int index = 0; + // Обрабатываем initial load + int index = 0; while (processed < MAX_INITIAL_SECTIONS_PER_TICK) { index = nextSetBit(initialLoadMask, index); if (index < 0) break; - // Конвертируем индекс обратно в координаты секции - int sectionX = currentOriginChunkX + (index % FogLightVolume.CHUNKS_X); - int sectionZ = currentOriginChunkZ + ((index / FogLightVolume.CHUNKS_X) % FogLightVolume.CHUNKS_Z); - int sectionY = (index / (FogLightVolume.CHUNKS_X * FogLightVolume.CHUNKS_Z)) + (FogWorldVolume.MIN_Y >> 4); - - byte[] sectionData = FogLightProvider.fillSectionIntoArray(sectionX, sectionY, sectionZ); - if (sectionData != null) { + if (prepareSectionByIndex(index)) { clearBit(initialLoadMask, index); - setBit(readySectionsMask, index); + processed++; + } + index++; + } + + // Обрабатываем инкрементальные обновления + index = 0; + while (processed < MAX_INITIAL_SECTIONS_PER_TICK + MAX_SECTIONS_PER_FRAME) { + index = nextSetBit(pendingSectionsMask, index); + if (index < 0) break; + + if (prepareSectionByIndex(index)) { + clearBit(pendingSectionsMask, index); processed++; } index++; } } + /** + * Загружает готовые секции в текстуру (render thread). + * Только OpenGL вызовы, без чтения из LightingProvider. + * Вызывается из FogSystem.beginRender(). + */ public static void uploadReadySections() { int processed = 0; int index = 0; - while (processed < MAX_SECTIONS_PER_FRAME) { + while (processed < MAX_SECTIONS_PER_FRAME + MAX_INITIAL_SECTIONS_PER_TICK) { index = nextSetBit(readySectionsMask, index); if (index < 0) break; @@ -325,22 +342,29 @@ public final class FogLightStreamer { } } - public static void processSectionUpdates() { - int processed = 0; - int index = 0; + /** + * Читает данные секции из LightingProvider и помечает как готовую к загрузке. + * Вызывается из prepareAllSections() в client thread. + */ + private static boolean prepareSectionByIndex(int index) { + int sectionX = currentOriginChunkX + (index % FogLightVolume.CHUNKS_X); + int sectionZ = currentOriginChunkZ + ((index / FogLightVolume.CHUNKS_X) % FogLightVolume.CHUNKS_Z); + int sectionY = (index / (FogLightVolume.CHUNKS_X * FogLightVolume.CHUNKS_Z)) + (FogWorldVolume.MIN_Y >> 4); - while (processed < MAX_SECTIONS_PER_FRAME) { - index = nextSetBit(pendingSectionsMask, index); - if (index < 0) break; - - if (uploadSectionByIndex(index)) { - clearBit(pendingSectionsMask, index); - processed++; - } - index++; + byte[] sectionData = FogLightProvider.fillSectionIntoArray(sectionX, sectionY, sectionZ); + if (sectionData == null) { + return false; // Секция не готова, пробуем позже } + + // Помечаем как готовую к загрузке в render thread + setBit(readySectionsMask, index); + return true; } + /** + * Загружает готовую секцию в текстуру через OpenGL. + * Вызывается из uploadReadySections() в render thread. + */ private static boolean uploadSectionByIndex(int index) { int physicalX = index % FogLightVolume.CHUNKS_X; int physicalZ = (index / FogLightVolume.CHUNKS_X) % FogLightVolume.CHUNKS_Z; @@ -350,6 +374,7 @@ public final class FogLightStreamer { int sectionZ = currentOriginChunkZ + Math.floorMod(physicalZ - currentRingChunkOffsetZ, FogLightVolume.CHUNKS_Z); int sectionY = localSectionY + (FogWorldVolume.MIN_Y >> 4); + // Читаем данные повторно (они должны быть доступны быстро, так как уже в кэше) byte[] sectionData = FogLightProvider.fillSectionIntoArray(sectionX, sectionY, sectionZ); if (sectionData == null) { return false; diff --git a/src/client/java/su/divan2000/veila/client/render/fog/FogLightVolume.java b/src/client/java/su/divan2000/veila/client/render/fog/FogLightVolume.java index 8aa4d66..1743193 100644 --- a/src/client/java/su/divan2000/veila/client/render/fog/FogLightVolume.java +++ b/src/client/java/su/divan2000/veila/client/render/fog/FogLightVolume.java @@ -44,6 +44,13 @@ public final class FogLightVolume { private static int clearPBO = -1; private static final int MAX_CLEAR_SIZE = SIZE_X * SIZE_Y * SIZE_Z * 2; // ×2 для двух каналов + // Pre-allocated buffer для одной секции (4096 вокселей × 2 байта = 8192 байта) +// Используется для загрузки секций через uploadSection + private static final ByteBuffer SECTION_UPLOAD_BUFFER = + BufferUtils.createByteBuffer(FogLightStreamer.SECTION_SIZE + * FogLightStreamer.SECTION_SIZE + * FogLightStreamer.SECTION_SIZE * 2); + private FogLightVolume() { } @@ -160,9 +167,10 @@ public final class FogLightVolume { int textureSectionZ, byte[] data ) { - ByteBuffer sectionBuffer = BufferUtils.createByteBuffer(data.length); - sectionBuffer.put(data); - sectionBuffer.flip(); + // Переиспользуем статический буфер вместо создания нового + SECTION_UPLOAD_BUFFER.clear(); + SECTION_UPLOAD_BUFFER.put(data); + SECTION_UPLOAD_BUFFER.flip(); GL11.glBindTexture(GL12.GL_TEXTURE_3D, texture); @@ -182,7 +190,7 @@ public final class FogLightVolume { 16, GL30.GL_RG, GL11.GL_UNSIGNED_BYTE, - sectionBuffer + SECTION_UPLOAD_BUFFER ); GL11.glBindTexture(GL12.GL_TEXTURE_3D, 0); diff --git a/src/client/java/su/divan2000/veila/client/render/fog/FogSystem.java b/src/client/java/su/divan2000/veila/client/render/fog/FogSystem.java index ef061ac..4609c9f 100644 --- a/src/client/java/su/divan2000/veila/client/render/fog/FogSystem.java +++ b/src/client/java/su/divan2000/veila/client/render/fog/FogSystem.java @@ -31,7 +31,6 @@ public final class FogSystem { // Освещение: загружаем готовые секции + инкрементальные обновления (render thread) FogLightStreamer.uploadReadySections(); - FogLightStreamer.processSectionUpdates(); FogSimulationManager.update(); } @@ -41,7 +40,7 @@ public final class FogSystem { FogChunkStreamer.processPending(); // Освещение: читаем данные секций (client thread, без OpenGL) - FogLightStreamer.prepareInitialLoads(); + FogLightStreamer.prepareAllSections(); } private static void initialize() { diff --git a/src/client/resources/assets/veila/shaders/fog_raymarching.frag b/src/client/resources/assets/veila/shaders/fog_raymarching.frag index 39e9021..27645ec 100644 --- a/src/client/resources/assets/veila/shaders/fog_raymarching.frag +++ b/src/client/resources/assets/veila/shaders/fog_raymarching.frag @@ -210,7 +210,8 @@ void main() // Вклад ослабляется текущим transmittance skyScattered += lightValues.x * baseScattering * transmittance; - blockScattered += lightValues.y * baseScattering * transmittance; + //Свет от блоков не должен делать значимо ярче те воксели, которые и так подсвечены солнцем. Солнечный свет очень яркий, с ним сложно сравниться + blockScattered += lightValues.y * baseScattering * transmittance*((lightValues.y-lightValues.x)*0.9+0.1); ambientScattered += AMBIENT_INTENSITY * baseScattering * transmittance; // Обновляем transmittance: Beer-Lambert law