diff --git a/assets/cubyz/shaders/chunks/depth_prepass_fragment.fs b/assets/cubyz/shaders/chunks/depth_prepass_fragment.fs new file mode 100644 index 00000000..ff575637 --- /dev/null +++ b/assets/cubyz/shaders/chunks/depth_prepass_fragment.fs @@ -0,0 +1,65 @@ +#version 430 + +in vec3 mvVertexPos; +in vec3 direction; +in vec2 uv; +flat in vec3 normal; +flat in uint textureIndexOffset; +flat in int isBackFace; +flat in int ditherSeed; +flat in uint lightBufferIndex; +flat in uvec2 lightArea; +in vec2 lightPosition; + +uniform sampler2DArray texture_sampler; +uniform sampler2DArray emissionSampler; +uniform sampler2DArray reflectivityAndAbsorptionSampler; +uniform samplerCube reflectionMap; +uniform float reflectionMapSize; +uniform float contrast; +uniform vec3 ambientLight; + +layout(std430, binding = 1) buffer _animatedTexture +{ + float animatedTexture[]; +}; + +layout(std430, binding = 11) buffer _textureData +{ + uint textureData[]; +}; + +float ditherThresholds[16] = float[16] ( + 1/17.0, 9/17.0, 3/17.0, 11/17.0, + 13/17.0, 5/17.0, 15/17.0, 7/17.0, + 4/17.0, 12/17.0, 2/17.0, 10/17.0, + 16/17.0, 8/17.0, 14/17.0, 6/17.0 +); + +ivec2 random1to2(int v) { + ivec4 fac = ivec4(11248723, 105436839, 45399083, 5412951); + int seed = v.x*fac.x ^ fac.y; + return seed*fac.zw; +} + +bool passDitherTest(float alpha) { + ivec2 screenPos = ivec2(gl_FragCoord.xy); + screenPos += random1to2(ditherSeed); + screenPos &= 3; + return alpha > ditherThresholds[screenPos.x*4 + screenPos.y]; +} + +uint readTextureIndex() { + uint x = clamp(uint(lightPosition.x), 0, lightArea.x - 2); + uint y = clamp(uint(lightPosition.y), 0, lightArea.y - 2); + uint index = textureIndexOffset + x*(lightArea.y - 1) + y; + return textureData[index >> 1] >> 16*(index & 1u) & 65535u; +} + +void main() { + uint textureIndex = readTextureIndex(); + float animatedTextureIndex = animatedTexture[textureIndex]; + vec3 textureCoords = vec3(uv, animatedTextureIndex); + float alpha = texture(texture_sampler, textureCoords).a; + if(!passDitherTest(alpha)) discard; +} diff --git a/src/gui/windows/gpu_performance_measuring.zig b/src/gui/windows/gpu_performance_measuring.zig index 995fd857..db100e95 100644 --- a/src/gui/windows/gpu_performance_measuring.zig +++ b/src/gui/windows/gpu_performance_measuring.zig @@ -16,9 +16,10 @@ pub const Samples = enum(u8) { clear, animation, chunk_rendering_preparation, - chunk_rendering_previous_visible, + chunk_rendering_depth_prepass_previous_visible, chunk_rendering_occlusion_test, - chunk_rendering_new_visible, + chunk_rendering_depth_prepass_new_visible, + chunk_rendering, entity_rendering, transparent_rendering_preparation, transparent_rendering_occlusion_test, @@ -35,9 +36,10 @@ const names = [_][]const u8 { "Clear", "Pre-processing Block Animations", "Chunk Rendering Preparation", - "Chunk Rendering Previous Visible", + "Chunk Rendering (Depth Prepass) Previous Visible", "Chunk Rendering Occlusion Test", - "Chunk Rendering New Visible", + "Chunk Rendering (Depth Prepass) New Visible", + "Chunk Rendering", "Entity Rendering", "Transparent Rendering Preparation", "Transparent Rendering Occlusion Test", diff --git a/src/renderer/chunk_meshing.zig b/src/renderer/chunk_meshing.zig index 8b90fb18..6caed600 100644 --- a/src/renderer/chunk_meshing.zig +++ b/src/renderer/chunk_meshing.zig @@ -24,6 +24,7 @@ const gpu_performance_measuring = main.gui.windowlist.gpu_performance_measuring; const mesh_storage = @import("mesh_storage.zig"); +var depthPrepassShader: Shader = undefined; var shader: Shader = undefined; var transparentShader: Shader = undefined; const UniformStruct = struct { @@ -45,6 +46,7 @@ const UniformStruct = struct { zFar: c_int, transparent: c_int, }; +pub var depthPrepassUniforms: UniformStruct = undefined; pub var uniforms: UniformStruct = undefined; pub var transparentUniforms: UniformStruct = undefined; pub var commandShader: Shader = undefined; @@ -77,6 +79,7 @@ pub var transparentQuadsDrawn: usize = 0; pub fn init() void { lighting.init(); + depthPrepassShader = Shader.initAndGetUniforms("assets/cubyz/shaders/chunks/chunk_vertex.vs", "assets/cubyz/shaders/chunks/depth_prepass_fragment.fs", &depthPrepassUniforms); shader = Shader.initAndGetUniforms("assets/cubyz/shaders/chunks/chunk_vertex.vs", "assets/cubyz/shaders/chunks/chunk_fragment.fs", &uniforms); transparentShader = Shader.initAndGetUniforms("assets/cubyz/shaders/chunks/chunk_vertex.vs", "assets/cubyz/shaders/chunks/transparent_fragment.fs", &transparentUniforms); commandShader = Shader.initComputeAndGetUniforms("assets/cubyz/shaders/chunks/fillIndirectBuffer.glsl", &commandUniforms); @@ -106,6 +109,7 @@ pub fn init() void { pub fn deinit() void { lighting.deinit(); + depthPrepassShader.deinit(); shader.deinit(); transparentShader.deinit(); commandShader.deinit(); @@ -160,6 +164,15 @@ fn bindCommonUniforms(locations: *UniformStruct, projMatrix: Mat4f, ambient: Vec c.glUniform3f(locations.playerPositionFraction, @floatCast(@mod(playerPos[0], 1)), @floatCast(@mod(playerPos[1], 1)), @floatCast(@mod(playerPos[2], 1))); } +pub fn bindPrepassShaderAndUniforms(projMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d) void { + depthPrepassShader.bind(); + + bindCommonUniforms(&depthPrepassUniforms, projMatrix, ambient, playerPos); + c.glUniform1i(depthPrepassUniforms.transparent, 0); + + c.glBindVertexArray(vao); +} + pub fn bindShaderAndUniforms(projMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d) void { shader.bind(); @@ -195,7 +208,7 @@ pub fn drawChunksIndirect(chunkIDs: []const u32, projMatrix: Mat4f, ambient: Vec c.glUniform1i(commandUniforms.isTransparent, @intFromBool(transparent)); c.glUniform3i(commandUniforms.playerPositionInteger, @intFromFloat(@floor(playerPos[0])), @intFromFloat(@floor(playerPos[1])), @intFromFloat(@floor(playerPos[2]))); if(!transparent) { - gpu_performance_measuring.startQuery(.chunk_rendering_previous_visible); + gpu_performance_measuring.startQuery(.chunk_rendering_depth_prepass_previous_visible); c.glUniform1i(commandUniforms.onlyDrawPreviouslyInvisible, 0); c.glDispatchCompute(@intCast(@divFloor(chunkIDs.len + 63, 64)), 1, 1); // TODO: Replace with @divCeil once available c.glMemoryBarrier(c.GL_SHADER_STORAGE_BARRIER_BIT | c.GL_COMMAND_BARRIER_BIT); @@ -203,7 +216,8 @@ pub fn drawChunksIndirect(chunkIDs: []const u32, projMatrix: Mat4f, ambient: Vec if(transparent) { bindTransparentShaderAndUniforms(projMatrix, ambient, playerPos); } else { - bindShaderAndUniforms(projMatrix, ambient, playerPos); + bindPrepassShaderAndUniforms(projMatrix, ambient, playerPos); + c.glColorMask(c.GL_FALSE, c.GL_FALSE, c.GL_FALSE, c.GL_FALSE); } c.glBindBuffer(c.GL_DRAW_INDIRECT_BUFFER, commandBuffer.ssbo.bufferID); c.glMultiDrawElementsIndirect(c.GL_TRIANGLES, c.GL_UNSIGNED_INT, @ptrFromInt(allocation.start*@sizeOf(IndirectData)), drawCallsEstimate, 0); @@ -220,14 +234,18 @@ pub fn drawChunksIndirect(chunkIDs: []const u32, projMatrix: Mat4f, ambient: Vec c.glDepthMask(c.GL_FALSE); c.glColorMask(c.GL_FALSE, c.GL_FALSE, c.GL_FALSE, c.GL_FALSE); c.glBindVertexArray(vao); + var oldDepthFunc: c_uint = undefined; + c.glGetIntegerv(c.GL_DEPTH_FUNC, @ptrCast(&oldDepthFunc)); + c.glDepthFunc(c.GL_LEQUAL); c.glDrawElementsBaseVertex(c.GL_TRIANGLES, @intCast(6*6*chunkIDs.len), c.GL_UNSIGNED_INT, null, chunkIDAllocation.start*24); + c.glDepthFunc(oldDepthFunc); c.glDepthMask(c.GL_TRUE); c.glColorMask(c.GL_TRUE, c.GL_TRUE, c.GL_TRUE, c.GL_TRUE); c.glMemoryBarrier(c.GL_SHADER_STORAGE_BARRIER_BIT); gpu_performance_measuring.stopQuery(); // Draw again: - gpu_performance_measuring.startQuery(if(transparent) .transparent_rendering else .chunk_rendering_new_visible); + gpu_performance_measuring.startQuery(if(transparent) .transparent_rendering else .chunk_rendering_depth_prepass_new_visible); commandShader.bind(); c.glUniform1i(commandUniforms.onlyDrawPreviouslyInvisible, 1); c.glDispatchCompute(@intCast(@divFloor(chunkIDs.len + 63, 64)), 1, 1); // TODO: Replace with @divCeil once available @@ -237,11 +255,31 @@ pub fn drawChunksIndirect(chunkIDs: []const u32, projMatrix: Mat4f, ambient: Vec bindTransparentShaderAndUniforms(projMatrix, ambient, playerPos); c.glDepthMask(c.GL_FALSE); } else { - bindShaderAndUniforms(projMatrix, ambient, playerPos); + bindPrepassShaderAndUniforms(projMatrix, ambient, playerPos); + c.glColorMask(c.GL_FALSE, c.GL_FALSE, c.GL_FALSE, c.GL_FALSE); } c.glBindBuffer(c.GL_DRAW_INDIRECT_BUFFER, commandBuffer.ssbo.bufferID); c.glMultiDrawElementsIndirect(c.GL_TRIANGLES, c.GL_UNSIGNED_INT, @ptrFromInt(allocation.start*@sizeOf(IndirectData)), drawCallsEstimate, 0); gpu_performance_measuring.stopQuery(); + + if(!transparent) { + gpu_performance_measuring.startQuery(.chunk_rendering); + // Draw after the depth prepass + commandShader.bind(); + c.glUniform1i(commandUniforms.onlyDrawPreviouslyInvisible, 0); + c.glDispatchCompute(@intCast(@divFloor(chunkIDs.len + 63, 64)), 1, 1); // TODO: Replace with @divCeil once available + c.glMemoryBarrier(c.GL_SHADER_STORAGE_BARRIER_BIT | c.GL_COMMAND_BARRIER_BIT); + + bindShaderAndUniforms(projMatrix, ambient, playerPos); + c.glDepthFunc(c.GL_EQUAL); + c.glDepthMask(c.GL_FALSE); + c.glColorMask(c.GL_TRUE, c.GL_TRUE, c.GL_TRUE, c.GL_TRUE); + c.glBindBuffer(c.GL_DRAW_INDIRECT_BUFFER, commandBuffer.ssbo.bufferID); + c.glMultiDrawElementsIndirect(c.GL_TRIANGLES, c.GL_UNSIGNED_INT, @ptrFromInt(allocation.start*@sizeOf(IndirectData)), drawCallsEstimate, 0); + c.glDepthFunc(c.GL_LESS); + c.glDepthMask(c.GL_TRUE); + gpu_performance_measuring.stopQuery(); + } } pub const FaceData = extern struct { @@ -430,9 +468,20 @@ const PrimitiveMesh = struct { @floatFromInt(face.position.y), @floatFromInt(face.position.z), }; - for(main.models.quads.items[face.blockAndQuad.quadIndex].corners) |cornerPos| { - self.min = @min(self.min, basePos + cornerPos); - self.max = @max(self.max, basePos + cornerPos); + + for(0..2) |x| { + for(0..2) |y| { + const quadIndex = face.blockAndQuad.quadIndex; + var vertexPos = models.quads.items[quadIndex].corners[0]; + vertexPos += (models.quads.items[quadIndex].corners[2] - models.quads.items[quadIndex].corners[0])*@as(Vec3f, @splat(@floatFromInt(x*(face.position.xSizeMinusOne + 1)))); + if(x != 0) { + vertexPos += (models.quads.items[quadIndex].corners[3] - models.quads.items[quadIndex].corners[2])*@as(Vec3f, @splat(@floatFromInt(y*(face.position.ySizeMinusOne + 1)))); + } else { + vertexPos += (models.quads.items[quadIndex].corners[1] - models.quads.items[quadIndex].corners[0])*@as(Vec3f, @splat(@floatFromInt(y*(face.position.ySizeMinusOne + 1)))); + } + self.min = @min(self.min, basePos + vertexPos); + self.max = @max(self.max, basePos + vertexPos); + } } } parent.lightingData[0].lock.unlockRead();