From 41238fddb07056dea8474c178f4d8cebef38a7cc Mon Sep 17 00:00:00 2001 From: DIvan2000 Date: Sat, 1 Aug 2026 18:06:41 +0400 Subject: [PATCH] =?UTF-8?q?=D0=A2=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=BE=D0=B8=D1=82=D1=81=D1=8F=20worldinfo=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=B1=D1=83=D0=B4=D1=83=D1=8E=D1=89=D0=B5=D0=B9?= =?UTF-8?q?=20=D1=81=D0=B8=D0=BC=D1=83=D0=BB=D1=8F=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/render/fog/FogBlockProperties.java | 49 ++++++ .../client/render/fog/FogBlockRegistry.java | 96 +++++++++++ .../client/render/fog/FogChunkProvider.java | 30 ++-- .../client/render/fog/FogChunkStreamer.java | 4 +- .../client/render/fog/FogWorldVolume.java | 153 ++++-------------- .../resources/assets/veila/shaders/red.frag | 10 +- 6 files changed, 195 insertions(+), 147 deletions(-) create mode 100644 src/client/java/su/divan2000/veila/client/render/fog/FogBlockProperties.java create mode 100644 src/client/java/su/divan2000/veila/client/render/fog/FogBlockRegistry.java diff --git a/src/client/java/su/divan2000/veila/client/render/fog/FogBlockProperties.java b/src/client/java/su/divan2000/veila/client/render/fog/FogBlockProperties.java new file mode 100644 index 0000000..0b5f023 --- /dev/null +++ b/src/client/java/su/divan2000/veila/client/render/fog/FogBlockProperties.java @@ -0,0 +1,49 @@ +package su.divan2000.veila.client.render.fog; + +public record FogBlockProperties( + boolean solid, + int sign, + int magnitude +) { + public static final FogBlockProperties DEFAULT = new FogBlockProperties(false, 0, 0); + + public FogBlockProperties { + if (sign < -1 || sign > 1) { + throw new IllegalArgumentException("sign must be -1, 0, or 1"); + } + if (magnitude < 0 || magnitude > 63) { + throw new IllegalArgumentException("magnitude must be 0-63"); + } + } + + /** + * Упаковывает свойства в один байт: + * Бит 7: solid (1 = solid) + * Бит 6: sign (0 = source/neutral, 1 = absorber) + * Биты 0-5: magnitude (0-63) + */ + public byte pack() { + int packed = 0; + + if (solid) { + packed |= 0b10000000; + } + + if (sign < 0) { + packed |= 0b01000000; + } + + packed |= (magnitude & 0b00111111); + + return (byte) packed; + } + + public static FogBlockProperties unpack(byte packed) { + boolean solid = (packed & 0b10000000) != 0; + boolean isAbsorber = (packed & 0b01000000) != 0; + int magnitude = packed & 0b00111111; + int sign = isAbsorber ? -1 : (magnitude > 0 ? 1 : 0); + + return new FogBlockProperties(solid, sign, magnitude); + } +} \ No newline at end of file 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 new file mode 100644 index 0000000..b533bbf --- /dev/null +++ b/src/client/java/su/divan2000/veila/client/render/fog/FogBlockRegistry.java @@ -0,0 +1,96 @@ +package su.divan2000.veila.client.render.fog; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.fluid.Fluids; +import net.minecraft.registry.Registries; +import net.minecraft.registry.tag.BlockTags; + +public final class FogBlockRegistry { + + private static final byte[] BLOCK_CACHE; + private static final boolean[] BLOCK_CACHE_INITIALIZED; + + // Предвычисленное значение для waterlogged блоков (вода) + private static final byte WATER_VALUE; + + static { + int blockCount = Registries.BLOCK.size(); + BLOCK_CACHE = new byte[blockCount]; + BLOCK_CACHE_INITIALIZED = new boolean[blockCount]; + + // Waterlogged блок всегда содержит воду + WATER_VALUE = new FogBlockProperties(true, 1, 60).pack(); + } + + private FogBlockRegistry() { + } + + public static byte getPackedProperties(BlockState state) { + // СНАЧАЛА проверяем конкретно воду (waterlogged или обычный блок воды) + if (state.getFluidState().getFluid() == Fluids.WATER) { + return WATER_VALUE; + } + + Block block = state.getBlock(); + int id = Registries.BLOCK.getRawId(block); + + if (BLOCK_CACHE_INITIALIZED[id]) { + return BLOCK_CACHE[id]; + } + + byte packed = computePackedProperties(state); + BLOCK_CACHE[id] = packed; + BLOCK_CACHE_INITIALIZED[id] = true; + + return packed; + } + + private static byte computePackedProperties(BlockState state) { + Block block = state.getBlock(); + boolean solid = computeSolid(state); + + // Источники тумана (sign = +1) + // WATER уже обработан в getPackedProperties + if (block == Blocks.GRASS || block == Blocks.TALL_GRASS || block == Blocks.FERN) { + return new FogBlockProperties(solid, 1, 10).pack(); + } + if (block == Blocks.VINE) { + return new FogBlockProperties(solid, 1, 5).pack(); + } + + // Поглотители тумана (sign = -1) + if (block == Blocks.LAVA) { + return new FogBlockProperties(solid, -1, 50).pack(); + } + if (block == Blocks.MAGMA_BLOCK) { + return new FogBlockProperties(solid, -1, 40).pack(); + } + if (block == Blocks.FIRE || block == Blocks.SOUL_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.TORCH || block == Blocks.WALL_TORCH || + block == Blocks.SOUL_TORCH || block == Blocks.SOUL_WALL_TORCH) { + return new FogBlockProperties(solid, -1, 15).pack(); + } + if (block == Blocks.LANTERN || block == Blocks.SOUL_LANTERN) { + return new FogBlockProperties(solid, -1, 10).pack(); + } + + // Нейтральные блоки (sign = 0) + return new FogBlockProperties(solid, 0, 0).pack(); + } + + @SuppressWarnings("deprecation") + private static boolean computeSolid(BlockState state) { + if (state.isAir()) return false; + // Waterlogged уже обработан в getPackedProperties + if (!state.getFluidState().isEmpty()) return true; // лава и другие жидкости + if (state.isIn(BlockTags.LEAVES)) return false; + return state.blocksMovement(); + } +} \ No newline at end of file diff --git a/src/client/java/su/divan2000/veila/client/render/fog/FogChunkProvider.java b/src/client/java/su/divan2000/veila/client/render/fog/FogChunkProvider.java index af41635..529f148 100644 --- a/src/client/java/su/divan2000/veila/client/render/fog/FogChunkProvider.java +++ b/src/client/java/su/divan2000/veila/client/render/fog/FogChunkProvider.java @@ -20,17 +20,7 @@ public final class FogChunkProvider { return client.world.getChunkManager().getWorldChunk(chunkX, chunkZ, false) != null; } - public static float computeVoxelValue(BlockState state) { - if (state.isOf(Blocks.POPPY)) { - return 0.2f; - } else if (state.isOf(Blocks.GRASS)) { - return 0.4f; - } else { - return 0.0f; - } - } - - public static boolean fillChunkIntoArray(int chunkX, int chunkZ, float[] array) { + public static boolean fillChunkIntoArray(int chunkX, int chunkZ, byte[] array) { MinecraftClient client = MinecraftClient.getInstance(); if (!isChunkLoaded(chunkX, chunkZ)) return false; @@ -42,10 +32,9 @@ public final class FogChunkProvider { int topYExclusive = bottomY + sections.length * 16; int index = 0; + byte airValue = FogBlockRegistry.getPackedProperties(Blocks.AIR.getDefaultState()); - // Порядок циклов Z -> Y -> X критичен для glTexSubImage3D for (int z = 0; z < FogWorldVolume.CHUNK_SIZE; z++) { - // Кэшируем секции и их состояние int currentSectionIndex = -1; ChunkSection currentSection = null; PalettedContainer currentContainer = null; @@ -54,9 +43,8 @@ public final class FogChunkProvider { for (int y = 0; y < FogWorldVolume.SIZE_Y; y++) { int worldY = FogWorldVolume.MIN_Y + y; - // За пределами мира (например, ниже -64) if (worldY < bottomY || worldY >= topYExclusive) { - Arrays.fill(array, index, index + FogWorldVolume.CHUNK_SIZE, 0.0f); + Arrays.fill(array, index, index + FogWorldVolume.CHUNK_SIZE, airValue); index += FogWorldVolume.CHUNK_SIZE; continue; } @@ -76,25 +64,25 @@ public final class FogChunkProvider { int localY = worldY & 15; - // ОПТИМИЗАЦИЯ: Если текущая секция полностью пустая (только воздух) if (currentIsEmpty) { - Arrays.fill(array, index, index + FogWorldVolume.CHUNK_SIZE, 0.0f); + Arrays.fill(array, index, index + FogWorldVolume.CHUNK_SIZE, airValue); index += FogWorldVolume.CHUNK_SIZE; continue; } - // Предварительное вычисление базового индекса для строки Y и Z - // Формула локального индекса в секции 16x16x16: (y << 8) | (z << 4) | x int baseIndex = (localY << 8) | (z << 4); for (int x = 0; x < FogWorldVolume.CHUNK_SIZE; x++) { BlockState currentBlock = currentContainer.get(baseIndex | x); - - array[index++] = computeVoxelValue(currentBlock); + array[index++] = FogBlockRegistry.getPackedProperties(currentBlock); } } } return true; } + + public static byte computePackedVoxelValue(BlockState state) { + return FogBlockRegistry.getPackedProperties(state); + } } \ No newline at end of file diff --git a/src/client/java/su/divan2000/veila/client/render/fog/FogChunkStreamer.java b/src/client/java/su/divan2000/veila/client/render/fog/FogChunkStreamer.java index 9b407c1..aaac3ff 100644 --- a/src/client/java/su/divan2000/veila/client/render/fog/FogChunkStreamer.java +++ b/src/client/java/su/divan2000/veila/client/render/fog/FogChunkStreamer.java @@ -28,7 +28,7 @@ public final class FogChunkStreamer { private static final ChunkStatus[] statuses = new ChunkStatus[TOTAL_CHUNKS]; private static final boolean[] readyToUpload = new boolean[TOTAL_CHUNKS]; - private static final float[][] preparedData = new float[TOTAL_CHUNKS][CHUNK_DATA_SIZE]; + private static final byte[][] preparedData = new byte[TOTAL_CHUNKS][CHUNK_DATA_SIZE]; private static final int[] preparedWorldChunkX = new int[TOTAL_CHUNKS]; private static final int[] preparedWorldChunkZ = new int[TOTAL_CHUNKS]; @@ -175,7 +175,7 @@ public final class FogChunkStreamer { } // Вычисляем значение для этого вокселя - float value = FogChunkProvider.computeVoxelValue(event.newState); + byte value = FogChunkProvider.computePackedVoxelValue(event.newState()); // Обновляем один воксель в текстуре FogWorldVolume.updateSingleVoxel( diff --git a/src/client/java/su/divan2000/veila/client/render/fog/FogWorldVolume.java b/src/client/java/su/divan2000/veila/client/render/fog/FogWorldVolume.java index bcc94c1..a379bc1 100644 --- a/src/client/java/su/divan2000/veila/client/render/fog/FogWorldVolume.java +++ b/src/client/java/su/divan2000/veila/client/render/fog/FogWorldVolume.java @@ -3,18 +3,12 @@ package su.divan2000.veila.client.render.fog; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; -import org.lwjgl.opengl.GL13; import org.lwjgl.opengl.GL30; import java.nio.ByteBuffer; -import java.nio.FloatBuffer; public final class FogWorldVolume { - /* - * Размер всего объёма - */ - public static final int SIZE_X = 256; public static final int SIZE_Z = 256; @@ -22,156 +16,81 @@ public final class FogWorldVolume { public static final int MAX_Y = 320; public static final int SIZE_Y = MAX_Y - MIN_Y; - /* - * Размер одного чанка - */ - public static final int CHUNK_SIZE = 16; public static final int CHUNKS_X = SIZE_X / CHUNK_SIZE; public static final int CHUNKS_Z = SIZE_Z / CHUNK_SIZE; - /* - * OpenGL - */ - private static int texture = -1; - /* - * Буфер для одного чанка - * - * 16 × 384 × 16 - */ - - private static final float[] ZEROS_ARRAY = new float[CHUNK_SIZE * SIZE_Y * CHUNK_SIZE]; + private static final byte[] ZEROS_ARRAY; static { - java.util.Arrays.fill(ZEROS_ARRAY, 0.0f); + ZEROS_ARRAY = new byte[CHUNK_SIZE * SIZE_Y * CHUNK_SIZE]; + byte airValue = new FogBlockProperties(false, 0, 0).pack(); + java.util.Arrays.fill(ZEROS_ARRAY, airValue); } - private static final FloatBuffer CHUNK_BUFFER = - BufferUtils.createFloatBuffer( - CHUNK_SIZE * - SIZE_Y * - CHUNK_SIZE - ); + private static final ByteBuffer CHUNK_BUFFER = + BufferUtils.createByteBuffer(CHUNK_SIZE * SIZE_Y * CHUNK_SIZE); - - private static final FloatBuffer SINGLE_VOXEL_BUFFER = - BufferUtils.createFloatBuffer(1); + private static final ByteBuffer SINGLE_VOXEL_BUFFER = + BufferUtils.createByteBuffer(1); private FogWorldVolume() { } public static void init() { - - if (texture != -1) - return; + if (texture != -1) return; texture = GL11.glGenTextures(); - GL11.glBindTexture( - GL12.GL_TEXTURE_3D, - texture - ); + GL11.glBindTexture(GL12.GL_TEXTURE_3D, texture); GL30.glTexImage3D( GL12.GL_TEXTURE_3D, 0, - GL30.GL_R16F, + GL30.GL_R8UI, SIZE_X, SIZE_Y, SIZE_Z, 0, - GL11.GL_RED, - GL11.GL_FLOAT, + GL30.GL_RED_INTEGER, + GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null ); - GL11.glTexParameteri( - GL12.GL_TEXTURE_3D, - GL11.GL_TEXTURE_MIN_FILTER, - GL11.GL_LINEAR - ); + GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST); + GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST); - GL11.glTexParameteri( - GL12.GL_TEXTURE_3D, - GL11.GL_TEXTURE_MAG_FILTER, - GL11.GL_LINEAR - ); + GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL12.GL_TEXTURE_WRAP_S, GL12.GL_REPEAT); + GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL12.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE); + GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL12.GL_TEXTURE_WRAP_R, GL12.GL_REPEAT); - GL11.glTexParameteri( - GL12.GL_TEXTURE_3D, - GL12.GL_TEXTURE_WRAP_S, - GL12.GL_REPEAT - ); - - GL11.glTexParameteri( - GL12.GL_TEXTURE_3D, - GL12.GL_TEXTURE_WRAP_T, - GL12.GL_CLAMP_TO_EDGE - ); - - GL11.glTexParameteri( - GL12.GL_TEXTURE_3D, - GL12.GL_TEXTURE_WRAP_R, - GL12.GL_REPEAT - ); - - GL11.glBindTexture( - GL12.GL_TEXTURE_3D, - 0 - ); + GL11.glBindTexture(GL12.GL_TEXTURE_3D, 0); clear(); } public static void clear() { - CHUNK_BUFFER.clear(); - - int size = - CHUNK_SIZE * - SIZE_Y * - CHUNK_SIZE; - - for (int i = 0; i < size; i++) - CHUNK_BUFFER.put(0.0f); - + CHUNK_BUFFER.put(ZEROS_ARRAY); CHUNK_BUFFER.flip(); - GL11.glBindTexture( - GL12.GL_TEXTURE_3D, - texture - ); + GL11.glBindTexture(GL12.GL_TEXTURE_3D, texture); for (int chunkZ = 0; chunkZ < CHUNKS_Z; chunkZ++) { - for (int chunkX = 0; chunkX < CHUNKS_X; chunkX++) { - - uploadChunk( - chunkX, - chunkZ, - CHUNK_BUFFER - ); - + uploadChunk(chunkX, chunkZ, CHUNK_BUFFER); CHUNK_BUFFER.rewind(); } } - GL11.glBindTexture( - GL12.GL_TEXTURE_3D, - 0 - ); + GL11.glBindTexture(GL12.GL_TEXTURE_3D, 0); } - public static void uploadChunk( - int textureChunkX, - int textureChunkZ, - FloatBuffer data - ) { + public static void uploadChunk(int textureChunkX, int textureChunkZ, ByteBuffer data) { GL11.glBindTexture(GL12.GL_TEXTURE_3D, texture); - // Сбрасываем ВСЕ pixel store параметры, которые мог оставить Minecraft GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); GL11.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 0); GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, 0); @@ -180,17 +99,14 @@ public final class FogWorldVolume { GL30.glTexSubImage3D( GL12.GL_TEXTURE_3D, 0, - textureChunkX * CHUNK_SIZE, 0, textureChunkZ * CHUNK_SIZE, - CHUNK_SIZE, SIZE_Y, CHUNK_SIZE, - - GL11.GL_RED, - GL11.GL_FLOAT, + GL30.GL_RED_INTEGER, + GL11.GL_UNSIGNED_BYTE, data ); @@ -203,7 +119,7 @@ public final class FogWorldVolume { int localX, int localY, int localZ, - float value + byte value ) { GL11.glBindTexture(GL12.GL_TEXTURE_3D, texture); @@ -223,8 +139,8 @@ public final class FogWorldVolume { localY, textureChunkZ * CHUNK_SIZE + localZ, 1, 1, 1, - GL11.GL_RED, - GL11.GL_FLOAT, + GL30.GL_RED_INTEGER, + GL11.GL_UNSIGNED_BYTE, SINGLE_VOXEL_BUFFER ); @@ -232,7 +148,7 @@ public final class FogWorldVolume { } public static void uploadZeros(int textureChunkX, int textureChunkZ) { - FloatBuffer buffer = chunkBuffer(); + ByteBuffer buffer = chunkBuffer(); buffer.put(ZEROS_ARRAY); buffer.flip(); uploadChunk(textureChunkX, textureChunkZ, buffer); @@ -241,23 +157,20 @@ public final class FogWorldVolume { public static void uploadChunkFromArray( int textureChunkX, int textureChunkZ, - float[] data + byte[] data ) { - FloatBuffer buffer = chunkBuffer(); + ByteBuffer buffer = chunkBuffer(); buffer.put(data); buffer.flip(); uploadChunk(textureChunkX, textureChunkZ, buffer); } - public static FloatBuffer chunkBuffer() { - + public static ByteBuffer chunkBuffer() { CHUNK_BUFFER.clear(); - return CHUNK_BUFFER; } public static int getTexture() { return texture; } - } \ No newline at end of file diff --git a/src/client/resources/assets/veila/shaders/red.frag b/src/client/resources/assets/veila/shaders/red.frag index ecddfd7..5923076 100644 --- a/src/client/resources/assets/veila/shaders/red.frag +++ b/src/client/resources/assets/veila/shaders/red.frag @@ -2,7 +2,7 @@ uniform sampler2D DiffuseSampler; uniform sampler2D DepthSampler; -uniform sampler3D FogVolume; +uniform usampler3D FogVolume; uniform mat4 InverseProjection; uniform mat4 InverseView; @@ -23,7 +23,7 @@ out vec4 FragColor; const float FOG_SIZE_X = 256.0; const float FOG_SIZE_Z = FOG_SIZE_X; -const int MAX_STEPS = 256; // Уменьшили количество шагов благодаря адаптивности +const int MAX_STEPS = 256; // Высота мира const float WORLD_BOTTOM = -64.0; @@ -39,7 +39,7 @@ const float MAX_DISTANCE = (FOG_SIZE_X / 2.0) - 18.0; const float GAMMA = 2.0; // Включение jittering для устранения banding артефактов -const bool USE_JITTERING = true; +const bool USE_JITTERING = false; //------------------------------------------------------------ @@ -103,7 +103,9 @@ float density(vec3 worldPos) // Псевдослучайное значение для jittering (детерминированное по UV) float random(vec2 st) { - return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123); + vec3 p3 = fract(vec3(st.xyx) * 0.1031); + p3 += dot(p3, p3.yzx + 33.33); + return fract((p3.x + p3.y) * p3.z); } // Функция нелинейного маппинга параметра t [0,1] в расстояние