diff --git a/assets/cubyz/shaders/bloom/color_extractor_downsample.fs b/assets/cubyz/shaders/bloom/color_extractor_downsample.fs index aad00e0c..5bbeaedc 100644 --- a/assets/cubyz/shaders/bloom/color_extractor_downsample.fs +++ b/assets/cubyz/shaders/bloom/color_extractor_downsample.fs @@ -6,8 +6,68 @@ in vec2 texCoords; layout(binding = 3) uniform sampler2D color; +uniform sampler2D depthTexture; +uniform int blockType; +uniform float nearPlane; + +struct TextureData { + int textureIndices[6]; + uint absorption; + float reflectivity; + float fogStrength; + uint fogColor; +}; + +layout(std430, binding = 1) buffer _textureData +{ + TextureData textureData[]; +}; + +vec3 unpackColor(uint color) { + return vec3( + color>>16 & 255u, + color>>8 & 255u, + color & 255u + )/255.0; +} + +float calculateFogDistance(float depthBufferValue) { + float fogStrength = textureData[blockType].fogStrength; + float distCameraTerrain = nearPlane*fogStrength/depthBufferValue; + float distFromCamera = 0; + float distFromTerrain = distFromCamera - distCameraTerrain; + if(distCameraTerrain < 10) { // Resolution range is sufficient. + return distFromTerrain; + } else { + // Here we have a few options to deal with this. We could for example weaken the fog effect to fit the entire range. + // I decided to keep the fog strength close to the camera and far away, with a fog-free region in between. + // I decided to this because I want far away fog to work (e.g. a distant ocean) as well as close fog(e.g. the top surface of the water when the player is under it) + if(distFromTerrain > -5 && depthBufferValue != 0) { + return distFromTerrain; + } else if(distFromCamera < 5) { + return distFromCamera - 10; + } else { + return -5; + } + } +} + vec3 fetch(ivec2 pos) { vec4 rgba = texelFetch(color, pos, 0); + if(blockType != 0) { // TODO: Handle air fog as well. + float fogDistance = calculateFogDistance(texelFetch(depthTexture, pos, 0).r); + vec3 fogColor = unpackColor(textureData[blockType].fogColor); + float fogFactor = exp(fogDistance); + vec4 sourceColor = vec4(fogColor, 1); + sourceColor.a = 1.0/fogFactor; + sourceColor.rgb *= sourceColor.a; + sourceColor.rgb -= fogColor; + vec3 source2Color = vec3(1); + rgba = vec4( + source2Color*rgba.rgb + rgba.a*sourceColor.rgb, + rgba.a*sourceColor.a + ); + } if(rgba.a < 1) return vec3(0); // Prevent t-junctions from transparency from making a huge mess. return rgba.rgb/rgba.a; } diff --git a/assets/cubyz/shaders/deferred_render_pass.fs b/assets/cubyz/shaders/deferred_render_pass.fs index 6ed7b9ce..84d6306c 100644 --- a/assets/cubyz/shaders/deferred_render_pass.fs +++ b/assets/cubyz/shaders/deferred_render_pass.fs @@ -5,8 +5,68 @@ in vec2 texCoords; uniform sampler2D color; +uniform sampler2D depthTexture; +uniform int blockType; +uniform float nearPlane; + +struct TextureData { + int textureIndices[6]; + uint absorption; + float reflectivity; + float fogStrength; + uint fogColor; +}; + +layout(std430, binding = 1) buffer _textureData +{ + TextureData textureData[]; +}; + +vec3 unpackColor(uint color) { + return vec3( + color>>16 & 255u, + color>>8 & 255u, + color & 255u + )/255.0; +} + +float calculateFogDistance(float depthBufferValue) { + float fogStrength = textureData[blockType].fogStrength; + float distCameraTerrain = nearPlane*fogStrength/depthBufferValue; + float distFromCamera = 0; + float distFromTerrain = distFromCamera - distCameraTerrain; + if(distCameraTerrain < 10) { // Resolution range is sufficient. + return distFromTerrain; + } else { + // Here we have a few options to deal with this. We could for example weaken the fog effect to fit the entire range. + // I decided to keep the fog strength close to the camera and far away, with a fog-free region in between. + // I decided to this because I want far away fog to work (e.g. a distant ocean) as well as close fog(e.g. the top surface of the water when the player is under it) + if(distFromTerrain > -5 && depthBufferValue != 0) { + return distFromTerrain; + } else if(distFromCamera < 5) { + return distFromCamera - 10; + } else { + return -5; + } + } +} + void main() { fragColor = texture(color, texCoords); + if(blockType != 0) { // TODO: Handle air fog as well. + float fogDistance = calculateFogDistance(texelFetch(depthTexture, ivec2(gl_FragCoord.xy), 0).r); + vec3 fogColor = unpackColor(textureData[blockType].fogColor); + float fogFactor = exp(fogDistance); + vec4 sourceColor = vec4(fogColor, 1); + sourceColor.a = 1.0/fogFactor; + sourceColor.rgb *= sourceColor.a; + sourceColor.rgb -= fogColor; + vec3 source2Color = vec3(1); + fragColor = vec4( + source2Color*fragColor.rgb + fragColor.a*sourceColor.rgb, + fragColor.a*sourceColor.a + ); + } fragColor.rgb /= fragColor.a; float maxColor = max(1.0, max(fragColor.r, max(fragColor.g, fragColor.b))); fragColor.rgb = fragColor.rgb/maxColor; diff --git a/src/gui/windows/gpu_performance_measuring.zig b/src/gui/windows/gpu_performance_measuring.zig index a7104169..abf6d1cf 100644 --- a/src/gui/windows/gpu_performance_measuring.zig +++ b/src/gui/windows/gpu_performance_measuring.zig @@ -18,7 +18,6 @@ pub const Samples = enum(u8) { chunk_rendering, entity_rendering, transparent_rendering, - normalization, bloom_extract_downsample, bloom_first_pass, bloom_second_pass, @@ -33,7 +32,6 @@ const names = [_][]const u8 { "Chunk Rendering", "Entity Rendering", "Transparent Rendering", - "Normalization", "Bloom - Extract color and downsample", "Bloom - First Pass", "Bloom - Second Pass", diff --git a/src/renderer.zig b/src/renderer.zig index 1c8aa7fc..f4c55b54 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -40,6 +40,9 @@ var fogUniforms: struct { var deferredRenderPassShader: graphics.Shader = undefined; var deferredUniforms: struct { color: c_int, + depthTexture: c_int, + blockType: c_int, + nearPlane: c_int, } = undefined; pub var activeFrameBuffer: c_uint = 0; @@ -193,6 +196,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo worldFrameBuffer.bindDepthTexture(c.GL_TEXTURE3); gpu_performance_measuring.startQuery(.transparent_rendering); + c.glTextureBarrier(); chunk.meshing.bindTransparentShaderAndUniforms(game.projectionMatrix, ambientLight, time); c.glBlendEquationSeparate(c.GL_FUNC_ADD, c.GL_FUNC_ADD); @@ -226,14 +230,20 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo fogShader.bind(); // TODO: Draw the water fog if the player is underwater. + const playerBlock = RenderStructure.getBlock(@intFromFloat(@floor(playerPos[0])), @intFromFloat(@floor(playerPos[1])), @intFromFloat(@floor(playerPos[2]))) orelse blocks.Block{.typ = 0, .data = 0}; + if(settings.bloom) { - Bloom.render(lastWidth, lastHeight); + Bloom.render(lastWidth, lastHeight, playerBlock); } gpu_performance_measuring.startQuery(.final_copy); - worldFrameBuffer.unbind(); worldFrameBuffer.bindTexture(c.GL_TEXTURE3); + worldFrameBuffer.bindDepthTexture(c.GL_TEXTURE4); + worldFrameBuffer.unbind(); deferredRenderPassShader.bind(); c.glUniform1i(deferredUniforms.color, 3); + c.glUniform1i(deferredUniforms.depthTexture, 4); + c.glUniform1i(deferredUniforms.blockType, playerBlock.typ); + c.glUniform1f(deferredUniforms.nearPlane, zNear); c.glBindFramebuffer(c.GL_FRAMEBUFFER, activeFrameBuffer); @@ -264,13 +274,18 @@ const Bloom = struct { var secondPassShader: graphics.Shader = undefined; var colorExtractAndDownsampleShader: graphics.Shader = undefined; var upscaleShader: graphics.Shader = undefined; + var colorExtractUniforms: struct { + depthTexture: c_int, + blockType: c_int, + nearPlane: c_int, + } = undefined; pub fn init() !void { buffer1.init(false, c.GL_NEAREST, c.GL_CLAMP_TO_EDGE); buffer2.init(false, c.GL_NEAREST, c.GL_CLAMP_TO_EDGE); firstPassShader = try graphics.Shader.init("assets/cubyz/shaders/bloom/first_pass.vs", "assets/cubyz/shaders/bloom/first_pass.fs"); secondPassShader = try graphics.Shader.init("assets/cubyz/shaders/bloom/second_pass.vs", "assets/cubyz/shaders/bloom/second_pass.fs"); - colorExtractAndDownsampleShader = try graphics.Shader.init("assets/cubyz/shaders/bloom/color_extractor_downsample.vs", "assets/cubyz/shaders/bloom/color_extractor_downsample.fs"); + colorExtractAndDownsampleShader = try graphics.Shader.initAndGetUniforms("assets/cubyz/shaders/bloom/color_extractor_downsample.vs", "assets/cubyz/shaders/bloom/color_extractor_downsample.fs", &colorExtractUniforms); upscaleShader = try graphics.Shader.init("assets/cubyz/shaders/bloom/upscale.vs", "assets/cubyz/shaders/bloom/upscale.fs"); } @@ -282,10 +297,14 @@ const Bloom = struct { upscaleShader.deinit(); } - fn extractImageDataAndDownsample() void { + fn extractImageDataAndDownsample(playerBlock: blocks.Block) void { colorExtractAndDownsampleShader.bind(); worldFrameBuffer.bindTexture(c.GL_TEXTURE3); + worldFrameBuffer.bindDepthTexture(c.GL_TEXTURE4); buffer1.bind(); + c.glUniform1i(colorExtractUniforms.depthTexture, 4); + c.glUniform1i(colorExtractUniforms.blockType, playerBlock.typ); + c.glUniform1f(colorExtractUniforms.nearPlane, zNear); c.glBindVertexArray(graphics.draw.rectVAO); c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4); } @@ -314,7 +333,7 @@ const Bloom = struct { c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4); } - pub fn render(currentWidth: u31, currentHeight: u31) void { + pub fn render(currentWidth: u31, currentHeight: u31, playerBlock: blocks.Block) void { if(width != currentWidth or height != currentHeight) { width = currentWidth; height = currentHeight; @@ -326,9 +345,10 @@ const Bloom = struct { gpu_performance_measuring.startQuery(.bloom_extract_downsample); c.glDisable(c.GL_DEPTH_TEST); c.glDisable(c.GL_CULL_FACE); + c.glDepthMask(c.GL_FALSE); c.glViewport(0, 0, width/2, height/2); - extractImageDataAndDownsample(); + extractImageDataAndDownsample(playerBlock); gpu_performance_measuring.stopQuery(); gpu_performance_measuring.startQuery(.bloom_first_pass); firstPass(); @@ -341,6 +361,7 @@ const Bloom = struct { c.glBlendFunc(c.GL_ONE, c.GL_ONE); upscale(); + c.glDepthMask(c.GL_TRUE); c.glEnable(c.GL_DEPTH_TEST); c.glEnable(c.GL_CULL_FACE); c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA);