Fabulously transparent

- Write out depth in the composite pass
- When fabulous is enabled, write to the item entity target
- Flip the total transmittance back to alpha when compositing so it can
  be consumed by the blit shader
This commit is contained in:
Jozufozu 2025-02-22 23:02:23 -08:00
parent 19c97df115
commit 452d912e7b
4 changed files with 72 additions and 53 deletions

View file

@ -160,7 +160,7 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
group.submitTransparent(PipelineCompiler.OitMode.GENERATE_COEFFICIENTS);
}
oitFramebuffer.renderDepth();
oitFramebuffer.renderDepthFromTransmittance();
// Need to bind this again because we just drew a full screen quad for OIT.
vertexArray.bindForDraw();
@ -198,6 +198,8 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
lightBuffers.delete();
matrixBuffer.delete();
oitFramebuffer.delete();
}
public void renderCrumbling(List<Engine.CrumblingBlock> crumblingBlocks) {

View file

@ -3,6 +3,7 @@ package dev.engine_room.flywheel.backend.engine.indirect;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL46;
import com.mojang.blaze3d.pipeline.RenderTarget;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
@ -33,19 +34,40 @@ public class OitFramebuffer {
* Set up the framebuffer.
*/
public void prepare() {
var mainRenderTarget = Minecraft.getInstance()
.getMainRenderTarget();
RenderTarget renderTarget;
maybeResizeFBO(mainRenderTarget.width, mainRenderTarget.height);
if (Minecraft.useShaderTransparency()) {
renderTarget = Minecraft.getInstance().levelRenderer.getItemEntityTarget();
GL46.glNamedFramebufferTexture(fbo, GL46.GL_DEPTH_ATTACHMENT, mainRenderTarget.getDepthTextureId(), 0);
renderTarget.copyDepthFrom(Minecraft.getInstance()
.getMainRenderTarget());
} else {
renderTarget = Minecraft.getInstance()
.getMainRenderTarget();
}
maybeResizeFBO(renderTarget.width, renderTarget.height);
GL46.glNamedFramebufferTexture(fbo, GL46.GL_DEPTH_ATTACHMENT, renderTarget.getDepthTextureId(), 0);
Samplers.COEFFICIENTS.makeActive();
RenderSystem.bindTexture(0);
GL46.glBindTextureUnit(Samplers.COEFFICIENTS.number, coefficients);
Samplers.DEPTH_RANGE.makeActive();
RenderSystem.bindTexture(depthBounds);
Samplers.NOISE.makeActive();
NoiseTextures.BLUE_NOISE.bind();
GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo);
}
/**
* Render out the min and max depth per fragment.
*/
public void depthRange() {
// No depth writes, but we'll still use the depth test
// No depth writes, but we'll still use the depth test.
RenderSystem.depthMask(false);
RenderSystem.colorMask(true, true, true, true);
RenderSystem.enableBlend();
@ -57,8 +79,6 @@ public class OitFramebuffer {
var far = Minecraft.getInstance().gameRenderer.getDepthFar();
GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{-far, -far, 0, 0});
GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo);
}
/**
@ -72,38 +92,24 @@ public class OitFramebuffer {
RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE);
RenderSystem.blendEquation(GL46.GL_FUNC_ADD);
Samplers.DEPTH_RANGE.makeActive();
GlStateManager._bindTexture(depthBounds);
Samplers.NOISE.makeActive();
NoiseTextures.BLUE_NOISE.bind();
GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT1, GL46.GL_COLOR_ATTACHMENT2, GL46.GL_COLOR_ATTACHMENT3, GL46.GL_COLOR_ATTACHMENT4});
GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{0, 0, 0, 0});
GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 1, new float[]{0, 0, 0, 0});
GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 2, new float[]{0, 0, 0, 0});
GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 3, new float[]{0, 0, 0, 0});
GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo);
}
/**
* If any fragment has its transmittance fall off to zero, search the transmittance
* function to determine at what depth that occurs and write out to the depth buffer.
*/
public void renderDepth() {
// No depth writes, but we'll still use the depth test
public void renderDepthFromTransmittance() {
// Only write to depth, not color.
RenderSystem.depthMask(true);
RenderSystem.colorMask(false, false, false, false);
RenderSystem.disableBlend();
Samplers.COEFFICIENTS.makeActive();
RenderSystem.bindTexture(0);
GL46.glBindTextureUnit(0, coefficients);
Samplers.DEPTH_RANGE.makeActive();
RenderSystem.bindTexture(depthBounds);
RenderSystem.depthFunc(GL32.GL_ALWAYS);
GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{});
@ -124,50 +130,53 @@ public class OitFramebuffer {
RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE);
RenderSystem.blendEquation(GL46.GL_FUNC_ADD);
Samplers.DEPTH_RANGE.makeActive();
GlStateManager._bindTexture(depthBounds);
Samplers.COEFFICIENTS.makeActive();
GlStateManager._bindTexture(0);
GL46.glBindTextureUnit(Samplers.COEFFICIENTS.number, coefficients);
Samplers.NOISE.makeActive();
NoiseTextures.BLUE_NOISE.bind();
GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT5});
GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{0, 0, 0, 0});
GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo);
}
/**
* Composite the accumulated luminance onto the main framebuffer.
*/
public void composite() {
// No depth writes, but we'll still use the depth test
RenderSystem.depthMask(false);
if (Minecraft.useShaderTransparency()) {
Minecraft.getInstance().levelRenderer.getItemEntityTarget()
.bindWrite(false);
} else {
Minecraft.getInstance()
.getMainRenderTarget()
.bindWrite(false);
}
// The composite shader writes out the closest depth to gl_FragDepth.
// depthMask = true: OIT stuff renders on top of other transparent stuff.
// depthMask = false: other transparent stuff renders on top of OIT stuff.
// If Neo gets wavelet OIT we can use their hooks to be correct with everything.
RenderSystem.depthMask(true);
RenderSystem.colorMask(true, true, true, true);
RenderSystem.enableBlend();
RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.DestFactor.SRC_ALPHA);
// We rely on the blend func to achieve:
// final color = (1 - transmittance_total) * sum(color_f * alpha_f * transmittance_f) / sum(alpha_f * transmittance_f)
// + color_dst * transmittance_total
//
// Though note that the alpha value we emit in the fragment shader is actually (1. - transmittance_total).
// The extra inversion step is so we can have a sane alpha value written out for the fabulous blit shader to consume.
RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
RenderSystem.blendEquation(GL46.GL_FUNC_ADD);
var mainRenderTarget = Minecraft.getInstance()
.getMainRenderTarget();
mainRenderTarget.bindWrite(false);
RenderSystem.depthFunc(GL32.GL_ALWAYS);
GlTextureUnit.T0.makeActive();
RenderSystem.bindTexture(0);
GL46.glBindTextureUnit(0, coefficients);
GlTextureUnit.T1.makeActive();
RenderSystem.bindTexture(accumulate);
programs.getOitCompositeProgram()
.bind();
drawFullscreenQuad();
Minecraft.getInstance()
.getMainRenderTarget()
.bindWrite(false);
}
public void delete() {

View file

@ -13,6 +13,7 @@ import dev.engine_room.flywheel.backend.mixin.LevelRendererAccessor;
import net.minecraft.Util;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.world.level.Level;
@ -198,7 +199,7 @@ public final class FrameUniforms extends UniformWriter {
int pyramidHeight = DepthPyramid.mip0Size(mainRenderTarget.height);
int pyramidDepth = DepthPyramid.getImageMipLevels(pyramidWidth, pyramidHeight);
ptr = writeFloat(ptr, 0.05F); // zNear
ptr = writeFloat(ptr, GameRenderer.PROJECTION_Z_NEAR); // zNear
ptr = writeFloat(ptr, mc.gameRenderer.getDepthFar()); // zFar
ptr = writeFloat(ptr, PROJECTION.m00()); // P00
ptr = writeFloat(ptr, PROJECTION.m11()); // P11

View file

@ -1,9 +1,12 @@
#include "flywheel:internal/wavelet.glsl"
#include "flywheel:internal/depth.glsl"
#include "flywheel:internal/uniforms/frame.glsl"
layout (location = 0) out vec4 frag;
layout (binding = 0) uniform sampler2DArray _flw_coefficients;
layout (binding = 1) uniform sampler2D _flw_accumulate;
layout (binding = 0) uniform sampler2D _flw_accumulate;
layout (binding = 7) uniform sampler2D _flw_depthRange;
layout (binding = 8) uniform sampler2DArray _flw_coefficients;
void main() {
vec4 texel = texelFetch(_flw_accumulate, ivec2(gl_FragCoord.xy), 0);
@ -14,5 +17,9 @@ void main() {
float total_transmittance = total_transmittance(_flw_coefficients);
frag = vec4(texel.rgb / texel.a, total_transmittance);
frag = vec4(texel.rgb / texel.a, 1. - total_transmittance);
float minDepth = -texelFetch(_flw_depthRange, ivec2(gl_FragCoord.xy), 0).r;
gl_FragDepth = delinearize_depth(minDepth, _flw_cullData.znear, _flw_cullData.zfar);
}