-
Notifications
You must be signed in to change notification settings - Fork 831
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Early draft for single-pass compositing
This is a very early revision of the idea. Some (most) things don't work currently.
- Loading branch information
1 parent
9b2dbff
commit 2fa6b08
Showing
18 changed files
with
571 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
172 changes: 172 additions & 0 deletions
172
common/src/main/java/net/caffeinemc/mods/sodium/client/render/CompositePass.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
package net.caffeinemc.mods.sodium.client.render; | ||
|
||
import com.mojang.blaze3d.pipeline.RenderTarget; | ||
import com.mojang.blaze3d.platform.GlStateManager; | ||
import com.mojang.blaze3d.systems.RenderSystem; | ||
import com.mojang.blaze3d.vertex.VertexBuffer; | ||
import net.caffeinemc.mods.sodium.client.gl.shader.GlProgram; | ||
import net.caffeinemc.mods.sodium.client.gl.shader.ShaderConstants; | ||
import net.caffeinemc.mods.sodium.client.gl.shader.ShaderLoader; | ||
import net.caffeinemc.mods.sodium.client.gl.shader.ShaderType; | ||
import net.caffeinemc.mods.sodium.client.gl.shader.uniform.GlUniformFloat4v; | ||
import net.caffeinemc.mods.sodium.client.gl.shader.uniform.GlUniformInt; | ||
import net.caffeinemc.mods.sodium.client.render.chunk.shader.ShaderBindingContext; | ||
import net.minecraft.client.Minecraft; | ||
import net.minecraft.client.renderer.texture.AbstractTexture; | ||
import net.minecraft.client.renderer.texture.TextureManager; | ||
import net.minecraft.resources.ResourceLocation; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.jetbrains.annotations.Nullable; | ||
import org.joml.Vector4f; | ||
import org.lwjgl.opengl.GL33C; | ||
import org.lwjgl.opengl.GL45C; | ||
|
||
import java.util.EnumSet; | ||
|
||
public class CompositePass { | ||
public static boolean ENABLED; | ||
|
||
public static boolean ENTITY_GLOW_IS_ACTIVE = false; | ||
|
||
private static final Vector4f VIGNETTE_COLOR = new Vector4f(); | ||
|
||
private static int DEFAULT_VERTEX_ARRAY; | ||
private static GlProgram<CompositeProgramInterface> COMPOSITE_PROGRAM; | ||
|
||
public static void composite(@NotNull RenderTarget mainRT, | ||
@Nullable RenderTarget entityRT, | ||
int width, | ||
int height) { | ||
EnumSet<BlendOption> blendOptions = EnumSet.noneOf(BlendOption.class); | ||
|
||
if (entityRT != null && isEntityGlowIsActive()) { | ||
blendOptions.add(BlendOption.USE_ENTITY_GLOW); | ||
} | ||
|
||
if (isVignetteActive()) { | ||
blendOptions.add(BlendOption.USE_VIGNETTE); | ||
} | ||
|
||
if (blendOptions.isEmpty()) { | ||
// If no compositing is necessary, we can just blit the main render target to | ||
// the default framebuffer and avoid using the rasterization pipeline. | ||
mainRT.blitToScreen(width, height, true); | ||
return; | ||
} | ||
|
||
if (COMPOSITE_PROGRAM == null) { | ||
COMPOSITE_PROGRAM = GlProgram.builder(ResourceLocation.fromNamespaceAndPath("sodium", "composite")) | ||
.attachShader(ShaderLoader.loadShader(ShaderType.VERTEX, ResourceLocation.fromNamespaceAndPath("sodium", "composite.vsh"), ShaderConstants.empty())) | ||
.attachShader(ShaderLoader.loadShader(ShaderType.FRAGMENT, ResourceLocation.fromNamespaceAndPath("sodium", "composite.fsh"), ShaderConstants.empty())) | ||
.bindFragmentData("fragColor", 0) | ||
.link(CompositeProgramInterface::new); | ||
|
||
DEFAULT_VERTEX_ARRAY = GL33C.glGenVertexArrays(); | ||
} | ||
|
||
VertexBuffer.unbind(); | ||
|
||
GlStateManager._disableDepthTest(); | ||
GlStateManager._disableBlend(); | ||
|
||
GlStateManager._colorMask(true, true, true, false); | ||
GlStateManager._depthMask(false); | ||
|
||
GlStateManager._glUseProgram(COMPOSITE_PROGRAM.handle()); | ||
GlStateManager._glBindVertexArray(DEFAULT_VERTEX_ARRAY); | ||
|
||
var uniforms = COMPOSITE_PROGRAM.getInterface(); | ||
uniforms.mainColorRT.set(0); | ||
uniforms.entityColorRT.set(1); | ||
uniforms.vignetteTexture.set(2); | ||
uniforms.vignetteColorModulator.set(VIGNETTE_COLOR); | ||
uniforms.blendOptions.set(toBitfield(blendOptions)); | ||
|
||
bindTextureToUnit(0, mainRT.getColorTextureId()); | ||
|
||
if (entityRT != null) { | ||
bindTextureToUnit(1, blendOptions.contains(BlendOption.USE_ENTITY_GLOW) ? entityRT.getColorTextureId() : 0 /* default texture */); | ||
} | ||
|
||
bindTextureToUnit(2, getVignetteTextureId()); | ||
|
||
GL45C.glDrawArrays(GL45C.GL_TRIANGLES, 0, 3); | ||
|
||
unbindTextureFromUnit(0); | ||
unbindTextureFromUnit(1); | ||
unbindTextureFromUnit(2); | ||
|
||
GlStateManager._depthMask(true); | ||
GlStateManager._colorMask(true, true, true, true); | ||
|
||
GlStateManager._enableBlend(); | ||
GlStateManager._enableDepthTest(); | ||
|
||
CompositePass.ENTITY_GLOW_IS_ACTIVE = false; | ||
} | ||
|
||
private static boolean isEntityGlowIsActive() { | ||
return CompositePass.ENTITY_GLOW_IS_ACTIVE; | ||
} | ||
|
||
private static boolean isVignetteActive() { | ||
return VIGNETTE_COLOR.w() >= 0.0025f; | ||
} | ||
|
||
private static int toBitfield(EnumSet<?> set) { | ||
int bits = 0; | ||
|
||
for (var value : set) { | ||
bits = (1 << value.ordinal()); | ||
} | ||
|
||
return bits; | ||
} | ||
|
||
private static void unbindTextureFromUnit(int slot) { | ||
RenderSystem.activeTexture(GL33C.GL_TEXTURE0 + slot); | ||
RenderSystem.bindTexture(0); | ||
} | ||
|
||
private static void bindTextureToUnit(int slot, int texture) { | ||
RenderSystem.activeTexture(GL33C.GL_TEXTURE0 + slot); | ||
RenderSystem.bindTexture(texture); | ||
} | ||
|
||
public static void setVignetteColor(float[] color) { | ||
VIGNETTE_COLOR.set(color); | ||
} | ||
|
||
private static class CompositeProgramInterface { | ||
private final GlUniformInt mainColorRT; | ||
private final GlUniformInt entityColorRT; | ||
|
||
private final GlUniformInt vignetteTexture; | ||
private final GlUniformFloat4v vignetteColorModulator; | ||
|
||
private final GlUniformInt blendOptions; | ||
|
||
public CompositeProgramInterface(ShaderBindingContext ctx) { | ||
this.mainColorRT = ctx.bindUniform("mainColorRT", GlUniformInt::new); | ||
this.entityColorRT = ctx.bindUniform("entityColorRT", GlUniformInt::new); | ||
|
||
this.vignetteTexture = ctx.bindUniform("vignetteTexture", GlUniformInt::new); | ||
this.vignetteColorModulator = ctx.bindUniform("vignetteColorModulator", GlUniformFloat4v::new); | ||
|
||
this.blendOptions = ctx.bindUniform("blendOptions", GlUniformInt::new); | ||
} | ||
} | ||
|
||
private static int getVignetteTextureId() { | ||
Minecraft minecraft = Minecraft.getInstance(); | ||
TextureManager textureManager = minecraft.getTextureManager(); | ||
AbstractTexture texture = textureManager.getTexture(ResourceLocation.parse("textures/misc/vignette.png")); | ||
|
||
return texture.getId(); | ||
} | ||
|
||
private enum BlendOption { | ||
USE_ENTITY_GLOW, | ||
USE_VIGNETTE | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
.../java/net/caffeinemc/mods/sodium/mixin/features/render/compositing/GameRendererMixin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package net.caffeinemc.mods.sodium.mixin.features.render.compositing; | ||
|
||
import com.mojang.blaze3d.pipeline.RenderTarget; | ||
import com.mojang.blaze3d.platform.Window; | ||
import net.caffeinemc.mods.sodium.client.render.CompositePass; | ||
import net.minecraft.client.DeltaTracker; | ||
import net.minecraft.client.Minecraft; | ||
import net.minecraft.client.renderer.GameRenderer; | ||
import org.spongepowered.asm.mixin.Mixin; | ||
import org.spongepowered.asm.mixin.injection.At; | ||
import org.spongepowered.asm.mixin.injection.Inject; | ||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; | ||
|
||
@Mixin(GameRenderer.class) | ||
public class GameRendererMixin { | ||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;clear(IZ)V", ordinal = 0, shift = At.Shift.BEFORE)) | ||
private void preRenderGui(DeltaTracker deltaTracker, boolean renderLevel, CallbackInfo ci) { | ||
if (!CompositePass.ENABLED) { | ||
return; | ||
} | ||
|
||
Minecraft minecraft = Minecraft.getInstance(); | ||
Window window = minecraft.getWindow(); | ||
|
||
int width = window.getWidth(); | ||
int height = window.getHeight(); | ||
|
||
RenderTarget mainRT = minecraft.getMainRenderTarget(); | ||
mainRT.unbindWrite(); | ||
|
||
if (minecraft.level != null) { | ||
CompositePass.composite(mainRT, minecraft.levelRenderer.entityTarget(), width, height); | ||
} else { | ||
mainRT.blitToScreen(width, height, true); | ||
} | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
.../src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/compositing/GuiMixin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package net.caffeinemc.mods.sodium.mixin.features.render.compositing; | ||
|
||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation; | ||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; | ||
import com.mojang.blaze3d.systems.RenderSystem; | ||
import net.caffeinemc.mods.sodium.client.render.CompositePass; | ||
import net.minecraft.client.gui.Gui; | ||
import net.minecraft.client.gui.GuiGraphics; | ||
import net.minecraft.resources.ResourceLocation; | ||
import org.spongepowered.asm.mixin.Mixin; | ||
import org.spongepowered.asm.mixin.injection.At; | ||
|
||
@Mixin(Gui.class) | ||
public class GuiMixin { | ||
// We can't separately query the vignette color, so we instead hook the render function | ||
// and look at the render state to figure out what color is used. | ||
@WrapOperation(method = "renderVignette", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphics;blit(Lnet/minecraft/resources/ResourceLocation;IIIFFIIII)V")) | ||
public void beforeBlit(GuiGraphics gui, ResourceLocation texture, int x, int y, int z, float u1, float v1, int u2, int v2, int textureWidth, int textureHeight, Operation<Void> original) { | ||
if (CompositePass.ENABLED) { | ||
// The blit will happen later in the final compositing pass | ||
CompositePass.setVignetteColor(RenderSystem.getShaderColor()); | ||
} else { | ||
original.call(gui, texture, x, y, z, u1, v1, u2, v2, textureWidth, textureHeight); | ||
} | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
...java/net/caffeinemc/mods/sodium/mixin/features/render/compositing/LevelRendererMixin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package net.caffeinemc.mods.sodium.mixin.features.render.compositing; | ||
|
||
import com.mojang.blaze3d.vertex.PoseStack; | ||
import net.caffeinemc.mods.sodium.client.render.CompositePass; | ||
import net.minecraft.client.renderer.LevelRenderer; | ||
import net.minecraft.client.renderer.MultiBufferSource; | ||
import net.minecraft.client.renderer.OutlineBufferSource; | ||
import net.minecraft.world.entity.Entity; | ||
import org.spongepowered.asm.mixin.Mixin; | ||
import org.spongepowered.asm.mixin.injection.At; | ||
import org.spongepowered.asm.mixin.injection.Inject; | ||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; | ||
|
||
@Mixin(LevelRenderer.class) | ||
public class LevelRendererMixin { | ||
@Inject(method = "doEntityOutline", at = @At("HEAD"), cancellable = true) | ||
public void cancelEntityOutlineComposite(CallbackInfo ci) { | ||
// Normally, the entity outline buffer would be blurred and then composited into the | ||
// main render target, but we want to defer this until our final compositing pass. | ||
if (CompositePass.ENABLED) { | ||
ci.cancel(); | ||
} | ||
} | ||
|
||
@Inject(method = "renderEntity", at = @At("HEAD")) | ||
private void onEntityRendered(Entity entity, double d, double e, double f, float g, PoseStack poseStack, MultiBufferSource multiBufferSource, CallbackInfo ci) { | ||
// If any entities are rendered with an outline effect, mark the entity glow render target | ||
// as needing to be composited into the final image. Otherwise, we can skip compositing when | ||
// there are no glowing entities. | ||
if (multiBufferSource instanceof OutlineBufferSource) { | ||
CompositePass.ENTITY_GLOW_IS_ACTIVE = true; | ||
} | ||
} | ||
} |
Oops, something went wrong.