diff --git a/assets/cubyz/shaders/bloom/color_extractor_downsample.fs b/assets/cubyz/shaders/bloom/color_extractor_downsample.fs index 5703f2db..aad00e0c 100644 --- a/assets/cubyz/shaders/bloom/color_extractor_downsample.fs +++ b/assets/cubyz/shaders/bloom/color_extractor_downsample.fs @@ -6,12 +6,18 @@ in vec2 texCoords; layout(binding = 3) uniform sampler2D color; +vec3 fetch(ivec2 pos) { + vec4 rgba = texelFetch(color, pos, 0); + if(rgba.a < 1) return vec3(0); // Prevent t-junctions from transparency from making a huge mess. + return rgba.rgb/rgba.a; +} + vec3 linearSample(ivec2 start) { vec3 outColor = vec3(0); - outColor += texelFetch(color, start, 0).rgb; - outColor += texelFetch(color, start + ivec2(0, 1), 0).rgb; - outColor += texelFetch(color, start + ivec2(1, 0), 0).rgb; - outColor += texelFetch(color, start + ivec2(1, 1), 0).rgb; + outColor += fetch(start); + outColor += fetch(start + ivec2(0, 1)); + outColor += fetch(start + ivec2(1, 0)); + outColor += fetch(start + ivec2(1, 1)); return outColor*0.25; } diff --git a/assets/cubyz/shaders/bloom/upscale.fs b/assets/cubyz/shaders/bloom/upscale.fs index f0c838e3..be3dea87 100644 --- a/assets/cubyz/shaders/bloom/upscale.fs +++ b/assets/cubyz/shaders/bloom/upscale.fs @@ -7,5 +7,5 @@ in vec2 texCoords; layout(binding = 3) uniform sampler2D color; void main() { - fragColor = vec4(texture(color, texCoords).rgb, 1); + fragColor = vec4(texture(color, texCoords).rgb, 0); } \ No newline at end of file diff --git a/assets/cubyz/shaders/chunks/transparent_fragment.fs b/assets/cubyz/shaders/chunks/transparent_fragment.fs index 83e9af25..a6489947 100644 --- a/assets/cubyz/shaders/chunks/transparent_fragment.fs +++ b/assets/cubyz/shaders/chunks/transparent_fragment.fs @@ -12,6 +12,8 @@ uniform vec3 ambientLight; uniform sampler2DArray texture_sampler; uniform sampler2DArray emissionSampler; +layout(binding = 3) uniform sampler2D depthTexture; + layout (location = 0, index = 0) out vec4 fragColor; layout (location = 0, index = 1) out vec4 blendColor; @@ -59,6 +61,7 @@ const vec3[6] normals = vec3[6]( vec3(0, 0, -1) ); +uniform float nearPlane; uniform Fog fog; uniform Fog waterFog; // TODO: Select fog from texture @@ -170,30 +173,92 @@ void main() { float normalVariation = normalVariations[faceNormal]; float lod = getLod(ivec3(startPosition), faceNormal, direction, variance); ivec2 textureCoords = getTextureCoords(ivec3(startPosition), faceNormal); - fragColor = mipMapSample(texture_sampler, textureCoords, textureIndex, lod)*vec4(ambientLight*normalVariation, 1); + float depthBufferValue = texelFetch(depthTexture, ivec2(gl_FragCoord.xy), 0).r; + float fogFactor = 1.0/32.0; // TODO: This should be configurable by the block. + float fogDistance; + { + float distCameraTerrain = nearPlane*fogFactor/depthBufferValue; + float distFromCamera = abs(mvVertexPos.z)*fogFactor; + float distFromTerrain = distFromCamera - distCameraTerrain; + if(distCameraTerrain < 10) { // Resolution range is sufficient. + fogDistance = 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) { + fogDistance = distFromTerrain; + } else if(distFromCamera < 5) { + fogDistance = distFromCamera - 10; + } else { + fogDistance = -5; + } + } - if (fragColor.a == 1) discard; - - if (fog.activ) { - // TODO: Underwater fog if possible. } - fragColor.rgb *= fragColor.a; - blendColor.rgb = unpackColor(textureData[blockType].absorption); + /*if(depthBufferValue == 0) { + fogDistance = abs(mvVertexPos.z); + } else { + fogDistance = abs(mvVertexPos.z) - nearPlane/depthBufferValue; + } + fogDistance /= 16.0; // TODO: This should be configurable by the block.*/ + bool opaqueFrontFace = false; + bool emptyBackFace = false; + //fogDistance = min(5, max(-5, fogDistance)); + //if(fogDistance > 0) fogDistance -= 10; + if(gl_FrontFacing) { + fragColor = mipMapSample(texture_sampler, textureCoords, textureIndex, lod)*vec4(ambientLight*normalVariation, 1); - // Fake reflection: - // TODO: Also allow this for opaque pixels. - // TODO: Change this when it rains. - // TODO: Normal mapping. - // TODO: Allow textures to contribute to this term. - fragColor.rgb += (textureData[blockType].reflectivity/2*vec3(snoise(normalize(reflect(direction, normals[faceNormal])))) + vec3(textureData[blockType].reflectivity))*ambientLight*normalVariation; - fragColor.rgb += mipMapSample(emissionSampler, textureCoords, textureIndex, lod).rgb; - blendColor.rgb *= 1 - fragColor.a; - fragColor.a = 1; + if (fragColor.a == 1) discard; - if (fog.activ) { - fragColor = calcFog(mvVertexPos, fragColor, fog); - blendColor.rgb *= fragColor.a; + if (fog.activ) { + // TODO: Underwater fog if possible. + } + fragColor.rgb *= fragColor.a; + blendColor.rgb = unpackColor(textureData[blockType].absorption); + + // Fake reflection: + // TODO: Also allow this for opaque pixels. + // TODO: Change this when it rains. + // TODO: Normal mapping. + // TODO: Allow textures to contribute to this term. + fragColor.rgb += (textureData[blockType].reflectivity/2*vec3(snoise(normalize(reflect(direction, normals[faceNormal])))) + vec3(textureData[blockType].reflectivity))*ambientLight*normalVariation; + fragColor.rgb += mipMapSample(emissionSampler, textureCoords, textureIndex, lod).rgb; + blendColor.rgb *= 1 - fragColor.a; fragColor.a = 1; + + if (fog.activ) { + fragColor = calcFog(mvVertexPos, fragColor, fog); + blendColor.rgb *= fragColor.a; + fragColor.a = 1; + } + + // TODO: Consider the case that the terrain is too far away, which would case infinity/nan. + float fogFactor = exp(fogDistance); + fragColor = vec4(0.5, 1.0, 0.0, 1); + fragColor.a = 1.0/fogFactor; + fragColor.rgb *= fragColor.a; + if(opaqueFrontFace) { + blendColor.rgb = vec3(0); + } else { + fragColor.rgb -= vec3(0.5, 1.0, 0.0); + blendColor.rgb = vec3(1); + } + //blendColor.rgb = vec3(0); + //fragColor = vec4(1, 1, 1, 1); + } else { + if(emptyBackFace) { + fragColor = vec4(0, 0, 0, 1); + } else { + float fogFactor = exp(fogDistance); + fragColor.a = fogFactor; + fragColor.rgb -= vec3(0.5, 1.0, 0.0); + fragColor.rgb += fogFactor*vec3(0.5, 1.0, 0.0); + } + blendColor.rgb = vec3(1); + + + //blendColor.rgb = vec3(0); + //fragColor = vec4(1, 0, 0, 1); } - // TODO: Update the depth. } diff --git a/assets/cubyz/shaders/deferred_render_pass.fs b/assets/cubyz/shaders/deferred_render_pass.fs index 8659a4cb..6ed7b9ce 100644 --- a/assets/cubyz/shaders/deferred_render_pass.fs +++ b/assets/cubyz/shaders/deferred_render_pass.fs @@ -7,6 +7,7 @@ uniform sampler2D color; void main() { fragColor = texture(color, texCoords); + fragColor.rgb /= fragColor.a; float maxColor = max(1.0, max(fragColor.r, max(fragColor.g, fragColor.b))); fragColor.rgb = fragColor.rgb/maxColor; } \ No newline at end of file diff --git a/src/chunk.zig b/src/chunk.zig index adff216d..91e911ed 100644 --- a/src/chunk.zig +++ b/src/chunk.zig @@ -404,6 +404,7 @@ pub const meshing = struct { time: c_int, visibilityMask: c_int, voxelSize: c_int, + nearPlane: c_int, }; pub var uniforms: UniformStruct = undefined; pub var transparentUniforms: UniformStruct = undefined; @@ -462,6 +463,8 @@ pub const meshing = struct { c.glUniform1i(uniforms.time, @as(u31, @truncate(time))); + c.glUniform1f(uniforms.nearPlane, renderer.zNear); + c.glBindVertexArray(vao); } @@ -483,6 +486,8 @@ pub const meshing = struct { c.glUniform1i(transparentUniforms.time, @as(u31, @truncate(time))); + c.glUniform1f(transparentUniforms.nearPlane, renderer.zNear); + c.glBindVertexArray(vao); } @@ -906,7 +911,7 @@ pub const meshing = struct { }; } - pub fn uploadDataAndFinishNeighbors(self: *ChunkMesh) !void { + pub fn uploadDataAndFinishNeighbors(self: *ChunkMesh) !void { // TODO: The way neighboring chunks are handled doesn't work well with transparency. std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function. const chunk = self.chunk.load(.Monotonic) orelse return; // In the mean-time the mesh was discarded and recreated and all the data was lost. self.opaqueMesh.resetToCore(); diff --git a/src/graphics.zig b/src/graphics.zig index 4e53fb5e..559fadf7 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -1264,22 +1264,26 @@ pub const LargeBuffer = struct { pub const FrameBuffer = struct { frameBuffer: c_uint, texture: c_uint, - hasDepthBuffer: bool, - depthBuffer: c_uint, + hasDepthTexture: bool, + depthTexture: c_uint, - pub fn init(self: *FrameBuffer, hasDepthBuffer: bool, textureFilter: c_int, textureWrap: c_int) void { + pub fn init(self: *FrameBuffer, hasDepthTexture: bool, textureFilter: c_int, textureWrap: c_int) void { self.* = FrameBuffer{ .frameBuffer = undefined, .texture = undefined, - .depthBuffer = undefined, - .hasDepthBuffer = hasDepthBuffer, + .depthTexture = undefined, + .hasDepthTexture = hasDepthTexture, }; c.glGenFramebuffers(1, &self.frameBuffer); c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.frameBuffer); - if(hasDepthBuffer) { - c.glGenRenderbuffers(1, &self.depthBuffer); - c.glBindRenderbuffer(c.GL_RENDERBUFFER, self.depthBuffer); - c.glFramebufferRenderbuffer(c.GL_FRAMEBUFFER, c.GL_DEPTH_ATTACHMENT, c.GL_RENDERBUFFER, self.depthBuffer); + if(hasDepthTexture) { + c.glGenTextures(1, &self.depthTexture); + c.glBindTexture(c.GL_TEXTURE_2D, self.depthTexture); + 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_DEPTH_ATTACHMENT, c.GL_TEXTURE_2D, self.depthTexture, 0); } c.glGenTextures(1, &self.texture); c.glBindTexture(c.GL_TEXTURE_2D, self.texture); @@ -1294,17 +1298,17 @@ pub const FrameBuffer = struct { pub fn deinit(self: *FrameBuffer) void { c.glDeleteFramebuffers(1, &self.frameBuffer); - if(self.hasDepthBuffer) { - c.glDeleteRenderbuffers(1, &self.depthBuffer); + if(self.hasDepthTexture) { + c.glDeleteRenderbuffers(1, &self.depthTexture); } c.glDeleteTextures(1, &self.texture); } pub fn updateSize(self: *FrameBuffer, width: u31, height: u31, internalFormat: c_int) void { c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.frameBuffer); - if(self.hasDepthBuffer) { - c.glBindRenderbuffer(c.GL_RENDERBUFFER, self.depthBuffer); - c.glRenderbufferStorage(c.GL_RENDERBUFFER, c.GL_DEPTH_COMPONENT32F, width, height); + if(self.hasDepthTexture) { + c.glBindTexture(c.GL_TEXTURE_2D, self.depthTexture); + c.glTexImage2D(c.GL_TEXTURE_2D, 0, c.GL_DEPTH_COMPONENT32F, width, height, 0, c.GL_DEPTH_COMPONENT, c.GL_FLOAT, null); } c.glBindTexture(c.GL_TEXTURE_2D, self.texture); @@ -1331,6 +1335,12 @@ pub const FrameBuffer = struct { c.glBindTexture(c.GL_TEXTURE_2D, self.texture); } + pub fn bindDepthTexture(self: *FrameBuffer, target: c_uint) void { + std.debug.assert(self.hasDepthTexture); + c.glActiveTexture(target); + c.glBindTexture(c.GL_TEXTURE_2D, self.depthTexture); + } + pub fn bind(self: *FrameBuffer) void { c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.frameBuffer); } diff --git a/src/renderer.zig b/src/renderer.zig index 4d3348ce..27a37670 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -28,7 +28,7 @@ const Mat4f = vec.Mat4f; /// The number of milliseconds after which no more chunk meshes are created. This allows the game to run smoother on movement. const maximumMeshTime = 12; -const zNear = 1e-10; +pub const zNear = 1e-10; var fogShader: graphics.Shader = undefined; var fogUniforms: struct { @@ -196,6 +196,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo gpu_performance_measuring.stopQuery(); // Render transparent chunk meshes: + worldFrameBuffer.bindDepthTexture(c.GL_TEXTURE3); gpu_performance_measuring.startQuery(.transparent_rendering); chunk.meshing.bindTransparentShaderAndUniforms(game.projectionMatrix, ambientLight, time); @@ -203,9 +204,11 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo c.glUniform3fv(chunk.meshing.transparentUniforms.@"waterFog.color", 1, @ptrCast(&waterFog.color)); c.glUniform1f(chunk.meshing.transparentUniforms.@"waterFog.density", waterFog.density); - c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_SRC1_COLOR); + c.glBlendEquationSeparate(c.GL_FUNC_ADD, c.GL_FUNC_ADD); + c.glBlendFuncSeparate(c.GL_DST_ALPHA, c.GL_SRC1_COLOR, c.GL_DST_ALPHA, c.GL_ZERO); c.glDepthFunc(c.GL_GEQUAL); c.glDepthMask(c.GL_FALSE); + c.glDisable(c.GL_CULL_FACE); { var i: usize = meshes.len; while(true) { @@ -214,6 +217,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo try meshes[i].renderTransparent(playerPos); } } + c.glEnable(c.GL_CULL_FACE); c.glDepthMask(c.GL_TRUE); c.glDepthFunc(c.GL_GREATER); c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA);