Skip to content

Commit

Permalink
Merge pull request #1 from vktec/oit
Browse files Browse the repository at this point in the history
Add order-independent transparency for translucent blocks
  • Loading branch information
Kubasor authored Feb 25, 2021
2 parents 38e5b45 + 2219b8c commit 4402324
Show file tree
Hide file tree
Showing 16 changed files with 395 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package me.jellysquid.mods.sodium.client.gl;

import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.platform.FramebufferInfo;
import com.mojang.blaze3d.systems.RenderSystem;
import me.jellysquid.mods.sodium.client.gl.shader.GlProgram;
import me.jellysquid.mods.sodium.client.gl.shader.ShaderConstants;
import me.jellysquid.mods.sodium.client.gl.shader.ShaderLoader;
import me.jellysquid.mods.sodium.client.gl.shader.ShaderType;
import net.minecraft.client.gl.Framebuffer;
import net.minecraft.client.texture.TextureUtil;
import net.minecraft.util.Identifier;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.ARBTextureFloat;
import org.lwjgl.system.MemoryUtil;

import java.nio.IntBuffer;

public class TranslucencyFramebuffer extends Framebuffer {
private static GlProgram clearProgram = null;

private int accumAttachment;
private int revealAttachment;
private int depthAttachment;

public TranslucencyFramebuffer(int width, int height, boolean useDepth, boolean getError) {
super(width, height, useDepth, getError);
this.accumAttachment = -1;
this.revealAttachment = -1;
this.depthAttachment = -1;
}

@Override
public void delete() {
super.delete();
if (this.accumAttachment > -1) {
TextureUtil.deleteId(this.accumAttachment);
this.accumAttachment = -1;
}
if (this.revealAttachment > -1) {
TextureUtil.deleteId(this.revealAttachment);
this.revealAttachment = -1;
}
if (this.depthAttachment > -1) {
TextureUtil.deleteId(this.depthAttachment);
this.depthAttachment = -1;
}
}

@Override
public void initFbo(int width, int height, boolean getError) {
RenderSystem.assertThread(RenderSystem::isOnRenderThreadOrInit);
this.viewportWidth = width;
this.viewportHeight = height;
this.textureWidth = width;
this.textureHeight = height;

if (this.useDepthAttachment) {
this.depthAttachment = TextureUtil.generateId();
GlStateManager.bindTexture(this.depthAttachment);
GlStateManager.texImage2D(GL20.GL_TEXTURE_2D, 0, GL20.GL_DEPTH_COMPONENT, width, height, 0, GL20.GL_DEPTH_COMPONENT, GL20.GL_FLOAT, null);
}

this.accumAttachment = TextureUtil.generateId();
this.revealAttachment = TextureUtil.generateId();
// This duplicates a vanilla bug, where framebuffer attachments change back to linear filtering when resized.
// Shouldn't affect us because the translucency compositor samples texels directly, bypassing filtering altogether.
this.setTexFilter(GL20.GL_LINEAR);

GlStateManager.bindTexture(this.accumAttachment);
GlStateManager.texImage2D(GL20.GL_TEXTURE_2D, 0, ARBTextureFloat.GL_RGBA16F_ARB, width, height, 0, GL20.GL_RGBA, GL20.GL_FLOAT, null);
GlStateManager.bindTexture(this.revealAttachment);
GlStateManager.texImage2D(GL20.GL_TEXTURE_2D, 0, GL20.GL_ALPHA8, width, height, 0, GL20.GL_ALPHA, GL20.GL_UNSIGNED_BYTE, null);

this.fbo = GlStateManager.genFramebuffers();
GlStateManager.bindFramebuffer(FramebufferInfo.FRAME_BUFFER, this.fbo);
GlStateManager.framebufferTexture2D(FramebufferInfo.FRAME_BUFFER, FramebufferInfo.COLOR_ATTACHMENT, GL20.GL_TEXTURE_2D, this.accumAttachment, 0);
GlStateManager.framebufferTexture2D(FramebufferInfo.FRAME_BUFFER, FramebufferInfo.COLOR_ATTACHMENT+1, GL20.GL_TEXTURE_2D, this.revealAttachment, 0);
if (this.useDepthAttachment) {
GlStateManager.framebufferTexture2D(FramebufferInfo.FRAME_BUFFER, FramebufferInfo.DEPTH_ATTACHMENT, GL20.GL_TEXTURE_2D, this.depthAttachment, 0);
}
this.checkFramebufferStatus();

IntBuffer attachments = MemoryUtil.memAllocInt(2);
attachments.put(0, FramebufferInfo.COLOR_ATTACHMENT);
attachments.put(1, FramebufferInfo.COLOR_ATTACHMENT+1);
GL20.glDrawBuffers(attachments);

this.clear(getError);
this.endRead();
}

@Override
public void setTexFilter(int filter) {
RenderSystem.assertThread(RenderSystem::isOnRenderThreadOrInit);
this.texFilter = filter;

GlStateManager.bindTexture(this.accumAttachment);
GlStateManager.texParameter(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MIN_FILTER, filter);
GlStateManager.texParameter(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MAG_FILTER, filter);
GlStateManager.texParameter(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_S, GL20.GL_CLAMP);
GlStateManager.texParameter(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_R, GL20.GL_CLAMP);

GlStateManager.bindTexture(this.revealAttachment);
GlStateManager.texParameter(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MIN_FILTER, filter);
GlStateManager.texParameter(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MAG_FILTER, filter);
GlStateManager.texParameter(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_S, GL20.GL_CLAMP);
GlStateManager.texParameter(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_R, GL20.GL_CLAMP);

GlStateManager.bindTexture(0);
}

@Override
public void beginRead() {
RenderSystem.assertThread(RenderSystem::isOnRenderThread);
GlStateManager.bindTexture(this.accumAttachment);
}

@Override
public void clear(boolean getError) {
RenderSystem.assertThread(RenderSystem::isOnRenderThreadOrInit);
this.beginWrite(true);

if (TranslucencyFramebuffer.clearProgram == null) {
ShaderConstants empty = ShaderConstants.builder().build();
TranslucencyFramebuffer.clearProgram = GlProgram.builder(new Identifier("sodium", "translucency_clear"))
.attachShader(ShaderLoader.loadShader(ShaderType.VERTEX, new Identifier("sodium", "fullscreen_gl20.v.glsl"), empty))
.attachShader(ShaderLoader.loadShader(ShaderType.FRAGMENT, new Identifier("sodium", "translucency_clear_gl20.f.glsl"), empty))
.build((program, name) -> new GlProgram(program, name) {});
}
// clearColor is ignored, but that shouldn't cause problems unless someone misuses this class
// If we do need it in future, it can be done through a uniform
TranslucencyFramebuffer.clearProgram.bind();
GlStateManager.disableDepthTest();
GlStateManager.disableBlend();
GL20.glDrawArrays(GL20.GL_TRIANGLES, 0, 3);
GlStateManager.enableBlend();
GlStateManager.enableDepthTest();
TranslucencyFramebuffer.clearProgram.unbind();

if (this.useDepthAttachment) {
GlStateManager.clear(GL20.GL_DEPTH_BUFFER_BIT, getError);
}
this.endWrite();
}

@Override
public int getColorAttachment() {
return this.accumAttachment;
}
@Override
public int getDepthAttachment() {
return this.depthAttachment;
}

public int getAccumAttachment() {
return this.accumAttachment;
}
public int getRevealAttachment() {
return this.revealAttachment;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;

public class ShaderLoader {
/**
Expand All @@ -23,14 +22,6 @@ public static GlShader loadShader(ShaderType type, Identifier name, ShaderConsta
return new GlShader(type, name, getShaderSource(getShaderPath(name)), constants);
}

/**
* Use {@link ShaderLoader#loadShader(ShaderType, Identifier, ShaderConstants)} instead. This will be removed.
*/
@Deprecated
public static GlShader loadShader(ShaderType type, Identifier name, List<String> constants) {
return new GlShader(type, name, getShaderSource(getShaderPath(name)), ShaderConstants.fromStringList(constants));
}

private static String getShaderPath(Identifier name) {
return String.format("/assets/%s/shaders/%s", name.getNamespace(), name.getPath());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import me.jellysquid.mods.sodium.client.SodiumClientMod;
import me.jellysquid.mods.sodium.client.gl.TranslucencyFramebuffer;
import me.jellysquid.mods.sodium.client.gl.shader.GlProgram;
import me.jellysquid.mods.sodium.client.gl.shader.ShaderConstants;
import me.jellysquid.mods.sodium.client.gl.shader.ShaderLoader;
import me.jellysquid.mods.sodium.client.gl.shader.ShaderType;
import me.jellysquid.mods.sodium.client.gui.SodiumGameOptions;
import me.jellysquid.mods.sodium.client.model.vertex.type.ChunkVertexType;
import me.jellysquid.mods.sodium.client.render.chunk.ChunkRenderBackend;
Expand All @@ -17,20 +22,23 @@
import me.jellysquid.mods.sodium.client.render.chunk.format.DefaultModelVertexFormats;
import me.jellysquid.mods.sodium.client.render.chunk.passes.BlockRenderPass;
import me.jellysquid.mods.sodium.client.render.chunk.passes.BlockRenderPassManager;
import me.jellysquid.mods.sodium.client.render.chunk.shader.TranslucencyProgram;
import me.jellysquid.mods.sodium.client.render.pipeline.context.GlobalRenderContext;
import me.jellysquid.mods.sodium.client.util.math.FrustumExtended;
import me.jellysquid.mods.sodium.client.world.ChunkStatusListener;
import me.jellysquid.mods.sodium.client.world.ChunkStatusListenerManager;
import me.jellysquid.mods.sodium.common.util.ListUtil;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gl.Framebuffer;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.render.*;
import net.minecraft.client.render.block.entity.BlockEntityRenderDispatcher;
import net.minecraft.client.render.model.ModelLoader;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.*;
import net.minecraft.util.profiler.Profiler;

Expand All @@ -43,6 +51,9 @@
public class SodiumWorldRenderer implements ChunkStatusListener {
private static SodiumWorldRenderer instance;

public final TranslucencyFramebuffer translucentFramebuffer;
public final TranslucencyProgram translucencyProgram;

private final MinecraftClient client;

private ClientWorld world;
Expand Down Expand Up @@ -86,6 +97,19 @@ public static SodiumWorldRenderer getInstance() {

private SodiumWorldRenderer(MinecraftClient client) {
this.client = client;

Framebuffer fb = this.client.getFramebuffer();
this.translucentFramebuffer = new TranslucencyFramebuffer(fb.viewportWidth, fb.viewportHeight, true, MinecraftClient.IS_SYSTEM_MAC);

ShaderConstants empty = ShaderConstants.builder().build();
this.translucencyProgram = GlProgram.builder(new Identifier("sodium", "translucency_compositor"))
.attachShader(ShaderLoader.loadShader(ShaderType.VERTEX, new Identifier("sodium", "fullscreen_gl20.v.glsl"), empty))
.attachShader(ShaderLoader.loadShader(ShaderType.FRAGMENT, new Identifier("sodium", "translucency_composite_gl20.f.glsl"), empty))
.build((program, name) -> new TranslucencyProgram(program, name));
}

public Framebuffer getMainFramebuffer() {
return this.client.getFramebuffer();
}

public void setWorld(ClientWorld world) {
Expand Down Expand Up @@ -240,6 +264,10 @@ public void reload() {
this.initRenderer();
}

public void onResized(int w, int h) {
this.translucentFramebuffer.resize(w, h, MinecraftClient.IS_SYSTEM_MAC);
}

private void initRenderer() {
if (this.chunkRenderManager != null) {
this.chunkRenderManager.destroy();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public interface ChunkRenderBackend<T extends ChunkGraphicsState> {

void createShaders();

void begin(MatrixStack matrixStack);
void begin(MatrixStack matrixStack, boolean translucent);

void end(MatrixStack matrixStack);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package me.jellysquid.mods.sodium.client.render.chunk;

import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import it.unimi.dsi.fastutil.ints.IntIterator;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
Expand All @@ -9,6 +11,7 @@
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectList;
import me.jellysquid.mods.sodium.client.SodiumClientMod;
import me.jellysquid.mods.sodium.client.gl.TranslucencyFramebuffer;
import me.jellysquid.mods.sodium.client.gl.util.GlFogHelper;
import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer;
import me.jellysquid.mods.sodium.client.render.chunk.compile.ChunkBuildResult;
Expand All @@ -28,13 +31,16 @@
import me.jellysquid.mods.sodium.common.util.IdTable;
import me.jellysquid.mods.sodium.common.util.collections.FutureDequeDrain;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gl.Framebuffer;
import net.minecraft.client.render.Camera;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.chunk.ChunkSection;
import org.lwjgl.opengl.GL20;

import java.util.ArrayDeque;
import java.util.Collection;
Expand Down Expand Up @@ -389,9 +395,51 @@ public void renderLayer(MatrixStack matrixStack, BlockRenderPass pass, double x,
ChunkRenderList<T> chunkRenderList = this.chunkRenderLists[pass.ordinal()];
ChunkRenderListIterator<T> iterator = chunkRenderList.iterator(pass.isTranslucent());

this.backend.begin(matrixStack);
this.backend.render(iterator, new ChunkCameraContext(x, y, z));
this.backend.end(matrixStack);
if (pass.isTranslucent()) {
Framebuffer mfb = this.renderer.getMainFramebuffer();
mfb.endWrite();

TranslucencyFramebuffer fb = this.renderer.translucentFramebuffer;
fb.clear(MinecraftClient.IS_SYSTEM_MAC);
fb.copyDepthFrom(mfb);

fb.beginWrite(false);
RenderSystem.depthFunc(GL20.GL_ALWAYS);
this.backend.begin(matrixStack, pass.isTranslucent());

GlStateManager.activeTexture(GL20.GL_TEXTURE5);
GlStateManager.bindTexture (mfb.getDepthAttachment());

this.backend.render(iterator, new ChunkCameraContext(x, y, z));

this.backend.end(matrixStack);
RenderSystem.depthFunc(GL20.GL_GREATER);
fb.endWrite();

mfb.copyDepthFrom(fb);
mfb.beginWrite(false);
this.compositeOIT(fb);
} else {
this.backend.begin(matrixStack, pass.isTranslucent());
this.backend.render(iterator, new ChunkCameraContext(x, y, z));
this.backend.end(matrixStack);
}
}

public void compositeOIT(TranslucencyFramebuffer fb) {
GlStateManager.disableDepthTest();
this.renderer.translucencyProgram.bind();
this.renderer.translucencyProgram.setup();

GlStateManager.activeTexture(GL20.GL_TEXTURE3);
GlStateManager.bindTexture(fb.getAccumAttachment());
GlStateManager.activeTexture(GL20.GL_TEXTURE4);
GlStateManager.bindTexture(fb.getRevealAttachment());

GL20.glDrawArrays(GL20.GL_TRIANGLES, 0, 3);

this.renderer.translucencyProgram.unbind();
GlStateManager.enableDepthTest();
}

public void tickVisibleRenders() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ public GL20ChunkRenderBackend(ChunkVertexType format) {
}

@Override
public void begin(MatrixStack matrixStack) {
super.begin(matrixStack);

public void begin(MatrixStack matrixStack, boolean translucent) {
super.begin(matrixStack, translucent);
this.vertexFormat.enableVertexAttributes();
}

Expand Down
Loading

0 comments on commit 4402324

Please sign in to comment.