mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-02-22 01:45:33 +01:00
Wbout it
- Implement WBOIT
This commit is contained in:
parent
c438fb57ca
commit
5d16ebda60
10 changed files with 310 additions and 16 deletions
|
@ -31,7 +31,9 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||
private static final ResourceLocation SCATTER_SHADER_MAIN = Flywheel.rl("internal/indirect/scatter.glsl");
|
||||
private static final ResourceLocation DOWNSAMPLE_FIRST = Flywheel.rl("internal/indirect/downsample_first.glsl");
|
||||
private static final ResourceLocation DOWNSAMPLE_SECOND = Flywheel.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 ResourceLocation FULLSCREEN = Flywheel.rl("internal/indirect/fullscreen.vert");
|
||||
private static final ResourceLocation OIT_COMPOSITE = Flywheel.rl("internal/indirect/oit_composite.frag");
|
||||
|
||||
private static final Compile<InstanceType<?>> CULL = new Compile<>();
|
||||
private static final Compile<ResourceLocation> UTIL = new Compile<>();
|
||||
|
@ -45,11 +47,13 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||
private final PipelineCompiler pipeline;
|
||||
private final CompilationHarness<InstanceType<?>> culling;
|
||||
private final CompilationHarness<ResourceLocation> utils;
|
||||
private final CompilationHarness<ResourceLocation> fullscreen;
|
||||
|
||||
private IndirectPrograms(PipelineCompiler pipeline, CompilationHarness<InstanceType<?>> culling, CompilationHarness<ResourceLocation> utils) {
|
||||
private IndirectPrograms(PipelineCompiler pipeline, CompilationHarness<InstanceType<?>> culling, CompilationHarness<ResourceLocation> utils, CompilationHarness<ResourceLocation> fullscreen) {
|
||||
this.pipeline = pipeline;
|
||||
this.culling = culling;
|
||||
this.utils = utils;
|
||||
this.fullscreen = fullscreen;
|
||||
}
|
||||
|
||||
private static List<String> getExtensions(GlslVersion glslVersion) {
|
||||
|
@ -88,8 +92,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 = createFullscreenCompiler(sources);
|
||||
|
||||
IndirectPrograms newInstance = new IndirectPrograms(pipelineCompiler, cullingCompiler, utilCompiler);
|
||||
IndirectPrograms newInstance = new IndirectPrograms(pipelineCompiler, cullingCompiler, utilCompiler, fullscreenCompiler);
|
||||
|
||||
setInstance(newInstance);
|
||||
}
|
||||
|
@ -125,6 +130,17 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||
.harness("utilities", sources);
|
||||
}
|
||||
|
||||
private static CompilationHarness<ResourceLocation> createFullscreenCompiler(ShaderSources sources) {
|
||||
return UTIL.program()
|
||||
.link(UTIL.shader(GlCompat.MAX_GLSL_VERSION, ShaderType.VERTEX)
|
||||
.nameMapper($ -> "fullscreen/fullscreen")
|
||||
.withResource(FULLSCREEN))
|
||||
.link(UTIL.shader(GlCompat.MAX_GLSL_VERSION, ShaderType.FRAGMENT)
|
||||
.nameMapper(rl -> "fullscreen/" + ResourceUtil.toDebugFileNameNoExtension(rl))
|
||||
.withResource(s -> s))
|
||||
.harness("fullscreen", sources);
|
||||
}
|
||||
|
||||
static void setInstance(@Nullable IndirectPrograms newInstance) {
|
||||
if (instance != null) {
|
||||
instance.release();
|
||||
|
@ -148,8 +164,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, boolean oit) {
|
||||
return pipeline.get(instanceType, contextShader, material, oit);
|
||||
}
|
||||
|
||||
public GlProgram getCullingProgram(InstanceType<?> instanceType) {
|
||||
|
@ -172,10 +188,15 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||
return utils.get(DOWNSAMPLE_SECOND);
|
||||
}
|
||||
|
||||
public GlProgram getOitCompositeProgram() {
|
||||
return fullscreen.get(OIT_COMPOSITE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _delete() {
|
||||
pipeline.delete();
|
||||
culling.delete();
|
||||
utils.delete();
|
||||
fullscreen.delete();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ public class InstancingPrograms extends AtomicReferenceCounted {
|
|||
}
|
||||
|
||||
public GlProgram get(InstanceType<?> instanceType, ContextShader contextShader, Material material) {
|
||||
return pipeline.get(instanceType, contextShader, material);
|
||||
return pipeline.get(instanceType, contextShader, material, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -50,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, boolean oit) {
|
||||
var light = material.light();
|
||||
var cutout = material.cutout();
|
||||
var shaders = material.shaders();
|
||||
|
@ -66,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() {
|
||||
|
@ -128,7 +128,8 @@ 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() ? "_oit" : "";
|
||||
return "pipeline/" + pipeline.compilerMarker() + "/frag/" + material + "/" + light + "_" + context + cutout + debug + oit;
|
||||
})
|
||||
.requireExtensions(extensions)
|
||||
.enableExtension("GL_ARB_conservative_depth")
|
||||
|
@ -146,6 +147,11 @@ public final class PipelineCompiler {
|
|||
comp.define("_FLW_USE_DISCARD");
|
||||
}
|
||||
})
|
||||
.onCompile((key, comp) -> {
|
||||
if (key.oit()) {
|
||||
comp.define("_FLW_OIT");
|
||||
}
|
||||
})
|
||||
.withResource(API_IMPL_FRAG)
|
||||
.withResource(key -> key.materialShaders()
|
||||
.fragmentSource())
|
||||
|
@ -217,6 +223,7 @@ 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,
|
||||
boolean oit) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -13,6 +13,7 @@ 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;
|
||||
|
@ -36,6 +37,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> transparentDraws = new ArrayList<>();
|
||||
|
||||
private final IndirectPrograms programs;
|
||||
private final GlProgram cullProgram;
|
||||
|
@ -130,6 +132,7 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
|
||||
private void sortDraws() {
|
||||
multiDraws.clear();
|
||||
transparentDraws.clear();
|
||||
// sort by visual type, then material
|
||||
indirectDraws.sort(DRAW_COMPARATOR);
|
||||
|
||||
|
@ -138,7 +141,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.TRANSLUCENT ? transparentDraws : multiDraws;
|
||||
dst.add(new MultiDraw(draw1.material(), draw1.isEmbedded(), start, i + 1));
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
|
@ -171,7 +176,7 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
needsDrawSort = true;
|
||||
}
|
||||
|
||||
public void submit() {
|
||||
public void submitSolid() {
|
||||
if (nothingToDo()) {
|
||||
return;
|
||||
}
|
||||
|
@ -183,7 +188,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, false);
|
||||
if (drawProgram != lastProgram) {
|
||||
lastProgram = drawProgram;
|
||||
|
||||
|
@ -197,8 +202,34 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
}
|
||||
}
|
||||
|
||||
public void submitTransparent() {
|
||||
if (nothingToDo()) {
|
||||
return;
|
||||
}
|
||||
|
||||
buffers.bindForDraw();
|
||||
|
||||
drawBarrier();
|
||||
|
||||
GlProgram lastProgram = null;
|
||||
|
||||
for (var multiDraw : transparentDraws) {
|
||||
var drawProgram = programs.getIndirectProgram(instanceType, multiDraw.embedded ? ContextShader.EMBEDDED : ContextShader.DEFAULT, multiDraw.material, true);
|
||||
if (drawProgram != lastProgram) {
|
||||
lastProgram = drawProgram;
|
||||
|
||||
// Don't need to do this unless the program changes.
|
||||
drawProgram.bind();
|
||||
}
|
||||
|
||||
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, false);
|
||||
|
||||
program.bind();
|
||||
|
||||
|
|
|
@ -48,6 +48,8 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
|||
|
||||
private final DepthPyramid depthPyramid;
|
||||
|
||||
private final WboitFrameBuffer wboitFrameBuffer;
|
||||
|
||||
public IndirectDrawManager(IndirectPrograms programs) {
|
||||
this.programs = programs;
|
||||
programs.acquire();
|
||||
|
@ -62,6 +64,8 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
|||
matrixBuffer = new MatrixBuffer();
|
||||
|
||||
depthPyramid = new DepthPyramid(programs);
|
||||
|
||||
wboitFrameBuffer = new WboitFrameBuffer(programs);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -138,9 +142,17 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
|||
Uniforms.bindAll();
|
||||
|
||||
for (var group : cullingGroups.values()) {
|
||||
group.submit();
|
||||
group.submitSolid();
|
||||
}
|
||||
|
||||
wboitFrameBuffer.setup();
|
||||
|
||||
for (var group : cullingGroups.values()) {
|
||||
group.submitTransparent();
|
||||
}
|
||||
|
||||
wboitFrameBuffer.composite();
|
||||
|
||||
MaterialRenderState.reset();
|
||||
TextureBinder.resetLightAndOverlay();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
package dev.engine_room.flywheel.backend.engine.indirect;
|
||||
|
||||
import org.lwjgl.opengl.ARBDrawBuffersBlend;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL46;
|
||||
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
import dev.engine_room.flywheel.backend.compile.IndirectPrograms;
|
||||
import dev.engine_room.flywheel.backend.gl.GlTextureUnit;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
public class WboitFrameBuffer {
|
||||
|
||||
public final int fbo;
|
||||
private final IndirectPrograms programs;
|
||||
private final int vao;
|
||||
|
||||
public int accum;
|
||||
public int reveal;
|
||||
|
||||
private int lastWidth = -1;
|
||||
private int lastHeight = -1;
|
||||
|
||||
public WboitFrameBuffer(IndirectPrograms programs) {
|
||||
this.programs = programs;
|
||||
fbo = GL46.glCreateFramebuffers();
|
||||
vao = GL46.glCreateVertexArrays();
|
||||
}
|
||||
|
||||
public void setup() {
|
||||
var mainRenderTarget = Minecraft.getInstance()
|
||||
.getMainRenderTarget();
|
||||
|
||||
createTextures(mainRenderTarget.width, mainRenderTarget.height);
|
||||
|
||||
// No depth writes, but we'll still use the depth test
|
||||
GlStateManager._depthMask(false);
|
||||
GlStateManager._enableBlend();
|
||||
ARBDrawBuffersBlend.glBlendFunciARB(0, GL46.GL_ONE, GL46.GL_ONE); // accumulation blend target
|
||||
ARBDrawBuffersBlend.glBlendFunciARB(1, GL46.GL_ZERO, GL46.GL_ONE_MINUS_SRC_COLOR); // revealage blend target
|
||||
GlStateManager._blendEquation(GL46.GL_FUNC_ADD);
|
||||
|
||||
GL46.glNamedFramebufferTexture(fbo, GL46.GL_DEPTH_ATTACHMENT, mainRenderTarget.getDepthTextureId(), 0);
|
||||
|
||||
GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo);
|
||||
|
||||
GL46.glClearBufferfv(GL46.GL_COLOR, 0, new float[]{0, 0, 0, 0});
|
||||
GL46.glClearBufferfv(GL46.GL_COLOR, 1, new float[]{1, 1, 1, 1});
|
||||
}
|
||||
|
||||
public void composite() {
|
||||
var mainRenderTarget = Minecraft.getInstance()
|
||||
.getMainRenderTarget();
|
||||
|
||||
mainRenderTarget.bindWrite(false);
|
||||
|
||||
var oitCompositeProgram = programs.getOitCompositeProgram();
|
||||
|
||||
GlStateManager._depthMask(false);
|
||||
GlStateManager._depthFunc(GL46.GL_ALWAYS);
|
||||
GlStateManager._enableBlend();
|
||||
RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
oitCompositeProgram.bind();
|
||||
|
||||
GlTextureUnit.T0.makeActive();
|
||||
GlStateManager._bindTexture(accum);
|
||||
|
||||
GlTextureUnit.T1.makeActive();
|
||||
GlStateManager._bindTexture(reveal);
|
||||
|
||||
// Empty VAO, the actual full screen triangle is generated in the vertex shader
|
||||
GlStateManager._glBindVertexArray(vao);
|
||||
|
||||
GL46.glDrawArrays(GL46.GL_TRIANGLES, 0, 3);
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
GL46.glDeleteTextures(accum);
|
||||
GL46.glDeleteTextures(reveal);
|
||||
GL46.glDeleteFramebuffers(fbo);
|
||||
GL46.glDeleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
private void createTextures(int width, int height) {
|
||||
if (lastWidth == width && lastHeight == height) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastWidth = width;
|
||||
lastHeight = height;
|
||||
|
||||
GL46.glDeleteTextures(accum);
|
||||
GL46.glDeleteTextures(reveal);
|
||||
|
||||
accum = GL46.glCreateTextures(GL46.GL_TEXTURE_2D);
|
||||
reveal = GL46.glCreateTextures(GL46.GL_TEXTURE_2D);
|
||||
|
||||
GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT0, GL46.GL_COLOR_ATTACHMENT1});
|
||||
|
||||
GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT0, accum, 0);
|
||||
GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT1, reveal, 0);
|
||||
|
||||
GL46.glTextureStorage2D(accum, 1, GL32.GL_RGBA32F, width, height);
|
||||
GL46.glTextureStorage2D(reveal, 1, GL32.GL_R8, width, height);
|
||||
|
||||
for (int tex : new int[]{accum, reveal}) {
|
||||
GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_NEAREST);
|
||||
GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_NEAREST);
|
||||
GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_COMPARE_MODE, GL32.GL_NONE);
|
||||
GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_WRAP_S, GL32.GL_CLAMP_TO_EDGE);
|
||||
GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_WRAP_T, GL32.GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,8 +17,20 @@ in vec2 _flw_crumblingTexCoord;
|
|||
flat in uvec2 _flw_ids;
|
||||
#endif
|
||||
|
||||
#ifdef _FLW_OIT
|
||||
|
||||
// your first render target which is used to accumulate pre-multiplied color values
|
||||
layout (location = 0) out vec4 accum;
|
||||
|
||||
// your second render target which is used to store pixel revealage
|
||||
layout (location = 1) out float reveal;
|
||||
|
||||
#else
|
||||
|
||||
out vec4 _flw_outputColor;
|
||||
|
||||
#endif
|
||||
|
||||
float _flw_diffuseFactor() {
|
||||
if (flw_material.cardinalLightingMode == 2u) {
|
||||
return diffuseFromLightDirections(flw_vertexNormal);
|
||||
|
@ -33,6 +45,11 @@ float _flw_diffuseFactor() {
|
|||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
void _flw_main() {
|
||||
flw_sampleColor = texture(flw_diffuseTex, flw_vertexTexCoord);
|
||||
flw_fragColor = flw_vertexColor * flw_sampleColor;
|
||||
|
@ -99,5 +116,31 @@ void _flw_main() {
|
|||
}
|
||||
#endif
|
||||
|
||||
_flw_outputColor = flw_fogFilter(color);
|
||||
color = flw_fogFilter(color);
|
||||
|
||||
color.a = 0.9;
|
||||
|
||||
#ifdef _FLW_OIT
|
||||
|
||||
float depth = linearize_depth(gl_FragCoord.z, _flw_cullData.znear, _flw_cullData.zfar);
|
||||
|
||||
// insert your favorite weighting function here. the color-based factor
|
||||
// avoids color pollution from the edges of wispy clouds. the z-based
|
||||
// factor gives precedence to nearer surfaces
|
||||
//float weight = clamp(pow(min(1.0, color.a * 10.0) + 0.01, 3.0) * 1e8 * pow(1.0 - gl_FragCoord.z * 0.9, 3.0), 1e-2, 3e3);
|
||||
float weight = max(min(1.0, max(max(color.r, color.g), color.b) * color.a), color.a) *
|
||||
clamp(0.03 / (1e-5 + pow(depth / 200, 4.0)), 1e-2, 3e3);
|
||||
|
||||
// blend func: GL_ONE, GL_ONE
|
||||
// switch to pre-multiplied alpha and weight
|
||||
accum = vec4(color.rgb * color.a, color.a) * weight;
|
||||
|
||||
// blend func: GL_ZERO, GL_ONE_MINUS_SRC_ALPHA
|
||||
reveal = color.a;
|
||||
|
||||
#else
|
||||
|
||||
_flw_outputColor = color;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
// shader outputs
|
||||
layout (location = 0) out vec4 frag;
|
||||
|
||||
// color accumulation buffer
|
||||
layout (binding = 0) uniform sampler2D accum;
|
||||
|
||||
// revealage threshold buffer
|
||||
layout (binding = 1) uniform sampler2D reveal;
|
||||
|
||||
// epsilon number
|
||||
const float EPSILON = 0.00001f;
|
||||
|
||||
// calculate floating point numbers equality accurately
|
||||
bool isApproximatelyEqual(float a, float b) {
|
||||
return abs(a - b) <= (abs(a) < abs(b) ? abs(b) : abs(a)) * EPSILON;
|
||||
}
|
||||
|
||||
// get the max value between three values
|
||||
float max3(vec3 v) {
|
||||
return max(max(v.x, v.y), v.z);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// fragment coordination
|
||||
ivec2 coords = ivec2(gl_FragCoord.xy);
|
||||
|
||||
// fragment revealage
|
||||
float revealage = texelFetch(reveal, coords, 0).r;
|
||||
|
||||
// save the blending and color texture fetch cost if there is not a transparent fragment
|
||||
if (isApproximatelyEqual(revealage, 1.0f)) {
|
||||
discard;
|
||||
}
|
||||
|
||||
// fragment color
|
||||
vec4 accumulation = texelFetch(accum, coords, 0);
|
||||
|
||||
// suppress overflow
|
||||
if (isinf(max3(abs(accumulation.rgb)))) {
|
||||
accumulation.rgb = vec3(accumulation.a);
|
||||
}
|
||||
|
||||
// prevent floating point precision bug
|
||||
vec3 average_color = accumulation.rgb / max(accumulation.a, EPSILON);
|
||||
|
||||
// blend pixels
|
||||
frag = vec4(average_color, 1.0f - revealage);
|
||||
}
|
Loading…
Add table
Reference in a new issue