mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-03-04 06:34:40 +01:00
Merge remote-tracking branch 'Engine-Room/1.20.1/oit' into 1.20.1/dev
# Conflicts: # common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java # fabric/src/backend/java/dev/engine_room/flywheel/backend/compile/FlwProgramsReloader.java
This commit is contained in:
commit
cbc524ca7a
26 changed files with 1188 additions and 199 deletions
|
@ -0,0 +1,52 @@
|
|||
package dev.engine_room.flywheel.backend;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
import dev.engine_room.flywheel.backend.gl.GlTextureUnit;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
import net.minecraft.client.renderer.texture.DynamicTexture;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
|
||||
public class NoiseTextures {
|
||||
public static final ResourceLocation NOISE_TEXTURE = ResourceUtil.rl("textures/flywheel/noise/blue.png");
|
||||
|
||||
@UnknownNullability
|
||||
public static DynamicTexture BLUE_NOISE;
|
||||
|
||||
public static void reload(ResourceManager manager) {
|
||||
if (BLUE_NOISE != null) {
|
||||
BLUE_NOISE.close();
|
||||
BLUE_NOISE = null;
|
||||
}
|
||||
var optional = manager.getResource(NOISE_TEXTURE);
|
||||
|
||||
if (optional.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (var is = optional.get()
|
||||
.open()) {
|
||||
var image = NativeImage.read(NativeImage.Format.LUMINANCE, is);
|
||||
|
||||
BLUE_NOISE = new DynamicTexture(image);
|
||||
|
||||
GlTextureUnit.T0.makeActive();
|
||||
BLUE_NOISE.bind();
|
||||
|
||||
NoiseTextures.BLUE_NOISE.setFilter(true, false);
|
||||
RenderSystem.texParameter(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_S, GL32.GL_REPEAT);
|
||||
RenderSystem.texParameter(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_T, GL32.GL_REPEAT);
|
||||
|
||||
RenderSystem.bindTexture(0);
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,4 +10,8 @@ public class Samplers {
|
|||
public static final GlTextureUnit INSTANCE_BUFFER = GlTextureUnit.T4;
|
||||
public static final GlTextureUnit LIGHT_LUT = GlTextureUnit.T5;
|
||||
public static final GlTextureUnit LIGHT_SECTIONS = GlTextureUnit.T6;
|
||||
|
||||
public static final GlTextureUnit DEPTH_RANGE = GlTextureUnit.T7;
|
||||
public static final GlTextureUnit COEFFICIENTS = GlTextureUnit.T8;
|
||||
public static final GlTextureUnit NOISE = GlTextureUnit.T9;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||
private static final ResourceLocation SCATTER_SHADER_MAIN = ResourceUtil.rl("internal/indirect/scatter.glsl");
|
||||
private static final ResourceLocation DOWNSAMPLE_FIRST = ResourceUtil.rl("internal/indirect/downsample_first.glsl");
|
||||
private static final ResourceLocation DOWNSAMPLE_SECOND = ResourceUtil.rl("internal/indirect/downsample_second.glsl");
|
||||
public static final List<ResourceLocation> UTIL_SHADERS = List.of(APPLY_SHADER_MAIN, SCATTER_SHADER_MAIN, DOWNSAMPLE_FIRST, DOWNSAMPLE_SECOND);
|
||||
|
||||
private static final Compile<InstanceType<?>> CULL = new Compile<>();
|
||||
private static final Compile<ResourceLocation> UTIL = new Compile<>();
|
||||
|
@ -44,11 +43,13 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||
private final PipelineCompiler pipeline;
|
||||
private final CompilationHarness<InstanceType<?>> culling;
|
||||
private final CompilationHarness<ResourceLocation> utils;
|
||||
private final OitPrograms oitPrograms;
|
||||
|
||||
private IndirectPrograms(PipelineCompiler pipeline, CompilationHarness<InstanceType<?>> culling, CompilationHarness<ResourceLocation> utils) {
|
||||
private IndirectPrograms(PipelineCompiler pipeline, CompilationHarness<InstanceType<?>> culling, CompilationHarness<ResourceLocation> utils, OitPrograms oitPrograms) {
|
||||
this.pipeline = pipeline;
|
||||
this.culling = culling;
|
||||
this.utils = utils;
|
||||
this.oitPrograms = oitPrograms;
|
||||
}
|
||||
|
||||
private static List<String> getExtensions(GlslVersion glslVersion) {
|
||||
|
@ -58,9 +59,11 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||
}
|
||||
if (glslVersion.compareTo(GlslVersion.V420) < 0) {
|
||||
extensions.add("GL_ARB_shading_language_420pack");
|
||||
extensions.add("GL_ARB_shader_image_load_store");
|
||||
}
|
||||
if (glslVersion.compareTo(GlslVersion.V430) < 0) {
|
||||
extensions.add("GL_ARB_shader_storage_buffer_object");
|
||||
extensions.add("GL_ARB_shader_image_size");
|
||||
}
|
||||
if (glslVersion.compareTo(GlslVersion.V460) < 0) {
|
||||
extensions.add("GL_ARB_shader_draw_parameters");
|
||||
|
@ -87,8 +90,9 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||
var pipelineCompiler = PipelineCompiler.create(sources, Pipelines.INDIRECT, vertexComponents, fragmentComponents, EXTENSIONS);
|
||||
var cullingCompiler = createCullingCompiler(sources);
|
||||
var utilCompiler = createUtilCompiler(sources);
|
||||
var fullscreenCompiler = OitPrograms.createFullscreenCompiler(sources);
|
||||
|
||||
IndirectPrograms newInstance = new IndirectPrograms(pipelineCompiler, cullingCompiler, utilCompiler);
|
||||
IndirectPrograms newInstance = new IndirectPrograms(pipelineCompiler, cullingCompiler, utilCompiler, fullscreenCompiler);
|
||||
|
||||
setInstance(newInstance);
|
||||
}
|
||||
|
@ -147,8 +151,8 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||
setInstance(null);
|
||||
}
|
||||
|
||||
public GlProgram getIndirectProgram(InstanceType<?> instanceType, ContextShader contextShader, Material material) {
|
||||
return pipeline.get(instanceType, contextShader, material);
|
||||
public GlProgram getIndirectProgram(InstanceType<?> instanceType, ContextShader contextShader, Material material, PipelineCompiler.OitMode oit) {
|
||||
return pipeline.get(instanceType, contextShader, material, oit);
|
||||
}
|
||||
|
||||
public GlProgram getCullingProgram(InstanceType<?> instanceType) {
|
||||
|
@ -171,10 +175,15 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||
return utils.get(DOWNSAMPLE_SECOND);
|
||||
}
|
||||
|
||||
public OitPrograms oitPrograms() {
|
||||
return oitPrograms;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _delete() {
|
||||
pipeline.delete();
|
||||
culling.delete();
|
||||
utils.delete();
|
||||
oitPrograms.delete();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,8 +23,11 @@ public class InstancingPrograms extends AtomicReferenceCounted {
|
|||
|
||||
private final PipelineCompiler pipeline;
|
||||
|
||||
private InstancingPrograms(PipelineCompiler pipeline) {
|
||||
private final OitPrograms oitPrograms;
|
||||
|
||||
private InstancingPrograms(PipelineCompiler pipeline, OitPrograms oitPrograms) {
|
||||
this.pipeline = pipeline;
|
||||
this.oitPrograms = oitPrograms;
|
||||
}
|
||||
|
||||
private static List<String> getExtensions(GlslVersion glslVersion) {
|
||||
|
@ -41,7 +44,8 @@ public class InstancingPrograms extends AtomicReferenceCounted {
|
|||
}
|
||||
|
||||
var pipelineCompiler = PipelineCompiler.create(sources, Pipelines.INSTANCING, vertexComponents, fragmentComponents, EXTENSIONS);
|
||||
InstancingPrograms newInstance = new InstancingPrograms(pipelineCompiler);
|
||||
var fullscreen = OitPrograms.createFullscreenCompiler(sources);
|
||||
InstancingPrograms newInstance = new InstancingPrograms(pipelineCompiler, fullscreen);
|
||||
|
||||
setInstance(newInstance);
|
||||
}
|
||||
|
@ -69,12 +73,17 @@ public class InstancingPrograms extends AtomicReferenceCounted {
|
|||
setInstance(null);
|
||||
}
|
||||
|
||||
public GlProgram get(InstanceType<?> instanceType, ContextShader contextShader, Material material) {
|
||||
return pipeline.get(instanceType, contextShader, material);
|
||||
public GlProgram get(InstanceType<?> instanceType, ContextShader contextShader, Material material, PipelineCompiler.OitMode mode) {
|
||||
return pipeline.get(instanceType, contextShader, material, mode);
|
||||
}
|
||||
|
||||
public OitPrograms oitPrograms() {
|
||||
return oitPrograms;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _delete() {
|
||||
pipeline.delete();
|
||||
oitPrograms.delete();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
package dev.engine_room.flywheel.backend.compile;
|
||||
|
||||
import dev.engine_room.flywheel.backend.Samplers;
|
||||
import dev.engine_room.flywheel.backend.compile.core.CompilationHarness;
|
||||
import dev.engine_room.flywheel.backend.compile.core.Compile;
|
||||
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
|
||||
import dev.engine_room.flywheel.backend.gl.GlCompat;
|
||||
import dev.engine_room.flywheel.backend.gl.GlTextureUnit;
|
||||
import dev.engine_room.flywheel.backend.gl.shader.GlProgram;
|
||||
import dev.engine_room.flywheel.backend.gl.shader.ShaderType;
|
||||
import dev.engine_room.flywheel.backend.glsl.GlslVersion;
|
||||
import dev.engine_room.flywheel.backend.glsl.ShaderSources;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public class OitPrograms {
|
||||
private static final ResourceLocation FULLSCREEN = ResourceUtil.rl("internal/fullscreen.vert");
|
||||
static final ResourceLocation OIT_COMPOSITE = ResourceUtil.rl("internal/oit_composite.frag");
|
||||
static final ResourceLocation OIT_DEPTH = ResourceUtil.rl("internal/oit_depth.frag");
|
||||
|
||||
private static final Compile<ResourceLocation> COMPILE = new Compile<>();
|
||||
|
||||
private final CompilationHarness<ResourceLocation> harness;
|
||||
|
||||
public OitPrograms(CompilationHarness<ResourceLocation> harness) {
|
||||
this.harness = harness;
|
||||
}
|
||||
|
||||
public static OitPrograms createFullscreenCompiler(ShaderSources sources) {
|
||||
var harness = COMPILE.program()
|
||||
.link(COMPILE.shader(GlCompat.MAX_GLSL_VERSION, ShaderType.VERTEX)
|
||||
.nameMapper($ -> "fullscreen/fullscreen")
|
||||
.withResource(FULLSCREEN))
|
||||
.link(COMPILE.shader(GlCompat.MAX_GLSL_VERSION, ShaderType.FRAGMENT)
|
||||
.nameMapper(rl -> "fullscreen/" + ResourceUtil.toDebugFileNameNoExtension(rl))
|
||||
.onCompile((rl, compilation) -> {
|
||||
if (GlCompat.MAX_GLSL_VERSION.compareTo(GlslVersion.V400) < 0) {
|
||||
// Need to define FMA for the wavelet calculations
|
||||
compilation.define("fma(a, b, c) ((a) * (b) + (c))");
|
||||
}
|
||||
})
|
||||
.withResource(s -> s))
|
||||
.postLink((key, program) -> {
|
||||
program.bind();
|
||||
Uniforms.setUniformBlockBindings(program);
|
||||
program.setSamplerBinding("_flw_accumulate", GlTextureUnit.T0);
|
||||
program.setSamplerBinding("_flw_depthRange", Samplers.DEPTH_RANGE);
|
||||
program.setSamplerBinding("_flw_coefficients", Samplers.COEFFICIENTS);
|
||||
|
||||
GlProgram.unbind();
|
||||
})
|
||||
.harness("fullscreen", sources);
|
||||
return new OitPrograms(harness);
|
||||
}
|
||||
|
||||
public GlProgram getOitCompositeProgram() {
|
||||
return harness.get(OitPrograms.OIT_COMPOSITE);
|
||||
}
|
||||
|
||||
public GlProgram getOitDepthProgram() {
|
||||
return harness.get(OitPrograms.OIT_DEPTH);
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
harness.delete();
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
|
|||
import dev.engine_room.flywheel.backend.gl.GlCompat;
|
||||
import dev.engine_room.flywheel.backend.gl.shader.GlProgram;
|
||||
import dev.engine_room.flywheel.backend.gl.shader.ShaderType;
|
||||
import dev.engine_room.flywheel.backend.glsl.GlslVersion;
|
||||
import dev.engine_room.flywheel.backend.glsl.ShaderSources;
|
||||
import dev.engine_room.flywheel.backend.glsl.SourceComponent;
|
||||
import dev.engine_room.flywheel.backend.glsl.generate.FnSignature;
|
||||
|
@ -49,7 +50,7 @@ public final class PipelineCompiler {
|
|||
ALL.add(this);
|
||||
}
|
||||
|
||||
public GlProgram get(InstanceType<?> instanceType, ContextShader contextShader, Material material) {
|
||||
public GlProgram get(InstanceType<?> instanceType, ContextShader contextShader, Material material, OitMode oit) {
|
||||
var light = material.light();
|
||||
var cutout = material.cutout();
|
||||
var shaders = material.shaders();
|
||||
|
@ -65,7 +66,7 @@ public final class PipelineCompiler {
|
|||
MaterialShaderIndices.cutoutSources()
|
||||
.index(cutout.source());
|
||||
|
||||
return harness.get(new PipelineProgramKey(instanceType, contextShader, light, shaders, cutout != CutoutShaders.OFF, FrameUniforms.debugOn()));
|
||||
return harness.get(new PipelineProgramKey(instanceType, contextShader, light, shaders, cutout != CutoutShaders.OFF, FrameUniforms.debugOn(), oit));
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
|
@ -95,6 +96,12 @@ public final class PipelineCompiler {
|
|||
return "pipeline/" + pipeline.compilerMarker() + "/" + instance + "/" + material + "_" + context + debug;
|
||||
})
|
||||
.requireExtensions(extensions)
|
||||
.onCompile((rl, compilation) -> {
|
||||
if (GlCompat.MAX_GLSL_VERSION.compareTo(GlslVersion.V400) < 0 && !extensions.contains("GL_ARB_gpu_shader5")) {
|
||||
// Only define fma if it wouldn't be declared by gpu shader 5
|
||||
compilation.define("fma(a, b, c) ((a) * (b) + (c))");
|
||||
}
|
||||
})
|
||||
.onCompile((key, comp) -> key.contextShader()
|
||||
.onCompile(comp))
|
||||
.onCompile((key, comp) -> BackendConfig.INSTANCE.lightSmoothness()
|
||||
|
@ -127,10 +134,17 @@ public final class PipelineCompiler {
|
|||
.source());
|
||||
var debug = key.debugEnabled() ? "_debug" : "";
|
||||
var cutout = key.useCutout() ? "_cutout" : "";
|
||||
return "pipeline/" + pipeline.compilerMarker() + "/frag/" + material + "/" + light + "_" + context + cutout + debug;
|
||||
var oit = key.oit().name;
|
||||
return "pipeline/" + pipeline.compilerMarker() + "/frag/" + material + "/" + light + "_" + context + cutout + debug + oit;
|
||||
})
|
||||
.requireExtensions(extensions)
|
||||
.enableExtension("GL_ARB_conservative_depth")
|
||||
.onCompile((rl, compilation) -> {
|
||||
if (GlCompat.MAX_GLSL_VERSION.compareTo(GlslVersion.V400) < 0 && !extensions.contains("GL_ARB_gpu_shader5")) {
|
||||
// Only define fma if it wouldn't be declared by gpu shader 5
|
||||
compilation.define("fma(a, b, c) ((a) * (b) + (c))");
|
||||
}
|
||||
})
|
||||
.onCompile((key, comp) -> key.contextShader()
|
||||
.onCompile(comp))
|
||||
.onCompile((key, comp) -> BackendConfig.INSTANCE.lightSmoothness()
|
||||
|
@ -145,6 +159,12 @@ public final class PipelineCompiler {
|
|||
comp.define("_FLW_USE_DISCARD");
|
||||
}
|
||||
})
|
||||
.onCompile((key, comp) -> {
|
||||
if (key.oit() != OitMode.OFF) {
|
||||
comp.define("_FLW_OIT");
|
||||
comp.define(key.oit().define);
|
||||
}
|
||||
})
|
||||
.withResource(API_IMPL_FRAG)
|
||||
.withResource(key -> key.materialShaders()
|
||||
.fragmentSource())
|
||||
|
@ -170,6 +190,9 @@ public final class PipelineCompiler {
|
|||
program.setSamplerBinding("flw_diffuseTex", Samplers.DIFFUSE);
|
||||
program.setSamplerBinding("flw_overlayTex", Samplers.OVERLAY);
|
||||
program.setSamplerBinding("flw_lightTex", Samplers.LIGHT);
|
||||
program.setSamplerBinding("_flw_depthRange", Samplers.DEPTH_RANGE);
|
||||
program.setSamplerBinding("_flw_coefficients", Samplers.COEFFICIENTS);
|
||||
program.setSamplerBinding("_flw_blueNoise", Samplers.NOISE);
|
||||
pipeline.onLink()
|
||||
.accept(program);
|
||||
key.contextShader()
|
||||
|
@ -216,6 +239,23 @@ public final class PipelineCompiler {
|
|||
* @param light The light shader to use.
|
||||
*/
|
||||
public record PipelineProgramKey(InstanceType<?> instanceType, ContextShader contextShader, LightShader light,
|
||||
MaterialShaders materialShaders, boolean useCutout, boolean debugEnabled) {
|
||||
MaterialShaders materialShaders, boolean useCutout, boolean debugEnabled,
|
||||
OitMode oit) {
|
||||
}
|
||||
|
||||
public enum OitMode {
|
||||
OFF("", ""),
|
||||
DEPTH_RANGE("_FLW_DEPTH_RANGE", "_depth_range"),
|
||||
GENERATE_COEFFICIENTS("_FLW_COLLECT_COEFFS", "_generate_coefficients"),
|
||||
EVALUATE("_FLW_EVALUATE", "_resolve"),
|
||||
;
|
||||
|
||||
public final String define;
|
||||
public final String name;
|
||||
|
||||
OitMode(String define, String name) {
|
||||
this.define = define;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,17 @@ public final class MaterialRenderState {
|
|||
setupWriteMask(material.writeMask());
|
||||
}
|
||||
|
||||
public static void setupOit(Material material) {
|
||||
setupTexture(material);
|
||||
setupBackfaceCulling(material.backfaceCulling());
|
||||
setupPolygonOffset(material.polygonOffset());
|
||||
setupDepthTest(material.depthTest());
|
||||
|
||||
WriteMask mask = material.writeMask();
|
||||
boolean writeColor = mask.color();
|
||||
RenderSystem.colorMask(writeColor, writeColor, writeColor, writeColor);
|
||||
}
|
||||
|
||||
private static void setupTexture(Material material) {
|
||||
Samplers.DIFFUSE.makeActive();
|
||||
AbstractTexture texture = Minecraft.getInstance()
|
||||
|
@ -135,7 +146,7 @@ public final class MaterialRenderState {
|
|||
RenderSystem.enableBlend();
|
||||
RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.DST_COLOR, GlStateManager.DestFactor.SRC_COLOR, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
|
||||
}
|
||||
case TRANSLUCENT, ORDER_INDEPENDENT -> {
|
||||
case TRANSLUCENT -> {
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
|
|
@ -13,9 +13,11 @@ import java.util.List;
|
|||
import dev.engine_room.flywheel.api.instance.Instance;
|
||||
import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||
import dev.engine_room.flywheel.api.material.Material;
|
||||
import dev.engine_room.flywheel.api.material.Transparency;
|
||||
import dev.engine_room.flywheel.api.model.Model;
|
||||
import dev.engine_room.flywheel.backend.compile.ContextShader;
|
||||
import dev.engine_room.flywheel.backend.compile.IndirectPrograms;
|
||||
import dev.engine_room.flywheel.backend.compile.PipelineCompiler;
|
||||
import dev.engine_room.flywheel.backend.engine.InstancerKey;
|
||||
import dev.engine_room.flywheel.backend.engine.MaterialRenderState;
|
||||
import dev.engine_room.flywheel.backend.engine.MeshPool;
|
||||
|
@ -36,6 +38,7 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
private final List<IndirectInstancer<I>> instancers = new ArrayList<>();
|
||||
private final List<IndirectDraw> indirectDraws = new ArrayList<>();
|
||||
private final List<MultiDraw> multiDraws = new ArrayList<>();
|
||||
private final List<MultiDraw> oitDraws = new ArrayList<>();
|
||||
|
||||
private final IndirectPrograms programs;
|
||||
private final GlProgram cullProgram;
|
||||
|
@ -54,7 +57,7 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
cullProgram = programs.getCullingProgram(instanceType);
|
||||
}
|
||||
|
||||
public void flushInstancers() {
|
||||
public boolean flushInstancers() {
|
||||
instanceCountThisFrame = 0;
|
||||
int modelIndex = 0;
|
||||
for (var iterator = instancers.iterator(); iterator.hasNext(); ) {
|
||||
|
@ -76,13 +79,17 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
if (indirectDraws.removeIf(IndirectDraw::deleted)) {
|
||||
needsDrawSort = true;
|
||||
}
|
||||
|
||||
var out = indirectDraws.isEmpty();
|
||||
|
||||
if (out) {
|
||||
delete();
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
public void upload(StagingBuffer stagingBuffer) {
|
||||
if (nothingToDo()) {
|
||||
return;
|
||||
}
|
||||
|
||||
buffers.updateCounts(instanceCountThisFrame, instancers.size(), indirectDraws.size());
|
||||
|
||||
// Upload only instances that have changed.
|
||||
|
@ -104,10 +111,6 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
}
|
||||
|
||||
public void dispatchCull() {
|
||||
if (nothingToDo()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Uniforms.bindAll();
|
||||
cullProgram.bind();
|
||||
|
||||
|
@ -116,20 +119,17 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
}
|
||||
|
||||
public void dispatchApply() {
|
||||
if (nothingToDo()) {
|
||||
return;
|
||||
}
|
||||
|
||||
buffers.bindForApply();
|
||||
glDispatchCompute(GlCompat.getComputeGroupCount(indirectDraws.size()), 1, 1);
|
||||
}
|
||||
|
||||
private boolean nothingToDo() {
|
||||
return indirectDraws.isEmpty() || instanceCountThisFrame == 0;
|
||||
public boolean hasOitDraws() {
|
||||
return !oitDraws.isEmpty();
|
||||
}
|
||||
|
||||
private void sortDraws() {
|
||||
multiDraws.clear();
|
||||
oitDraws.clear();
|
||||
// sort by visual type, then material
|
||||
indirectDraws.sort(DRAW_COMPARATOR);
|
||||
|
||||
|
@ -138,7 +138,9 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
|
||||
// if the next draw call has a different VisualType or Material, start a new MultiDraw
|
||||
if (i == indirectDraws.size() - 1 || incompatibleDraws(draw1, indirectDraws.get(i + 1))) {
|
||||
multiDraws.add(new MultiDraw(draw1.material(), draw1.isEmbedded(), start, i + 1));
|
||||
var dst = draw1.material()
|
||||
.transparency() == Transparency.ORDER_INDEPENDENT ? oitDraws : multiDraws;
|
||||
dst.add(new MultiDraw(draw1.material(), draw1.isEmbedded(), start, i + 1));
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
|
@ -171,8 +173,8 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
needsDrawSort = true;
|
||||
}
|
||||
|
||||
public void submit() {
|
||||
if (nothingToDo()) {
|
||||
public void submitSolid() {
|
||||
if (multiDraws.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -183,7 +185,7 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
GlProgram lastProgram = null;
|
||||
|
||||
for (var multiDraw : multiDraws) {
|
||||
var drawProgram = programs.getIndirectProgram(instanceType, multiDraw.embedded ? ContextShader.EMBEDDED : ContextShader.DEFAULT, multiDraw.material);
|
||||
var drawProgram = programs.getIndirectProgram(instanceType, multiDraw.embedded ? ContextShader.EMBEDDED : ContextShader.DEFAULT, multiDraw.material, PipelineCompiler.OitMode.OFF);
|
||||
if (drawProgram != lastProgram) {
|
||||
lastProgram = drawProgram;
|
||||
|
||||
|
@ -197,8 +199,36 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
}
|
||||
}
|
||||
|
||||
public void submitTransparent(PipelineCompiler.OitMode oit) {
|
||||
if (oitDraws.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
buffers.bindForDraw();
|
||||
|
||||
drawBarrier();
|
||||
|
||||
GlProgram lastProgram = null;
|
||||
|
||||
for (var multiDraw : oitDraws) {
|
||||
var drawProgram = programs.getIndirectProgram(instanceType, multiDraw.embedded ? ContextShader.EMBEDDED : ContextShader.DEFAULT, multiDraw.material, oit);
|
||||
if (drawProgram != lastProgram) {
|
||||
lastProgram = drawProgram;
|
||||
|
||||
// Don't need to do this unless the program changes.
|
||||
drawProgram.bind();
|
||||
|
||||
drawProgram.setFloat("_flw_blueNoiseFactor", 0.07f);
|
||||
}
|
||||
|
||||
MaterialRenderState.setupOit(multiDraw.material);
|
||||
|
||||
multiDraw.submit(drawProgram);
|
||||
}
|
||||
}
|
||||
|
||||
public void bindForCrumbling(Material material) {
|
||||
var program = programs.getIndirectProgram(instanceType, ContextShader.CRUMBLING, material);
|
||||
var program = programs.getIndirectProgram(instanceType, ContextShader.CRUMBLING, material, PipelineCompiler.OitMode.OFF);
|
||||
|
||||
program.bind();
|
||||
|
||||
|
@ -256,16 +286,6 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
buffers.delete();
|
||||
}
|
||||
|
||||
public boolean checkEmptyAndDelete() {
|
||||
var out = indirectDraws.isEmpty();
|
||||
|
||||
if (out) {
|
||||
delete();
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
private record MultiDraw(Material material, boolean embedded, int start, int end) {
|
||||
private void submit(GlProgram drawProgram) {
|
||||
GlCompat.safeMultiDrawElementsIndirect(drawProgram, GL_TRIANGLES, GL_UNSIGNED_INT, this.start, this.end, IndirectBuffers.DRAW_COMMAND_STRIDE);
|
||||
|
|
|
@ -17,6 +17,7 @@ import dev.engine_room.flywheel.api.instance.Instance;
|
|||
import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||
import dev.engine_room.flywheel.backend.Samplers;
|
||||
import dev.engine_room.flywheel.backend.compile.IndirectPrograms;
|
||||
import dev.engine_room.flywheel.backend.compile.PipelineCompiler;
|
||||
import dev.engine_room.flywheel.backend.engine.AbstractInstancer;
|
||||
import dev.engine_room.flywheel.backend.engine.CommonCrumbling;
|
||||
import dev.engine_room.flywheel.backend.engine.DrawManager;
|
||||
|
@ -48,6 +49,8 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
|||
|
||||
private final DepthPyramid depthPyramid;
|
||||
|
||||
private final OitFramebuffer oitFramebuffer;
|
||||
|
||||
public IndirectDrawManager(IndirectPrograms programs) {
|
||||
this.programs = programs;
|
||||
programs.acquire();
|
||||
|
@ -62,6 +65,8 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
|||
matrixBuffer = new MatrixBuffer();
|
||||
|
||||
depthPyramid = new DepthPyramid(programs);
|
||||
|
||||
oitFramebuffer = new OitFramebuffer(programs.oitPrograms());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -80,13 +85,11 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
|||
public void render(LightStorage lightStorage, EnvironmentStorage environmentStorage) {
|
||||
super.render(lightStorage, environmentStorage);
|
||||
|
||||
for (var group : cullingGroups.values()) {
|
||||
group.flushInstancers();
|
||||
}
|
||||
|
||||
// Flush instance counts, page mappings, and prune empty groups.
|
||||
cullingGroups.values()
|
||||
.removeIf(IndirectCullingGroup::checkEmptyAndDelete);
|
||||
.removeIf(IndirectCullingGroup::flushInstancers);
|
||||
|
||||
// Instancers may have been emptied in the above call, now remove them here.
|
||||
instancers.values()
|
||||
.removeIf(instancer -> instancer.instanceCount() == 0);
|
||||
|
||||
|
@ -94,6 +97,12 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
|||
|
||||
stagingBuffer.reclaim();
|
||||
|
||||
// Genuinely nothing to do, we can just early out.
|
||||
// Still process the mesh pool and reclaim fenced staging regions though.
|
||||
if (cullingGroups.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
lightBuffers.flush(stagingBuffer, lightStorage);
|
||||
|
||||
matrixBuffer.flush(stagingBuffer, environmentStorage);
|
||||
|
@ -138,7 +147,45 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
|||
Uniforms.bindAll();
|
||||
|
||||
for (var group : cullingGroups.values()) {
|
||||
group.submit();
|
||||
group.submitSolid();
|
||||
}
|
||||
|
||||
// Let's avoid invoking the oit chain if we don't have anything to do
|
||||
boolean useOit = false;
|
||||
for (var group : cullingGroups.values()) {
|
||||
if (group.hasOitDraws()) {
|
||||
useOit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (useOit) {
|
||||
oitFramebuffer.prepare();
|
||||
|
||||
oitFramebuffer.depthRange();
|
||||
|
||||
for (var group : cullingGroups.values()) {
|
||||
group.submitTransparent(PipelineCompiler.OitMode.DEPTH_RANGE);
|
||||
}
|
||||
|
||||
oitFramebuffer.renderTransmittance();
|
||||
|
||||
for (var group : cullingGroups.values()) {
|
||||
group.submitTransparent(PipelineCompiler.OitMode.GENERATE_COEFFICIENTS);
|
||||
}
|
||||
|
||||
oitFramebuffer.renderDepthFromTransmittance();
|
||||
|
||||
// Need to bind this again because we just drew a full screen quad for OIT.
|
||||
vertexArray.bindForDraw();
|
||||
|
||||
oitFramebuffer.accumulate();
|
||||
|
||||
for (var group : cullingGroups.values()) {
|
||||
group.submitTransparent(PipelineCompiler.OitMode.EVALUATE);
|
||||
}
|
||||
|
||||
oitFramebuffer.composite();
|
||||
}
|
||||
|
||||
MaterialRenderState.reset();
|
||||
|
@ -166,6 +213,8 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
|||
lightBuffers.delete();
|
||||
|
||||
matrixBuffer.delete();
|
||||
|
||||
oitFramebuffer.delete();
|
||||
}
|
||||
|
||||
public void renderCrumbling(List<Engine.CrumblingBlock> crumblingBlocks) {
|
||||
|
|
|
@ -0,0 +1,319 @@
|
|||
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;
|
||||
|
||||
import dev.engine_room.flywheel.backend.NoiseTextures;
|
||||
import dev.engine_room.flywheel.backend.Samplers;
|
||||
import dev.engine_room.flywheel.backend.compile.OitPrograms;
|
||||
import dev.engine_room.flywheel.backend.gl.GlCompat;
|
||||
import dev.engine_room.flywheel.backend.gl.GlTextureUnit;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
public class OitFramebuffer {
|
||||
public static final float[] CLEAR_TO_ZERO = {0, 0, 0, 0};
|
||||
public static final int[] DEPTH_RANGE_DRAW_BUFFERS = {GL46.GL_COLOR_ATTACHMENT0};
|
||||
public static final int[] RENDER_TRANSMITTANCE_DRAW_BUFFERS = {GL46.GL_COLOR_ATTACHMENT1, GL46.GL_COLOR_ATTACHMENT2, GL46.GL_COLOR_ATTACHMENT3, GL46.GL_COLOR_ATTACHMENT4};
|
||||
public static final int[] ACCUMULATE_DRAW_BUFFERS = {GL46.GL_COLOR_ATTACHMENT5};
|
||||
public static final int[] DEPTH_ONLY_DRAW_BUFFERS = {};
|
||||
|
||||
private final OitPrograms programs;
|
||||
private final int vao;
|
||||
|
||||
public int fbo = -1;
|
||||
public int depthBounds = -1;
|
||||
public int coefficients = -1;
|
||||
public int accumulate = -1;
|
||||
|
||||
private int lastWidth = -1;
|
||||
private int lastHeight = -1;
|
||||
|
||||
public OitFramebuffer(OitPrograms programs) {
|
||||
this.programs = programs;
|
||||
if (GlCompat.SUPPORTS_DSA) {
|
||||
vao = GL46.glCreateVertexArrays();
|
||||
} else {
|
||||
vao = GL32.glGenVertexArrays();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the framebuffer.
|
||||
*/
|
||||
public void prepare() {
|
||||
RenderTarget renderTarget;
|
||||
|
||||
if (Minecraft.useShaderTransparency()) {
|
||||
renderTarget = Minecraft.getInstance().levelRenderer.getItemEntityTarget();
|
||||
|
||||
renderTarget.copyDepthFrom(Minecraft.getInstance()
|
||||
.getMainRenderTarget());
|
||||
} else {
|
||||
renderTarget = Minecraft.getInstance()
|
||||
.getMainRenderTarget();
|
||||
}
|
||||
|
||||
maybeResizeFBO(renderTarget.width, renderTarget.height);
|
||||
|
||||
Samplers.COEFFICIENTS.makeActive();
|
||||
// Bind zero to render system to make sure we clear their internal state
|
||||
RenderSystem.bindTexture(0);
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D_ARRAY, coefficients);
|
||||
|
||||
Samplers.DEPTH_RANGE.makeActive();
|
||||
RenderSystem.bindTexture(depthBounds);
|
||||
|
||||
Samplers.NOISE.makeActive();
|
||||
NoiseTextures.BLUE_NOISE.bind();
|
||||
|
||||
GlStateManager._glBindFramebuffer(GL32.GL_FRAMEBUFFER, fbo);
|
||||
GL32.glFramebufferTexture(GL32.GL_FRAMEBUFFER, GL32.GL_DEPTH_ATTACHMENT, renderTarget.getDepthTextureId(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render out the min and max depth per fragment.
|
||||
*/
|
||||
public void depthRange() {
|
||||
// No depth writes, but we'll still use the depth test.
|
||||
RenderSystem.depthMask(false);
|
||||
RenderSystem.colorMask(true, true, true, true);
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE);
|
||||
RenderSystem.blendEquation(GL32.GL_MAX);
|
||||
|
||||
var far = Minecraft.getInstance().gameRenderer.getDepthFar();
|
||||
|
||||
if (GlCompat.SUPPORTS_DSA) {
|
||||
GL46.glNamedFramebufferDrawBuffers(fbo, DEPTH_RANGE_DRAW_BUFFERS);
|
||||
GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{-far, -far, 0, 0});
|
||||
} else {
|
||||
GL32.glDrawBuffers(DEPTH_RANGE_DRAW_BUFFERS);
|
||||
RenderSystem.clearColor(-far, -far, 0, 0);
|
||||
RenderSystem.clear(GL32.GL_COLOR_BUFFER_BIT, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the coefficients to the transmittance function.
|
||||
*/
|
||||
public void renderTransmittance() {
|
||||
// No depth writes, but we'll still use the depth test
|
||||
RenderSystem.depthMask(false);
|
||||
RenderSystem.colorMask(true, true, true, true);
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE);
|
||||
RenderSystem.blendEquation(GL32.GL_FUNC_ADD);
|
||||
|
||||
if (GlCompat.SUPPORTS_DSA) {
|
||||
GL46.glNamedFramebufferDrawBuffers(fbo, RENDER_TRANSMITTANCE_DRAW_BUFFERS);
|
||||
|
||||
GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, CLEAR_TO_ZERO);
|
||||
GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 1, CLEAR_TO_ZERO);
|
||||
GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 2, CLEAR_TO_ZERO);
|
||||
GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 3, CLEAR_TO_ZERO);
|
||||
} else {
|
||||
GL32.glDrawBuffers(RENDER_TRANSMITTANCE_DRAW_BUFFERS);
|
||||
RenderSystem.clearColor(0, 0, 0, 0);
|
||||
RenderSystem.clear(GL32.GL_COLOR_BUFFER_BIT, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 renderDepthFromTransmittance() {
|
||||
// Only write to depth, not color.
|
||||
RenderSystem.depthMask(true);
|
||||
RenderSystem.colorMask(false, false, false, false);
|
||||
RenderSystem.disableBlend();
|
||||
RenderSystem.depthFunc(GL32.GL_ALWAYS);
|
||||
|
||||
if (GlCompat.SUPPORTS_DSA) {
|
||||
GL46.glNamedFramebufferDrawBuffers(fbo, DEPTH_ONLY_DRAW_BUFFERS);
|
||||
} else {
|
||||
GL32.glDrawBuffers(DEPTH_ONLY_DRAW_BUFFERS);
|
||||
}
|
||||
|
||||
programs.getOitDepthProgram()
|
||||
.bind();
|
||||
|
||||
drawFullscreenQuad();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample the transmittance function and accumulate.
|
||||
*/
|
||||
public void accumulate() {
|
||||
// No depth writes, but we'll still use the depth test
|
||||
RenderSystem.depthMask(false);
|
||||
RenderSystem.colorMask(true, true, true, true);
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE);
|
||||
RenderSystem.blendEquation(GL32.GL_FUNC_ADD);
|
||||
|
||||
if (GlCompat.SUPPORTS_DSA) {
|
||||
GL46.glNamedFramebufferDrawBuffers(fbo, ACCUMULATE_DRAW_BUFFERS);
|
||||
|
||||
GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, CLEAR_TO_ZERO);
|
||||
} else {
|
||||
GL32.glDrawBuffers(ACCUMULATE_DRAW_BUFFERS);
|
||||
RenderSystem.clearColor(0, 0, 0, 0);
|
||||
RenderSystem.clear(GL32.GL_COLOR_BUFFER_BIT, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Composite the accumulated luminance onto the main framebuffer.
|
||||
*/
|
||||
public void composite() {
|
||||
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();
|
||||
|
||||
// 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(GL32.GL_FUNC_ADD);
|
||||
RenderSystem.depthFunc(GL32.GL_ALWAYS);
|
||||
|
||||
GlTextureUnit.T0.makeActive();
|
||||
RenderSystem.bindTexture(accumulate);
|
||||
|
||||
programs.getOitCompositeProgram()
|
||||
.bind();
|
||||
|
||||
drawFullscreenQuad();
|
||||
|
||||
Minecraft.getInstance()
|
||||
.getMainRenderTarget()
|
||||
.bindWrite(false);
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
deleteTextures();
|
||||
GL32.glDeleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
private void drawFullscreenQuad() {
|
||||
// Empty VAO, the actual full screen triangle is generated in the vertex shader
|
||||
GlStateManager._glBindVertexArray(vao);
|
||||
|
||||
GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, 3);
|
||||
}
|
||||
|
||||
private void deleteTextures() {
|
||||
if (depthBounds != -1) {
|
||||
GL32.glDeleteTextures(depthBounds);
|
||||
}
|
||||
if (coefficients != -1) {
|
||||
GL32.glDeleteTextures(coefficients);
|
||||
}
|
||||
if (accumulate != -1) {
|
||||
GL32.glDeleteTextures(accumulate);
|
||||
}
|
||||
if (fbo != -1) {
|
||||
GL32.glDeleteFramebuffers(fbo);
|
||||
}
|
||||
|
||||
// We sometimes get the same texture ID back when creating new textures,
|
||||
// so bind zero to clear the GlStateManager
|
||||
Samplers.COEFFICIENTS.makeActive();
|
||||
RenderSystem.bindTexture(0);
|
||||
Samplers.DEPTH_RANGE.makeActive();
|
||||
RenderSystem.bindTexture(0);
|
||||
}
|
||||
|
||||
private void maybeResizeFBO(int width, int height) {
|
||||
if (lastWidth == width && lastHeight == height) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastWidth = width;
|
||||
lastHeight = height;
|
||||
|
||||
deleteTextures();
|
||||
|
||||
if (GlCompat.SUPPORTS_DSA) {
|
||||
fbo = GL46.glCreateFramebuffers();
|
||||
|
||||
depthBounds = GL46.glCreateTextures(GL46.GL_TEXTURE_2D);
|
||||
coefficients = GL46.glCreateTextures(GL46.GL_TEXTURE_2D_ARRAY);
|
||||
accumulate = GL46.glCreateTextures(GL46.GL_TEXTURE_2D);
|
||||
|
||||
GL46.glTextureStorage2D(depthBounds, 1, GL32.GL_RG32F, width, height);
|
||||
GL46.glTextureStorage3D(coefficients, 1, GL32.GL_RGBA16F, width, height, 4);
|
||||
GL46.glTextureStorage2D(accumulate, 1, GL32.GL_RGBA16F, width, height);
|
||||
|
||||
GL46.glNamedFramebufferTexture(fbo, GL32.GL_COLOR_ATTACHMENT0, depthBounds, 0);
|
||||
GL46.glNamedFramebufferTextureLayer(fbo, GL32.GL_COLOR_ATTACHMENT1, coefficients, 0, 0);
|
||||
GL46.glNamedFramebufferTextureLayer(fbo, GL32.GL_COLOR_ATTACHMENT2, coefficients, 0, 1);
|
||||
GL46.glNamedFramebufferTextureLayer(fbo, GL32.GL_COLOR_ATTACHMENT3, coefficients, 0, 2);
|
||||
GL46.glNamedFramebufferTextureLayer(fbo, GL32.GL_COLOR_ATTACHMENT4, coefficients, 0, 3);
|
||||
GL46.glNamedFramebufferTexture(fbo, GL32.GL_COLOR_ATTACHMENT5, accumulate, 0);
|
||||
} else {
|
||||
fbo = GL46.glGenFramebuffers();
|
||||
|
||||
depthBounds = GL32.glGenTextures();
|
||||
coefficients = GL32.glGenTextures();
|
||||
accumulate = GL32.glGenTextures();
|
||||
|
||||
GlTextureUnit.T0.makeActive();
|
||||
RenderSystem.bindTexture(0);
|
||||
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, depthBounds);
|
||||
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RG32F, width, height, 0, GL46.GL_RGBA, GL46.GL_BYTE, 0);
|
||||
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_NEAREST);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_NEAREST);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_S, GL32.GL_CLAMP_TO_EDGE);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_T, GL32.GL_CLAMP_TO_EDGE);
|
||||
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D_ARRAY, coefficients);
|
||||
GL32.glTexImage3D(GL32.GL_TEXTURE_2D_ARRAY, 0, GL32.GL_RGBA16F, width, height, 4, 0, GL46.GL_RGBA, GL46.GL_BYTE, 0);
|
||||
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D_ARRAY, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_NEAREST);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D_ARRAY, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_NEAREST);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D_ARRAY, GL32.GL_TEXTURE_WRAP_S, GL32.GL_CLAMP_TO_EDGE);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D_ARRAY, GL32.GL_TEXTURE_WRAP_T, GL32.GL_CLAMP_TO_EDGE);
|
||||
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, accumulate);
|
||||
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA16F, width, height, 0, GL46.GL_RGBA, GL46.GL_BYTE, 0);
|
||||
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_NEAREST);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_NEAREST);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_S, GL32.GL_CLAMP_TO_EDGE);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_T, GL32.GL_CLAMP_TO_EDGE);
|
||||
|
||||
GlStateManager._glBindFramebuffer(GL32.GL_FRAMEBUFFER, fbo);
|
||||
|
||||
GL46.glFramebufferTexture(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, depthBounds, 0);
|
||||
GL46.glFramebufferTextureLayer(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT1, coefficients, 0, 0);
|
||||
GL46.glFramebufferTextureLayer(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT2, coefficients, 0, 1);
|
||||
GL46.glFramebufferTextureLayer(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT3, coefficients, 0, 2);
|
||||
GL46.glFramebufferTextureLayer(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT4, coefficients, 0, 3);
|
||||
GL46.glFramebufferTexture(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT5, accumulate, 0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +1,17 @@
|
|||
package dev.engine_room.flywheel.backend.engine.instancing;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import dev.engine_room.flywheel.api.backend.Engine;
|
||||
import dev.engine_room.flywheel.api.instance.Instance;
|
||||
import dev.engine_room.flywheel.api.material.Material;
|
||||
import dev.engine_room.flywheel.api.material.Transparency;
|
||||
import dev.engine_room.flywheel.backend.Samplers;
|
||||
import dev.engine_room.flywheel.backend.compile.ContextShader;
|
||||
import dev.engine_room.flywheel.backend.compile.InstancingPrograms;
|
||||
import dev.engine_room.flywheel.backend.compile.PipelineCompiler;
|
||||
import dev.engine_room.flywheel.backend.engine.AbstractInstancer;
|
||||
import dev.engine_room.flywheel.backend.engine.CommonCrumbling;
|
||||
import dev.engine_room.flywheel.backend.engine.DrawManager;
|
||||
|
@ -19,6 +23,7 @@ import dev.engine_room.flywheel.backend.engine.MaterialRenderState;
|
|||
import dev.engine_room.flywheel.backend.engine.MeshPool;
|
||||
import dev.engine_room.flywheel.backend.engine.TextureBinder;
|
||||
import dev.engine_room.flywheel.backend.engine.embed.EnvironmentStorage;
|
||||
import dev.engine_room.flywheel.backend.engine.indirect.OitFramebuffer;
|
||||
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
|
||||
import dev.engine_room.flywheel.backend.gl.TextureBuffer;
|
||||
import dev.engine_room.flywheel.backend.gl.array.GlVertexArray;
|
||||
|
@ -28,7 +33,16 @@ import net.minecraft.client.Minecraft;
|
|||
import net.minecraft.client.resources.model.ModelBakery;
|
||||
|
||||
public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
||||
private final InstancedRenderStage draws = new InstancedRenderStage();
|
||||
private static final Comparator<InstancedDraw> DRAW_COMPARATOR = Comparator.comparing(InstancedDraw::bias)
|
||||
.thenComparing(InstancedDraw::indexOfMeshInModel)
|
||||
.thenComparing(InstancedDraw::material, MaterialRenderState.COMPARATOR);
|
||||
|
||||
private final List<InstancedDraw> allDraws = new ArrayList<>();
|
||||
private boolean needSort = false;
|
||||
|
||||
private final List<InstancedDraw> draws = new ArrayList<>();
|
||||
private final List<InstancedDraw> oitDraws = new ArrayList<>();
|
||||
|
||||
private final InstancingPrograms programs;
|
||||
/**
|
||||
* A map of vertex types to their mesh pools.
|
||||
|
@ -38,6 +52,8 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||
private final TextureBuffer instanceTexture;
|
||||
private final InstancedLight light;
|
||||
|
||||
private final OitFramebuffer oitFramebuffer;
|
||||
|
||||
public InstancedDrawManager(InstancingPrograms programs) {
|
||||
programs.acquire();
|
||||
this.programs = programs;
|
||||
|
@ -48,6 +64,9 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||
light = new InstancedLight();
|
||||
|
||||
meshPool.bind(vao);
|
||||
|
||||
oitFramebuffer = new OitFramebuffer(programs.oitPrograms());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -66,13 +85,31 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||
});
|
||||
|
||||
// Remove the draw calls for any instancers we deleted.
|
||||
draws.flush();
|
||||
needSort |= allDraws.removeIf(InstancedDraw::deleted);
|
||||
|
||||
if (needSort) {
|
||||
allDraws.sort(DRAW_COMPARATOR);
|
||||
|
||||
draws.clear();
|
||||
oitDraws.clear();
|
||||
|
||||
for (var draw : allDraws) {
|
||||
if (draw.material()
|
||||
.transparency() == Transparency.ORDER_INDEPENDENT) {
|
||||
oitDraws.add(draw);
|
||||
} else {
|
||||
draws.add(draw);
|
||||
}
|
||||
}
|
||||
|
||||
needSort = false;
|
||||
}
|
||||
|
||||
meshPool.flush();
|
||||
|
||||
light.flush(lightStorage);
|
||||
|
||||
if (draws.isEmpty()) {
|
||||
if (allDraws.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -81,18 +118,92 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||
TextureBinder.bindLightAndOverlay();
|
||||
light.bind();
|
||||
|
||||
draws.draw(instanceTexture, programs);
|
||||
submitDraws();
|
||||
|
||||
if (!oitDraws.isEmpty()) {
|
||||
oitFramebuffer.prepare();
|
||||
|
||||
oitFramebuffer.depthRange();
|
||||
|
||||
submitOitDraws(PipelineCompiler.OitMode.DEPTH_RANGE);
|
||||
|
||||
oitFramebuffer.renderTransmittance();
|
||||
|
||||
submitOitDraws(PipelineCompiler.OitMode.GENERATE_COEFFICIENTS);
|
||||
|
||||
oitFramebuffer.renderDepthFromTransmittance();
|
||||
|
||||
// Need to bind this again because we just drew a full screen quad for OIT.
|
||||
vao.bindForDraw();
|
||||
|
||||
oitFramebuffer.accumulate();
|
||||
|
||||
submitOitDraws(PipelineCompiler.OitMode.EVALUATE);
|
||||
|
||||
oitFramebuffer.composite();
|
||||
}
|
||||
|
||||
MaterialRenderState.reset();
|
||||
TextureBinder.resetLightAndOverlay();
|
||||
}
|
||||
|
||||
private void submitDraws() {
|
||||
for (var drawCall : draws) {
|
||||
var material = drawCall.material();
|
||||
var groupKey = drawCall.groupKey;
|
||||
var environment = groupKey.environment();
|
||||
|
||||
var program = programs.get(groupKey.instanceType(), environment.contextShader(), material, PipelineCompiler.OitMode.OFF);
|
||||
program.bind();
|
||||
|
||||
environment.setupDraw(program);
|
||||
|
||||
uploadMaterialUniform(program, material);
|
||||
|
||||
program.setUInt("_flw_vertexOffset", drawCall.mesh()
|
||||
.baseVertex());
|
||||
|
||||
MaterialRenderState.setup(material);
|
||||
|
||||
Samplers.INSTANCE_BUFFER.makeActive();
|
||||
|
||||
drawCall.render(instanceTexture);
|
||||
}
|
||||
}
|
||||
|
||||
private void submitOitDraws(PipelineCompiler.OitMode mode) {
|
||||
for (var drawCall : oitDraws) {
|
||||
var material = drawCall.material();
|
||||
var groupKey = drawCall.groupKey;
|
||||
var environment = groupKey.environment();
|
||||
|
||||
var program = programs.get(groupKey.instanceType(), environment.contextShader(), material, mode);
|
||||
program.bind();
|
||||
|
||||
environment.setupDraw(program);
|
||||
|
||||
uploadMaterialUniform(program, material);
|
||||
|
||||
program.setUInt("_flw_vertexOffset", drawCall.mesh()
|
||||
.baseVertex());
|
||||
|
||||
MaterialRenderState.setupOit(material);
|
||||
|
||||
Samplers.INSTANCE_BUFFER.makeActive();
|
||||
|
||||
drawCall.render(instanceTexture);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
instancers.values()
|
||||
.forEach(InstancedInstancer::delete);
|
||||
|
||||
draws.delete();
|
||||
allDraws.forEach(InstancedDraw::delete);
|
||||
allDraws.clear();
|
||||
draws.clear();
|
||||
oitDraws.clear();
|
||||
|
||||
meshPool.delete();
|
||||
instanceTexture.delete();
|
||||
|
@ -101,6 +212,8 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||
|
||||
light.delete();
|
||||
|
||||
oitFramebuffer.delete();
|
||||
|
||||
super.delete();
|
||||
}
|
||||
|
||||
|
@ -122,7 +235,8 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||
GroupKey<?> groupKey = new GroupKey<>(key.type(), key.environment());
|
||||
InstancedDraw instancedDraw = new InstancedDraw(instancer, mesh, groupKey, entry.material(), key.bias(), i);
|
||||
|
||||
draws.put(groupKey, instancedDraw);
|
||||
allDraws.add(instancedDraw);
|
||||
needSort = true;
|
||||
instancer.addDrawCall(instancedDraw);
|
||||
}
|
||||
}
|
||||
|
@ -165,7 +279,7 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||
|
||||
for (InstancedDraw draw : instancer.draws()) {
|
||||
CommonCrumbling.applyCrumblingProperties(crumblingMaterial, draw.material());
|
||||
var program = programs.get(shader.instanceType(), ContextShader.CRUMBLING, crumblingMaterial);
|
||||
var program = programs.get(shader.instanceType(), ContextShader.CRUMBLING, crumblingMaterial, PipelineCompiler.OitMode.OFF);
|
||||
program.bind();
|
||||
program.setInt("_flw_baseInstance", index);
|
||||
uploadMaterialUniform(program, crumblingMaterial);
|
||||
|
|
|
@ -1,106 +0,0 @@
|
|||
package dev.engine_room.flywheel.backend.engine.instancing;
|
||||
|
||||
import static dev.engine_room.flywheel.backend.engine.instancing.InstancedDrawManager.uploadMaterialUniform;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import dev.engine_room.flywheel.backend.Samplers;
|
||||
import dev.engine_room.flywheel.backend.compile.InstancingPrograms;
|
||||
import dev.engine_room.flywheel.backend.engine.GroupKey;
|
||||
import dev.engine_room.flywheel.backend.engine.MaterialRenderState;
|
||||
import dev.engine_room.flywheel.backend.gl.TextureBuffer;
|
||||
|
||||
public class InstancedRenderStage {
|
||||
private static final Comparator<InstancedDraw> DRAW_COMPARATOR = Comparator.comparing(InstancedDraw::bias)
|
||||
.thenComparing(InstancedDraw::indexOfMeshInModel)
|
||||
.thenComparing(InstancedDraw::material, MaterialRenderState.COMPARATOR);
|
||||
|
||||
private final Map<GroupKey<?>, DrawGroup> groups = new HashMap<>();
|
||||
|
||||
public InstancedRenderStage() {
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
groups.values()
|
||||
.forEach(DrawGroup::delete);
|
||||
groups.clear();
|
||||
}
|
||||
|
||||
public void put(GroupKey<?> groupKey, InstancedDraw instancedDraw) {
|
||||
groups.computeIfAbsent(groupKey, $ -> new DrawGroup())
|
||||
.put(instancedDraw);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return groups.isEmpty();
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
groups.values()
|
||||
.forEach(DrawGroup::flush);
|
||||
|
||||
groups.values()
|
||||
.removeIf(DrawGroup::isEmpty);
|
||||
}
|
||||
|
||||
public void draw(TextureBuffer instanceTexture, InstancingPrograms programs) {
|
||||
for (var entry : groups.entrySet()) {
|
||||
var shader = entry.getKey();
|
||||
var drawCalls = entry.getValue();
|
||||
|
||||
var environment = shader.environment();
|
||||
|
||||
for (var drawCall : drawCalls.draws) {
|
||||
var material = drawCall.material();
|
||||
|
||||
var program = programs.get(shader.instanceType(), environment.contextShader(), material);
|
||||
program.bind();
|
||||
|
||||
environment.setupDraw(program);
|
||||
|
||||
uploadMaterialUniform(program, material);
|
||||
|
||||
program.setUInt("_flw_vertexOffset", drawCall.mesh()
|
||||
.baseVertex());
|
||||
|
||||
MaterialRenderState.setup(material);
|
||||
|
||||
Samplers.INSTANCE_BUFFER.makeActive();
|
||||
|
||||
drawCall.render(instanceTexture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class DrawGroup {
|
||||
private final List<InstancedDraw> draws = new ArrayList<>();
|
||||
private boolean needSort = false;
|
||||
|
||||
public void put(InstancedDraw instancedDraw) {
|
||||
draws.add(instancedDraw);
|
||||
needSort = true;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
draws.forEach(InstancedDraw::delete);
|
||||
draws.clear();
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
needSort |= draws.removeIf(InstancedDraw::deleted);
|
||||
|
||||
if (needSort) {
|
||||
draws.sort(DRAW_COMPARATOR);
|
||||
needSort = false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return draws.isEmpty();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
@ -116,6 +117,9 @@ public final class FrameUniforms extends UniformWriter {
|
|||
|
||||
ptr = writeInt(ptr, debugMode);
|
||||
|
||||
// OIT noise factor
|
||||
ptr = writeFloat(ptr, 0.07f);
|
||||
|
||||
firstWrite = false;
|
||||
BUFFER.markDirty();
|
||||
}
|
||||
|
@ -195,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
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package dev.engine_room.flywheel.backend.gl;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.lwjgl.PointerBuffer;
|
||||
import org.lwjgl.opengl.GL;
|
||||
|
@ -13,6 +11,7 @@ import org.lwjgl.opengl.GL43;
|
|||
import org.lwjgl.opengl.GLCapabilities;
|
||||
import org.lwjgl.opengl.KHRShaderSubgroup;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import dev.engine_room.flywheel.backend.FlwBackend;
|
||||
import dev.engine_room.flywheel.backend.compile.core.Compilation;
|
||||
|
@ -40,6 +39,8 @@ public final class GlCompat {
|
|||
public static final boolean ALLOW_DSA = true;
|
||||
public static final GlslVersion MAX_GLSL_VERSION = maxGlslVersion();
|
||||
|
||||
public static final boolean SUPPORTS_DSA = ALLOW_DSA && isDsaSupported();
|
||||
|
||||
public static final boolean SUPPORTS_INSTANCING = isInstancingSupported();
|
||||
public static final boolean SUPPORTS_INDIRECT = isIndirectSupported();
|
||||
|
||||
|
@ -67,10 +68,11 @@ public final class GlCompat {
|
|||
*/
|
||||
public static void safeShaderSource(int glId, CharSequence source) {
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
final ByteBuffer sourceBuffer = stack.UTF8(source, true);
|
||||
var sourceBuffer = MemoryUtil.memUTF8(source, true);
|
||||
final PointerBuffer pointers = stack.mallocPointer(1);
|
||||
pointers.put(sourceBuffer);
|
||||
GL20C.nglShaderSource(glId, 1, pointers.address0(), 0);
|
||||
MemoryUtil.memFree(sourceBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,8 +163,15 @@ public final class GlCompat {
|
|||
&& CAPABILITIES.GL_ARB_multi_draw_indirect
|
||||
&& CAPABILITIES.GL_ARB_shader_draw_parameters
|
||||
&& CAPABILITIES.GL_ARB_shader_storage_buffer_object
|
||||
&& CAPABILITIES.GL_ARB_shading_language_420pack
|
||||
&& CAPABILITIES.GL_ARB_vertex_attrib_binding;
|
||||
&& CAPABILITIES.GL_ARB_shading_language_420pack && CAPABILITIES.GL_ARB_vertex_attrib_binding && CAPABILITIES.GL_ARB_shader_image_load_store && CAPABILITIES.GL_ARB_shader_image_size;
|
||||
}
|
||||
|
||||
private static boolean isDsaSupported() {
|
||||
if (CAPABILITIES == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return CAPABILITIES.GL_ARB_direct_state_access;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package dev.engine_room.flywheel.backend.glsl.parse;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
@ -34,20 +33,21 @@ public class ShaderField {
|
|||
* Scan the source for function definitions and "parse" them into objects that contain properties of the function.
|
||||
*/
|
||||
public static ImmutableMap<String, ShaderField> parseFields(SourceLines source) {
|
||||
Matcher matcher = PATTERN.matcher(source);
|
||||
// Matcher matcher = PATTERN.matcher(source);
|
||||
//
|
||||
// ImmutableMap.Builder<String, ShaderField> fields = ImmutableMap.builder();
|
||||
// while (matcher.find()) {
|
||||
// Span self = Span.fromMatcher(source, matcher);
|
||||
// Span location = Span.fromMatcher(source, matcher, 1);
|
||||
// Span decoration = Span.fromMatcher(source, matcher, 2);
|
||||
// Span type = Span.fromMatcher(source, matcher, 3);
|
||||
// Span name = Span.fromMatcher(source, matcher, 4);
|
||||
//
|
||||
// fields.put(location.get(), new ShaderField(self, location, decoration, type, name));
|
||||
// }
|
||||
|
||||
ImmutableMap.Builder<String, ShaderField> fields = ImmutableMap.builder();
|
||||
while (matcher.find()) {
|
||||
Span self = Span.fromMatcher(source, matcher);
|
||||
Span location = Span.fromMatcher(source, matcher, 1);
|
||||
Span decoration = Span.fromMatcher(source, matcher, 2);
|
||||
Span type = Span.fromMatcher(source, matcher, 3);
|
||||
Span name = Span.fromMatcher(source, matcher, 4);
|
||||
|
||||
fields.put(location.get(), new ShaderField(self, location, decoration, type, name));
|
||||
}
|
||||
|
||||
return fields.build();
|
||||
return ImmutableMap.<String, ShaderField>builder()
|
||||
.build();
|
||||
}
|
||||
|
||||
public enum Qualifier {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include "flywheel:internal/packed_material.glsl"
|
||||
#include "flywheel:internal/diffuse.glsl"
|
||||
#include "flywheel:internal/colorizer.glsl"
|
||||
#include "flywheel:internal/wavelet.glsl"
|
||||
#include "flywheel:internal/depth.glsl"
|
||||
|
||||
// optimize discard usage
|
||||
#if defined(GL_ARB_conservative_depth) && defined(_FLW_USE_DISCARD)
|
||||
|
@ -17,8 +19,67 @@ in vec2 _flw_crumblingTexCoord;
|
|||
flat in uvec2 _flw_ids;
|
||||
#endif
|
||||
|
||||
#ifdef _FLW_OIT
|
||||
|
||||
uniform sampler2D _flw_depthRange;
|
||||
|
||||
uniform sampler2DArray _flw_coefficients;
|
||||
|
||||
uniform sampler2D _flw_blueNoise;
|
||||
|
||||
float tented_blue_noise(float normalizedDepth) {
|
||||
|
||||
float tentIn = abs(normalizedDepth * 2. - 1);
|
||||
float tentIn2 = tentIn * tentIn;
|
||||
float tentIn4 = tentIn2 * tentIn2;
|
||||
float tent = 1 - (tentIn2 * tentIn4);
|
||||
|
||||
float b = texture(_flw_blueNoise, gl_FragCoord.xy / vec2(64)).r;
|
||||
|
||||
return b * tent;
|
||||
}
|
||||
|
||||
float linear_depth() {
|
||||
return linearize_depth(gl_FragCoord.z, _flw_cullData.znear, _flw_cullData.zfar);
|
||||
}
|
||||
|
||||
float depth() {
|
||||
float linearDepth = linear_depth();
|
||||
|
||||
vec2 depthRange = texelFetch(_flw_depthRange, ivec2(gl_FragCoord.xy), 0).rg;
|
||||
float delta = depthRange.x + depthRange.y;
|
||||
float depth = (linearDepth + depthRange.x) / delta;
|
||||
|
||||
return depth - tented_blue_noise(depth) * _flw_oitNoise;
|
||||
}
|
||||
|
||||
#ifdef _FLW_DEPTH_RANGE
|
||||
|
||||
out vec2 _flw_depthRange_out;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _FLW_COLLECT_COEFFS
|
||||
|
||||
out vec4 _flw_coeffs0;
|
||||
out vec4 _flw_coeffs1;
|
||||
out vec4 _flw_coeffs2;
|
||||
out vec4 _flw_coeffs3;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _FLW_EVALUATE
|
||||
|
||||
out vec4 _flw_accumulate;
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
out vec4 _flw_outputColor;
|
||||
|
||||
#endif
|
||||
|
||||
float _flw_diffuseFactor() {
|
||||
if (flw_material.cardinalLightingMode == 2u) {
|
||||
return diffuseFromLightDirections(flw_vertexNormal);
|
||||
|
@ -99,5 +160,47 @@ void _flw_main() {
|
|||
}
|
||||
#endif
|
||||
|
||||
_flw_outputColor = flw_fogFilter(color);
|
||||
color = flw_fogFilter(color);
|
||||
|
||||
#ifdef _FLW_OIT
|
||||
|
||||
#ifdef _FLW_DEPTH_RANGE
|
||||
float linearDepth = linear_depth();
|
||||
|
||||
// Pad the depth by some unbalanced epsilons because minecraft has a lot of single-quad tranparency.
|
||||
// The unbalance means our fragment will be considered closer to the screen in the normalization,
|
||||
// which helps prevent unnecessary noise as it'll be closer to the edge of our tent function.
|
||||
_flw_depthRange_out = vec2(-linearDepth + 1e-5, linearDepth + 1e-2);
|
||||
#endif
|
||||
|
||||
#ifdef _FLW_COLLECT_COEFFS
|
||||
|
||||
vec4[4] result;
|
||||
result[0] = vec4(0.);
|
||||
result[1] = vec4(0.);
|
||||
result[2] = vec4(0.);
|
||||
result[3] = vec4(0.);
|
||||
|
||||
add_transmittance(result, 1. - color.a, depth());
|
||||
|
||||
_flw_coeffs0 = result[0];
|
||||
_flw_coeffs1 = result[1];
|
||||
_flw_coeffs2 = result[2];
|
||||
_flw_coeffs3 = result[3];
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _FLW_EVALUATE
|
||||
|
||||
float transmittance = signal_corrected_transmittance(_flw_coefficients, depth(), 1. - color.a);
|
||||
|
||||
_flw_accumulate = vec4(color.rgb * color.a, color.a) * transmittance;
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
_flw_outputColor = color;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
float linearize_depth(float d, float zNear, float zFar) {
|
||||
float z_n = 2.0 * d - 1.0;
|
||||
return 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear));
|
||||
}
|
||||
|
||||
float delinearize_depth(float linearDepth, float zNear, float zFar) {
|
||||
float z_n = (2.0 * zNear * zFar / linearDepth) - (zFar + zNear);
|
||||
return 0.5 * (z_n / (zNear - zFar) + 1.0);
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
void main() {
|
||||
vec2 vertices[3] = vec2[3](vec2(-1, -1), vec2(3, -1), vec2(-1, 3));
|
||||
gl_Position = vec4(vertices[gl_VertexID], 0, 1);
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
const uint _FLW_BLOCKS_PER_SECTION = 18 * 18 * 18;
|
||||
const uint _FLW_BLOCKS_PER_SECTION = 18u * 18u * 18u;
|
||||
const uint _FLW_LIGHT_SIZE_BYTES = _FLW_BLOCKS_PER_SECTION;
|
||||
const uint _FLW_SOLID_SIZE_BYTES = ((_FLW_BLOCKS_PER_SECTION + 31) / 32) * 4;
|
||||
const uint _FLW_SOLID_SIZE_BYTES = ((_FLW_BLOCKS_PER_SECTION + 31u) / 32u) * 4u;
|
||||
const uint _FLW_LIGHT_START_BYTES = _FLW_SOLID_SIZE_BYTES;
|
||||
const uint _FLW_LIGHT_SECTION_SIZE_BYTES = _FLW_SOLID_SIZE_BYTES + _FLW_LIGHT_SIZE_BYTES;
|
||||
|
||||
const uint _FLW_SOLID_START_INTS = 0;
|
||||
const uint _FLW_LIGHT_START_INTS = _FLW_SOLID_SIZE_BYTES / 4;
|
||||
const uint _FLW_LIGHT_SECTION_SIZE_INTS = _FLW_LIGHT_SECTION_SIZE_BYTES / 4;
|
||||
const uint _FLW_SOLID_START_INTS = 0u;
|
||||
const uint _FLW_LIGHT_START_INTS = _FLW_SOLID_SIZE_BYTES / 4u;
|
||||
const uint _FLW_LIGHT_SECTION_SIZE_INTS = _FLW_LIGHT_SECTION_SIZE_BYTES / 4u;
|
||||
|
||||
const uint _FLW_COMPLETELY_SOLID = 0x7FFFFFFu;
|
||||
const float _FLW_EPSILON = 1e-5;
|
||||
|
@ -29,39 +29,39 @@ bool _flw_nextLut(uint base, int coord, out uint next) {
|
|||
// The base coordinate.
|
||||
int start = int(_flw_indexLut(base));
|
||||
// The width of the coordinate span.
|
||||
uint size = _flw_indexLut(base + 1);
|
||||
uint size = _flw_indexLut(base + 1u);
|
||||
|
||||
// Index of the coordinate in the span.
|
||||
int i = coord - start;
|
||||
|
||||
if (i < 0 || i >= size) {
|
||||
if (i < 0 || i >= int(size)) {
|
||||
// We missed.
|
||||
return true;
|
||||
}
|
||||
|
||||
next = _flw_indexLut(base + 2 + i);
|
||||
next = _flw_indexLut(base + 2u + uint(i));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _flw_chunkCoordToSectionIndex(ivec3 sectionPos, out uint index) {
|
||||
uint first;
|
||||
if (_flw_nextLut(0, sectionPos.y, first) || first == 0) {
|
||||
if (_flw_nextLut(0u, sectionPos.y, first) || first == 0u) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint second;
|
||||
if (_flw_nextLut(first, sectionPos.x, second) || second == 0) {
|
||||
if (_flw_nextLut(first, sectionPos.x, second) || second == 0u) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint sectionIndex;
|
||||
if (_flw_nextLut(second, sectionPos.z, sectionIndex) || sectionIndex == 0) {
|
||||
if (_flw_nextLut(second, sectionPos.z, sectionIndex) || sectionIndex == 0u) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The index is written as 1-based so we can properly detect missing sections.
|
||||
index = sectionIndex - 1;
|
||||
index = sectionIndex - 1u;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ bool _flw_isSolid(uint sectionOffset, uvec3 blockInSectionPos) {
|
|||
|
||||
uint word = _flw_indexLight(sectionOffset + _FLW_SOLID_START_INTS + uintOffset);
|
||||
|
||||
return (word & (1u << bitInWordOffset)) != 0;
|
||||
return (word & (1u << bitInWordOffset)) != 0u;
|
||||
}
|
||||
|
||||
bool flw_lightFetch(ivec3 blockPos, out vec2 lightCoord) {
|
||||
|
@ -98,7 +98,7 @@ bool flw_lightFetch(ivec3 blockPos, out vec2 lightCoord) {
|
|||
// The offset of the section in the light buffer.
|
||||
uint sectionOffset = lightSectionIndex * _FLW_LIGHT_SECTION_SIZE_INTS;
|
||||
|
||||
uvec3 blockInSectionPos = (blockPos & 0xF) + 1;
|
||||
uvec3 blockInSectionPos = uvec3((blockPos & 0xF) + 1);
|
||||
|
||||
lightCoord = vec2(_flw_lightAt(sectionOffset, blockInSectionPos)) * _FLW_LIGHT_NORMALIZER;
|
||||
return true;
|
||||
|
@ -106,7 +106,7 @@ bool flw_lightFetch(ivec3 blockPos, out vec2 lightCoord) {
|
|||
|
||||
|
||||
uint _flw_fetchSolid3x3x3(uint sectionOffset, ivec3 blockInSectionPos) {
|
||||
uint ret = 0;
|
||||
uint ret = 0u;
|
||||
|
||||
// The formatter does NOT like these macros
|
||||
// @formatter:off
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
#include "flywheel:internal/wavelet.glsl"
|
||||
#include "flywheel:internal/depth.glsl"
|
||||
#include "flywheel:internal/uniforms/frame.glsl"
|
||||
|
||||
out vec4 frag;
|
||||
|
||||
uniform sampler2D _flw_accumulate;
|
||||
uniform sampler2D _flw_depthRange;
|
||||
uniform sampler2DArray _flw_coefficients;
|
||||
|
||||
void main() {
|
||||
vec4 texel = texelFetch(_flw_accumulate, ivec2(gl_FragCoord.xy), 0);
|
||||
|
||||
if (texel.a < 1e-5) {
|
||||
discard;
|
||||
}
|
||||
|
||||
float total_transmittance = total_transmittance(_flw_coefficients);
|
||||
|
||||
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);
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
#include "flywheel:internal/uniforms/frame.glsl"
|
||||
#include "flywheel:internal/wavelet.glsl"
|
||||
#include "flywheel:internal/depth.glsl"
|
||||
|
||||
uniform sampler2D _flw_depthRange;
|
||||
|
||||
uniform sampler2DArray _flw_coefficients;
|
||||
|
||||
float eye_depth_from_normalized_transparency_depth(float tDepth) {
|
||||
vec2 depthRange = texelFetch(_flw_depthRange, ivec2(gl_FragCoord.xy), 0).rg;
|
||||
|
||||
float delta = depthRange.x + depthRange.y;
|
||||
|
||||
return tDepth * delta - depthRange.x;
|
||||
}
|
||||
|
||||
void main() {
|
||||
float threshold = 0.0001;
|
||||
|
||||
//
|
||||
// If transmittance an infinite depth is above the threshold, it doesn't ever become
|
||||
// zero, so we can bail out.
|
||||
//
|
||||
float transmittance_at_far_depth = total_transmittance(_flw_coefficients);
|
||||
if (transmittance_at_far_depth > threshold) {
|
||||
discard;
|
||||
}
|
||||
|
||||
float normalized_depth_at_zero_transmittance = 1.0;
|
||||
float sample_depth = 0.5;
|
||||
float delta = 0.25;
|
||||
|
||||
//
|
||||
// Quick & Dirty way to binary search through the transmittance function
|
||||
// looking for a value that's below the threshold.
|
||||
//
|
||||
int steps = 6;
|
||||
for (int i = 0; i < steps; ++i) {
|
||||
float transmittance = transmittance(_flw_coefficients, sample_depth);
|
||||
if (transmittance <= threshold) {
|
||||
normalized_depth_at_zero_transmittance = sample_depth;
|
||||
sample_depth -= delta;
|
||||
} else {
|
||||
sample_depth += delta;
|
||||
}
|
||||
delta *= 0.5;
|
||||
}
|
||||
|
||||
//
|
||||
// Searching inside the transparency depth bounds, so have to transform that to
|
||||
// a world-space linear-depth and that into a device depth we can output into
|
||||
// the currently bound depth buffer.
|
||||
//
|
||||
float eyeDepth = eye_depth_from_normalized_transparency_depth(normalized_depth_at_zero_transmittance);
|
||||
gl_FragDepth = delinearize_depth(eyeDepth, _flw_cullData.znear, _flw_cullData.zfar);
|
||||
}
|
|
@ -62,6 +62,8 @@ layout(std140) uniform _FlwFrameUniforms {
|
|||
uint flw_cameraInBlock;
|
||||
|
||||
uint _flw_debugMode;
|
||||
|
||||
float _flw_oitNoise;
|
||||
};
|
||||
|
||||
#define flw_renderOrigin (_flw_renderOrigin.xyz)
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
#define TRANSPARENCY_WAVELET_RANK 3
|
||||
#define TRANSPARENCY_WAVELET_COEFFICIENT_COUNT 16
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// WRITING
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
void add_to_index(inout vec4[4] coefficients, int index, float addend) {
|
||||
coefficients[index >> 2][index & 3] = addend;
|
||||
}
|
||||
|
||||
void add_absorbance(inout vec4[4] coefficients, float signal, float depth) {
|
||||
depth *= float(TRANSPARENCY_WAVELET_COEFFICIENT_COUNT-1) / TRANSPARENCY_WAVELET_COEFFICIENT_COUNT;
|
||||
|
||||
int index = clamp(int(floor(depth * TRANSPARENCY_WAVELET_COEFFICIENT_COUNT)), 0, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1);
|
||||
index += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1;
|
||||
|
||||
for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) {
|
||||
int power = TRANSPARENCY_WAVELET_RANK - i;
|
||||
int new_index = (index - 1) >> 1;
|
||||
float k = float((new_index + 1) & ((1 << power) - 1));
|
||||
|
||||
int wavelet_sign = ((index & 1) << 1) - 1;
|
||||
float wavelet_phase = ((index + 1) & 1) * exp2(-power);
|
||||
float addend = fma(fma(-exp2(-power), k, depth), wavelet_sign, wavelet_phase) * exp2(power * 0.5) * signal;
|
||||
add_to_index(coefficients, new_index, addend);
|
||||
|
||||
index = new_index;
|
||||
}
|
||||
|
||||
float addend = fma(signal, -depth, signal);
|
||||
add_to_index(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1, addend);
|
||||
}
|
||||
|
||||
void add_transmittance(inout vec4[4] coefficients, float transmittance, float depth) {
|
||||
float absorbance = -log(max(transmittance, 0.00001));// transforming the signal from multiplicative transmittance to additive absorbance
|
||||
add_absorbance(coefficients, absorbance, depth);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// READING
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// TODO: maybe we could reduce the number of texel fetches below?
|
||||
float get_coefficients(in sampler2DArray coefficients, int index) {
|
||||
return texelFetch(coefficients, ivec3(gl_FragCoord.xy, index >> 2), 0)[index & 3];
|
||||
}
|
||||
|
||||
/// Compute the total absorbance, as if at infinite depth.
|
||||
float total_absorbance(in sampler2DArray coefficients) {
|
||||
float scale_coefficient = get_coefficients(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1);
|
||||
if (scale_coefficient == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int index_b = TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1;
|
||||
|
||||
index_b += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1;
|
||||
|
||||
float b = scale_coefficient;
|
||||
|
||||
for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) {
|
||||
int power = TRANSPARENCY_WAVELET_RANK - i;
|
||||
|
||||
int new_index_b = (index_b - 1) >> 1;
|
||||
int wavelet_sign_b = ((index_b & 1) << 1) - 1;
|
||||
float coeff_b = get_coefficients(coefficients, new_index_b);
|
||||
b -= exp2(float(power) * 0.5) * coeff_b * wavelet_sign_b;
|
||||
index_b = new_index_b;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/// Compute the absorbance at a given normalized depth.
|
||||
float absorbance(in sampler2DArray coefficients, float depth) {
|
||||
float scale_coefficient = get_coefficients(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1);
|
||||
if (scale_coefficient == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
depth *= float(TRANSPARENCY_WAVELET_COEFFICIENT_COUNT-1) / TRANSPARENCY_WAVELET_COEFFICIENT_COUNT;
|
||||
|
||||
float coefficient_depth = depth * TRANSPARENCY_WAVELET_COEFFICIENT_COUNT;
|
||||
int index_b = clamp(int(floor(coefficient_depth)), 0, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1);
|
||||
bool sample_a = index_b >= 1;
|
||||
int index_a = sample_a ? (index_b - 1) : index_b;
|
||||
|
||||
index_b += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1;
|
||||
index_a += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1;
|
||||
|
||||
float b = scale_coefficient;
|
||||
float a = sample_a ? scale_coefficient : 0;
|
||||
|
||||
for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) {
|
||||
int power = TRANSPARENCY_WAVELET_RANK - i;
|
||||
|
||||
int new_index_b = (index_b - 1) >> 1;
|
||||
int wavelet_sign_b = ((index_b & 1) << 1) - 1;
|
||||
float coeff_b = get_coefficients(coefficients, new_index_b);
|
||||
b -= exp2(float(power) * 0.5) * coeff_b * wavelet_sign_b;
|
||||
index_b = new_index_b;
|
||||
|
||||
if (sample_a) {
|
||||
int new_index_a = (index_a - 1) >> 1;
|
||||
int wavelet_sign_a = ((index_a & 1) << 1) - 1;
|
||||
float coeff_a = (new_index_a == new_index_b) ? coeff_b : get_coefficients(coefficients, new_index_a);
|
||||
a -= exp2(float(power) * 0.5) * coeff_a * wavelet_sign_a;
|
||||
index_a = new_index_a;
|
||||
}
|
||||
}
|
||||
|
||||
float t = coefficient_depth >= TRANSPARENCY_WAVELET_COEFFICIENT_COUNT ? 1.0 : fract(coefficient_depth);
|
||||
|
||||
return mix(a, b, t);
|
||||
}
|
||||
|
||||
/// Compute the absorbance at a given normalized depth,
|
||||
/// correcting for self-occlusion by undoing the previously recorded absorbance event.
|
||||
float signal_corrected_absorbance(in sampler2DArray coefficients, float depth, float signal) {
|
||||
float scale_coefficient = get_coefficients(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1);
|
||||
if (scale_coefficient == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
depth *= float(TRANSPARENCY_WAVELET_COEFFICIENT_COUNT-1) / TRANSPARENCY_WAVELET_COEFFICIENT_COUNT;
|
||||
|
||||
float scale_coefficient_addend = fma(signal, -depth, signal);
|
||||
scale_coefficient -= scale_coefficient_addend;
|
||||
|
||||
float coefficient_depth = depth * TRANSPARENCY_WAVELET_COEFFICIENT_COUNT;
|
||||
int index_b = clamp(int(floor(coefficient_depth)), 0, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1);
|
||||
bool sample_a = index_b >= 1;
|
||||
int index_a = sample_a ? (index_b - 1) : index_b;
|
||||
|
||||
index_b += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1;
|
||||
index_a += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1;
|
||||
|
||||
float b = scale_coefficient;
|
||||
float a = sample_a ? scale_coefficient : 0;
|
||||
|
||||
for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) {
|
||||
int power = TRANSPARENCY_WAVELET_RANK - i;
|
||||
|
||||
int new_index_b = (index_b - 1) >> 1;
|
||||
int wavelet_sign_b = ((index_b & 1) << 1) - 1;
|
||||
float coeff_b = get_coefficients(coefficients, new_index_b);
|
||||
|
||||
float wavelet_phase_b = ((index_b + 1) & 1) * exp2(-power);
|
||||
float k = float((new_index_b + 1) & ((1 << power) - 1));
|
||||
float addend = fma(fma(-exp2(-power), k, depth), wavelet_sign_b, wavelet_phase_b) * exp2(power * 0.5) * signal;
|
||||
coeff_b -= addend;
|
||||
|
||||
b -= exp2(float(power) * 0.5) * coeff_b * wavelet_sign_b;
|
||||
index_b = new_index_b;
|
||||
|
||||
if (sample_a) {
|
||||
int new_index_a = (index_a - 1) >> 1;
|
||||
int wavelet_sign_a = ((index_a & 1) << 1) - 1;
|
||||
float coeff_a = (new_index_a == new_index_b) ? coeff_b : get_coefficients(coefficients, new_index_a);// No addend here on purpose, the original signal didn't contribute to this coefficient
|
||||
a -= exp2(float(power) * 0.5) * coeff_a * wavelet_sign_a;
|
||||
index_a = new_index_a;
|
||||
}
|
||||
}
|
||||
|
||||
float t = coefficient_depth >= TRANSPARENCY_WAVELET_COEFFICIENT_COUNT ? 1.0 : fract(coefficient_depth);
|
||||
|
||||
return mix(a, b, t);
|
||||
}
|
||||
|
||||
// Helpers below to deal directly in transmittance.
|
||||
|
||||
#define ABSORBANCE_TO_TRANSMITTANCE(a) clamp(exp(-(a)), 0., 1.)
|
||||
|
||||
float total_transmittance(in sampler2DArray coefficients) {
|
||||
return ABSORBANCE_TO_TRANSMITTANCE(total_absorbance(coefficients));
|
||||
}
|
||||
|
||||
float transmittance(in sampler2DArray coefficients, float depth) {
|
||||
return ABSORBANCE_TO_TRANSMITTANCE(absorbance(coefficients, depth));
|
||||
}
|
||||
|
||||
float signal_corrected_transmittance(in sampler2DArray coefficients, float depth, float signal) {
|
||||
return ABSORBANCE_TO_TRANSMITTANCE(signal_corrected_absorbance(coefficients, depth, signal));
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 6.9 KiB |
|
@ -1,6 +1,7 @@
|
|||
package dev.engine_room.flywheel.backend.compile;
|
||||
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
import dev.engine_room.flywheel.backend.NoiseTextures;
|
||||
import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
|
@ -16,6 +17,7 @@ public final class FlwProgramsReloader implements SimpleSynchronousResourceReloa
|
|||
@Override
|
||||
public void onResourceManagerReload(ResourceManager manager) {
|
||||
FlwPrograms.reload(manager);
|
||||
NoiseTextures.reload(manager);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package dev.engine_room.flywheel.backend.compile;
|
||||
|
||||
import dev.engine_room.flywheel.backend.NoiseTextures;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.server.packs.resources.ResourceManagerReloadListener;
|
||||
|
||||
|
@ -12,5 +13,6 @@ public final class FlwProgramsReloader implements ResourceManagerReloadListener
|
|||
@Override
|
||||
public void onResourceManagerReload(ResourceManager manager) {
|
||||
FlwPrograms.reload(manager);
|
||||
NoiseTextures.reload(manager);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue