diff --git a/assets/cubyz/shaders/bloom/color_extractor.fs b/assets/cubyz/shaders/bloom/color_extractor.fs new file mode 100644 index 00000000..bbf8deac --- /dev/null +++ b/assets/cubyz/shaders/bloom/color_extractor.fs @@ -0,0 +1,13 @@ +#version 430 + +layout(location=0) out vec4 fragColor; + +in vec2 texCoords; + +layout(binding = 3) uniform sampler2D color; + +void main() { + vec3 bufferData = texture(color, texCoords).rgb; + float bloomFactor = max(max(bufferData.x, max(bufferData.y, bufferData.z))*4 - 1.0, 0); + fragColor = vec4(bufferData*bloomFactor, 1); +} \ No newline at end of file diff --git a/assets/cubyz/shaders/bloom/color_extractor.vs b/assets/cubyz/shaders/bloom/color_extractor.vs new file mode 100644 index 00000000..ef5ed408 --- /dev/null +++ b/assets/cubyz/shaders/bloom/color_extractor.vs @@ -0,0 +1,10 @@ +#version 430 + +layout (location=0) in vec2 inTexCoords; + +out vec2 texCoords; + +void main() { + texCoords = inTexCoords; + gl_Position = vec4(inTexCoords*2 - vec2(1, 1), 0, 1); +} \ No newline at end of file diff --git a/assets/cubyz/shaders/bloom/first_pass.fs b/assets/cubyz/shaders/bloom/first_pass.fs new file mode 100644 index 00000000..a5b5598c --- /dev/null +++ b/assets/cubyz/shaders/bloom/first_pass.fs @@ -0,0 +1,19 @@ +#version 430 + +layout(location=0) out vec4 fragColor; + +in vec2 texCoords; + +layout(binding = 3) uniform sampler2D color; + +float weights[16] = float[] (0.14804608426116522, 0.14511457538105424, 0.13666376040001485, 0.12365848409943446, 0.10750352152877563, 0.08979448915479665, 0.07206176550016223, 0.055563338564704794, 0.041162333610640485, 0.029298127479707798, 0.02003585874555622, 0.01316449727103141, 0.008310531828522687, 0.005040592352516703, 0.002937396384358805, 0.001644643437557613); + +void main() { + vec2 tex_offset = 1.0/textureSize(color, 0); + vec3 result = texture(color, texCoords).rgb * weights[0]; + for(int i = 1; i < 16; i++) { + result += texture(color, texCoords + vec2(tex_offset.x * i, 0.0)).rgb * weights[i]; + result += texture(color, texCoords - vec2(tex_offset.x * i, 0.0)).rgb * weights[i]; + } + fragColor = vec4(result, 1); +} \ No newline at end of file diff --git a/assets/cubyz/shaders/bloom/first_pass.vs b/assets/cubyz/shaders/bloom/first_pass.vs new file mode 100644 index 00000000..ef5ed408 --- /dev/null +++ b/assets/cubyz/shaders/bloom/first_pass.vs @@ -0,0 +1,10 @@ +#version 430 + +layout (location=0) in vec2 inTexCoords; + +out vec2 texCoords; + +void main() { + texCoords = inTexCoords; + gl_Position = vec4(inTexCoords*2 - vec2(1, 1), 0, 1); +} \ No newline at end of file diff --git a/assets/cubyz/shaders/bloom/scale.fs b/assets/cubyz/shaders/bloom/scale.fs new file mode 100644 index 00000000..f0c838e3 --- /dev/null +++ b/assets/cubyz/shaders/bloom/scale.fs @@ -0,0 +1,11 @@ +#version 430 + +layout(location=0) out vec4 fragColor; + +in vec2 texCoords; + +layout(binding = 3) uniform sampler2D color; + +void main() { + fragColor = vec4(texture(color, texCoords).rgb, 1); +} \ No newline at end of file diff --git a/assets/cubyz/shaders/bloom/scale.vs b/assets/cubyz/shaders/bloom/scale.vs new file mode 100644 index 00000000..ef5ed408 --- /dev/null +++ b/assets/cubyz/shaders/bloom/scale.vs @@ -0,0 +1,10 @@ +#version 430 + +layout (location=0) in vec2 inTexCoords; + +out vec2 texCoords; + +void main() { + texCoords = inTexCoords; + gl_Position = vec4(inTexCoords*2 - vec2(1, 1), 0, 1); +} \ No newline at end of file diff --git a/assets/cubyz/shaders/bloom/second_pass.fs b/assets/cubyz/shaders/bloom/second_pass.fs new file mode 100644 index 00000000..9d9dc7ad --- /dev/null +++ b/assets/cubyz/shaders/bloom/second_pass.fs @@ -0,0 +1,19 @@ +#version 430 + +layout(location=0) out vec4 fragColor; + +in vec2 texCoords; + +layout(binding = 3) uniform sampler2D color; + +float weights[16] = float[] (0.14804608426116522, 0.14511457538105424, 0.13666376040001485, 0.12365848409943446, 0.10750352152877563, 0.08979448915479665, 0.07206176550016223, 0.055563338564704794, 0.041162333610640485, 0.029298127479707798, 0.02003585874555622, 0.01316449727103141, 0.008310531828522687, 0.005040592352516703, 0.002937396384358805, 0.001644643437557613); + +void main() { + vec2 tex_offset = 1.0/textureSize(color, 0); + vec3 result = texture(color, texCoords).rgb * weights[0]; + for(int i = 1; i < 16; i++) { + result += texture(color, texCoords + vec2(0, tex_offset.y * i)).rgb * weights[i]; + result += texture(color, texCoords - vec2(0, tex_offset.y * i)).rgb * weights[i]; + } + fragColor = vec4(result, 1); +} \ No newline at end of file diff --git a/assets/cubyz/shaders/bloom/second_pass.vs b/assets/cubyz/shaders/bloom/second_pass.vs new file mode 100644 index 00000000..80e8c588 --- /dev/null +++ b/assets/cubyz/shaders/bloom/second_pass.vs @@ -0,0 +1,10 @@ +#version 430 + +layout(location=0) in vec2 inTexCoords; + +out vec2 texCoords; + +void main() { + texCoords = inTexCoords; + gl_Position = vec4(inTexCoords*2 - vec2(1, 1), 0, 1); +} \ No newline at end of file diff --git a/assets/cubyz/shaders/chunks/chunk_fragment.fs b/assets/cubyz/shaders/chunks/chunk_fragment.fs index 43065120..34e88b69 100644 --- a/assets/cubyz/shaders/chunks/chunk_fragment.fs +++ b/assets/cubyz/shaders/chunks/chunk_fragment.fs @@ -25,8 +25,8 @@ vec4 calcFog(vec3 pos, vec4 color, Fog fog) { float distance = length(pos); float fogFactor = 1.0/exp((distance*fog.density)*(distance*fog.density)); fogFactor = clamp(fogFactor, 0.0, 1.0); - vec3 resultColor = mix(fog.color, color.xyz, fogFactor); - return vec4(resultColor.xyz, color.w + 1 - fogFactor); + vec4 resultColor = mix(vec4(fog.color, 1), color, fogFactor); + return resultColor; } void main() { diff --git a/assets/cubyz/shaders/chunks/chunk_vertex.vs b/assets/cubyz/shaders/chunks/chunk_vertex.vs index f864ac1f..797dbe3f 100644 --- a/assets/cubyz/shaders/chunks/chunk_vertex.vs +++ b/assets/cubyz/shaders/chunks/chunk_vertex.vs @@ -32,11 +32,11 @@ uniform int time; const float[6] outNormalVariations = float[6]( 1.0, //vec3(0, 1, 0), - 0.8, //vec3(0, -1, 0), - 0.9, //vec3(1, 0, 0), - 0.9, //vec3(-1, 0, 0), - 0.95, //vec3(0, 0, 1), - 0.8 //vec3(0, 0, -1) + 0.88, //vec3(0, -1, 0), + 0.92, //vec3(1, 0, 0), + 0.92, //vec3(-1, 0, 0), + 0.96, //vec3(0, 0, 1), + 0.88 //vec3(0, 0, -1) ); const vec3[6] normals = vec3[6]( vec3(0, 1, 0), diff --git a/src/graphics.zig b/src/graphics.zig index 1eef68b9..927c3b1b 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -425,6 +425,70 @@ pub const SSBO = struct { } }; +pub const FrameBuffer = struct { + frameBuffer: c_uint, + texture: c_uint, + hasDepthBuffer: bool, + renderBuffer: c_uint, + + pub fn init(self: *FrameBuffer, hasDepthBuffer: bool) void { + self.* = FrameBuffer{ + .frameBuffer = undefined, + .texture = undefined, + .renderBuffer = undefined, + .hasDepthBuffer = hasDepthBuffer, + }; + c.glGenFramebuffers(1, &self.frameBuffer); + if(hasDepthBuffer) { + c.glGenRenderbuffers(1, &self.renderBuffer); + } + c.glGenTextures(1, &self.texture); + } + + pub fn deinit(self: *FrameBuffer) void { + c.glDeleteFramebuffers(1, &self.frameBuffer); + if(self.hasDepthBuffer) { + c.glDeleteRenderbuffers(1, &self.renderBuffer); + } + c.glDeleteTextures(1, &self.texture); + } + + pub fn updateSize(self: *FrameBuffer, width: u31, height: u31, textureFilter: c_int, textureWrap: c_int) void { + c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.frameBuffer); + if(self.hasDepthBuffer) { + c.glBindRenderbuffer(c.GL_RENDERBUFFER, self.renderBuffer); + c.glRenderbufferStorage(c.GL_RENDERBUFFER, c.GL_DEPTH24_STENCIL8, width, height); + c.glFramebufferRenderbuffer(c.GL_FRAMEBUFFER, c.GL_DEPTH_STENCIL_ATTACHMENT, c.GL_RENDERBUFFER, self.renderBuffer); + } + + c.glBindTexture(c.GL_TEXTURE_2D, self.texture); + c.glTexImage2D(c.GL_TEXTURE_2D, 0, c.GL_RGBA8, width, height, 0, c.GL_RGBA, c.GL_UNSIGNED_BYTE, null); + c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MIN_FILTER, textureFilter); + c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MAG_FILTER, textureFilter); + c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_S, textureWrap); + c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_T, textureWrap); + c.glFramebufferTexture2D(c.GL_FRAMEBUFFER, c.GL_COLOR_ATTACHMENT0, c.GL_TEXTURE_2D, self.texture, 0); + } + + pub fn validate(self: *FrameBuffer) bool { + c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.frameBuffer); + defer c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0); + if(c.glCheckFramebufferStatus(c.GL_FRAMEBUFFER) != c.GL_FRAMEBUFFER_COMPLETE) { + std.log.err("Frame Buffer Object error: {}", .{c.glCheckFramebufferStatus(c.GL_FRAMEBUFFER)}); + return false; + } + return true; + } + + pub fn bind(self: *FrameBuffer) void { + c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.frameBuffer); + } + + pub fn unbind() void { + c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0); + } +}; + pub const TextureArray = struct { textureID: c_uint, diff --git a/src/renderer.zig b/src/renderer.zig index 1535563b..cf8677ee 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -47,12 +47,14 @@ pub fn init() !void { deferredRenderPassShader = try Shader.create("assets/cubyz/shaders/deferred_render_pass.vs", "assets/cubyz/shaders/deferred_render_pass.fs"); deferredUniforms = deferredRenderPassShader.bulkGetUniformLocation(@TypeOf(deferredUniforms)); buffers.init(); + try Bloom.init(); } pub fn deinit() void { fogShader.delete(); deferredRenderPassShader.delete(); buffers.deinit(); + Bloom.deinit(); } const buffers = struct { @@ -321,9 +323,9 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo // glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // } // } -// if(ClientSettings.BLOOM) { -// BloomRenderer.render(buffers, Window.getWidth(), Window.getHeight()); // TODO: Use true width/height -// } + if(settings.bloom) { + Bloom.render(main.Window.width, main.Window.height); + } buffers.unbind(); buffers.bindTextures(); deferredRenderPassShader.bind(); @@ -385,6 +387,110 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo // } //} +const Bloom = struct { + var buffer1: graphics.FrameBuffer = undefined; + var buffer2: graphics.FrameBuffer = undefined; + var extractedBuffer: graphics.FrameBuffer = undefined; + var width: u31 = std.math.maxInt(u31); + var height: u31 = std.math.maxInt(u31); + var firstPassShader: graphics.Shader = undefined; + var secondPassShader: graphics.Shader = undefined; + var colorExtractShader: graphics.Shader = undefined; + var scaleShader: graphics.Shader = undefined; + + pub fn init() !void { + buffer1.init(false); + buffer2.init(false); + extractedBuffer.init(false); + firstPassShader = try graphics.Shader.create("assets/cubyz/shaders/bloom/first_pass.vs", "assets/cubyz/shaders/bloom/first_pass.fs"); + secondPassShader = try graphics.Shader.create("assets/cubyz/shaders/bloom/second_pass.vs", "assets/cubyz/shaders/bloom/second_pass.fs"); + colorExtractShader = try graphics.Shader.create("assets/cubyz/shaders/bloom/color_extractor.vs", "assets/cubyz/shaders/bloom/color_extractor.fs"); + scaleShader = try graphics.Shader.create("assets/cubyz/shaders/bloom/scale.vs", "assets/cubyz/shaders/bloom/scale.fs"); + } + + pub fn deinit() void { + buffer1.deinit(); + buffer2.deinit(); + extractedBuffer.deinit(); + firstPassShader.delete(); + secondPassShader.delete(); + colorExtractShader.delete(); + scaleShader.delete(); + } + + fn extractImageData() void { + colorExtractShader.bind(); + buffers.bindTextures(); + extractedBuffer.bind(); + c.glBindVertexArray(graphics.Draw.rectVAO); + c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4); + } + + fn downscale() void { + scaleShader.bind(); + c.glActiveTexture(c.GL_TEXTURE3); + c.glBindTexture(c.GL_TEXTURE_2D, extractedBuffer.texture); + buffer1.bind(); + c.glBindVertexArray(graphics.Draw.rectVAO); + c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4); + } + + fn firstPass() void { + firstPassShader.bind(); + c.glActiveTexture(c.GL_TEXTURE3); + c.glBindTexture(c.GL_TEXTURE_2D, buffer1.texture); + buffer2.bind(); + c.glBindVertexArray(graphics.Draw.rectVAO); + c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4); + } + + fn secondPass() void { + secondPassShader.bind(); + c.glActiveTexture(c.GL_TEXTURE3); + c.glBindTexture(c.GL_TEXTURE_2D, buffer2.texture); + buffer1.bind(); + c.glBindVertexArray(graphics.Draw.rectVAO); + c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4); + } + + fn upscale() void { + scaleShader.bind(); + c.glActiveTexture(c.GL_TEXTURE3); + c.glBindTexture(c.GL_TEXTURE_2D, buffer1.texture); + buffers.bind(); + c.glBindVertexArray(graphics.Draw.rectVAO); + c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4); + } + + pub fn render(currentWidth: u31, currentHeight: u31) void { + if(width != currentWidth or height != currentHeight) { + width = currentWidth; + height = currentHeight; + buffer1.updateSize(width/2, height/2, c.GL_LINEAR, c.GL_CLAMP_TO_EDGE); + std.debug.assert(buffer1.validate()); + buffer2.updateSize(width/2, height/2, c.GL_LINEAR, c.GL_CLAMP_TO_EDGE); + std.debug.assert(buffer2.validate()); + extractedBuffer.updateSize(width, height, c.GL_LINEAR, c.GL_CLAMP_TO_EDGE); + std.debug.assert(extractedBuffer.validate()); + } + c.glDisable(c.GL_DEPTH_TEST); + c.glDisable(c.GL_CULL_FACE); + + extractImageData(); + c.glViewport(0, 0, width/2, height/2); + downscale(); + firstPass(); + secondPass(); + c.glViewport(0, 0, width, height); + c.glBlendFunc(c.GL_ONE, c.GL_ONE); + upscale(); + + c.glEnable(c.GL_DEPTH_TEST); + c.glEnable(c.GL_CULL_FACE); + c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA); + } +}; + pub const Frustum = struct { const Plane = struct { pos: Vec3f, diff --git a/src/settings.zig b/src/settings.zig index 0ec7bf8a..5d88dae8 100644 --- a/src/settings.zig +++ b/src/settings.zig @@ -16,4 +16,6 @@ pub var fov: f32 = 45; pub var mouseSensitivity: f32 = 1; pub var renderDistance: i32 = 4; -pub var LODFactor: f32 = 2.0; \ No newline at end of file +pub var LODFactor: f32 = 2.0; + +pub var bloom: bool = true; \ No newline at end of file