diff --git a/game/client/build.gradle.kts b/game/client/build.gradle.kts
index 4a0b5b0f6..79643b5ff 100644
--- a/game/client/build.gradle.kts
+++ b/game/client/build.gradle.kts
@@ -290,6 +290,35 @@ fun generateMcPatchesFile(inputDir: File, outputDir: File, sbom: File) {
     patchMC.writeText(gson.toJson(json))
 }
 
+val testShaders by tasks.registering {
+    outputs.upToDateWhen { false }
+
+    val shaderDir = project.layout.projectDirectory.dir("/src/main/resources/shaders/").asFile
+
+    shaderDir.walkTopDown().forEach { file ->
+        run {
+            if (file.isFile) {
+                val type = when(file.extension) {
+                    "fsh" -> "frag"
+                    "gsh" -> "geom"
+                    "vsh" -> "vert"
+                    else -> throw RuntimeException("Unknown shader type '${file.extension}' in shader '${file.canonicalPath}'!")
+                }
+                val command = "glslangValidator ${file.canonicalPath} -S $type"
+                val process = command.runCommand(shaderDir)
+                process.waitFor(60, TimeUnit.MINUTES)
+                if (process.exitValue() != 0) {
+                    System.err.println("Failed to compile shader file:///${file.canonicalPath.replace("\\", "/")}")
+                    System.out.print(process.inputStream.bufferedReader().readText())
+                    throw RuntimeException(process.errorStream.bufferedReader().readText())
+                } else{
+                    print(process.inputStream.bufferedReader().readText())
+                }
+            }
+        }
+    }
+}
+
 tasks.cyclonedxBom {
     setIncludeConfigs(listOf("runtimeClasspath"))
     setSkipConfigs(listOf("compileClasspath", "testCompileClasspath"))
@@ -321,7 +350,7 @@ tasks.withType(JavaCompile::class.java) {
 }
 
 tasks.withType(Jar::class.java) {
-    dependsOn(tasks.test)
+    dependsOn(tasks.test, "testShaders")
 }
 
 tasks.test {
@@ -336,3 +365,14 @@ sourceSets.main {
     }
 }
 
+fun String.runCommand(workingDir: File): Process {
+    println("Running command $this")
+    val parts = this.split("\\s".toRegex())
+    val proc = ProcessBuilder(*parts.toTypedArray())
+        .directory(workingDir)
+        .redirectOutput(ProcessBuilder.Redirect.PIPE)
+        .redirectError(ProcessBuilder.Redirect.PIPE)
+        .start()
+    return proc
+}
+
diff --git a/game/client/src/main/java/net/minecraft/client/GLAllocation.java b/game/client/src/main/java/net/minecraft/client/GLAllocation.java
index 9c33e6719..a21921e5f 100644
--- a/game/client/src/main/java/net/minecraft/client/GLAllocation.java
+++ b/game/client/src/main/java/net/minecraft/client/GLAllocation.java
@@ -8,21 +8,11 @@ import org.lwjgl.BufferUtils;
 import org.lwjgl.opengl.GL11;
 
 public class GLAllocation {
-
-//	public static IntList displayLists = new IntArrayList();
+	
 	public static IntList textureNames = new IntArrayList();
 
 	public GLAllocation() {}
 
-	@Deprecated
-	public static synchronized int generateDisplayLists(int range) {
-//		int list = GL11.glGenLists(range);
-//		displayLists.add(list);
-//		displayLists.add(range);
-//		return list;
-		return 0;
-	}
-
 	public static synchronized int generateTexture() {
 		int tex = GL11.glGenTextures();
 		textureNames.add(tex);
@@ -39,20 +29,7 @@ public class GLAllocation {
 		}
 	}
 
-	@Deprecated
-	public static synchronized void deleteDisplayList(int displayList)
-	{
-//		int displayListIndex = displayLists.indexOf(displayList);
-//		if (displayListIndex < 0) return;
-//		GL11.glDeleteLists(displayLists.getInt(displayListIndex), displayLists.getInt(displayListIndex + 1));
-//		displayLists.removeInt(displayListIndex);
-//		displayLists.removeInt(displayListIndex);
-	}
-
 	public static synchronized void deleteTexturesAndDisplayLists() {
-//		for (int i = 0; i < displayLists.size(); i += 2) {
-//			GL11.glDeleteLists(displayLists.getInt(i), displayLists.getInt(i + 1));
-//		}
 
 		IntBuffer intbuffer = BufferUtils.createIntBuffer(textureNames.size());
 		for (Integer textureName : textureNames) {
@@ -61,7 +38,6 @@ public class GLAllocation {
 
 		intbuffer.flip();
 		GL11.glDeleteTextures(intbuffer);
-//		displayLists.clear();
 		textureNames.clear();
 	}
 }
diff --git a/game/client/src/main/java/net/minecraft/client/gui/ScreenConsole.java b/game/client/src/main/java/net/minecraft/client/gui/ScreenConsole.java
index f9f4fc7cb..92594d1fb 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/ScreenConsole.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/ScreenConsole.java
@@ -16,7 +16,6 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.lwjgl.input.Keyboard;
 import org.lwjgl.input.Mouse;
-import org.lwjgl.opengl.GL11;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -442,7 +441,6 @@ public class ScreenConsole extends Screen {
             heightLines = Math.max(1, ((this.height - (PADDING + PADDING + PADDING + PADDING)) / LINE_HEIGHT) - 1);
         }
 
-        GL11.glDisable(GL11.GL_TEXTURE_2D);
         drawRect(0, 0, this.width, getMaxY(), 0xDF_00_00_00);
 
         this.logRenderer.render(PADDING, PADDING, heightLines - this.inputRenderer.getHeightLines());
diff --git a/game/client/src/main/java/net/minecraft/client/gui/container/ScreenContainer.java b/game/client/src/main/java/net/minecraft/client/gui/container/ScreenContainer.java
index 1fccc142a..0d4807527 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/container/ScreenContainer.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/container/ScreenContainer.java
@@ -5,7 +5,6 @@ import net.minecraft.client.render.renderer.State;
 import net.minecraft.core.lang.I18n;
 import net.minecraft.core.player.inventory.menu.MenuContainer;
 import net.minecraft.core.player.inventory.container.Container;
-import org.lwjgl.opengl.GL11;
 
 public class ScreenContainer extends ScreenContainerAbstract {
 
diff --git a/game/client/src/main/java/net/minecraft/client/gui/container/ScreenFlagEditor.java b/game/client/src/main/java/net/minecraft/client/gui/container/ScreenFlagEditor.java
index f60947684..26727e541 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/container/ScreenFlagEditor.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/container/ScreenFlagEditor.java
@@ -4,13 +4,13 @@ import net.minecraft.client.gui.ButtonElement;
 import net.minecraft.client.gui.ClickableLabelElement;
 import net.minecraft.client.gui.DrawableSurfaceElement;
 import net.minecraft.client.gui.TexturedButtonElement;
+import net.minecraft.client.render.renderer.BlendFactor;
 import net.minecraft.client.render.renderer.GLRenderer;
 import net.minecraft.client.render.renderer.State;
 import net.minecraft.core.block.entity.TileEntityFlag;
 import net.minecraft.core.entity.player.Player;
 import org.jetbrains.annotations.NotNull;
 import org.lwjgl.input.Keyboard;
-import org.lwjgl.opengl.GL11;
 
 import net.minecraft.client.gui.drawing.DrawableEditor;
 import net.minecraft.client.gui.drawing.IDrawableSurface;
@@ -117,7 +117,7 @@ public class ScreenFlagEditor
         this.drawOverlaySurface.colors = colors;
 
         GLRenderer.enableState(State.BLEND);
-        GL11.glBlendFunc(GL11.GL_ONE_MINUS_SRC_COLOR, GL11.GL_SRC_COLOR);
+        GLRenderer.setBlendFunc(BlendFactor.ONE_MINUS_SRC_ALPHA, BlendFactor.SRC_COLOR);
         this.flagSurface.render(this.canvasX, this.canvasY);
         GLRenderer.disableState(State.BLEND);
         this.drawOverlaySurface.render(this.canvasX, this.canvasY);
diff --git a/game/client/src/main/java/net/minecraft/client/gui/error/ScreenMissingMinecraft.java b/game/client/src/main/java/net/minecraft/client/gui/error/ScreenMissingMinecraft.java
index 4be4956b8..df2e2866b 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/error/ScreenMissingMinecraft.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/error/ScreenMissingMinecraft.java
@@ -11,7 +11,6 @@ import net.minecraft.client.util.helper.UrlHelper;
 import net.minecraft.core.lang.I18n;
 import net.minecraft.core.util.helper.MathHelper;
 import org.jetbrains.annotations.NotNull;
-import org.lwjgl.opengl.GL11;
 import org.slf4j.Logger;
 
 public class ScreenMissingMinecraft extends Screen {
@@ -64,7 +63,6 @@ public class ScreenMissingMinecraft extends Screen {
 
     public void drawBackground(float partialTick) {
         GLRenderer.globalSetLightEnabled(false);
-        GL11.glDisable(GL11.GL_FOG);
 
         TessellatorGeneral tessellator = GLRenderer.getTessellator();
         this.mc.textureManager.loadTexture("/assets/minecraft/textures/block/cobbled_netherrack_mossy.png").bind();
diff --git a/game/client/src/main/java/net/minecraft/client/gui/guidebook/index/GuidebookPageIndex.java b/game/client/src/main/java/net/minecraft/client/gui/guidebook/index/GuidebookPageIndex.java
index 53d2fcf23..060b49b00 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/guidebook/index/GuidebookPageIndex.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/guidebook/index/GuidebookPageIndex.java
@@ -8,7 +8,6 @@ import net.minecraft.client.render.TextureManager;
 import net.minecraft.client.render.font.FontRenderer;
 import net.minecraft.core.lang.I18n;
 import net.minecraft.core.util.helper.MathHelper;
-import org.lwjgl.opengl.GL11;
 
 import java.util.List;
 
@@ -58,9 +57,7 @@ public class GuidebookPageIndex
 
             int lineStrWidth = MathHelper.ceil(sr.stringWidthDouble(lineStr));
 
-            GL11.glDisable(GL11.GL_TEXTURE_2D);
             drawRect(x + 10 + lineStrWidth + 4 + lineXMod, y + yOffset + 10 + j * 12 + 6, x + 158 - 10 - pageStrWidth - 4, y + yOffset + 10 + j * 12 + 7, 0xFFA0A0A0);
-            GL11.glEnable(GL11.GL_TEXTURE_2D);
         }
     }
 
diff --git a/game/client/src/main/java/net/minecraft/client/render/FogManager.java b/game/client/src/main/java/net/minecraft/client/render/FogManager.java
index 803b2f89a..2dc9aa35d 100644
--- a/game/client/src/main/java/net/minecraft/client/render/FogManager.java
+++ b/game/client/src/main/java/net/minecraft/client/render/FogManager.java
@@ -1,5 +1,6 @@
 package net.minecraft.client.render;
 
+import static net.minecraft.client.render.uniform.UniformBlock.N;
 import static org.lwjgl.opengl.GL11.*;
 
 import net.minecraft.client.world.WorldClient;
@@ -50,7 +51,7 @@ public class FogManager {
     }
 
     public @NotNull FogState setupFog(int fogMode, float farPlaneDistance, float partialTick, @NotNull FogState dest) {
-        dest.fogColor.set(this.fogRed, this.fogGreen, this.fogBlue, 0.5f);
+        dest.fogColor.set(this.fogRed, this.fogGreen, this.fogBlue);
 
         byte cameraState;
         if (CameraUtil.isUnderLiquid(this.mc.activeCamera, this.mc.currentWorld, Materials.WATER, partialTick)) {
@@ -184,7 +185,7 @@ public class FogManager {
         }
 
         public @NotNull FogState reset() {
-            this.fogColor.set(0, 0, 0, 0);
+            this.fogColor.set(0, 0, 0);
             this.fogMode = Mode.EXP;
             this.fogStart = 0f;
             this.fogEnd = 1f;
@@ -216,5 +217,23 @@ public class FogManager {
             EXP,
             EXP2
         }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof FogState otherState) {
+                return
+                    otherState.fogColor.equals(this.fogColor) &&
+                        otherState.fogStart == this.fogStart &&
+                        otherState.fogEnd == this.fogEnd &&
+                        otherState.fogMode == this.fogMode &&
+                        otherState.fogDensity == this.fogDensity;
+            }
+            return false;
+        }
+
+        public static int uboSize() {
+            return 8 * N; // 4N for color and 1N for each scalar (mode, start, end, and density)
+        }
+
     }
 }
diff --git a/game/client/src/main/java/net/minecraft/client/render/Lighting.java b/game/client/src/main/java/net/minecraft/client/render/Lighting.java
index 71754b6de..bcc030c1e 100644
--- a/game/client/src/main/java/net/minecraft/client/render/Lighting.java
+++ b/game/client/src/main/java/net/minecraft/client/render/Lighting.java
@@ -3,9 +3,12 @@ package net.minecraft.client.render;
 import net.minecraft.client.render.renderer.GLRenderer;
 import org.jetbrains.annotations.NotNull;
 import org.joml.Math;
+import org.joml.Matrix4f;
 import org.joml.Vector3f;
 import org.joml.Vector4f;
 
+import static net.minecraft.client.render.uniform.UniformBlock.N;
+
 public final class Lighting {
     private Lighting() {
     }
@@ -68,6 +71,20 @@ public final class Lighting {
             reset();
         }
 
+        public @NotNull LightSource set(@NotNull LightSource source) {
+            this.ambient.set(source.ambient);
+            this.diffuse.set(source.diffuse);
+            this.specular.set(source.specular);
+            this.position.set(source.position);
+            this.spotDirection.set(source.spotDirection);
+            this.spotExponent = source.spotExponent;
+            this.spotCutoff = source.spotCutoff;
+            this.constantAttenuation = source.constantAttenuation;
+            this.linearAttenuation = source.linearAttenuation;
+            this.quadraticAttenuation = source.quadraticAttenuation;
+            return this;
+        }
+
         public void reset() {
             this.ambient.set(0, 0, 0, 1);
             this.diffuse.set(0, 0, 0, 1);
diff --git a/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java b/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java
index 8b159ac28..2e29a1c71 100644
--- a/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java
+++ b/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java
@@ -970,8 +970,8 @@ public class RenderGlobal
         // Hack for not needing to record the cloud buffer twice
         GLRenderer.setColorMask(true, true, true, true);
         GLRenderer.getShader().bind();
-        GL41.glBindVertexArray(tessellator.vao);
-        GL41.glBindBuffer(GL41.GL_ARRAY_BUFFER, tessellator.vbo);
+        GL41.glBindVertexArray(tessellator.vaos[tessellator.config]);
+        GL41.glBindBuffer(GL41.GL_ARRAY_BUFFER, tessellator.vbos[tessellator.config]);
         GL41.glDrawArrays(tessellator.drawMode.cap, 0, tessellator.vertexCount);
         GL41.glBindVertexArray(0);
 
diff --git a/game/client/src/main/java/net/minecraft/client/render/entity/EntityRendererLightningBolt.java b/game/client/src/main/java/net/minecraft/client/render/entity/EntityRendererLightningBolt.java
index ad6a23eae..1d1294cab 100644
--- a/game/client/src/main/java/net/minecraft/client/render/entity/EntityRendererLightningBolt.java
+++ b/game/client/src/main/java/net/minecraft/client/render/entity/EntityRendererLightningBolt.java
@@ -5,6 +5,7 @@ import java.util.Random;
 import net.minecraft.client.render.renderer.BlendFactor;
 import net.minecraft.client.render.renderer.DrawMode;
 import net.minecraft.client.render.renderer.GLRenderer;
+import net.minecraft.client.render.renderer.Shaders;
 import net.minecraft.client.render.renderer.State;
 import net.minecraft.client.render.tessellator.TessellatorGeneral;
 import net.minecraft.core.entity.EntityLightning;
@@ -19,12 +20,13 @@ public class EntityRendererLightningBolt extends EntityRenderer<EntityLightning>
     @Override
     public void render(@NotNull TessellatorGeneral tessellator, @NotNull EntityLightning lightning, double x, double y, double z,
                        float yaw, float partialTick) {
-        GL11.glDisable(GL11.GL_TEXTURE_2D);
+        GLRenderer.pushFrame();
+        GLRenderer.setShader(Shaders.COLOR_WORLD);
         GLRenderer.globalSetLightEnabled(false);
         GLRenderer.enableState(State.BLEND);
-        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
-        double ad[] = new double[8];
-        double ad1[] = new double[8];
+        GLRenderer.setBlendFunc(BlendFactor.SRC_ALPHA, BlendFactor.ONE);
+        double[] ad = new double[8];
+        double[] ad1 = new double[8];
         double d3 = 0.0D;
         double d4 = 0.0D;
         Random random = new Random(lightning.seed);
@@ -60,7 +62,7 @@ public class EntityRendererLightningBolt extends EntityRenderer<EntityLightning>
                     }
                     tessellator.startDrawing(DrawMode.TRIANGLE_STRIP);
                     float f2 = 0.5F;
-                    tessellator.setColor4f(0.9F * f2, 0.9F * f2, 1.0F * f2, 0.3F);
+                    tessellator.setColor4f(0.9F * f2, 0.9F * f2, f2, 0.3F);
                     double d9 = 0.1D + (double) i * 0.2D;
                     if (k == 0) {
                         d9 *= (double) j1 * 0.1D + 1.0D;
@@ -99,7 +101,7 @@ public class EntityRendererLightningBolt extends EntityRenderer<EntityLightning>
 
         GLRenderer.disableState(State.BLEND);
         GLRenderer.globalSetLightEnabled(true);
-        GL11.glEnable(GL11.GL_TEXTURE_2D);
+        GLRenderer.popFrame();
         GLRenderer.setBlendFunc(BlendFactor.SRC_ALPHA, BlendFactor.ONE_MINUS_SRC_ALPHA);
     }
 }
diff --git a/game/client/src/main/java/net/minecraft/client/render/entity/EntityRendererTNT.java b/game/client/src/main/java/net/minecraft/client/render/entity/EntityRendererTNT.java
index f166ff51f..dedc841a1 100644
--- a/game/client/src/main/java/net/minecraft/client/render/entity/EntityRendererTNT.java
+++ b/game/client/src/main/java/net/minecraft/client/render/entity/EntityRendererTNT.java
@@ -2,14 +2,15 @@ package net.minecraft.client.render.entity;
 
 import net.minecraft.client.render.block.model.BlockModel;
 import net.minecraft.client.render.block.model.BlockModelDispatcher;
+import net.minecraft.client.render.renderer.BlendFactor;
 import net.minecraft.client.render.renderer.GLRenderer;
+import net.minecraft.client.render.renderer.Shaders;
 import net.minecraft.client.render.renderer.State;
 import net.minecraft.client.render.texture.stitcher.TextureRegistry;
 import net.minecraft.client.render.tessellator.TessellatorGeneral;
 import net.minecraft.core.entity.EntityPrimedTNT;
 import net.minecraft.core.block.Blocks;
 import org.jetbrains.annotations.NotNull;
-import org.lwjgl.opengl.GL11;
 
 public class EntityRendererTNT extends EntityRenderer<EntityPrimedTNT> {
     public EntityRendererTNT() {
@@ -38,16 +39,17 @@ public class EntityRendererTNT extends EntityRenderer<EntityPrimedTNT> {
         BlockModel<?> model = BlockModelDispatcher.getInstance().getDispatch(Blocks.TNT);
         model.renderStandalone(tessellator, 0, tnt.getLightmap(partialTick));
         if ((tnt.fuse / 5) % 2 == 0) {
-            GL11.glDisable(GL11.GL_TEXTURE_2D);
+            GLRenderer.pushFrame();
+            GLRenderer.setShader(Shaders.COLOR_WORLD);
             GLRenderer.globalSetLightEnabled(false);
             GLRenderer.enableState(State.BLEND);
-            GL11.glBlendFunc(770, 772);
+            GLRenderer.setBlendFunc(BlendFactor.SRC_ALPHA, BlendFactor.DST_ALPHA);
             GLRenderer.setColor4f(1.0F, 1.0F, 1.0F, f3);
             model.renderStandalone(tessellator, 0, tnt.getLightmap(partialTick));
             GLRenderer.setColor4f(1.0F, 1.0F, 1.0F, 1.0F);
             GLRenderer.disableState(State.BLEND);
             GLRenderer.globalSetLightEnabled(true);
-            GL11.glEnable(GL11.GL_TEXTURE_2D);
+            GLRenderer.popFrame();
         }
         GLRenderer.popFrame();
     }
diff --git a/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererSheep.java b/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererSheep.java
index fa28ac2ce..f4728437a 100644
--- a/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererSheep.java
+++ b/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererSheep.java
@@ -18,20 +18,25 @@ public class MobRendererSheep extends MobRenderer<MobSheep> {
     protected @Nullable StaticEntityModel getAndSetupModelForLayer(@NotNull final MobSheep entity, final float brightness, final float partialTick, final int layer) {
         final StaticEntityModel model;
         switch (layer) {
-            case 1:
+            case 1: {
                 final int colorIndex = entity.getFleeceColor().blockMeta;
                 GLRenderer.setColor3f(brightness * MobSheep.FLEECE_COLOR_TABLE[colorIndex][0], brightness * MobSheep.FLEECE_COLOR_TABLE[colorIndex][1], brightness * MobSheep.FLEECE_COLOR_TABLE[colorIndex][2]);
                 model = getModel("main");
                 bindTexture("/assets/minecraft/textures/entity/sheep/wool_overlay.png");
                 break;
-            case 2:
+            }
+            case 2: {
+                final int colorIndex = entity.getFleeceColor().blockMeta;
+                GLRenderer.setColor3f(brightness * MobSheep.FLEECE_COLOR_TABLE[colorIndex][0], brightness * MobSheep.FLEECE_COLOR_TABLE[colorIndex][1], brightness * MobSheep.FLEECE_COLOR_TABLE[colorIndex][2]);
                 model = getModel("wool");
                 bindTexture("/assets/minecraft/textures/entity/sheep/wool_overlay.png");
                 break;
+            }
             case 0:
-            default:
+            default: {
                 model = getModel("main");
                 break;
+            }
         }
         model.resetBones();
 
diff --git a/game/client/src/main/java/net/minecraft/client/render/font/FontDefault.java b/game/client/src/main/java/net/minecraft/client/render/font/FontDefault.java
index 50e03f9e1..52c3b02cb 100644
--- a/game/client/src/main/java/net/minecraft/client/render/font/FontDefault.java
+++ b/game/client/src/main/java/net/minecraft/client/render/font/FontDefault.java
@@ -68,7 +68,7 @@ public class FontDefault extends FontFile {
         final double width = uWidth/2d;
         final double height = vHeight/2d;
 
-        tessellator.addChar(x, y, width, height, shift, u/256d, v/256d, uWidth/256d, vHeight/256d);
+        tessellator.addChar(x, y, width, height, shift, u, v, uWidth, vHeight);
 
         return (fLower - fUpper)/2d + 1;
     }
diff --git a/game/client/src/main/java/net/minecraft/client/render/font/FontFile.java b/game/client/src/main/java/net/minecraft/client/render/font/FontFile.java
index 58dc898fc..a99be0772 100644
--- a/game/client/src/main/java/net/minecraft/client/render/font/FontFile.java
+++ b/game/client/src/main/java/net/minecraft/client/render/font/FontFile.java
@@ -128,7 +128,7 @@ public class FontFile implements Font {
         final double width = uWidth/2d;
         final double height = vHeight/2d;
 
-        tessellator.addChar(x, y, width, height, shift, u/256d, v/256d, uWidth/256d, vHeight/256d);
+        tessellator.addChar(x, y, width, height, shift, u, v, uWidth, vHeight);
 
         return (fLower - fUpper)/2d + 1;
     }
diff --git a/game/client/src/main/java/net/minecraft/client/render/font/FontMonospace.java b/game/client/src/main/java/net/minecraft/client/render/font/FontMonospace.java
index 254252278..d7b56d405 100644
--- a/game/client/src/main/java/net/minecraft/client/render/font/FontMonospace.java
+++ b/game/client/src/main/java/net/minecraft/client/render/font/FontMonospace.java
@@ -49,7 +49,7 @@ public class FontMonospace extends FontFile{
             final double width = uWidth/2d;
             final double height = vHeight/2d;
 
-            tessellator.addChar(x, y, width, height, shift, u/256d, v/256d, uWidth/256d, vHeight/256d);
+            tessellator.addChar(x, y, width, height, shift, u, v, uWidth, vHeight);
         }
         return MONOSPACE_WIDTH / 2d + 1;
     }
diff --git a/game/client/src/main/java/net/minecraft/client/render/renderer/GLRenderer.java b/game/client/src/main/java/net/minecraft/client/render/renderer/GLRenderer.java
index cec7baa36..1cc69a45b 100644
--- a/game/client/src/main/java/net/minecraft/client/render/renderer/GLRenderer.java
+++ b/game/client/src/main/java/net/minecraft/client/render/renderer/GLRenderer.java
@@ -8,6 +8,7 @@ import net.minecraft.client.render.LightmapHelper;
 import net.minecraft.client.render.shader.Shader;
 import net.minecraft.client.render.tessellator.RenderBuffer;
 import net.minecraft.client.render.tessellator.TessellatorShader;
+import net.minecraft.client.render.uniform.UniformBlockManager;
 import net.minecraft.core.util.helper.Color;
 import org.jetbrains.annotations.NotNull;
 import org.joml.Matrix4f;
@@ -23,6 +24,8 @@ import org.slf4j.Logger;
 
 import java.nio.FloatBuffer;
 
+import static net.minecraft.client.render.uniform.UniformBlock.N;
+
 public class GLRenderer {
     private static final Logger LOGGER = LogUtils.getLogger();
     public static boolean DEBUG = false;
@@ -67,6 +70,7 @@ public class GLRenderer {
     private final StackTraceElement[] popListenStack = new StackTraceElement[RENDER_FRAMES_STACK_SIZE];
 
     // Stacked configuration
+    private boolean viewProjMatDirty = true;
     private final @NotNull Matrix4fStack projectionMatrixStack = new Matrix4fStack(RENDER_FRAMES_STACK_SIZE);
     private final @NotNull Matrix4fStack viewMatrixStack = new Matrix4fStack(RENDER_FRAMES_STACK_SIZE);
     private final @NotNull Matrix4fStack modelMatrixStack = new Matrix4fStack(RENDER_FRAMES_STACK_SIZE);
@@ -82,6 +86,7 @@ public class GLRenderer {
 
     private final FogManager.FogState[] fogStateStack = new FogManager.FogState[RENDER_FRAMES_STACK_SIZE];
     private @NotNull FogManager.FogState currentFogState = new FogManager.FogState().disable();
+    private boolean fogStateDirty = true;
 
     private final CompareFunc[] depthFuncStack = new CompareFunc[RENDER_FRAMES_STACK_SIZE];
     private @NotNull CompareFunc currentDepthFunc = CompareFunc.LESS;
@@ -127,57 +132,70 @@ public class GLRenderer {
     }
 
     public static void init() {
+
     }
 
     private static final FloatBuffer lightBuf = BufferUtils.createFloatBuffer(8);
     private static final Matrix4f matBuf = new Matrix4f();
     public static void globalSetUniforms(@NotNull Shader shader) {
         shader.bind();
-        GL41.glUniformMatrix4fv(shader.getUniform("projection"), false, projectionM4f().get(instance.mat4fBuf));
-        GL41.glUniformMatrix4fv(shader.getUniform("projectionInv"), false, projectionM4f().invert(matBuf).get(instance.mat4fBuf));
-        GL41.glUniformMatrix4fv(shader.getUniform("view"), false, viewM4f().get(instance.mat4fBuf));
-        GL41.glUniformMatrix4fv(shader.getUniform("viewInv"), false, viewM4f().invert(matBuf).get(instance.mat4fBuf));
-        GL41.glUniformMatrix4fv(shader.getUniform("model"), false, modelM4f().get(instance.mat4fBuf));
-
-        GL41.glUniform4f(shader.getUniform("ucolor"), Color.redFromInt(instance.currentColor)/255f, Color.greenFromInt(instance.currentColor)/255f, Color.blueFromInt(instance.currentColor)/255f, Color.alphaFromInt(instance.currentColor)/255f);
-        GL41.glUniform1i(shader.getUniform("uticks"), instance.mc.ticksRan);
-        GL41.glUniform1f(shader.getUniform("upartialTick"), instance.mc.timer.partialTicks);
+        if (instance.viewProjMatDirty) {
+            GL41.glBindBuffer(GL41.GL_UNIFORM_BUFFER, UniformBlockManager.BLOCK_MATRICES.ubo);
+            UniformBlockManager.BLOCK_MATRICES.adapter.bufferData(projectionM4f(), 0, UniformBlockManager.BLOCK_MATRICES.buffer.clear());
+            UniformBlockManager.BLOCK_MATRICES.adapter.bufferData(projectionM4f().invert(matBuf), N * 4 * 4, UniformBlockManager.BLOCK_MATRICES.buffer);
+            UniformBlockManager.BLOCK_MATRICES.adapter.bufferData(viewM4f(), N * 4 * 4 * 2, UniformBlockManager.BLOCK_MATRICES.buffer);
+            UniformBlockManager.BLOCK_MATRICES.adapter.bufferData(viewM4f().invert(matBuf), N * 4 * 4 * 3, UniformBlockManager.BLOCK_MATRICES.buffer);
+            GL41.glBufferSubData(GL41.GL_UNIFORM_BUFFER, 0, UniformBlockManager.BLOCK_MATRICES.buffer);
+            GL41.glBindBuffer(GL41.GL_UNIFORM_BUFFER, 0);
+            instance.viewProjMatDirty = false;
+        }
+        shader.uniformMat4f("model", modelM4f());
 
-        GL41.glUniform1f(shader.getUniform("uLineWidth"), instance.currentLineWidth);
+        shader.uniformVec4f("uColor", Color.redFromInt(instance.currentColor)/255f, Color.greenFromInt(instance.currentColor)/255f, Color.blueFromInt(instance.currentColor)/255f, Color.alphaFromInt(instance.currentColor)/255f);
+        shader.uniformFloat("uCurrentTick", instance.mc.ticksRan + instance.mc.timer.partialTicks);
+        shader.uniformFloat("uLineWidth", instance.currentLineWidth);
 
         double renderScale = (float) instance.mc.gameSettings.renderScale.value.scale;
         int scaledWidth = (int) (renderScale * instance.mc.gameWindow.getWidthPixels());
         int scaledHeight = (int) (renderScale * instance.mc.gameWindow.getHeightPixels());
+        shader.uniformVec2f("uViewPortSize", scaledWidth, scaledHeight);
 
-        GL41.glUniform1f(shader.getUniform("viewWidth"), scaledWidth);
-        GL41.glUniform1f(shader.getUniform("viewHeight"), scaledHeight);
-
-        GL41.glUniform1i(shader.getUniform("texture0"), 0);
-        GL41.glUniform1i(shader.getUniform("texture1"), 1);
-
-        GL41.glUniform4f(shader.getUniform("ufogColor"), instance.currentFogState.fogColor.x, instance.currentFogState.fogColor.y, instance.currentFogState.fogColor.z, instance.currentFogState.fogColor.w);
-        GL41.glUniform1i(shader.getUniform("ufogMode"), instance.currentFogState.fogMode.ordinal());
-        GL41.glUniform1f(shader.getUniform("ufogStart"), instance.currentFogState.fogStart);
-        GL41.glUniform1f(shader.getUniform("ufogEnd"), instance.currentFogState.fogEnd);
-        GL41.glUniform1f(shader.getUniform("ufogDensity"), instance.currentFogState.fogDensity);
+        if (instance.fogStateDirty) {
+            GL41.glBindBuffer(GL41.GL_UNIFORM_BUFFER, UniformBlockManager.BLOCK_FOG_STATE.ubo);
+            GL41.glBufferSubData(GL41.GL_UNIFORM_BUFFER, 0, UniformBlockManager.BLOCK_FOG_STATE.adapter.bufferData(instance.currentFogState, 0, UniformBlockManager.BLOCK_FOG_STATE.buffer.clear()));
+            GL41.glBindBuffer(GL41.GL_UNIFORM_BUFFER, 0);
+            instance.fogStateDirty = false;
+        }
 
-        GL41.glUniformMatrix4fv(shader.getUniform("normalTransform"), false, NORMAL_TRANSFORM_MATRIX.get(instance.mat4fBuf));
+        shader.uniformMat4f("normalTransform", NORMAL_TRANSFORM_MATRIX);
         if (LIGHT_ENABLED) {
-            GL41.glUniform4f(shader.getUniform("lightModelAmbient"), LIGHT_MODEL_AMBIENT.x(), LIGHT_MODEL_AMBIENT.y(), LIGHT_MODEL_AMBIENT.z(), LIGHT_MODEL_AMBIENT.w());
-            LIGHT_0.position.get(lightBuf);
-            LIGHT_1.position.get(lightBuf.position(4));
-            GL41.glUniform4fv(shader.getUniform("lightSourcePosition"), lightBuf.position(8).flip());
-            LIGHT_0.diffuse.get(lightBuf);
-            LIGHT_1.diffuse.get(lightBuf.position(4));
-            GL41.glUniform4fv(shader.getUniform("lightSourceDiffuse"), lightBuf.position(8).flip());
+            shader.uniformVec4f("lightModelAmbient", LIGHT_MODEL_AMBIENT);
+            int loc = shader.getUniform("lightSourcePosition");
+            if (loc >= 0) {
+                LIGHT_0.position.get(lightBuf);
+                LIGHT_1.position.get(lightBuf.position(4));
+                GL41.glUniform4fv(loc, lightBuf.position(8).flip());
+            }
+            loc = shader.getUniform("lightSourceDiffuse");
+            if (loc >= 0) {
+                LIGHT_0.diffuse.get(lightBuf);
+                LIGHT_1.diffuse.get(lightBuf.position(4));
+                GL41.glUniform4fv(loc, lightBuf.position(8).flip());
+            }
         } else {
-            GL41.glUniform4f(shader.getUniform("lightModelAmbient"), 1f, 1f, 1f, 1f);
-            LIGHT_0.position.get(lightBuf);
-            LIGHT_1.position.get(lightBuf.position(4));
-            GL41.glUniform4fv(shader.getUniform("lightSourcePosition"), lightBuf.position(8).flip());
-            lightBuf.put(0f).put(0f).put(0f).put(0f);
-            lightBuf.put(0f).put(0f).put(0f).put(0f);
-            GL41.glUniform4fv(shader.getUniform("lightSourceDiffuse"), lightBuf.position(8).flip());
+            shader.uniformVec4f("lightModelAmbient", 1f, 1f, 1f, 1f);
+            int loc = shader.getUniform("lightSourcePosition");
+            if (loc >= 0) {
+                LIGHT_0.position.get(lightBuf);
+                LIGHT_1.position.get(lightBuf.position(4));
+                GL41.glUniform4fv(loc, lightBuf.position(8).flip());
+            }
+            loc = shader.getUniform("lightSourceDiffuse");
+            if (loc >= 0) {
+                lightBuf.put(0f).put(0f).put(0f).put(0f);
+                lightBuf.put(0f).put(0f).put(0f).put(0f);
+                GL41.glUniform4fv(loc, lightBuf.position(8).flip());
+            }
         }
     }
 
@@ -213,10 +231,12 @@ public class GLRenderer {
     }
 
     public static @NotNull Matrix4f projectionM4f() {
+        instance.viewProjMatDirty = true;
         return instance.projectionMatrixStack;
     }
 
     public static @NotNull Matrix4f viewM4f() {
+        instance.viewProjMatDirty = true;
         return instance.viewMatrixStack;
     }
 
@@ -233,6 +253,7 @@ public class GLRenderer {
     }
 
     public static @NotNull FogManager.FogState getFogState() {
+        instance.fogStateDirty = true;
         return instance.currentFogState;
     }
 
@@ -393,7 +414,7 @@ public class GLRenderer {
         instance.stateConfigStack[instance.stackIndex] = instance.currentStateConfig;
         instance.colorStack[instance.stackIndex] = instance.currentColor;
         instance.shaderStack[instance.stackIndex] = instance.currentShader;
-        instance.fogStateStack[instance.stackIndex] =instance.currentFogState.copy();
+        instance.fogStateStack[instance.stackIndex] = instance.currentFogState.copy();
         instance.depthFuncStack[instance.stackIndex] = instance.currentDepthFunc;
         instance.cullFaceStack[instance.stackIndex] = instance.currentCullFace;
         instance.sFactorStack[instance.stackIndex] = instance.currentSFactor;
@@ -456,6 +477,10 @@ public class GLRenderer {
 
         instance.currentColor = instance.colorStack[instance.stackIndex];
         instance.currentShader = instance.shaderStack[instance.stackIndex];
+
+        if (!instance.currentFogState.equals(instance.fogStateStack[instance.stackIndex])) {
+            instance.fogStateDirty = true;
+        }
         instance.currentFogState = instance.fogStateStack[instance.stackIndex];
 
         if (instance.currentDepthFunc != instance.depthFuncStack[instance.stackIndex]) {
diff --git a/game/client/src/main/java/net/minecraft/client/render/renderer/Shaders.java b/game/client/src/main/java/net/minecraft/client/render/renderer/Shaders.java
index 689a8feb2..f75ee0b23 100644
--- a/game/client/src/main/java/net/minecraft/client/render/renderer/Shaders.java
+++ b/game/client/src/main/java/net/minecraft/client/render/renderer/Shaders.java
@@ -5,7 +5,10 @@ import net.minecraft.client.render.shader.Shader;
 import net.minecraft.client.render.shader.ShaderGeometry;
 import net.minecraft.client.render.shader.ShaderProvider;
 import net.minecraft.client.render.shader.ShaderProviderInternal;
+import net.minecraft.client.render.uniform.UniformBlock;
+import net.minecraft.client.render.uniform.UniformBlockManager;
 import org.jetbrains.annotations.NotNull;
+import org.lwjgl.opengl.GL41;
 import org.slf4j.Logger;
 
 import java.util.ArrayList;
@@ -38,6 +41,13 @@ public final class Shaders {
         for (Map.Entry<String, Shader> entry : SHADER_MAP.entrySet()) {
             try {
                 entry.getValue().compile(PROVIDER_INTERNAL, entry.getKey());
+                for (UniformBlock<?> block : UniformBlockManager.nameToBlockMap.values()) {
+                    entry.getValue().uniformBlockBinding(block.name, block.index);
+                }
+                entry.getValue().bind();
+                GL41.glUniform1i(entry.getValue().getUniform("texture0"), 0);
+                GL41.glUniform1i(entry.getValue().getUniform("texture1"), 1);
+                GL41.glUseProgram(0);
             } catch (Exception e) {
                 LOGGER.error("", e);
             }
@@ -49,6 +59,13 @@ public final class Shaders {
             try {
                 entry.getValue().delete();
                 entry.getValue().compile(PROVIDER_INTERNAL, entry.getKey());
+                for (UniformBlock<?> block : UniformBlockManager.nameToBlockMap.values()) {
+                    entry.getValue().uniformBlockBinding(block.name, block.index);
+                }
+                entry.getValue().bind();
+                GL41.glUniform1i(entry.getValue().getUniform("texture0"), 0);
+                GL41.glUniform1i(entry.getValue().getUniform("texture1"), 1);
+                GL41.glUseProgram(0);
             } catch (Exception e) {
                 LOGGER.error("", e);
             }
diff --git a/game/client/src/main/java/net/minecraft/client/render/shader/Shader.java b/game/client/src/main/java/net/minecraft/client/render/shader/Shader.java
index d16c55ea5..e29be30c7 100644
--- a/game/client/src/main/java/net/minecraft/client/render/shader/Shader.java
+++ b/game/client/src/main/java/net/minecraft/client/render/shader/Shader.java
@@ -2,19 +2,28 @@ package net.minecraft.client.render.shader;
 
 import static org.lwjgl.opengl.GL20.*;
 
-import java.util.HashMap;
-import java.util.Map;
+import java.nio.FloatBuffer;
 
 import com.mojang.logging.LogUtils;
-import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
 import it.unimi.dsi.fastutil.objects.Object2IntMap;
 import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
 import net.minecraft.client.render.OpenGLHelper;
 import org.jetbrains.annotations.NotNull;
+import org.joml.Matrix4f;
+import org.joml.Matrix4fc;
+import org.joml.Vector2f;
+import org.joml.Vector2fc;
+import org.joml.Vector3f;
+import org.joml.Vector3fc;
+import org.joml.Vector4f;
+import org.joml.Vector4fc;
+import org.lwjgl.BufferUtils;
+import org.lwjgl.opengl.GL41;
 import org.slf4j.Logger;
 
 public class Shader {
     protected static final Logger LOGGER = LogUtils.getLogger();
+    private static final FloatBuffer mat4fBuf = BufferUtils.createFloatBuffer(16);
     protected int program;
 
     protected int vertexShader;
@@ -23,10 +32,11 @@ public class Shader {
     public boolean supported = true;
     protected boolean enabled = false;
 
-    protected final Object2IntMap<String> uniformLocations = new Object2IntOpenHashMap<>();
+    protected final @NotNull Object2IntMap<String> uniformLocations = new Object2IntOpenHashMap<>();
+    protected final @NotNull Object2IntMap<String> uniformBlockLocations = new Object2IntOpenHashMap<>();
 
     public @NotNull Shader compile(@NotNull ShaderProvider folder, @NotNull String name) {
-        if (!supported) return this;
+        if (!this.supported) return this;
         OpenGLHelper.checkError("pre compile shader " + name);
 
         delete();
@@ -91,7 +101,7 @@ public class Shader {
         return this;
     }
 
-    public int getUniform(String uniform) {
+    public int getUniform(@NotNull String uniform) {
         int location = this.uniformLocations.getOrDefault(uniform, -2);
         if (location == -2) {
             location = glGetUniformLocation(this.program, uniform);
@@ -100,6 +110,15 @@ public class Shader {
         return location;
     }
 
+    public int getUniformBlock(@NotNull String uniform) {
+        int location = this.uniformBlockLocations.getOrDefault(uniform, -2);
+        if (location == -2) {
+            location = GL41.glGetUniformBlockIndex(this.program, uniform);
+            this.uniformBlockLocations.put(uniform, location);
+        }
+        return location;
+    }
+
     public void delete() {
         this.enabled = false;
 
@@ -123,6 +142,10 @@ public class Shader {
         OpenGLHelper.checkError("delete shader");
     }
 
+    public int id() {
+        return this.program;
+    }
+
     public boolean isEnabled() {
         return this.enabled;
     }
@@ -139,20 +162,72 @@ public class Shader {
         return glGetShaderi(shader, GL_COMPILE_STATUS);
     }
 
-    public void uniformFloat(String name, float value) {
-        glUniform1f(getUniform(name), value);
+    public void uniformFloat(@NotNull String name, float value) {
+        int loc = getUniform(name);
+        if (loc >= 0) {
+            glUniform1f(loc, value);
+        }
     }
 
-    public void uniformFloat(String name, boolean value) {
+    public void uniformFloat(@NotNull String name, boolean value) {
         uniformFloat(name, value ? 1.0f : 0.0f);
     }
 
-    public void uniformInt(String name, int value) {
-        glUniform1i(getUniform(name), value);
+    public void uniformInt(@NotNull String name, int value) {
+        int loc = getUniform(name);
+        if (loc >= 0) {
+            glUniform1i(loc, value);
+        }
+    }
+
+    public void uniformBool(@NotNull String name, boolean value) {
+        uniformInt(name, value ? 1 : 0);
     }
 
-    public void uniformBool(String name, boolean value) {
-        glUniform1i(getUniform(name), value ? 1 : 0);
+    public void uniformMat4f(@NotNull String name, @NotNull Matrix4fc matrix4f) {
+        int loc = getUniform(name);
+        if (loc >= 0) {
+            GL41.glUniformMatrix4fv(loc, false, matrix4f.get(mat4fBuf));
+        }
+    }
+
+    public void uniformVec2f(@NotNull String name, float x, float y) {
+        int loc = getUniform(name);
+        if (loc >= 0) {
+            GL41.glUniform2f(loc, x, y);
+        }
+    }
+
+    public void uniformVec2f(@NotNull String name, @NotNull Vector2fc vector2f) {
+        uniformVec2f(name, vector2f.x(), vector2f.y());
+    }
+
+    public void uniformVec3f(@NotNull String name, float x, float y, float z) {
+        int loc = getUniform(name);
+        if (loc >= 0) {
+            GL41.glUniform3f(loc, x, y, z);
+        }
+    }
+
+    public void uniformVec3f(@NotNull String name, @NotNull Vector3fc vector3f) {
+        uniformVec3f(name, vector3f.x(), vector3f.y(), vector3f.z());
+    }
+
+    public void uniformVec4f(@NotNull String name, float x, float y, float z, float w) {
+        int loc = getUniform(name);
+        if (loc >= 0) {
+            GL41.glUniform4f(loc, x, y, z, w);
+        }
+    }
+    public void uniformVec4f(@NotNull String name, @NotNull Vector4fc vector4f) {
+        uniformVec4f(name, vector4f.x(), vector4f.y(), vector4f.z(), vector4f.w());
+    }
+
+    public void uniformBlockBinding(@NotNull String name, int index) {
+        int loc = getUniformBlock(name);
+        if (loc >= 0) {
+            GL41.glUniformBlockBinding(this.program, loc, index);
+        }
     }
 
 }
diff --git a/game/client/src/main/java/net/minecraft/client/render/tessellator/TessellatorFont.java b/game/client/src/main/java/net/minecraft/client/render/tessellator/TessellatorFont.java
index f2a5d17ed..7f5c3215a 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tessellator/TessellatorFont.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tessellator/TessellatorFont.java
@@ -34,14 +34,14 @@ public class TessellatorFont {
         GL41.glBindVertexArray(this.vao);
         GL41.glBindBuffer(GL41.GL_ARRAY_BUFFER, this.vbo);
 
-        int vertSize = 9 * Float.BYTES + 4;
+        int vertSize = 4 * Float.BYTES + 9;
         int offset = 0;
         GL41.glVertexAttribPointer(0, 2, GL41.GL_FLOAT, false,          vertSize, offset); offset += Float.BYTES * 2; // Pos XY
         GL41.glVertexAttribPointer(1, 2, GL41.GL_FLOAT, false,          vertSize, offset); offset += Float.BYTES * 2; // Width Height
-        GL41.glVertexAttribPointer(2, 1, GL41.GL_FLOAT, false,          vertSize, offset); offset += Float.BYTES * 1; // Shift
+        GL41.glVertexAttribPointer(2, 1, GL41.GL_BYTE, false,          vertSize, offset); offset += 1; // Shift
         GL41.glVertexAttribPointer(3, 4, GL41.GL_UNSIGNED_BYTE, true,   vertSize, offset); offset += 4;               // Color
-        GL41.glVertexAttribPointer(4, 2, GL41.GL_FLOAT, false,          vertSize, offset); offset += Float.BYTES * 2; // UV
-        GL41.glVertexAttribPointer(5, 2, GL41.GL_FLOAT, false,          vertSize, offset); offset += Float.BYTES * 2; // UV Sizes
+        GL41.glVertexAttribPointer(4, 2, GL41.GL_UNSIGNED_BYTE, false,   vertSize, offset); offset += 2; // UV
+        GL41.glVertexAttribPointer(5, 2, GL41.GL_UNSIGNED_BYTE, false,   vertSize, offset); offset += 2; // UV Sizes
         GL41.glVertexAttrib2f(6, 15.5f/16f, 15.5f/16f);
 
         GL41.glEnableVertexAttribArray(0);
@@ -77,19 +77,14 @@ public class TessellatorFont {
         this.data.putFloat((float) width);
         this.data.putFloat((float) height);
 
-        this.data.putFloat((float) shift);
+        this.data.put((byte) shift);
 
         this.data.putInt(this.color);
 
-//        this.data.put((byte) (u * 255));
-//        this.data.put((byte) (v * 255));
-//        this.data.put((byte) (uSize * 255));
-//        this.data.put((byte) (vSize * 255));
-
-        this.data.putFloat((float) (u));
-        this.data.putFloat((float) (v));
-        this.data.putFloat((float) (uSize));
-        this.data.putFloat((float) (vSize));
+        this.data.put((byte) u);
+        this.data.put((byte) v);
+        this.data.put((byte) uSize);
+        this.data.put((byte) vSize);
 
         this.vertexCount++;
     }
diff --git a/game/client/src/main/java/net/minecraft/client/render/tessellator/TessellatorShader.java b/game/client/src/main/java/net/minecraft/client/render/tessellator/TessellatorShader.java
index 82a565cf2..a345f7fea 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tessellator/TessellatorShader.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tessellator/TessellatorShader.java
@@ -7,14 +7,14 @@ import org.jetbrains.annotations.NotNull;
 import org.lwjgl.opengl.GL41;
 
 public class TessellatorShader extends TessellatorBase implements TessellatorGeneral{
-
+    private static final int MASK_ENABLE_COLOR = 0b0000_0001;
+    private static final int MASK_ENABLE_TEXTURE = 0b0000_0010;
+    private static final int MASK_ENABLE_LIGHTMAP = 0b0000_0100;
+    private static final int MASK_ENABLE_NORMAL = 0b0000_1000;
     private boolean lockColor = false;
     private boolean quadEmulation = false;
 
-    private boolean enableColor;
-    private boolean enableTexture;
-    private boolean enableLightmap;
-    private boolean enableNormal;
+    public byte config = 0;
 
     private int color;
     private int lightmap;
@@ -26,15 +26,25 @@ public class TessellatorShader extends TessellatorBase implements TessellatorGen
     private byte normalY;
     private byte normalZ;
 
-    public final int vao;
-    public final int vbo;
+    // Pre-setup all possible configs since total permutations is fairly low and this tessellator is used throughout
+    public final int[] vaos = new int[16]; // Holds vertex array objects for each possible configuration
+    public final int[] vbos = new int[16]; // Holds buffers for each possible configuration
 
     private int quadVertCount = 0;
 
     public TessellatorShader(@NotNull String name, int initialCapacity) {
         super(name, initialCapacity);
-        this.vao = GL41.glGenVertexArrays();
-        this.vbo = GL41.glGenBuffers();
+        GL41.glGenVertexArrays(this.vaos);
+        GL41.glGenBuffers(this.vbos);
+
+        for (int i = 0; i < 16; i++) {
+            enableVertexAttribs(this.vaos[i], this.vbos[i],
+                (i & MASK_ENABLE_COLOR) != 0,
+                (i & MASK_ENABLE_TEXTURE) != 0,
+                (i & MASK_ENABLE_LIGHTMAP) != 0,
+                (i & MASK_ENABLE_NORMAL) != 0);
+            GL41.glBindVertexArray(0);
+        }
     }
 
     @Override
@@ -42,10 +52,7 @@ public class TessellatorShader extends TessellatorBase implements TessellatorGen
         super.startDrawing(drawMode);
 
         this.lockColor = false;
-        this.enableColor = false;
-        this.enableTexture = false;
-        this.enableLightmap = false;
-        this.enableNormal = false;
+        this.config = 0;
 
         this.quadEmulation = drawMode == DrawMode.QUADS && Tessellator.QUAD_EMULATION;
         if (this.quadEmulation) {
@@ -63,21 +70,21 @@ public class TessellatorShader extends TessellatorBase implements TessellatorGen
         this.data.putFloat((float) (this.offsetY + y));
         this.data.putFloat((float) (this.offsetZ + z));
 
-        if(this.enableColor) {
+        if((this.config & MASK_ENABLE_COLOR) != 0) {
             this.data.putInt(this.color);
         }
 
-        if(this.enableTexture) {
+        if((this.config & MASK_ENABLE_TEXTURE) != 0) {
             this.data.putFloat(this.texU);
             this.data.putFloat(this.texV);
         }
 
-        if(this.enableLightmap) {
+        if((this.config & MASK_ENABLE_LIGHTMAP) != 0) {
             this.data.put((byte) ((LightmapHelper.getBlockLightFromCoord(this.lightmap) << 4) + 7));
             this.data.put((byte) ((LightmapHelper.getSkyLightFromCoord(this.lightmap) << 4) + 7));
         }
 
-        if(this.enableNormal) {
+        if((this.config & MASK_ENABLE_NORMAL) != 0) {
             this.data.put(this.normalX);
             this.data.put(this.normalY);
             this.data.put(this.normalZ);
@@ -85,7 +92,10 @@ public class TessellatorShader extends TessellatorBase implements TessellatorGen
         this.vertexCount++;
 
         if (this.quadEmulation && this.quadVertCount % 4 == 2) {
-            int vertSize = getVertexSize();
+            int vertSize = getVertexSize((this.config & MASK_ENABLE_COLOR) != 0,
+                (this.config & MASK_ENABLE_TEXTURE) != 0,
+                (this.config & MASK_ENABLE_LIGHTMAP) != 0,
+                (this.config & MASK_ENABLE_NORMAL) != 0);
 
             checkCanFit(128);
             this.data.put(this.data.position(), this.data, this.data.position() - vertSize * 3, vertSize);
@@ -109,12 +119,12 @@ public class TessellatorShader extends TessellatorBase implements TessellatorGen
         if (this.lockColor) {
             return;
         }
-        if (!this.enableColor) {
+        if ((this.config & MASK_ENABLE_COLOR) == 0) {
             if (this.vertexCount > 0) {
                 this.drawing = false;
                 throw new IllegalStateException(this.name + " color is disabled!");
             }
-            this.enableColor = true;
+            this.config |= MASK_ENABLE_COLOR;
         }
 
         int a = (argb >> 24) & 0xFF;
@@ -128,12 +138,12 @@ public class TessellatorShader extends TessellatorBase implements TessellatorGen
     @Override
     public void setTextureUV(double u, double v) {
         checkIsDrawing();
-        if (!this.enableTexture) {
+        if ((this.config & MASK_ENABLE_TEXTURE) == 0) {
             if (this.vertexCount > 0) {
                 this.drawing = false;
                 throw new IllegalStateException(this.name + " texture is disabled!");
             }
-            this.enableTexture = true;
+            this.config |= MASK_ENABLE_TEXTURE;
         }
 
         this.texU = (float) u;
@@ -143,12 +153,12 @@ public class TessellatorShader extends TessellatorBase implements TessellatorGen
     @Override
     public void setLightmapCoord1i(int lightmapCoord) {
         checkIsDrawing();
-        if (!this.enableLightmap) {
+        if ((this.config & MASK_ENABLE_LIGHTMAP) == 0) {
             if (this.vertexCount > 0) {
                 this.drawing = false;
                 throw new IllegalStateException(this.name + " lightmap is disabled!");
             }
-            this.enableLightmap = true;
+            this.config |= MASK_ENABLE_LIGHTMAP;
         }
 
         this.lightmap = lightmapCoord;
@@ -157,12 +167,12 @@ public class TessellatorShader extends TessellatorBase implements TessellatorGen
     @Override
     public void setNormal(float x, float y, float z) {
         checkIsDrawing();
-        if (!this.enableNormal) {
+        if ((this.config & MASK_ENABLE_NORMAL) == 0) {
             if (this.vertexCount > 0) {
                 this.drawing = false;
                 throw new IllegalStateException(this.name + " normal is disabled!");
             }
-            this.enableNormal = true;
+            this.config |= MASK_ENABLE_NORMAL;
         }
 
         this.normalX = (byte) (x * 127.0f);
@@ -177,9 +187,18 @@ public class TessellatorShader extends TessellatorBase implements TessellatorGen
 
         if (this.vertexCount == 0) return;
 
-        enableVertexAttribs(this.vao, this.vbo);
+        enableBufferedConfig(this.vaos[this.config], this.vbos[this.config],
+            (this.config & MASK_ENABLE_COLOR) != 0,
+            (this.config & MASK_ENABLE_TEXTURE) != 0,
+            (this.config & MASK_ENABLE_LIGHTMAP) != 0,
+            (this.config & MASK_ENABLE_NORMAL) != 0);
         GL41.glBufferData(GL41.GL_ARRAY_BUFFER, this.data.flip(), GL41.GL_STREAM_DRAW);
-        GLRenderer.render(this.vao, this.vbo, 0, this.vertexCount, this.drawMode);
+
+        GLRenderer.getShader().bind();
+        GLRenderer.globalSetUniforms(GLRenderer.getShader());
+        GL41.glDrawArrays(this.drawMode.cap, 0, this.vertexCount);
+        GL41.glUseProgram(0);
+        GL41.glBindVertexArray(0);
     }
 
     @Override
@@ -193,18 +212,18 @@ public class TessellatorShader extends TessellatorBase implements TessellatorGen
         return new RenderBuffer(vao, vbo, 0, this.vertexCount, this.drawMode);
     }
 
-    protected int getVertexSize() {
+    protected int getVertexSize(boolean color, boolean texture, boolean lightmap, boolean normal) {
         int size = Float.BYTES * 3;
-        if(this.enableColor) {
+        if(color) {
             size += 4;
         }
-        if(this.enableTexture) {
+        if(texture) {
             size += Float.BYTES * 2;
         }
-        if(this.enableNormal) {
+        if(normal) {
             size += 3;
         }
-        if(this.enableLightmap) {
+        if(lightmap) {
             size += 2;
         }
 
@@ -212,41 +231,59 @@ public class TessellatorShader extends TessellatorBase implements TessellatorGen
     }
 
     protected void enableVertexAttribs(int vao, int vbo) {
+        enableVertexAttribs(vao, vbo, (this.config & MASK_ENABLE_COLOR) != 0, (this.config & MASK_ENABLE_TEXTURE) != 0, (this.config & MASK_ENABLE_LIGHTMAP) != 0, (this.config & MASK_ENABLE_NORMAL) != 0);
+    }
+
+    protected void enableVertexAttribs(int vao, int vbo, boolean color, boolean texture, boolean lightmap, boolean normal) {
         GL41.glBindVertexArray(vao);
         GL41.glBindBuffer(GL41.GL_ARRAY_BUFFER, vbo);
 
-        int vertexSize = getVertexSize();
+        int vertexSize = getVertexSize(color, texture, lightmap, normal);
         int offset = 0;
         GL41.glVertexAttribPointer(0, 3, GL41.GL_FLOAT, false, vertexSize, offset); offset += 3 * Float.BYTES;    // Pos
         GL41.glEnableVertexAttribArray(0);
-        if (this.enableColor) {
+        if (color) {
             GL41.glVertexAttribPointer(1, 4, GL41.GL_UNSIGNED_BYTE, true, vertexSize, offset); offset += 4;    // Color
             GL41.glEnableVertexAttribArray(1);
         } else {
             GL41.glDisableVertexAttribArray(1);
-            GL41.glVertexAttrib4f(1, 1f, 1f, 1f, 1f);
         }
 
-        if (this.enableTexture) {
+        if (texture) {
             GL41.glVertexAttribPointer(2, 2, GL41.GL_FLOAT, false, vertexSize, offset); offset += 2 * Float.BYTES;    // UV
             GL41.glEnableVertexAttribArray(2);
         } else {
             GL41.glDisableVertexAttribArray(2);
         }
 
-        if (this.enableLightmap) {
+        if (lightmap) {
             GL41.glVertexAttribPointer(3, 2, GL41.GL_UNSIGNED_BYTE, true, vertexSize, offset); offset += 2;    // Lightmap
             GL41.glEnableVertexAttribArray(3);
         } else {
             GL41.glDisableVertexAttribArray(3);
-            GL41.glVertexAttrib2f(3, GLRenderer.getLightmapBlock(), GLRenderer.getLightmapSky());
         }
 
-        if (this.enableNormal) {
+        if (normal) {
             GL41.glVertexAttribPointer(4, 3, GL41.GL_BYTE, true, vertexSize, offset); offset += 3;    // Normal
             GL41.glEnableVertexAttribArray(4);
         } else {
             GL41.glDisableVertexAttribArray(4);
+        }
+    }
+
+    protected void enableBufferedConfig(int vao, int vbo, boolean color, boolean texture, boolean lightmap, boolean normal) {
+        GL41.glBindVertexArray(vao);
+        GL41.glBindBuffer(GL41.GL_ARRAY_BUFFER, vbo);
+
+        if (!color) {
+            GL41.glVertexAttrib4f(1, 1f, 1f, 1f, 1f);
+        }
+
+        if (!lightmap) {
+            GL41.glVertexAttrib2f(3, GLRenderer.getLightmapBlock(), GLRenderer.getLightmapSky());
+        }
+
+        if (!normal) {
             GL41.glVertexAttrib3f(4, 0, -1, 0);
         }
     }
diff --git a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMovingPiston.java b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMovingPiston.java
index ddab17cb8..74d1e7ac7 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMovingPiston.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMovingPiston.java
@@ -19,7 +19,6 @@ import net.minecraft.core.block.piston.BlockLogicPistonHead;
 import net.minecraft.core.block.piston.TileEntityMovingPistonBlock;
 import net.minecraft.core.world.BlocksContainer;
 import net.minecraft.core.world.pos.TilePos;
-import org.lwjgl.opengl.GL11;
 
 public class TileEntityRendererMovingPiston extends TileEntityRenderer<TileEntityMovingPistonBlock> {
     private final Minecraft mc;
@@ -44,11 +43,6 @@ public class TileEntityRendererMovingPiston extends TileEntityRenderer<TileEntit
             GLRenderer.setBlendFunc(BlendFactor.SRC_ALPHA, BlendFactor.ONE_MINUS_SRC_ALPHA);
             GLRenderer.enableState(State.BLEND);
             GLRenderer.disableState(State.CULL_FACE);
-            if (this.mc.isAmbientOcclusionEnabled()) {
-                GL11.glShadeModel(GL11.GL_SMOOTH);
-            } else {
-                GL11.glShadeModel(GL11.GL_FLAT);
-            }
             tessellator.startDrawingQuads();
             tessellator.setTranslation(((float) x - (float) tileEntity.x) + tileEntity.getXOff(partialTick), ((float) y - (float) tileEntity.y) + tileEntity.getYOff(partialTick), ((float) z - (float) tileEntity.z) + tileEntity.getZOff(partialTick));
             if (Block.hasLogicClass(block, BlockLogicPistonHead.class) && tileEntity.getProgress(partialTick) < 0.5F) {
diff --git a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java
index d88f1ddb7..844eecd9f 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java
@@ -184,11 +184,6 @@ public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign> {
         this.textMeshRenderer.tick();
     }
 
-    @Override
-    public void onWorldChanged(final World world) {
-        this.textMeshRenderer.flushCaches();
-    }
-
     @Override
     public boolean isVisible(@NotNull final TileEntitySign tileEntity, final @NotNull ICamera camera, final float partialTick) {
         return camera.getFrustum().isVisible(new AABBd(tileEntity.x, tileEntity.y, tileEntity.z, tileEntity.x + 1, tileEntity.y + 1, tileEntity.z + 1), partialTick);
@@ -213,17 +208,6 @@ public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign> {
             }
         }
 
-        public void flushCaches() {
-            for (final MeshEntry entry : this.bufferedMeshes) {
-                GLAllocation.deleteDisplayList(entry.list);
-            }
-            this.bufferedMeshes.clear();
-            for (final int list : this.freeDisplayLists) {
-                GLAllocation.deleteDisplayList(list);
-            }
-            this.freeDisplayLists.clear();
-        }
-
         public void render(@NotNull final FontRenderer sr,
                            @NotNull final TessellatorGeneral t,
                            @NotNull final CharSequence line1,
@@ -281,14 +265,6 @@ public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign> {
 
         }
 
-        private int getDisplayList() {
-            if (this.freeDisplayLists.isEmpty()) {
-                return GLAllocation.generateDisplayLists(1);
-            } else {
-                return this.freeDisplayLists.removeInt(0);
-            }
-        }
-
         public static class MeshEntry {
             public long config;
             public @NotNull String line1;
diff --git a/game/client/src/main/java/net/minecraft/client/render/uniform/BufferAdapter.java b/game/client/src/main/java/net/minecraft/client/render/uniform/BufferAdapter.java
new file mode 100644
index 000000000..8e0d39461
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/uniform/BufferAdapter.java
@@ -0,0 +1,9 @@
+package net.minecraft.client.render.uniform;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.nio.ByteBuffer;
+
+public interface BufferAdapter<T> {
+    @NotNull ByteBuffer bufferData(T src, int offset, @NotNull ByteBuffer dest);
+}
diff --git a/game/client/src/main/java/net/minecraft/client/render/uniform/UniformBlock.java b/game/client/src/main/java/net/minecraft/client/render/uniform/UniformBlock.java
new file mode 100644
index 000000000..495d57069
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/uniform/UniformBlock.java
@@ -0,0 +1,31 @@
+package net.minecraft.client.render.uniform;
+
+import org.jetbrains.annotations.NotNull;
+import org.lwjgl.BufferUtils;
+import org.lwjgl.opengl.GL41;
+
+import java.nio.ByteBuffer;
+
+public class UniformBlock<T> {
+    public static final int N = 4; // GL operates in multiples of 4 bytes for everything
+    public final @NotNull String name;
+    public final int index;
+    public final int ubo;
+    public final @NotNull ByteBuffer buffer;
+    public final @NotNull BufferAdapter<T> adapter;
+
+    public UniformBlock(@NotNull String name, int index, int size, @NotNull BufferAdapter<T> adapter) {
+        assert size % N == 0 : "Size must be a multiple of GL's uniform slot size!";
+        this.name = name;
+        this.index = index;
+        this.adapter = adapter;
+        this.buffer = BufferUtils.createByteBuffer(size);
+
+        this.ubo = GL41.glGenBuffers();
+        GL41.glBindBuffer(GL41.GL_UNIFORM_BUFFER, this.ubo);
+        GL41.glBufferData(GL41.GL_UNIFORM_BUFFER, size, GL41.GL_DYNAMIC_DRAW);
+        GL41.glBindBuffer(GL41.GL_UNIFORM_BUFFER, 0);
+
+        GL41.glBindBufferRange(GL41.GL_UNIFORM_BUFFER, index, this.ubo, 0, size);
+    }
+}
diff --git a/game/client/src/main/java/net/minecraft/client/render/uniform/UniformBlockManager.java b/game/client/src/main/java/net/minecraft/client/render/uniform/UniformBlockManager.java
new file mode 100644
index 000000000..8a691c93e
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/uniform/UniformBlockManager.java
@@ -0,0 +1,38 @@
+package net.minecraft.client.render.uniform;
+
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+import net.minecraft.client.render.FogManager;
+import org.jetbrains.annotations.NotNull;
+import org.joml.Matrix4f;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+
+import static net.minecraft.client.render.uniform.UniformBlock.N;
+
+public class UniformBlockManager {
+    public static final @NotNull Map<String, UniformBlock<?>> nameToBlockMap = new Object2ObjectOpenHashMap<>();
+    public static final @NotNull Int2ObjectMap<UniformBlock<?>> indexToBlockMap = new Int2ObjectOpenHashMap<>();
+
+    public static final @NotNull UniformBlock<Matrix4f> BLOCK_MATRICES = register("Matrices", 0, N * 4 * 4 * 4, Matrix4f::get);
+    public static final @NotNull UniformBlock<FogManager.FogState> BLOCK_FOG_STATE = register("FogState", 1, FogManager.FogState.uboSize(), new BufferAdapter<FogManager.FogState>() {
+        @Override
+        public @NotNull ByteBuffer bufferData(FogManager.FogState src, int offset, @NotNull ByteBuffer dest) {
+            src.fogColor.get(offset, dest); offset += N * 4;
+            dest.putInt(offset, src.fogMode.ordinal()); offset += N;
+            dest.putFloat(offset, src.fogStart); offset += N;
+            dest.putFloat(offset, src.fogEnd); offset += N;
+            dest.putFloat(offset, src.fogDensity); offset += N;
+            return dest;
+        }
+    });
+
+    public static <T> UniformBlock<T> register(@NotNull String name, int index, int size, @NotNull BufferAdapter<T> adapter) {
+        UniformBlock<T> block = new UniformBlock<>(name, index, size, adapter);
+        nameToBlockMap.put(name, block);
+        indexToBlockMap.put(index, block);
+        return block;
+    }
+}
diff --git a/game/client/src/main/resources/shaders/clouds.fsh b/game/client/src/main/resources/shaders/clouds.fsh
index 00746bf35..dcaad24a3 100644
--- a/game/client/src/main/resources/shaders/clouds.fsh
+++ b/game/client/src/main/resources/shaders/clouds.fsh
@@ -3,26 +3,39 @@
 #define FOG
 #define RADIAL_FOG
 
-uniform int isEyeInLiquid;
-
-uniform mat4 projectionInv;
-uniform float viewWidth;
-uniform float viewHeight;
-
 in vec4 Color;
 in vec2 TexCoord;
 
 out vec4 FragColor;
 
-uniform vec4 ufogColor;
-uniform int ufogMode;
-uniform float ufogStart;
-uniform float ufogEnd;
-uniform float ufogDensity;
+layout (std140) uniform FogState {
+	vec4 color;
+	int mode;
+	float start;
+	float end;
+	float density;
+} fogState;
+
+layout (std140) uniform Matrices {
+	mat4 projection;
+	mat4 projectionInv;
+	mat4 view;
+	mat4 viewInv;
+} matrices;
+
+uniform int isEyeInLiquid;
+uniform vec2 uViewPortSize;
 
 uniform sampler2D texture0;
 uniform sampler2D texture1;
 
+float distance() {
+	vec4 fragPos = matrices.projectionInv * vec4((gl_FragCoord.xy / uViewPortSize) * 2.0f - 1.0f, gl_FragCoord.z * 2.0f - 1.0f, 1.0f);
+	fragPos /= fragPos.w;
+
+	return length(fragPos.xyz);
+}
+
 void main() {
 	vec4 tex = texture(texture0, TexCoord);
 	
@@ -40,14 +53,10 @@ void main() {
 
 	float fogStrength = 0.0f;
 
-	vec4 fragPos = projectionInv * vec4((gl_FragCoord.xy / vec2(viewWidth, viewHeight)) * 2.0f - 1.0f, gl_FragCoord.z * 2.0f - 1.0f, 1.0f);
-	fragPos /= fragPos.w;
-	
-	float dist = length(fragPos.xyz);
-	float end = (ufogEnd * 1.5f);
-	float start = ufogStart / (ufogEnd * 1.5f);
+	float end = (fogState.end * 1.5f);
+	float start = fogState.start / (fogState.end * 1.5f);
 
-	fogStrength = dist / end;
+	fogStrength = distance() / end;
 	fogStrength -= start;
 	fogStrength /= (1.0f - start);
 	fogStrength = clamp(fogStrength, 0.0f, 1.0f);
diff --git a/game/client/src/main/resources/shaders/clouds.vsh b/game/client/src/main/resources/shaders/clouds.vsh
index ec9b57fd0..cb437bb97 100644
--- a/game/client/src/main/resources/shaders/clouds.vsh
+++ b/game/client/src/main/resources/shaders/clouds.vsh
@@ -9,12 +9,17 @@ out vec4 Color;
 out vec2 TexCoord;
 
 uniform mat4 model;
-uniform mat4 view;
-uniform mat4 projection;
+
+layout (std140) uniform Matrices {
+	mat4 projection;
+	mat4 projectionInv;
+	mat4 view;
+	mat4 viewInv;
+} matrices;
 
 void main() {
 	TexCoord = aUV;
 	Color = aColor;
 
-	gl_Position = projection * view * model * vec4(aPos, 1);
+	gl_Position = matrices.projection * matrices.view * model * vec4(aPos, 1);
 }
\ No newline at end of file
diff --git a/game/client/src/main/resources/shaders/color.fsh b/game/client/src/main/resources/shaders/color.fsh
index a0dabc0a1..b2645ee5e 100644
--- a/game/client/src/main/resources/shaders/color.fsh
+++ b/game/client/src/main/resources/shaders/color.fsh
@@ -4,8 +4,8 @@ out vec4 FragColor;
 
 in vec4 Color;
 
-uniform vec4 ucolor;
+uniform vec4 uColor;
 
 void main() {
-    FragColor = Color * ucolor;
+    FragColor = Color * uColor;
 }
diff --git a/game/client/src/main/resources/shaders/color.vsh b/game/client/src/main/resources/shaders/color.vsh
index 963b1691f..c4ea49571 100644
--- a/game/client/src/main/resources/shaders/color.vsh
+++ b/game/client/src/main/resources/shaders/color.vsh
@@ -5,10 +5,15 @@ layout (location = 1) in vec4 aColor;
 out vec4 Color;
 
 uniform mat4 model;
-uniform mat4 view;
-uniform mat4 projection;
+
+layout (std140) uniform Matrices {
+    mat4 projection;
+    mat4 projectionInv;
+    mat4 view;
+    mat4 viewInv;
+} matrices;
 
 void main() {
-    gl_Position = projection * view * model * vec4(aPos, 1);
+    gl_Position = matrices.projection * matrices.view * model * vec4(aPos, 1);
     Color = aColor;
 }
diff --git a/game/client/src/main/resources/shaders/color_world.fsh b/game/client/src/main/resources/shaders/color_world.fsh
index a3c438d38..dcfc19dd5 100644
--- a/game/client/src/main/resources/shaders/color_world.fsh
+++ b/game/client/src/main/resources/shaders/color_world.fsh
@@ -4,53 +4,69 @@ out vec4 FragColor;
 
 in vec4 Color;
 in vec2 LightMapCoord;
-in float Distance;
 in vec3 Light;
 
-uniform vec4 ucolor;
+uniform vec4 uColor;
+uniform vec2 uViewPortSize;
 
-uniform vec4 ufogColor;
-uniform int ufogMode;
-uniform float ufogStart;
-uniform float ufogEnd;
-uniform float ufogDensity;
+layout (std140) uniform FogState {
+    vec4 color;
+    int mode;
+    float start;
+    float end;
+    float density;
+} fogState;
+
+layout (std140) uniform Matrices {
+    mat4 projection;
+    mat4 projectionInv;
+    mat4 view;
+    mat4 viewInv;
+} matrices;
 
 uniform sampler2D texture1;
 
+float distance() {
+    vec4 fragPos = matrices.projectionInv * vec4((gl_FragCoord.xy / uViewPortSize) * 2.0f - 1.0f, gl_FragCoord.z * 2.0f - 1.0f, 1.0f);
+    fragPos /= fragPos.w;
+
+    return length(fragPos.xyz);
+}
+
 // Taken from https://github.com/hughsk/glsl-fog
 float fogFactorLinear() {
-    return 1.0 - clamp(((ufogEnd - (Distance)) / (ufogEnd - ufogStart)), 0.0, 1.0);
+    return 1.0 - clamp(((fogState.end - (distance())) / (fogState.end - fogState.start)), 0.0, 1.0);
 }
 
 float fogFactorExp() {
-    return 1.0 - clamp(exp(-ufogDensity * (Distance)), 0.0, 1.0);
+    return 1.0 - clamp(exp(-fogState.density * (distance())), 0.0, 1.0);
 }
 
 float fogFactorExp2() {
     float LOG2 = -1.442695;
-    float d = ufogDensity * (Distance);
+    float d = fogState.density * (distance());
     return 1.0 - clamp(exp2(d * d * LOG2), 0.0, 1.0);
 }
 
 
 vec4 computeFog(vec4 color) {
     float f = 0;
-    if (ufogMode == 0) { // Linear
+    if (fogState.mode == 0) { // Linear
         f = fogFactorLinear();
     }
-    else if (ufogMode == 1) { // EXP
+    else if (fogState.mode == 1) { // EXP
         f = fogFactorExp();
     }
-    else if (ufogMode == 2) { // EXP2
+    else if (fogState.mode == 2) { // EXP2
         f = fogFactorExp2();
     }
-    return vec4(mix(color.rgb, ufogColor.rgb, f), color.a);
+    return vec4(mix(color.rgb, fogState.color.rgb, f), color.a);
 }
 
 void main() {
     vec4 lightColor = texture(texture1, LightMapCoord);
 
-    vec4 oColor = lightColor * Color * ucolor;
+    vec4 oColor = lightColor * Color * uColor;
     vec4 litColor = vec4(oColor.rgb * clamp(Light, 0, 1), oColor.a);
 
     FragColor = computeFog(oColor);
diff --git a/game/client/src/main/resources/shaders/color_world.vsh b/game/client/src/main/resources/shaders/color_world.vsh
index 6eec498b0..d6403f084 100644
--- a/game/client/src/main/resources/shaders/color_world.vsh
+++ b/game/client/src/main/resources/shaders/color_world.vsh
@@ -7,7 +7,6 @@ layout (location = 4) in vec3 aNormal;
 out vec4 Color;
 out vec2 TexCoord;
 out vec2 LightMapCoord;
-out float Distance;
 out vec3 Light;
 
 uniform mat4 normalTransform;
@@ -16,19 +15,20 @@ uniform vec4 lightSourcePosition[];
 uniform vec4 lightSourceDiffuse[];
 
 uniform mat4 model;
-uniform mat4 view;
-uniform mat4 projection;
+layout (std140) uniform Matrices {
+    mat4 projection;
+    mat4 projectionInv;
+    mat4 view;
+    mat4 viewInv;
+} matrices;
 
 void main() {
     vec4 p = vec4(aPos, 1);
-    gl_Position = projection * view * model * p;
+    gl_Position = matrices.projection * matrices.view * model * p;
 
     Color = aColor;
     LightMapCoord = aLightmap;
 
-    // Used for the radial fog in shader
-    Distance = length((view * model * p).xyz);
-
     vec4 norm = normalize(normalTransform * model * vec4(aNormal, 0));
     float dir1 = max(0.0f, dot(norm, lightSourcePosition[0]));
     float dir2 = max(0.0f, dot(norm, lightSourcePosition[1]));
diff --git a/game/client/src/main/resources/shaders/font.fsh b/game/client/src/main/resources/shaders/font.fsh
index 969fa5cdb..b347f8cba 100644
--- a/game/client/src/main/resources/shaders/font.fsh
+++ b/game/client/src/main/resources/shaders/font.fsh
@@ -5,13 +5,13 @@ in vec4 Color;
 in vec2 TexCoord;
 in vec2 Lightmap;
 
-uniform vec4 ucolor;
+uniform vec4 uColor;
 
 uniform sampler2D texture0;
 uniform sampler2D texture1;
 
 void main() {
-    FragColor = texture(texture0, TexCoord) * texture(texture1, Lightmap) * Color * ucolor;
+    FragColor = texture(texture0, TexCoord) * texture(texture1, Lightmap) * Color * uColor;
     if (FragColor.a < 0.1f) {
         discard;
     }
diff --git a/game/client/src/main/resources/shaders/font.gsh b/game/client/src/main/resources/shaders/font.gsh
index 0cf0dc235..c3c377ebc 100644
--- a/game/client/src/main/resources/shaders/font.gsh
+++ b/game/client/src/main/resources/shaders/font.gsh
@@ -16,27 +16,31 @@ out vec2 TexCoord;
 out vec2 Lightmap;
 
 uniform mat4 model;
-uniform mat4 view;
-uniform mat4 projection;
+layout (std140) uniform Matrices {
+    mat4 projection;
+    mat4 projectionInv;
+    mat4 view;
+    mat4 viewInv;
+} matrices;
 
 void main() {
     Color = gs_in[0].color;
     Lightmap = gs_in[0].lightmap;
 
     TexCoord = gs_in[0].uv + vec2(0, gs_in[0].uvSizes.y);
-    gl_Position = projection * view * model * (gl_in[0].gl_Position + vec4(-gs_in[0].shift,  gs_in[0].size.y, 0.0, 0.0));
+    gl_Position = matrices.projection * matrices.view * model * (gl_in[0].gl_Position + vec4(-gs_in[0].shift,  gs_in[0].size.y, 0.0, 0.0));
     EmitVertex();
 
     TexCoord = gs_in[0].uv + vec2(gs_in[0].uvSizes.x, gs_in[0].uvSizes.y);
-    gl_Position = projection * view * model * (gl_in[0].gl_Position + vec4(gs_in[0].size.x - gs_in[0].shift,  gs_in[0].size.y, 0.0, 0.0));
+    gl_Position = matrices.projection * matrices.view * model * (gl_in[0].gl_Position + vec4(gs_in[0].size.x - gs_in[0].shift,  gs_in[0].size.y, 0.0, 0.0));
     EmitVertex();
 
     TexCoord = gs_in[0].uv + vec2(0, 0);
-    gl_Position = projection * view * model * (gl_in[0].gl_Position + vec4(gs_in[0].shift, 0, 0.0, 0.0));
+    gl_Position = matrices.projection * matrices.view * model * (gl_in[0].gl_Position + vec4(gs_in[0].shift, 0, 0.0, 0.0));
     EmitVertex();
 
     TexCoord = gs_in[0].uv + vec2(gs_in[0].uvSizes.x, 0);
-    gl_Position = projection * view * model * (gl_in[0].gl_Position + vec4(gs_in[0].size.x + gs_in[0].shift, 0, 0.0, 0.0));
+    gl_Position = matrices.projection * matrices.view * model * (gl_in[0].gl_Position + vec4(gs_in[0].size.x + gs_in[0].shift, 0, 0.0, 0.0));
     EmitVertex();
 
     EndPrimitive();
diff --git a/game/client/src/main/resources/shaders/font.vsh b/game/client/src/main/resources/shaders/font.vsh
index 3c9e8bb1a..a8f45ac77 100644
--- a/game/client/src/main/resources/shaders/font.vsh
+++ b/game/client/src/main/resources/shaders/font.vsh
@@ -24,7 +24,7 @@ void main() {
     vs_out.size = aSize;
     vs_out.shift = aShift;
     vs_out.color = aColor;
-    vs_out.uv = aUv;
-    vs_out.uvSizes = aUvSizes;
+    vs_out.uv = aUv/256f;
+    vs_out.uvSizes = aUvSizes/256f;
     vs_out.lightmap = aLightmap;
 }
diff --git a/game/client/src/main/resources/shaders/interface.fsh b/game/client/src/main/resources/shaders/interface.fsh
index ded77c9ca..8017968fb 100644
--- a/game/client/src/main/resources/shaders/interface.fsh
+++ b/game/client/src/main/resources/shaders/interface.fsh
@@ -5,9 +5,9 @@ out vec4 FragColor;
 in vec4 Color;
 in vec2 TexCoord;
 
-uniform vec4 ucolor;
+uniform vec4 uColor;
 uniform sampler2D texture0;
 
 void main() {
-    FragColor = texture(texture0, TexCoord) * Color * ucolor;
+    FragColor = texture(texture0, TexCoord) * Color * uColor;
 }
diff --git a/game/client/src/main/resources/shaders/interface.vsh b/game/client/src/main/resources/shaders/interface.vsh
index 1df6d673c..f01aa4cf6 100644
--- a/game/client/src/main/resources/shaders/interface.vsh
+++ b/game/client/src/main/resources/shaders/interface.vsh
@@ -7,11 +7,15 @@ out vec4 Color;
 out vec2 TexCoord;
 
 uniform mat4 model;
-uniform mat4 view;
-uniform mat4 projection;
+layout (std140) uniform Matrices {
+    mat4 projection;
+    mat4 projectionInv;
+    mat4 view;
+    mat4 viewInv;
+} matrices;
 
 void main() {
-    gl_Position = projection * view * model * vec4(aPos, 1);
+    gl_Position = matrices.projection * matrices.view * model * vec4(aPos, 1);
 
     TexCoord = aUV;
     Color = aColor;
diff --git a/game/client/src/main/resources/shaders/lines.fsh b/game/client/src/main/resources/shaders/lines.fsh
index 8b8fb3a78..856034cf0 100644
--- a/game/client/src/main/resources/shaders/lines.fsh
+++ b/game/client/src/main/resources/shaders/lines.fsh
@@ -2,10 +2,10 @@
 out vec4 FragColor;
 
 in vec4 Color;
-uniform vec4 ucolor;
+uniform vec4 uColor;
 
 void main() {
-    FragColor = Color * ucolor;
+    FragColor = Color * uColor;
     if (FragColor.a < 0.1f) {
         discard;
     }
diff --git a/game/client/src/main/resources/shaders/lines.gsh b/game/client/src/main/resources/shaders/lines.gsh
index 811cb2e7a..9dfe7be05 100644
--- a/game/client/src/main/resources/shaders/lines.gsh
+++ b/game/client/src/main/resources/shaders/lines.gsh
@@ -8,8 +8,7 @@ in VS_OUT {
 
 out vec4 Color;
 
-uniform float viewWidth;
-uniform float viewHeight;
+uniform vec2 uViewPortSize;
 uniform float uLineWidth;
 
 void main()
@@ -17,8 +16,8 @@ void main()
     vec4 p1 = gl_in[0].gl_Position;
     vec4 p2 = gl_in[1].gl_Position;
 
-    vec2 dir    = normalize((p2.xy/p2.w - p1.xy/p1.w) * vec2(viewWidth, viewHeight));
-    vec2 offset = vec2(-dir.y, dir.x) * uLineWidth / vec2(viewWidth, viewHeight);
+    vec2 dir    = normalize((p2.xy/p2.w - p1.xy/p1.w) * uViewPortSize);
+    vec2 offset = vec2(-dir.y, dir.x) * uLineWidth / uViewPortSize;
 
     Color = gs_in[0].color;
     gl_Position = p1 + vec4(offset.xy * p1.w, 0.0, 0.0);
diff --git a/game/client/src/main/resources/shaders/lines.vsh b/game/client/src/main/resources/shaders/lines.vsh
index e88cd1228..828627c93 100644
--- a/game/client/src/main/resources/shaders/lines.vsh
+++ b/game/client/src/main/resources/shaders/lines.vsh
@@ -8,11 +8,15 @@ out VS_OUT {
 } vs_out;
 
 uniform mat4 model;
-uniform mat4 view;
-uniform mat4 projection;
+layout (std140) uniform Matrices {
+    mat4 projection;
+    mat4 projectionInv;
+    mat4 view;
+    mat4 viewInv;
+} matrices;
 
 void main() {
-    gl_Position = projection * view * model * vec4(aPos, 1);
+    gl_Position = matrices.projection * matrices.view * model * vec4(aPos, 1);
 
     vs_out.color = aColor;
 }
diff --git a/game/client/src/main/resources/shaders/particle.gsh b/game/client/src/main/resources/shaders/particle.gsh
index 00be1c410..fe109c241 100644
--- a/game/client/src/main/resources/shaders/particle.gsh
+++ b/game/client/src/main/resources/shaders/particle.gsh
@@ -15,28 +15,31 @@ out vec2 TexCoord;
 out vec2 Lightmap;
 
 uniform mat4 model;
-uniform mat4 view;
-uniform mat4 viewInv;
-uniform mat4 projection;
+layout (std140) uniform Matrices {
+    mat4 projection;
+    mat4 projectionInv;
+    mat4 view;
+    mat4 viewInv;
+} matrices;
 
 void main() {
     Color = gs_in[0].color;
     Lightmap = gs_in[0].lightmap;
 
     TexCoord = gs_in[0].uv;
-    gl_Position = projection * view * model * (gl_in[0].gl_Position + viewInv * vec4(-gs_in[0].scale,  gs_in[0].scale, 0.0, 0.0));
+    gl_Position = matrices.projection * matrices.view * model * (gl_in[0].gl_Position + matrices.viewInv * vec4(-gs_in[0].scale,  gs_in[0].scale, 0.0, 0.0));
     EmitVertex();
 
     TexCoord = gs_in[0].uv + vec2(0, gs_in[0].uvSizes.y);
-    gl_Position = projection * view * model * (gl_in[0].gl_Position + viewInv * vec4(-gs_in[0].scale, -gs_in[0].scale, 0.0, 0.0));
+    gl_Position = matrices.projection * matrices.view * model * (gl_in[0].gl_Position + matrices.viewInv * vec4(-gs_in[0].scale, -gs_in[0].scale, 0.0, 0.0));
     EmitVertex();
 
     TexCoord = gs_in[0].uv + vec2(gs_in[0].uvSizes.x, 0);
-    gl_Position = projection * view * model * (gl_in[0].gl_Position + viewInv * vec4( gs_in[0].scale,  gs_in[0].scale, 0.0, 0.0));
+    gl_Position = matrices.projection * matrices.view * model * (gl_in[0].gl_Position + matrices.viewInv * vec4( gs_in[0].scale,  gs_in[0].scale, 0.0, 0.0));
     EmitVertex();
 
     TexCoord = gs_in[0].uv + gs_in[0].uvSizes;
-    gl_Position = projection * view * model * (gl_in[0].gl_Position + viewInv * vec4( gs_in[0].scale, -gs_in[0].scale, 0.0, 0.0));
+    gl_Position = matrices.projection * matrices.view * model * (gl_in[0].gl_Position + matrices.viewInv * vec4( gs_in[0].scale, -gs_in[0].scale, 0.0, 0.0));
     EmitVertex();
 
     EndPrimitive();
diff --git a/game/client/src/main/resources/shaders/particle.vsh b/game/client/src/main/resources/shaders/particle.vsh
index 429cab385..f9bbfcdcd 100644
--- a/game/client/src/main/resources/shaders/particle.vsh
+++ b/game/client/src/main/resources/shaders/particle.vsh
@@ -14,10 +14,6 @@ out VS_OUT {
     vec2 lightmap;
 } vs_out;
 
-uniform mat4 model;
-uniform mat4 view;
-uniform mat4 projection;
-
 void main() {
     gl_Position = vec4(aPos, 1);
 
diff --git a/game/client/src/main/resources/shaders/post/showdepth.fsh b/game/client/src/main/resources/shaders/post/showdepth.fsh
index bc1360925..b9a62f6d9 100644
--- a/game/client/src/main/resources/shaders/post/showdepth.fsh
+++ b/game/client/src/main/resources/shaders/post/showdepth.fsh
@@ -1,13 +1,14 @@
-#version 120
+#version 410
 
-uniform sampler2D depthtex0;
+out vec4 FragColor;
+in vec2 TexCoords;
 
-varying vec2 texcoord;
+uniform sampler2D depthtex0;
 
 void main(){
-	float depth = texture(depthtex0, texcoord).r;
+	float depth = texture(depthtex0, TexCoords).r;
 	
 	depth = pow(depth, 1000);
 	
-	gl_FragColor = vec4(depth, depth, depth, 1.0);
+	FragColor = vec4(depth, depth, depth, 1.0);
 }
\ No newline at end of file
diff --git a/game/client/src/main/resources/shaders/post/showdepth.vsh b/game/client/src/main/resources/shaders/post/showdepth.vsh
index 9c7ba53f5..bceceacef 100644
--- a/game/client/src/main/resources/shaders/post/showdepth.vsh
+++ b/game/client/src/main/resources/shaders/post/showdepth.vsh
@@ -1,9 +1,10 @@
-#version 120
+#version 410 core
+layout (location = 0) in vec2 aPos;
+layout (location = 1) in vec2 aTexCoords;
 
-varying vec2 texcoord;
+out vec2 TexCoords;
 
 void main(){
-	gl_Position = ftransform();
-	
-	texcoord = gl_MultiTexCoord0.xy;
+    TexCoords = aTexCoords;
+    gl_Position = vec4(aPos, 0.0, 1.0);
 }
\ No newline at end of file
diff --git a/game/client/src/main/resources/shaders/world.fsh b/game/client/src/main/resources/shaders/world.fsh
index 9d9f5081b..e4ecc285d 100644
--- a/game/client/src/main/resources/shaders/world.fsh
+++ b/game/client/src/main/resources/shaders/world.fsh
@@ -5,55 +5,71 @@ out vec4 FragColor;
 in vec4 Color;
 in vec2 TexCoord;
 in vec2 LightMapCoord;
-in float Distance;
 in vec3 Light;
 
-uniform vec4 ucolor;
+uniform vec4 uColor;
+uniform vec2 uViewPortSize;
 
-uniform vec4 ufogColor;
-uniform int ufogMode;
-uniform float ufogStart;
-uniform float ufogEnd;
-uniform float ufogDensity;
+layout (std140) uniform FogState {
+    vec4 color;
+    int mode;
+    float start;
+    float end;
+    float density;
+} fogState;
+
+layout (std140) uniform Matrices {
+    mat4 projection;
+    mat4 projectionInv;
+    mat4 view;
+    mat4 viewInv;
+} matrices;
 
 uniform sampler2D texture0;
 uniform sampler2D texture1;
 
+float distance() {
+    vec4 fragPos = matrices.projectionInv * vec4((gl_FragCoord.xy / uViewPortSize) * 2.0f - 1.0f, gl_FragCoord.z * 2.0f - 1.0f, 1.0f);
+    fragPos /= fragPos.w;
+
+    return length(fragPos.xyz);
+}
+
 // Taken from https://github.com/hughsk/glsl-fog
 float fogFactorLinear() {
-    return 1.0 - clamp(((ufogEnd - (Distance)) / (ufogEnd - ufogStart)), 0.0, 1.0);
+    return 1.0 - clamp(((fogState.end - (distance())) / (fogState.end - fogState.start)), 0.0, 1.0);
 }
 
 float fogFactorExp() {
-    return 1.0 - clamp(exp(-ufogDensity * (Distance)), 0.0, 1.0);
+    return 1.0 - clamp(exp(-fogState.density * (distance())), 0.0, 1.0);
 }
 
 float fogFactorExp2() {
     float LOG2 = -1.442695;
-    float d = ufogDensity * (Distance);
+    float d = fogState.density * (distance());
     return 1.0 - clamp(exp2(d * d * LOG2), 0.0, 1.0);
 }
 
 
 vec4 computeFog(vec4 color) {
     float f = 0;
-    if (ufogMode == 0) { // Linear
+    if (fogState.mode == 0) { // Linear
         f = fogFactorLinear();
     }
-    else if (ufogMode == 1) { // EXP
+    else if (fogState.mode == 1) { // EXP
         f = fogFactorExp();
     }
-    else if (ufogMode == 2) { // EXP2
+    else if (fogState.mode == 2) { // EXP2
         f = fogFactorExp2();
     }
-    return vec4(mix(color.rgb, ufogColor.rgb, f), color.a);
+    return vec4(mix(color.rgb, fogState.color.rgb, f), color.a);
 }
 
 void main() {
     vec4 lightColor = texture(texture1, LightMapCoord);
     vec4 texColor = texture(texture0, TexCoord);
 
-    vec4 oColor = texColor * lightColor * Color * ucolor;
+    vec4 oColor = texColor * lightColor * Color * uColor;
     vec4 litColor = vec4(oColor.rgb * clamp(Light, 0, 1), oColor.a);
 
     vec4 fColor = computeFog(litColor);
diff --git a/game/client/src/main/resources/shaders/world.vsh b/game/client/src/main/resources/shaders/world.vsh
index 97d6d3f8e..fdc0c5a29 100644
--- a/game/client/src/main/resources/shaders/world.vsh
+++ b/game/client/src/main/resources/shaders/world.vsh
@@ -8,7 +8,6 @@ layout (location = 4) in vec3 aNormal;
 out vec4 Color;
 out vec2 TexCoord;
 out vec2 LightMapCoord;
-out float Distance;
 out vec3 Light;
 
 uniform mat4 normalTransform;
@@ -17,20 +16,22 @@ uniform vec4 lightSourcePosition[];
 uniform vec4 lightSourceDiffuse[];
 
 uniform mat4 model;
-uniform mat4 view;
-uniform mat4 projection;
+
+layout (std140) uniform Matrices {
+    mat4 projection;
+    mat4 projectionInv;
+    mat4 view;
+    mat4 viewInv;
+} matrices;
 
 void main() {
     vec4 p = vec4(aPos, 1);
-    gl_Position = projection * view * model * p;
+    gl_Position = matrices.projection * matrices.view * model * p;
 
     TexCoord = aUV;
     Color = aColor;
     LightMapCoord = aLightmap;
 
-    // Used for the radial fog in shader
-    Distance = length((view * model * p).xyz);
-
     vec4 norm = normalize(normalTransform * model * vec4(aNormal, 0));
     float dir1 = max(0.0f, dot(norm, lightSourcePosition[0]));
     float dir2 = max(0.0f, dot(norm, lightSourcePosition[1]));
diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/IceFloeLargeFeature.java b/game/core/src/main/java/net/minecraft/core/world/generate/IceFloeLargeFeature.java
index b1c7abc79..db184f2c9 100644
--- a/game/core/src/main/java/net/minecraft/core/world/generate/IceFloeLargeFeature.java
+++ b/game/core/src/main/java/net/minecraft/core/world/generate/IceFloeLargeFeature.java
@@ -1,6 +1,5 @@
 package net.minecraft.core.world.generate;
 
-import net.minecraft.core.block.Block;
 import net.minecraft.core.block.Blocks;
 import net.minecraft.core.util.helper.MathHelper;
 import net.minecraft.core.world.World;
@@ -21,14 +20,14 @@ import java.util.Random;
 public class IceFloeLargeFeature
     extends LargeFeature {
     private @Nullable PerlinNoise perlinNoise = null;
-    private @Nullable PerlinNoise heightPerlinNoise = null;
+    private @Nullable PerlinNoise scalePerlinNoise = null;
     private @Nullable World lastWorld = null;
 
     @Override
     protected void doGeneration(@NotNull final World world, @NotNull final Random random, final int chunkX, final int chunkZ, final int baseChunkX, final int baseChunkZ, @NotNull final ChunkGeneratorResult result) {
-        if (this.perlinNoise == null || this.heightPerlinNoise == null || this.lastWorld != world) {
+        if (this.perlinNoise == null || this.scalePerlinNoise == null || this.lastWorld != world) {
             this.perlinNoise = new PerlinNoise(world.getRandomSeed(), 4);
-            this.heightPerlinNoise = new PerlinNoise(world.getRandomSeed(), 8);
+            this.scalePerlinNoise = new PerlinNoise(world.getRandomSeed(), 2);
             this.lastWorld = world;
         }
 
@@ -44,11 +43,13 @@ public class IceFloeLargeFeature
                 tilePos.set(Chunk.CHUNK_SIZE_X * baseChunkX + x, oceanY, Chunk.CHUNK_SIZE_Z * baseChunkZ + z);
                 final @NotNull Biome biome = world.getBlockBiome(tilePos);
                 if (biome == Biomes.OVERWORLD_GLACIER) {
-                    vec.x = baseChunkX + ((float) x / (float) Chunk.CHUNK_SIZE_X);
-                    vec.y = baseChunkZ + ((float) z / (float) Chunk.CHUNK_SIZE_Z);
+                    final float worleyScaleScale = 50.0f;
+                    final float worleyScale = 0.875f + (float) (this.scalePerlinNoise.getNormalized((baseChunkX * Chunk.CHUNK_SIZE_X + x) / worleyScaleScale, (baseChunkZ * Chunk.CHUNK_SIZE_Z + z) / worleyScaleScale)) * 0.25f;
+                    vec.x = (baseChunkX + ((float) x / (float) Chunk.CHUNK_SIZE_X)) * worleyScale;
+                    vec.y = (baseChunkZ + ((float) z / (float) Chunk.CHUNK_SIZE_Z)) * worleyScale;
                     WorleyNoise.getWorleyDistance2d(vec, worley);
-                    final float scale = 10.0f;
-                    final float lineWidth = (float) (this.perlinNoise.get((baseChunkX * Chunk.CHUNK_SIZE_X + x) / scale, (baseChunkZ * Chunk.CHUNK_SIZE_Z + z) / scale) / 2.0 + 0.5);
+                    final float lineScale = 10.0f;
+                    final float lineWidth = (float) (this.perlinNoise.get((baseChunkX * Chunk.CHUNK_SIZE_X + x) / lineScale, (baseChunkZ * Chunk.CHUNK_SIZE_Z + z) / lineScale) / 2.0 + 0.5);
                     if (MathHelper.unsignedMod(worley.index, 15) != 0) {
                         if (worley.distance > 0.9f + lineWidth * 0.05f) {
                             continue;
diff --git a/game/core/src/main/java/net/minecraft/core/world/noise/BaseImprovedNoise.java b/game/core/src/main/java/net/minecraft/core/world/noise/BaseImprovedNoise.java
index 0301eeb7d..00d57e28d 100644
--- a/game/core/src/main/java/net/minecraft/core/world/noise/BaseImprovedNoise.java
+++ b/game/core/src/main/java/net/minecraft/core/world/noise/BaseImprovedNoise.java
@@ -1,91 +1,120 @@
 package net.minecraft.core.world.noise;
 
+import org.jetbrains.annotations.NotNull;
+
 import java.util.Random;
 
-public abstract class BaseImprovedNoise
-{
-    protected final int[] p;
+// Derived from Ken Perlin's Improved Noise reference implementation: https://cs.nyu.edu/~perlin/noise/
+public abstract class BaseImprovedNoise {
+    protected final int @NotNull [] p;
     public final double xo;
     public final double yo;
     public final double zo;
 
-    public BaseImprovedNoise(Random random) {
+    public BaseImprovedNoise(final @NotNull Random random) {
         this.p = new int[512];
         this.xo = random.nextDouble() * 256;
         this.yo = random.nextDouble() * 256;
         this.zo = random.nextDouble() * 256;
 
         // Assign initial values for each permutation
-        for (int i = 0; i < 256; i++)
-        {
-            p[i] = i;
+        for (int i = 0; i < 256; i++) {
+            this.p[i] = i;
         }
 
         // Shuffle values
-        for(int i = 0; i < 256; i++)
-        {
-            int newI = random.nextInt(256 - i) + i;
-            int temp = this.p[i];
+        for (int i = 0; i < 256; i++) {
+            final int newI = random.nextInt(256 - i) + i;
+            final int temp = this.p[i];
             this.p[i] = this.p[newI];
             this.p[newI] = temp;
             this.p[i + 256] = this.p[i];
         }
     }
 
-    public double getValue(double x, double y)
-    {
+    public double getValue(final double x, final double y) {
         return this.getValue(x, y, 0);
     }
 
-    public double getValue(double x, double y, double z)
-    {
-        int X = (int)((long)Math.floor(x) & 255),                  // FIND UNIT CUBE THAT
-            Y = (int)((long)Math.floor(y) & 255),                  // CONTAINS POINT.
-            Z = (int)((long)Math.floor(z) & 255);
-        x -= Math.floor(x);                                // FIND RELATIVE X,Y,Z
-        y -= Math.floor(y);                                // OF POINT IN CUBE.
+    public double getValue(double x, double y, double z) {
+        // Find unit cube that contains point
+        final int X = (int) Math.floor(x) & 255;
+        final int Y = (int) Math.floor(y) & 255;
+        final int Z = (int) Math.floor(z) & 255;
+
+        // Find relative XYZ of point in cube
+        x -= Math.floor(x);
+        y -= Math.floor(y);
         z -= Math.floor(z);
-        double u = fade(x),                                // COMPUTE FADE CURVES
-               v = fade(y),                                // FOR EACH OF X,Y,Z.
-               w = fade(z);
-        int A = p[X  ]+Y, AA = p[A]+Z, AB = p[A+1]+Z,      // HASH COORDINATES OF
-            B = p[X+1]+Y, BA = p[B]+Z, BB = p[B+1]+Z;      // THE 8 CUBE CORNERS,
-
-        return lerp(w, lerp(v, lerp(u, grad(p[AA  ], x  , y  , z   ),  // AND ADD
-                                                            grad(p[BA  ], x-1, y  , z   )), // BLENDED
-                                             lerp(u, grad(p[AB  ], x  , y-1, z   ),  // RESULTS
-                                                            grad(p[BB  ], x-1, y-1, z   ))),// FROM  8
-                              lerp(v, lerp(u, grad(p[AA+1], x  , y  , z-1 ),  // CORNERS
-                                                            grad(p[BA+1], x-1, y  , z-1 )), // OF CUBE
-                                             lerp(u, grad(p[AB+1], x  , y-1, z-1 ),
-                                                         grad(p[BB+1], x-1, y-1, z-1 ))));
+
+        // Compute fade curves for each of X, Y, and Z
+        final double u = this.fade(x);
+        final double v = this.fade(y);
+        final double w = this.fade(z);
+
+        // Hash coordinates of the 8 cube corners
+        final int A  = this.p[X    ] + Y;
+        final int AA = this.p[A    ] + Z;
+        final int AB = this.p[A + 1] + Z;
+        final int B =  this.p[X + 1] + Y;
+        final int BA = this.p[B    ] + Z;
+        final int BB = this.p[B + 1] + Z;
+
+        // Add blended results from 8 corners of the cube
+        return this.lerp(
+            w,
+            this.lerp(
+                v,
+                this.lerp(
+                    u,
+                    this.grad(this.p[AA], x, y, z),
+                    this.grad(this.p[BA], x - 1, y, z)
+                ),
+                this.lerp(
+                    u,
+                    this.grad(this.p[AB], x, y - 1, z),
+                    this.grad(this.p[BB], x - 1, y - 1, z)
+                )
+            ),
+            this.lerp(
+                v,
+                this.lerp(
+                    u,
+                    this.grad(this.p[AA + 1], x, y, z - 1),
+                    this.grad(this.p[BA + 1], x - 1, y, z - 1)),
+                this.lerp(
+                    u,
+                    this.grad(this.p[AB + 1], x, y - 1, z - 1),
+                    this.grad(this.p[BB + 1], x - 1, y - 1, z - 1)
+                )
+            )
+        );
     }
 
-    protected double grad(int hash, double x, double y)
-    {
-        int j = hash & 0xf;
-        double d2 = (double)(1 - ((j & 8) >> 3)) * x;
-        double d3 = j >= 4 ? j != 12 && j != 14 ? y : x : 0.0D;
-        return ((j & 1) != 0 ? -d2 : d2) + ((j & 2) != 0 ? -d3 : d3);
+    protected double grad2(final int hash, final double x, final double y) {
+        final int h = hash & 15;
+        final double u = (double) (1 - ((h & 8) >> 3)) * x;
+        final double v = h < 4 ? 0.0D : h == 12 || h == 14 ? x : y;
+
+        return ((h & 1) != 0 ? -u : u) + ((h & 2) != 0 ? -v : v);
     }
 
-    protected double fade(double t)
-    {
+    protected double fade(final double t) {
         return t * t * t * (t * (t * 6 - 15) + 10);
     }
 
-    protected double lerp(double t, double a, double b)
-    {
+    protected double lerp(final double t, final double a, final double b) {
         return a + t * (b - a);
     }
 
-    protected double grad(int hash, double x, double y, double z)
-    {
-        int h = hash & 15;                      // CONVERT LO 4 BITS OF HASH CODE
-        double u = h<8 ? x : y,                 // INTO 12 GRADIENT DIRECTIONS.
-                v = h<4 ? y : h==12||h==14 ? x : z;
-        return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
+    protected double grad(final int hash, final double x, final double y, final double z) {
+        // Converts low 4 bits of hash code into 12 gradient directions.
+        final int h = hash & 15;
+        final double u = h < 8 ? x : y;
+        final double v = h < 4 ? y : h == 12 || h == 14 ? x : z;
+
+        return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
     }
 
-    public abstract void add(double[] arr, double x, double y, double z, int xSize, int ySize, int zSize, double xScale, double yScale, double zScale, double levelScale);
+    public abstract void add(double[] out, double x, double y, double z, int xSize, int ySize, int zSize, double xScale, double yScale, double zScale, double valueScale);
 }
diff --git a/game/core/src/main/java/net/minecraft/core/world/noise/BasePerlinNoise.java b/game/core/src/main/java/net/minecraft/core/world/noise/BasePerlinNoise.java
index 6657ee29f..578e223c0 100644
--- a/game/core/src/main/java/net/minecraft/core/world/noise/BasePerlinNoise.java
+++ b/game/core/src/main/java/net/minecraft/core/world/noise/BasePerlinNoise.java
@@ -6,8 +6,11 @@ import java.util.Random;
 public abstract class BasePerlinNoise<T extends BaseImprovedNoise>
     extends SurfaceNoise
 {
+    private static final double MAX_2D = Math.sqrt(2.0 / 4.0);
+
     private final T[] octaves;
     private final int numOctaves;
+    final double maxValue;
 
     public BasePerlinNoise(long seed, int numOctaves)
     {
@@ -31,6 +34,15 @@ public abstract class BasePerlinNoise<T extends BaseImprovedNoise>
         }
         this.numOctaves = numOctaves;
         this.octaves = newOctaves(random, numOctaves);
+
+        double maxValue = 0.0;
+        double levelScale = 1.0;
+        for (int i = 0; i < this.numOctaves; i++) {
+            maxValue += MAX_2D / levelScale;
+            levelScale /= 2.0;
+        }
+
+        this.maxValue = maxValue;
     }
 
     protected abstract T[] newOctaves(Random random, int numOctaves);
@@ -49,6 +61,18 @@ public abstract class BasePerlinNoise<T extends BaseImprovedNoise>
         return out;
     }
 
+    public double getNormalized(double x, double y) {
+        final double value = this.get(x, y);
+
+        // Map to +-1.0
+        double workingValue = value / this.maxValue;
+
+        // Map to 0.0-1.0
+        workingValue = 0.5 + (workingValue / 2.0);
+
+        return workingValue;
+    }
+
     public double[] get(double[] noiseArray, double x, double y, double z,
                         int xSize, int ySize, int zSize, double scaleX, double scaleY,
                         double scaleZ)
diff --git a/game/core/src/main/java/net/minecraft/core/world/noise/BetaImprovedNoise.java b/game/core/src/main/java/net/minecraft/core/world/noise/BetaImprovedNoise.java
deleted file mode 100644
index 4e88dac6e..000000000
--- a/game/core/src/main/java/net/minecraft/core/world/noise/BetaImprovedNoise.java
+++ /dev/null
@@ -1,131 +0,0 @@
-package net.minecraft.core.world.noise;
-
-import java.util.Random;
-
-public class BetaImprovedNoise
-    extends BaseImprovedNoise
-{
-    public BetaImprovedNoise(final Random random) {
-        super(random);
-    }
-
-    @Override
-    public void add(final double[] arr, final double x, final double y, final double z, final int xSize, final int ySize, final int zSize, final double xScale, final double yScale, final double zScale, final double levelScale) {
-        if (ySize == 1) {
-            int var64 = 0;
-            int var66 = 0;
-            int var21 = 0;
-            int var69 = 0;
-            double var72 = 0.0;
-            double var76 = 0.0;
-            int var80 = 0;
-            double var82 = 1.0 / levelScale;
-
-            for (int var30 = 0; var30 < xSize; var30++) {
-                double var83 = (x + (double)var30) * xScale + this.xo;
-                int var85 = (int)var83;
-                if (var83 < (double)var85) {
-                    var85--;
-                }
-
-                int var34 = var85 & 0xFF;
-                var83 -= (double)var85;
-                double var86 = var83 * var83 * var83 * (var83 * (var83 * 6.0 - 15.0) + 10.0);
-
-                for (int var87 = 0; var87 < zSize; var87++) {
-                    double var89 = (z + (double)var87) * zScale + this.zo;
-                    int var91 = (int)var89;
-                    if (var89 < (double)var91) {
-                        var91--;
-                    }
-
-                    int var92 = var91 & 0xFF;
-                    var89 -= (double)var91;
-                    double var93 = var89 * var89 * var89 * (var89 * (var89 * 6.0 - 15.0) + 10.0);
-                    var64 = this.p[var34] + 0;
-                    var66 = this.p[var64] + var92;
-                    var21 = this.p[var34 + 1] + 0;
-                    var69 = this.p[var21] + var92;
-                    var72 = this.lerp(var86, this.grad(this.p[var66], var83, var89), this.grad(this.p[var69], var83 - 1.0, 0.0, var89));
-                    var76 = this.lerp(var86, this.grad(this.p[var66 + 1], var83, 0.0, var89 - 1.0), this.grad(this.p[var69 + 1], var83 - 1.0, 0.0, var89 - 1.0));
-                    double var94 = this.lerp(var93, var72, var76);
-                    arr[var80++] += var94 * var82;
-                }
-            }
-        } else {
-            int var19 = 0;
-            double var20 = 1.0 / levelScale;
-            int var22 = -1;
-            int var23 = 0;
-            int var24 = 0;
-            int var25 = 0;
-            int var26 = 0;
-            int var27 = 0;
-            int var28 = 0;
-            double var29 = 0.0;
-            double var31 = 0.0;
-            double var33 = 0.0;
-            double var35 = 0.0;
-
-            for (int var37 = 0; var37 < xSize; var37++) {
-                double var38 = (x + (double)var37) * xScale + this.xo;
-                int var40 = (int)var38;
-                if (var38 < (double)var40) {
-                    var40--;
-                }
-
-                int var41 = var40 & 0xFF;
-                var38 -= (double)var40;
-                double var42 = var38 * var38 * var38 * (var38 * (var38 * 6.0 - 15.0) + 10.0);
-
-                for (int var44 = 0; var44 < zSize; var44++) {
-                    double var45 = (z + (double)var44) * zScale + this.zo;
-                    int var47 = (int)var45;
-                    if (var45 < (double)var47) {
-                        var47--;
-                    }
-
-                    int var48 = var47 & 0xFF;
-                    var45 -= (double)var47;
-                    double var49 = var45 * var45 * var45 * (var45 * (var45 * 6.0 - 15.0) + 10.0);
-
-                    for (int var51 = 0; var51 < ySize; var51++) {
-                        double var52 = (y + (double)var51) * yScale + this.yo;
-                        int var54 = (int)var52;
-                        if (var52 < (double)var54) {
-                            var54--;
-                        }
-
-                        int var55 = var54 & 0xFF;
-                        var52 -= (double)var54;
-                        double var56 = var52 * var52 * var52 * (var52 * (var52 * 6.0 - 15.0) + 10.0);
-                        if (var51 == 0 || var55 != var22) {
-                            var22 = var55;
-                            var23 = this.p[var41] + var55;
-                            var24 = this.p[var23] + var48;
-                            var25 = this.p[var23 + 1] + var48;
-                            var26 = this.p[var41 + 1] + var55;
-                            var27 = this.p[var26] + var48;
-                            var28 = this.p[var26 + 1] + var48;
-                            var29 = this.lerp(var42, this.grad(this.p[var24], var38, var52, var45), this.grad(this.p[var27], var38 - 1.0, var52, var45));
-                            var31 = this.lerp(var42, this.grad(this.p[var25], var38, var52 - 1.0, var45), this.grad(this.p[var28], var38 - 1.0, var52 - 1.0, var45));
-                            var33 = this.lerp(
-                                var42, this.grad(this.p[var24 + 1], var38, var52, var45 - 1.0), this.grad(this.p[var27 + 1], var38 - 1.0, var52, var45 - 1.0)
-                            );
-                            var35 = this.lerp(
-                                var42,
-                                this.grad(this.p[var25 + 1], var38, var52 - 1.0, var45 - 1.0),
-                                this.grad(this.p[var28 + 1], var38 - 1.0, var52 - 1.0, var45 - 1.0)
-                            );
-                        }
-
-                        double var58 = this.lerp(var56, var29, var31);
-                        double var60 = this.lerp(var56, var33, var35);
-                        double var62 = this.lerp(var49, var58, var60);
-                        arr[var19++] += var62 * var20;
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/game/core/src/main/java/net/minecraft/core/world/noise/BetaPerlinNoise.java b/game/core/src/main/java/net/minecraft/core/world/noise/BetaPerlinNoise.java
index a117b2687..60b83da3b 100644
--- a/game/core/src/main/java/net/minecraft/core/world/noise/BetaPerlinNoise.java
+++ b/game/core/src/main/java/net/minecraft/core/world/noise/BetaPerlinNoise.java
@@ -3,7 +3,7 @@ package net.minecraft.core.world.noise;
 import java.util.Random;
 
 public class BetaPerlinNoise
-    extends BasePerlinNoise<BetaImprovedNoise>
+    extends BasePerlinNoise<ImprovedNoise>
 {
     public BetaPerlinNoise(final long seed, final int numOctaves) {
         super(seed, numOctaves);
@@ -14,11 +14,11 @@ public class BetaPerlinNoise
     }
 
     @Override
-    protected BetaImprovedNoise[] newOctaves(final Random random, final int numOctaves) {
-        BetaImprovedNoise[] levels = new BetaImprovedNoise[numOctaves];
+    protected ImprovedNoise[] newOctaves(final Random random, final int numOctaves) {
+        ImprovedNoise[] levels = new ImprovedNoise[numOctaves];
 
         for (int i = 0; i < numOctaves; i++) {
-            levels[i] = new BetaImprovedNoise(random);
+            levels[i] = new ImprovedNoise(random, LegacyNoiseType.BETA);
         }
 
         return levels;
diff --git a/game/core/src/main/java/net/minecraft/core/world/noise/ImprovedNoise.java b/game/core/src/main/java/net/minecraft/core/world/noise/ImprovedNoise.java
index 036048bef..a2f3d8542 100644
--- a/game/core/src/main/java/net/minecraft/core/world/noise/ImprovedNoise.java
+++ b/game/core/src/main/java/net/minecraft/core/world/noise/ImprovedNoise.java
@@ -1,162 +1,220 @@
 package net.minecraft.core.world.noise;
 
+import org.jetbrains.annotations.NotNull;
+
 import java.util.Random;
 
 public class ImprovedNoise
-    extends BaseImprovedNoise
-{
-    public ImprovedNoise(Random random)
-    {
+    extends BaseImprovedNoise {
+    private final @NotNull LegacyNoiseType legacyNoiseType;
+
+    public ImprovedNoise(final @NotNull Random random, final @NotNull LegacyNoiseType legacyNoiseType) {
         super(random);
+
+        this.legacyNoiseType = legacyNoiseType;
+    }
+
+    public ImprovedNoise(final @NotNull Random random) {
+        this(random, LegacyNoiseType.NONE);
     }
 
     @Override
-    public void add(double[] densityArray, double x, double y, double z,
-                    int xSize, int ySize, int zSize, double xScale, double yScale,
-                    double zScale, double levelScale)
-    {
-        if(ySize == 1)
-        {
-            int j3 = 0;
-            double d12 = 1.0D / levelScale;
-            for(int i4 = 0; i4 < xSize; i4++)
-            {
-                double d14 = (x + (double)i4) * xScale + xo;
-                int j4 = (int)d14;
-                if(d14 < (double)j4)
-                {
-                    j4--;
-                }
-                int k4 = j4 & 0xff;
-                d14 -= j4;
-                double d17 = d14 * d14 * d14 * (d14 * (d14 * 6D - 15D) + 10D);
-                for(int l4 = 0; l4 < zSize; l4++)
-                {
-                    double d19 = (z + (double)l4) * zScale + zo;
-                    int j5 = (int)d19;
-                    if(d19 < (double)j5)
-                    {
-                        j5--;
-                    }
-                    int l5 = j5 & 0xff;
-                    d19 -= j5;
-                    double d21 = d19 * d19 * d19 * (d19 * (d19 * 6D - 15D) + 10D);
-                    int l = p[k4] + 0;
-                    int j1 = p[l] + l5;
-                    int k1 = p[k4 + 1] + 0;
-                    int l1 = p[k1] + l5;
-                    double d9 = lerp(d17, grad(p[j1], d14, d19), grad(p[l1], d14 - 1.0D, 0.0D, d19));
-                    double d11 = lerp(d17, grad(p[j1 + 1], d14, 0.0D, d19 - 1.0D), grad(p[l1 + 1], d14 - 1.0D, 0.0D, d19 - 1.0D));
-                    double d23 = lerp(d21, d9, d11);
-                    densityArray[j3++] += d23 * d12;
-                }
+    public void add(
+        final double @NotNull [] out,
+        final double x, final double y, final double z,
+        final int xSize, final int ySize, final int zSize,
+        final double xScale, final double yScale, final double zScale,
+        final double valueScale
+    ) {
+        final double scale = 1.0 / valueScale;
 
-            }
-            return;
-        }
-        if(zSize == 1)
-        {
-            int j3 = 0;
-            double d12 = 1.0D / levelScale;
-            for(int i4 = 0; i4 < xSize; i4++)
-            {
-                double d14 = (x + (double)i4) * xScale + xo;
-                int j4 = (int)d14;
-                if(d14 < (double)j4)
-                {
-                    j4--;
-                }
-                int k4 = j4 & 0xff;
-                d14 -= j4;
-                double d17 = d14 * d14 * d14 * (d14 * (d14 * 6D - 15D) + 10D);
-                for(int l4 = 0; l4 < ySize; l4++)
-                {
-                    double d19 = (y + (double)l4) * yScale + zo;
-                    int j5 = (int)d19;
-                    if(d19 < (double)j5)
-                    {
-                        j5--;
-                    }
-                    int l5 = j5 & 0xff;
-                    d19 -= j5;
-                    double d21 = d19 * d19 * d19 * (d19 * (d19 * 6D - 15D) + 10D);
-                    int l = p[k4] + 0;
-                    int j1 = p[l] + l5;
-                    int k1 = p[k4 + 1] + 0;
-                    int l1 = p[k1] + l5;
-                    double d9 = lerp(d17, grad(p[j1], d14, d19), grad(p[l1], d14 - 1.0D, 0.0D, d19));
-                    double d11 = lerp(d17, grad(p[j1 + 1], d14, 0.0D, d19 - 1.0D), grad(p[l1 + 1], d14 - 1.0D, 0.0D, d19 - 1.0D));
-                    double d23 = lerp(d21, d9, d11);
-                    densityArray[j3++] += d23 * d12;
-                }
+        if (ySize == 1 && this.legacyNoiseType.handleEdgeCaseY) {
+            int i = 0;
+            for (int xi = 0; xi < xSize; xi++) {
+                double xx = (x + xi) * xScale + this.xo;
 
-            }
+                // Find X coordinate of unit square that contains point
+                final int X = (int) Math.floor(xx) & 255;
 
-            return;
-        }
-        int i1 = 0;
-        double d7 = 1.0D / levelScale;
-        int i2 = -1;
-        double d13 = 0.0D;
-        double d15 = 0.0D;
-        double d16 = 0.0D;
-        double d18 = 0.0D;
-        for(int i5 = 0; i5 < xSize; i5++)
-        {
-            double d20 = (x + (double)i5) * xScale + xo;
-            int k5 = (int)d20;
-            if(d20 < (double)k5)
-            {
-                k5--;
+                // Find relative X of point in square
+                xx -= Math.floor(xx);
+
+                // Compute fade curve for X
+                final double u = this.fade(xx);
+
+                for (int zi = 0; zi < zSize; zi++) {
+                    double zz = (z + zi) * zScale + this.zo;
+
+                    // Find Z coordinate of unit square that contains point
+                    final int Z = (int) Math.floor(zz) & 255;
+
+                    // Find relative Z of point in square
+                    zz -= Math.floor(zz);
+
+                    // Compute fade curve for Z
+                    final double w = this.fade(zz);
+
+                    // Hash coordinates of the 4 square corners
+                    final int A =  this.p[X    ];
+                    final int AA = this.p[A    ] + Z;
+                    final int B =  this.p[X + 1];
+                    final int BA = this.p[B    ] + Z;
+
+                    // Add blended results from 4 corners of the square
+                    final double value = this.lerp(
+                        w,
+                        this.lerp(
+                            u,
+                            this.grad2(this.p[AA], xx, zz),
+                            this.grad(this.p[BA], xx - 1, 0, zz)
+                        ),
+                        this.lerp(
+                            u,
+                            this.grad(this.p[AA + 1], xx, 0, zz - 1),
+                            this.grad(this.p[BA + 1], xx - 1, 0, zz - 1)
+                        )
+                    );
+
+                    out[i++] += value * scale;
+                }
             }
-            int i6 = k5 & 0xff;
-            d20 -= k5;
-            double d22 = d20 * d20 * d20 * (d20 * (d20 * 6D - 15D) + 10D);
-            for(int j6 = 0; j6 < zSize; j6++)
-            {
-                double d24 = (z + (double)j6) * zScale + zo;
-                int k6 = (int)d24;
-                if(d24 < (double)k6)
-                {
-                    k6--;
+        } else if (zSize == 1 && this.legacyNoiseType.handleEdgeCaseZ) {
+            int i = 0;
+            for (int xi = 0; xi < xSize; xi++) {
+                double xx = (x + xi) * xScale + this.xo;
+
+                // Find X coordinate of unit square that contains point
+                final int X = (int) Math.floor(xx) & 255;
+
+                // Find relative X of point in square
+                xx -= Math.floor(xx);
+
+                // Compute fade curve for X
+                final double u = this.fade(xx);
+
+                for (int yi = 0; yi < ySize; yi++) {
+                    double yy = (y + yi) * yScale + this.zo;
+
+                    // Find Y coordinate of unit square that contains point
+                    final int Y = (int) Math.floor(yy) & 255;
+
+                    // Find relative Y of point in cube
+                    yy -= Math.floor(yy);
+
+                    // Compute fade curve for Y
+                    final double v = this.fade(yy);
+
+                    // Hash coordinates of the 4 square corners
+                    final int A =  this.p[X    ];
+                    final int AA = this.p[A    ] + Y;
+                    final int B =  this.p[X + 1];
+                    final int BA = this.p[B    ] + Y;
+
+                    // Add blended results from 4 corners of the square
+                    final double value = this.lerp(
+                        v,
+                        this.lerp(
+                            u,
+                            this.grad2(this.p[AA], xx, yy),
+                            this.grad(this.p[BA], xx - 1, 0, yy)
+                        ),
+                        this.lerp(
+                            u,
+                            this.grad(this.p[AA + 1], xx, 0, yy - 1),
+                            this.grad(this.p[BA + 1], xx - 1, 0, yy - 1)
+                        )
+                    );
+
+                    out[i++] += value * scale;
                 }
-                int l6 = k6 & 0xff;
-                d24 -= k6;
-                double d25 = d24 * d24 * d24 * (d24 * (d24 * 6D - 15D) + 10D);
-                for(int i7 = 0; i7 < ySize; i7++)
-                {
-                    double d26 = (y + (double)i7) * yScale + yo;
-                    int j7 = (int)d26;
-                    if(d26 < (double)j7)
-                    {
-                        j7--;
-                    }
-                    int k7 = j7 & 0xff;
-                    d26 -= j7;
-                    double d27 = d26 * d26 * d26 * (d26 * (d26 * 6D - 15D) + 10D);
-                    if(i7 == 0 || k7 != i2)
-                    {
-                        i2 = k7;
-                        int j2 = p[i6] + k7;
-                        int k2 = p[j2] + l6;
-                        int l2 = p[j2 + 1] + l6;
-                        int i3 = p[i6 + 1] + k7;
-                        int k3 = p[i3] + l6;
-                        int l3 = p[i3 + 1] + l6;
-                        d13 = lerp(d22, grad(p[k2], d20, d26, d24), grad(p[k3], d20 - 1.0D, d26, d24));
-                        d15 = lerp(d22, grad(p[l2], d20, d26 - 1.0D, d24), grad(p[l3], d20 - 1.0D, d26 - 1.0D, d24));
-                        d16 = lerp(d22, grad(p[k2 + 1], d20, d26, d24 - 1.0D), grad(p[k3 + 1], d20 - 1.0D, d26, d24 - 1.0D));
-                        d18 = lerp(d22, grad(p[l2 + 1], d20, d26 - 1.0D, d24 - 1.0D), grad(p[l3 + 1], d20 - 1.0D, d26 - 1.0D, d24 - 1.0D));
+            }
+        } else {
+            double lerpedX = 0.0;
+            double lerpedXNegY = 0.0;
+            double lerpedXNegZ = 0.0;
+            double lerpedXNegYNegZ = 0.0;
+
+            int i = 0;
+            int lastY = -1;
+            for (int xi = 0; xi < xSize; xi++) {
+                double xx = (x + xi) * xScale + this.xo;
+
+                // Find X coordinate of unit cube that contains point
+                final int X = (int) Math.floor(xx) & 255;
+
+                // Find relative X of point in cube
+                xx -= Math.floor(xx);
+
+                // Compute fade curve for X
+                final double u = this.fade(xx);
+
+                for (int zi = 0; zi < zSize; zi++) {
+                    double zz = (z + zi) * zScale + this.zo;
+
+                    // Find Z coordinate of unit cube that contains point
+                    final int Z = (int) Math.floor(zz) & 255;
+
+                    // Find relative Z of point in cube
+                    zz -= Math.floor(zz);
+
+                    // Compute fade curve for Z
+                    final double w = this.fade(zz);
+
+                    for (int yi = 0; yi < ySize; yi++) {
+                        double yy = (y + yi) * yScale + this.yo;
+
+                        // Find Y coordinate of unit cube that contains point
+                        final int Y = (int) Math.floor(yy) & 255;
+
+                        // Find relative Y of point in cube
+                        yy -= Math.floor(yy);
+
+                        // Compute fade curve for Y
+                        final double v = this.fade(yy);
+
+                        // Optimization - don't recalculate for same unit cube Y coordinate
+                        if (yi == 0 || Y != lastY) {
+                            lastY = Y;
+
+                            // Hash coordinates of the 8 cube corners
+                            final int A =  this.p[X    ] + Y;
+                            final int AA = this.p[A    ] + Z;
+                            final int AB = this.p[A + 1] + Z;
+                            final int B =  this.p[X + 1] + Y;
+                            final int BA = this.p[B    ] + Z;
+                            final int BB = this.p[B + 1] + Z;
+
+                            // Add blended results from 8 corners of the cube
+                            lerpedX = this.lerp(
+                                u,
+                                this.grad(this.p[AA], xx, yy, zz),
+                                this.grad(this.p[BA], xx - 1, yy, zz)
+                            );
+                            lerpedXNegY = this.lerp(
+                                u,
+                                this.grad(this.p[AB], xx, yy - 1, zz),
+                                this.grad(this.p[BB], xx - 1, yy - 1, zz)
+                            );
+                            lerpedXNegZ = this.lerp(
+                                u,
+                                this.grad(this.p[AA + 1], xx, yy, zz - 1),
+                                this.grad(this.p[BA + 1], xx - 1, yy, zz - 1)
+                            );
+                            lerpedXNegYNegZ = this.lerp(
+                                u,
+                                this.grad(this.p[AB + 1], xx, yy - 1, zz - 1),
+                                this.grad(this.p[BB + 1], xx - 1, yy - 1, zz - 1)
+                            );
+                        }
+
+                        final double lerpedXY = this.lerp(v, lerpedX, lerpedXNegY);
+                        final double lerpedXYNegZ = this.lerp(v, lerpedXNegZ, lerpedXNegYNegZ);
+                        final double lerpedXYZ = this.lerp(w, lerpedXY, lerpedXYNegZ);
+
+                        out[i++] += lerpedXYZ * scale;
                     }
-                    double d28 = lerp(d27, d13, d15);
-                    double d29 = lerp(d27, d16, d18);
-                    double d30 = lerp(d25, d28, d29);
-                    densityArray[i1++] += d30 * d7;
                 }
-
             }
-
         }
-
     }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/world/noise/LegacyNoiseType.java b/game/core/src/main/java/net/minecraft/core/world/noise/LegacyNoiseType.java
new file mode 100644
index 000000000..1d8b330f8
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/noise/LegacyNoiseType.java
@@ -0,0 +1,15 @@
+package net.minecraft.core.world.noise;
+
+public enum LegacyNoiseType {
+    NONE(true, true),
+    ALPHA(false, false),
+    BETA(true, false);
+
+    public final boolean handleEdgeCaseY;
+    public final boolean handleEdgeCaseZ;
+
+    LegacyNoiseType(final boolean handleEdgeCaseY, final boolean handleEdgeCaseZ) {
+        this.handleEdgeCaseY = handleEdgeCaseY;
+        this.handleEdgeCaseZ = handleEdgeCaseZ;
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/noise/RetroImprovedNoise.java b/game/core/src/main/java/net/minecraft/core/world/noise/RetroImprovedNoise.java
deleted file mode 100644
index 33a1491ba..000000000
--- a/game/core/src/main/java/net/minecraft/core/world/noise/RetroImprovedNoise.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package net.minecraft.core.world.noise;
-
-import java.util.Random;
-
-public class RetroImprovedNoise
-	extends BaseImprovedNoise
-{
-	public RetroImprovedNoise(Random random)
-	{
-		super(random);
-	}
-
-	public void add(double[] arr, double x, double y, double z, int xSize, int ySize, int zSize, double xScale, double yScale, double zScale, double levelScale)
-	{
-		int var19 = 0;
-		double var20 = 1.0D / levelScale;
-		int var22 = -1;
-		double var29 = 0.0D;
-		double var31 = 0.0D;
-		double var33 = 0.0D;
-		double var35 = 0.0D;
-
-		for(int ix = 0; ix < xSize; ++ix)
-		{
-			double X = (x + ix) * xScale + this.xo;
-			int Xi = (int)X;
-			if (X < Xi)
-			{
-				--Xi;
-			}
-
-			int Xb = Xi & 255;
-			X -= Xi;
-			double fadeX = X * X * X * (X * (X * 6 - 15) + 10);
-
-			for(int iz = 0; iz < zSize; ++iz)
-			{
-				double Z = (z + iz) * zScale + this.zo;
-				int Zi = (int)Z;
-				if (Z < (double)Zi) {
-					--Zi;
-				}
-
-				int Zb = Zi & 255;
-				Z -= Zi;
-				double fadeZ = Z * Z * Z * (Z * (Z * 6.0D - 15.0D) + 10.0D);
-
-				for(int iy = 0; iy < ySize; ++iy)
-				{
-					double Y = (y + (double)iy) * yScale + this.yo;
-					int Yi = (int)Y;
-					if(Y < Yi)
-					{
-						--Yi;
-					}
-
-					int Yb = Yi & 255;
-					Y -= Yi;
-					double fadeY = Y * Y * Y * (Y * (Y * 6.0D - 15.0D) + 10.0D);
-					if (iy == 0 || Yb != var22)
-					{
-						var22 = Yb;
-						int var64 = this.p[Xb] + Yb;
-						int var65 = this.p[var64] + Zb;
-						int var66 = this.p[var64 + 1] + Zb;
-						int var67 = this.p[Xb + 1] + Yb;
-						int var68 = this.p[var67] + Zb;
-						int var69 = this.p[var67 + 1] + Zb;
-						var29 = this.lerp(
-								fadeX,
-								this.grad(this.p[var65], X, Y, Z),
-								this.grad(this.p[var68], X - 1, Y, Z)
-						);
-						var31 = this.lerp(
-								fadeX,
-								this.grad(this.p[var66], X, Y - 1, Z),
-								this.grad(this.p[var69], X - 1, Y - 1, Z)
-						);
-						var33 = this.lerp(
-								fadeX,
-								this.grad(this.p[var65 + 1], X, Y, Z - 1),
-								this.grad(this.p[var68 + 1], X - 1, Y, Z - 1)
-						);
-						var35 = this.lerp(
-								fadeX,
-								this.grad(this.p[var66 + 1], X, Y - 1, Z - 1),
-								this.grad(this.p[var69 + 1], X - 1, Y - 1, Z - 1)
-						);
-					}
-
-					double var58 = this.lerp(fadeY, var29, var31);
-					double var60 = this.lerp(fadeY, var33, var35);
-					double var62 = this.lerp(fadeZ, var58, var60);
-					int var10001 = var19++;
-					arr[var10001] += var62 * var20;
-				}
-			}
-		}
-
-	}
-}
diff --git a/game/core/src/main/java/net/minecraft/core/world/noise/RetroPerlinNoise.java b/game/core/src/main/java/net/minecraft/core/world/noise/RetroPerlinNoise.java
index 7e1bd7b75..c73f9a104 100644
--- a/game/core/src/main/java/net/minecraft/core/world/noise/RetroPerlinNoise.java
+++ b/game/core/src/main/java/net/minecraft/core/world/noise/RetroPerlinNoise.java
@@ -3,7 +3,7 @@ package net.minecraft.core.world.noise;
 import java.util.Random;
 
 public class RetroPerlinNoise
-		extends BasePerlinNoise<RetroImprovedNoise>
+		extends BasePerlinNoise<ImprovedNoise>
 {
 	public RetroPerlinNoise(long seed, int levels)
 	{
@@ -16,13 +16,13 @@ public class RetroPerlinNoise
 	}
 
 	@Override
-	protected RetroImprovedNoise[] newOctaves(Random random, int numOctaves)
+	protected ImprovedNoise[] newOctaves(Random random, int numOctaves)
 	{
-		RetroImprovedNoise[] levels = new RetroImprovedNoise[numOctaves];
+		ImprovedNoise[] levels = new ImprovedNoise[numOctaves];
 
 		for (int i = 0; i < numOctaves; i++)
 		{
-			levels[i] = new RetroImprovedNoise(random);
+			levels[i] = new ImprovedNoise(random, LegacyNoiseType.ALPHA);
 		}
 
 		return levels;
