первый коммит, мусор в 3д текстуре

This commit is contained in:
2026-07-30 18:52:46 +04:00
commit 15f484a39b
29 changed files with 2717 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
package su.divan2000.veila.client;
import net.fabricmc.api.ClientModInitializer;
public class VeilaClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
}
}

View File

@@ -0,0 +1,12 @@
package su.divan2000.veila.client;
import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint;
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
public class VeilaDataGenerator implements DataGeneratorEntrypoint {
@Override
public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) {
FabricDataGenerator.Pack pack = fabricDataGenerator.createPack();
}
}

View File

@@ -0,0 +1,111 @@
package su.divan2000.veila.client.gl;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;
import java.nio.ByteBuffer;
public class DepthCopy {
private static int texture = -1;
private static int width = -1;
private static int height = -1;
public static void init(int newWidth, int newHeight) {
if (texture != -1 &&
width == newWidth &&
height == newHeight) {
return;
}
if (texture != -1) {
GL11.glDeleteTextures(texture);
}
texture = GL11.glGenTextures();
GL11.glBindTexture(
GL11.GL_TEXTURE_2D,
texture
);
GL11.glTexImage2D(
GL11.GL_TEXTURE_2D,
0,
GL30.GL_DEPTH_COMPONENT32F,
newWidth,
newHeight,
0,
GL11.GL_DEPTH_COMPONENT,
GL11.GL_FLOAT,
(ByteBuffer)null
);
GL11.glTexParameteri(
GL11.GL_TEXTURE_2D,
GL11.GL_TEXTURE_MIN_FILTER,
GL11.GL_NEAREST
);
GL11.glTexParameteri(
GL11.GL_TEXTURE_2D,
GL11.GL_TEXTURE_MAG_FILTER,
GL11.GL_NEAREST
);
GL11.glBindTexture(
GL11.GL_TEXTURE_2D,
0
);
width = newWidth;
height = newHeight;
System.out.println(
"Depth texture resized: "
+ width + "x" + height
);
}
public static void copy(int width, int height) {
GL11.glBindTexture(
GL11.GL_TEXTURE_2D,
texture
);
GL11.glCopyTexSubImage2D(
GL11.GL_TEXTURE_2D,
0,
0,
0,
0,
0,
width,
height
);
GL11.glBindTexture(
GL11.GL_TEXTURE_2D,
0
);
}
public static int getTexture() {
return texture;
}
}

View File

@@ -0,0 +1,75 @@
package su.divan2000.veila.client.gl;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import java.nio.FloatBuffer;
import static org.lwjgl.opengl.GL11.glGetInteger;
import static org.lwjgl.opengl.GL30.GL_VERTEX_ARRAY_BINDING;
public final class FullscreenQuad {
private static int vao = 0;
private static int vbo = 0;
public static void init() {
if (vao != 0)
return;
float[] vertices = {
-1f, -1f,
1f, -1f,
1f, 1f,
-1f, -1f,
1f, 1f,
-1f, 1f
};
FloatBuffer buffer = BufferUtils.createFloatBuffer(vertices.length);
buffer.put(vertices);
buffer.flip();
vao = GL30.glGenVertexArrays();
vbo = GL15.glGenBuffers();
int previous = glGetInteger(GL_VERTEX_ARRAY_BINDING);
GL30.glBindVertexArray(vao);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
GL20.glEnableVertexAttribArray(0);
GL20.glVertexAttribPointer(
0,
2,
GL11.GL_FLOAT,
false,
2 * Float.BYTES,
0
);
GL30.glBindVertexArray(previous);
}
public static void draw() {
int previous = glGetInteger(GL_VERTEX_ARRAY_BINDING);
GL30.glBindVertexArray(vao);
GL11.glDrawArrays(
GL11.GL_TRIANGLES,
0,
6
);
GL30.glBindVertexArray(previous);
}
}

View File

@@ -0,0 +1,55 @@
package su.divan2000.veila.client.gl;
import org.lwjgl.opengl.GL20;
public class GLProgram {
private final int id;
public GLProgram(GLShader vertex, GLShader fragment) {
id = GL20.glCreateProgram();
GL20.glBindAttribLocation(id, 0, "aPos");
GL20.glAttachShader(id, vertex.id());
GL20.glAttachShader(id, fragment.id());
GL20.glLinkProgram(id);
if (GL20.glGetProgrami(id, GL20.GL_LINK_STATUS) == GL20.GL_FALSE) {
throw new RuntimeException(
"Program link failed:\n" +
GL20.glGetProgramInfoLog(id)
);
}
}
public void bind() {
GL20.glUseProgram(id);
}
public void unbind() {
GL20.glUseProgram(0);
}
public int id() {
return id;
}
public void delete() {
GL20.glDeleteProgram(id);
}
public int uniform(String name) {
return GL20.glGetUniformLocation(id, name);
}
public void uniform1i(String name, int value) {
GL20.glUniform1i(uniform(name), value);
}
public void uniform1f(String name, float value) {
GL20.glUniform1f(uniform(name), value);
}
}

View File

@@ -0,0 +1,30 @@
package su.divan2000.veila.client.gl;
import org.lwjgl.opengl.GL20;
public class GLShader {
private final int id;
public GLShader(int type, String source) {
id = GL20.glCreateShader(type);
GL20.glShaderSource(id, source);
GL20.glCompileShader(id);
if (GL20.glGetShaderi(id, GL20.GL_COMPILE_STATUS) == GL20.GL_FALSE) {
throw new RuntimeException(
"Shader compile failed:\n" +
GL20.glGetShaderInfoLog(id)
);
}
}
public int id() {
return id;
}
public void delete() {
GL20.glDeleteShader(id);
}
}

View File

@@ -0,0 +1,34 @@
package su.divan2000.veila.client.gl;
import net.minecraft.client.MinecraftClient;
import net.minecraft.resource.Resource;
import net.minecraft.util.Identifier;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
public final class ResourceUtil {
private ResourceUtil() {
}
public static String load(String path) {
Identifier id = new Identifier("veila", path);
try {
Resource resource = MinecraftClient.getInstance()
.getResourceManager()
.getResource(id)
.orElseThrow();
try (InputStream in = resource.getInputStream()) {
return new String(in.readAllBytes(), StandardCharsets.UTF_8);
}
} catch (IOException e) {
throw new RuntimeException("Cannot load shader " + id, e);
}
}
}

View File

@@ -0,0 +1,282 @@
package su.divan2000.veila.client.render;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gl.Framebuffer;
import net.minecraft.client.render.Camera;
import net.minecraft.util.math.Vec3d;
import org.joml.Matrix4f;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.*;
import su.divan2000.veila.client.gl.DepthCopy;
import su.divan2000.veila.client.gl.FullscreenQuad;
import su.divan2000.veila.client.gl.GLProgram;
import su.divan2000.veila.client.gl.GLShader;
import su.divan2000.veila.client.gl.ResourceUtil;
import su.divan2000.veila.client.render.fog.FogSystem;
import su.divan2000.veila.client.render.fog.FogWorldVolume;
import java.nio.FloatBuffer;
public class PostProcessingManager {
private static GLProgram program;
/*
* Матрицы
*/
private static final Matrix4f projection = new Matrix4f();
private static final Matrix4f inverseProjection = new Matrix4f();
private static final FloatBuffer projectionBuffer =
BufferUtils.createFloatBuffer(16);
private static final FloatBuffer inverseProjectionBuffer =
BufferUtils.createFloatBuffer(16);
/*
* Uniform locations
*/
private static int projectionLocation;
private static int inverseProjectionLocation;
private static int diffuseSamplerLocation;
private static int depthSamplerLocation;
private static int cameraPositionLocation;
private static int screenSizeLocation;
private static final Matrix4f view = new Matrix4f();
private static final Matrix4f inverseView = new Matrix4f();
private static final FloatBuffer viewBuffer =
BufferUtils.createFloatBuffer(16);
private static final FloatBuffer inverseViewBuffer =
BufferUtils.createFloatBuffer(16);
private static int viewLocation;
private static int inverseViewLocation;
private static int fogVolumeLocation;
private static int fogOriginLocation;
private static int ringOffsetLocation;
public static void init() {
if (program != null)
return;
GLShader vertex = new GLShader(
GL20.GL_VERTEX_SHADER,
ResourceUtil.load("shaders/fullscreen.vert")
);
GLShader fragment = new GLShader(
GL20.GL_FRAGMENT_SHADER,
ResourceUtil.load("shaders/red.frag")
);
program = new GLProgram(vertex, fragment);
projectionLocation = program.uniform("Projection");
inverseProjectionLocation = program.uniform("InverseProjection");
diffuseSamplerLocation = program.uniform("DiffuseSampler");
depthSamplerLocation = program.uniform("DepthSampler");
fogVolumeLocation = program.uniform("FogVolume");
fogOriginLocation = program.uniform("FogOrigin");
ringOffsetLocation = program.uniform("RingBlockOffset");
cameraPositionLocation = program.uniform("CameraPosition");
screenSizeLocation = program.uniform("ScreenSize");
viewLocation = program.uniform("View");
inverseViewLocation = program.uniform("InverseView");
FullscreenQuad.init();
vertex.delete();
fragment.delete();
System.out.println("VEILA shader compiled!");
}
public static void render() {
if (program == null)
return;
MinecraftClient client = MinecraftClient.getInstance();
Framebuffer framebuffer = client.getFramebuffer();
int width = framebuffer.textureWidth;
int height = framebuffer.textureHeight;
GL30.glBindFramebuffer(
GL30.GL_READ_FRAMEBUFFER,
framebuffer.fbo
);
DepthCopy.init(width, height);
DepthCopy.copy(width, height);
program.bind();
/*
* Diffuse
*/
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(
GL11.GL_TEXTURE_2D,
framebuffer.getColorAttachment()
);
GL20.glUniform1i(diffuseSamplerLocation, 0);
/*
* Depth
*/
GL13.glActiveTexture(GL13.GL_TEXTURE1);
GL11.glBindTexture(
GL11.GL_TEXTURE_2D,
DepthCopy.getTexture()
);
GL20.glUniform1i(depthSamplerLocation, 1);
GL13.glActiveTexture(GL13.GL_TEXTURE2);
GL11.glBindTexture(
GL12.GL_TEXTURE_3D,
FogWorldVolume.getTexture()
);
GL20.glUniform1i(
fogVolumeLocation,
2
);
GL20.glUniform2i(
fogOriginLocation,
FogSystem.originChunkX() * FogWorldVolume.CHUNK_SIZE,
FogSystem.originChunkZ() * FogWorldVolume.CHUNK_SIZE
);
GL20.glUniform2i(
ringOffsetLocation,
FogSystem.ringChunkOffsetX() * FogWorldVolume.CHUNK_SIZE,
FogSystem.ringChunkOffsetZ() * FogWorldVolume.CHUNK_SIZE
);
/*
* Матрицы
*/
updateProjectionMatrices();
GL20.glUniformMatrix4fv(
projectionLocation,
false,
projectionBuffer
);
GL20.glUniformMatrix4fv(
inverseProjectionLocation,
false,
inverseProjectionBuffer
);
GL20.glUniformMatrix4fv(
viewLocation,
false,
viewBuffer
);
GL20.glUniformMatrix4fv(
inverseViewLocation,
false,
inverseViewBuffer
);
/*
* Камера
*/
Camera camera = client.gameRenderer.getCamera();
Vec3d pos = camera.getPos();
GL20.glUniform3f(
cameraPositionLocation,
(float) pos.x,
(float) pos.y,
(float) pos.z
);
/*
* Размер экрана
*/
GL20.glUniform2f(
screenSizeLocation,
width,
height
);
FullscreenQuad.draw();
GL13.glActiveTexture(GL13.GL_TEXTURE2);
GL11.glBindTexture(GL12.GL_TEXTURE_3D, 0);
GL13.glActiveTexture(GL13.GL_TEXTURE1);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
program.unbind();
GL30.glBindFramebuffer(
GL30.GL_READ_FRAMEBUFFER,
0
);
GL13.glActiveTexture(GL13.GL_TEXTURE0);
}
private static void updateProjectionMatrices() {
projection.set(RenderSystem.getProjectionMatrix());
inverseProjection
.set(projection)
.invert();
projectionBuffer.clear();
projection.get(projectionBuffer);
inverseProjectionBuffer.clear();
inverseProjection.get(inverseProjectionBuffer);
}
public static void updateViewMatrix(Matrix4f matrix) {
view.set(matrix);
inverseView
.set(matrix)
.invert();
viewBuffer.clear();
view.get(viewBuffer);
inverseViewBuffer.clear();
inverseView.get(inverseViewBuffer);
}
}

View File

@@ -0,0 +1,58 @@
package su.divan2000.veila.client.render.fog;
import java.nio.FloatBuffer;
public final class FogChunkProvider {
static void fillChunk(
int chunkX,
int chunkZ,
FloatBuffer buffer
) {
float density =
hash(chunkX, chunkZ);
for (int z = 0;
z < FogWorldVolume.CHUNK_SIZE;
z++) {
for (int y = 0;
y < FogWorldVolume.SIZE_Y;
y++) {
int worldY =
FogWorldVolume.MIN_Y + y;
float value =
worldY < 70
? density
: 0.0f;
for (int x = 0;
x < FogWorldVolume.CHUNK_SIZE;
x++) {
buffer.put(value);
}
}
}
}
private static float hash(
int x,
int z
) {
int h = x * 73428767;
h ^= z * 912931;
h ^= h >> 13;
h *= 1274126177;
return ((h >>> 24) & 255)
/ 255.0f
* 0.08f;
}
}

View File

@@ -0,0 +1,182 @@
package su.divan2000.veila.client.render.fog;
import java.nio.FloatBuffer;
import static su.divan2000.veila.client.render.fog.FogWorldVolume.CHUNK_SIZE;
import static su.divan2000.veila.client.render.fog.FogWorldVolume.SIZE_Y;
public final class FogChunkStreamer {
private FogChunkStreamer() {
}
public static void update(
int oldOriginChunkX,
int oldOriginChunkZ,
int newOriginChunkX,
int newOriginChunkZ
) {
int dx = newOriginChunkX - oldOriginChunkX;
int dz = newOriginChunkZ - oldOriginChunkZ;
/*
* Телепорт.
*/
if (Math.abs(dx) >= FogWorldVolume.CHUNKS_X ||
Math.abs(dz) >= FogWorldVolume.CHUNKS_Z) {
refillAll(
newOriginChunkX,
newOriginChunkZ
);
return;
}
/*
* Новые колонки.
*/
if (dx > 0) {
for (int i = 0; i < dx; i++) {
uploadColumn(
newOriginChunkX +
FogWorldVolume.CHUNKS_X - dx + i,
newOriginChunkZ
);
}
}
if (dx < 0) {
for (int i = 0; i < -dx; i++) {
uploadColumn(
newOriginChunkX + i,
newOriginChunkZ
);
}
}
/*
* Новые строки.
*/
if (dz > 0) {
for (int i = 0; i < dz; i++) {
uploadRow(
newOriginChunkX,
newOriginChunkZ +
FogWorldVolume.CHUNKS_Z - dz + i
);
}
}
if (dz < 0) {
for (int i = 0; i < -dz; i++) {
uploadRow(
newOriginChunkX,
newOriginChunkZ + i
);
}
}
}
private static void refillAll(
int originChunkX,
int originChunkZ
) {
for (int z = 0; z < FogWorldVolume.CHUNKS_Z; z++) {
for (int x = 0; x < FogWorldVolume.CHUNKS_X; x++) {
uploadChunk(
originChunkX + x,
originChunkZ + z
);
}
}
}
private static void uploadColumn(
int worldChunkX,
int originChunkZ
) {
for (int z = 0; z < FogWorldVolume.CHUNKS_Z; z++) {
uploadChunk(
worldChunkX,
originChunkZ + z
);
}
}
private static void uploadRow(
int originChunkX,
int worldChunkZ
) {
for (int x = 0; x < FogWorldVolume.CHUNKS_X; x++) {
uploadChunk(
originChunkX + x,
worldChunkZ
);
}
}
private static void uploadChunk(
int worldChunkX,
int worldChunkZ
) {
FloatBuffer buffer =
FogWorldVolume.chunkBuffer();
FogChunkProvider.fillChunk(
worldChunkX,
worldChunkZ,
buffer
);
buffer.flip();
int physicalChunkX =
Math.floorMod(
worldChunkX,
FogWorldVolume.CHUNKS_X
);
int physicalChunkZ =
Math.floorMod(
worldChunkZ,
FogWorldVolume.CHUNKS_Z
);
System.out.println(
worldChunkX + " " +
worldChunkZ + " -> " +
physicalChunkX + " " +
physicalChunkZ
);
FogWorldVolume.uploadChunk(
physicalChunkX,
physicalChunkZ,
buffer
);
}
}

View File

@@ -0,0 +1,148 @@
package su.divan2000.veila.client.render.fog;
import net.minecraft.client.MinecraftClient;
import java.nio.FloatBuffer;
public final class FogSystem {
private static boolean initialized;
private static int originChunkX;
private static int originChunkZ;
private static int ringChunkOffsetX;
private static int ringChunkOffsetZ;
private FogSystem() {
}
/*
* Вызывается в начале render()
*
* Только Render Thread.
*/
public static void beginRender() {
if (!initialized) {
initialize();
}
updateWindow();
}
/*
* Вызывается каждый client tick.
*
* Пока пусто.
*/
public static void tick() {
}
/*
* Инициализация всей системы.
*/
private static void initialize() {
FogWorldVolume.init();
initialized = true;
}
/*
* Пока просто заполняем весь объём
* тестовыми чанками.
*/
private static void fillTestVolume() {
for (int chunkZ = 0;
chunkZ < FogWorldVolume.CHUNKS_Z;
chunkZ++) {
for (int chunkX = 0;
chunkX < FogWorldVolume.CHUNKS_X;
chunkX++) {
FloatBuffer buffer =
FogWorldVolume.chunkBuffer();
FogChunkProvider.fillChunk(
originChunkX + chunkX,
originChunkZ + chunkZ,
buffer
);
buffer.flip();
FogWorldVolume.uploadChunk(
chunkX,
chunkZ,
buffer
);
}
}
}
private static void updateWindow() {
MinecraftClient client = MinecraftClient.getInstance();
if (client.world == null || client.player == null)
return;
int playerChunkX = client.player.getBlockX() >> 4;
int playerChunkZ = client.player.getBlockZ() >> 4;
int newOriginChunkX =
playerChunkX - FogWorldVolume.CHUNKS_X / 2;
int newOriginChunkZ =
playerChunkZ - FogWorldVolume.CHUNKS_Z / 2;
if (newOriginChunkX == originChunkX &&
newOriginChunkZ == originChunkZ)
return;
int oldOriginChunkX = originChunkX;
int oldOriginChunkZ = originChunkZ;
originChunkX = newOriginChunkX;
originChunkZ = newOriginChunkZ;
FogChunkStreamer.update(
oldOriginChunkX,
oldOriginChunkZ,
originChunkX,
originChunkZ
);
ringChunkOffsetX =
Math.floorMod(originChunkX,
FogWorldVolume.CHUNKS_X);
ringChunkOffsetZ =
Math.floorMod(originChunkZ,
FogWorldVolume.CHUNKS_Z);
}
public static int originChunkX() {
return originChunkX;
}
public static int originChunkZ() {
return originChunkZ;
}
public static int ringChunkOffsetX() {
return ringChunkOffsetX;
}
public static int ringChunkOffsetZ() {
return ringChunkOffsetZ;
}
}

View File

@@ -0,0 +1,217 @@
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;
public static final int MIN_Y = -64;
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 FloatBuffer CHUNK_BUFFER =
BufferUtils.createFloatBuffer(
CHUNK_SIZE *
SIZE_Y *
CHUNK_SIZE
);
private FogWorldVolume() {
}
public static void init() {
if (texture != -1)
return;
texture = GL11.glGenTextures();
GL11.glBindTexture(
GL12.GL_TEXTURE_3D,
texture
);
GL30.glTexImage3D(
GL12.GL_TEXTURE_3D,
0,
GL30.GL_R16F,
SIZE_X,
SIZE_Y,
SIZE_Z,
0,
GL11.GL_RED,
GL11.GL_FLOAT,
(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_MAG_FILTER,
GL11.GL_LINEAR
);
GL11.glTexParameteri(
GL12.GL_TEXTURE_3D,
GL12.GL_TEXTURE_WRAP_S,
GL12.GL_CLAMP_TO_EDGE
);
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_CLAMP_TO_EDGE
);
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.flip();
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
);
CHUNK_BUFFER.rewind();
}
}
GL11.glBindTexture(
GL12.GL_TEXTURE_3D,
0
);
}
public static void uploadChunk(
int textureChunkX,
int textureChunkZ,
FloatBuffer data
) {
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL12.GL_TEXTURE_3D, texture);
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
System.out.println(
data.remaining()
);
int error = GL11.glGetError();
if (error != GL11.GL_NO_ERROR) {
System.out.println("GL BEFORE = " + error);
}
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,
data
);
error = GL11.glGetError();
if (error != GL11.GL_NO_ERROR) {
System.out.println("GL AFTER = " + error);
}
GL11.glBindTexture(GL12.GL_TEXTURE_3D, 0);
}
public static FloatBuffer chunkBuffer() {
CHUNK_BUFFER.clear();
return CHUNK_BUFFER;
}
public static int getTexture() {
return texture;
}
}

View File

@@ -0,0 +1,66 @@
package su.divan2000.veila.mixin.client;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.util.math.MatrixStack;
import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import su.divan2000.veila.client.render.PostProcessingManager;
import su.divan2000.veila.client.render.fog.FogSystem;
@Mixin(GameRenderer.class)
public class GameRendererMixin {
@Inject(
method = "renderWorld",
at = @At(
value = "INVOKE",
target = "Lcom/mojang/blaze3d/systems/RenderSystem;clear(IZ)V"
)
)
private void veila$postProcess(
float tickDelta,
long limitTime,
MatrixStack matrices,
CallbackInfo ci
) {
PostProcessingManager.init();
PostProcessingManager.render();
}
@Inject(
method = "renderWorld",
at = @At(
value = "INVOKE",
target =
"Lnet/minecraft/client/render/WorldRenderer;render(Lnet/minecraft/client/util/math/MatrixStack;FJZLnet/minecraft/client/render/Camera;Lnet/minecraft/client/render/GameRenderer;Lnet/minecraft/client/render/LightmapTextureManager;Lorg/joml/Matrix4f;)V"
)
)
private void veila$captureView(
float tickDelta,
long limitTime,
MatrixStack matrices,
CallbackInfo ci
) {
PostProcessingManager.updateViewMatrix(
new Matrix4f(
matrices.peek().getPositionMatrix()
)
);
}
@Inject(
method = "render",
at = @At("HEAD")
)
private void veila$beginRender(
float tickDelta,
long startTime,
boolean tick,
CallbackInfo ci
) {
FogSystem.beginRender();
}
}

View File

@@ -0,0 +1,20 @@
package su.divan2000.veila.mixin.client;
import net.minecraft.client.MinecraftClient;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import su.divan2000.veila.client.render.fog.FogSystem;
@Mixin(MinecraftClient.class)
public class MinecraftClientMixin {
@Inject(
method = "tick",
at = @At("TAIL")
)
private void veila$fogTick(CallbackInfo ci) {
FogSystem.tick();
}
}

View File

@@ -0,0 +1,11 @@
#version 150
in vec2 aPos;
out vec2 texCoord;
void main()
{
texCoord = aPos * 0.5 + 0.5;
gl_Position = vec4(aPos, 0.0, 1.0);
}

View File

@@ -0,0 +1,137 @@
#version 150
uniform sampler2D DiffuseSampler;
uniform sampler2D DepthSampler;
uniform sampler3D FogVolume;
uniform mat4 InverseProjection;
uniform mat4 InverseView;
uniform vec3 CameraPosition;
uniform ivec2 FogOrigin;
uniform ivec2 RingBlockOffset;
in vec2 texCoord;
out vec4 FragColor;
//------------------------------------------------------------
// Настройки
//------------------------------------------------------------
const float STEP_SIZE = 0.5;
const float MAX_DISTANCE = 256.0;
const int MAX_STEPS = int(MAX_DISTANCE / STEP_SIZE);
// Размер туманного объёма
const float FOG_SIZE_X = 256.0;
const float FOG_SIZE_Z = 256.0;
// Высота мира
const float WORLD_BOTTOM = -64.0;
const float WORLD_HEIGHT = 384.0;
//------------------------------------------------------------
vec3 reconstructViewPosition(vec2 uv)
{
float depth = texture(DepthSampler, uv).r;
vec4 clip = vec4(
uv * 2.0 - 1.0,
depth * 2.0 - 1.0,
1.0
);
vec4 view = InverseProjection * clip;
return view.xyz / view.w;
}
vec3 reconstructWorldPosition(vec2 uv)
{
vec3 viewPos = reconstructViewPosition(uv);
vec4 world = InverseView * vec4(viewPos, 1.0);
return world.xyz + CameraPosition;
}
//------------------------------------------------------------
float density(vec3 worldPos)
{
vec3 uv;
float localX = worldPos.x - float(FogOrigin.x);
float localZ = worldPos.z - float(FogOrigin.y);
if (
localX < 0.0 || localX >= FOG_SIZE_X ||
localZ < 0.0 || localZ >= FOG_SIZE_Z ||
worldPos.y < WORLD_BOTTOM ||
worldPos.y >= WORLD_BOTTOM + WORLD_HEIGHT
){
return 0.0;
}
float physicalX = mod(
localX + float(RingBlockOffset.x),
FOG_SIZE_X
);
float physicalZ = mod(
localZ + float(RingBlockOffset.y),
FOG_SIZE_Z
);
uv.x = physicalX / FOG_SIZE_X;
uv.z = physicalZ / FOG_SIZE_Z;
uv.y = (worldPos.y - WORLD_BOTTOM) / WORLD_HEIGHT;
return texture(FogVolume, uv).r;
}
//------------------------------------------------------------
void main()
{
vec4 sceneColor = texture(DiffuseSampler, texCoord);
vec3 worldPos = reconstructWorldPosition(texCoord);
vec3 rayDir = normalize(worldPos - CameraPosition);
float rayLength = distance(worldPos, CameraPosition);
rayLength = min(rayLength, MAX_DISTANCE);
vec3 samplePos = CameraPosition;
float fog = 0.0;
for (int i = 0; i < MAX_STEPS; i++)
{
float traveled = float(i) * STEP_SIZE;
if (traveled >= rayLength)
break;
samplePos += rayDir * STEP_SIZE;
fog += density(samplePos) * STEP_SIZE;
}
fog = clamp(fog, 0.0, 1.0);
vec3 fogColor = vec3(
0.75,
0.80,
0.90
);
FragColor = vec4(
mix(sceneColor.rgb, fogColor, fog),
1.0
);
}

View File

@@ -0,0 +1,16 @@
{
"required": true,
"minVersion": "0.8",
"package": "su.divan2000.veila.mixin.client",
"compatibilityLevel": "JAVA_17",
"client": [
"GameRendererMixin",
"MinecraftClientMixin"
],
"injectors": {
"defaultRequire": 1
},
"overwrites": {
"requireAnnotations": true
}
}

View File

@@ -0,0 +1,10 @@
package su.divan2000.veila;
import net.fabricmc.api.ModInitializer;
public class Veila implements ModInitializer {
@Override
public void onInitialize() {
}
}

View File

@@ -0,0 +1,34 @@
{
"schemaVersion": 1,
"id": "veila",
"version": "${version}",
"name": "Veila",
"description": "",
"authors": [],
"contact": {},
"license": "GPL-3.0",
"icon": "assets/veila/icon.png",
"environment": "client",
"entrypoints": {
"fabric-datagen": ["su.divan2000.veila.client.VeilaDataGenerator"],
"client": ["su.divan2000.veila.client.VeilaClient"],
"main": ["su.divan2000.veila.Veila"]
},
"mixins": [
"veila.mixins.json"
,{
"config": "veila.client.mixins.json",
"environment": "client"
}
],
"depends": {
"fabricloader": ">=${loader_version}",
"fabric-api": "*",
"minecraft": "${minecraft_version}"
}
}

View File

@@ -0,0 +1,14 @@
{
"required": true,
"minVersion": "0.8",
"package": "su.divan2000.veila.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
],
"injectors": {
"defaultRequire": 1
},
"overwrites": {
"requireAnnotations": true
}
}