diff --git a/assets/cubyz/shaders/chunks/chunk_depth_fragment.frag b/assets/cubyz/shaders/chunks/chunk_depth_fragment.frag new file mode 100644 index 0000000000..f6935d5f18 --- /dev/null +++ b/assets/cubyz/shaders/chunks/chunk_depth_fragment.frag @@ -0,0 +1,25 @@ +#version 460 + +layout(location = 1) in vec3 direction; +layout(location = 2) in vec2 uv; +layout(location = 3) flat in vec3 normal; +layout(location = 4) flat in int textureIndex; +layout(location = 5) flat in int isBackFace; +layout(location = 7) flat in int opaqueInLod; + +layout(location = 5) uniform float lodDistance; + +layout(binding = 0) uniform sampler2DArray textureSampler; +layout(binding = 5) uniform sampler2D ditherTexture; + +layout(std430, binding = 1) buffer _animatedTexture +{ + float animatedTexture[]; +}; + +void main() { + float animatedTextureIndex = animatedTexture[textureIndex]; + vec3 textureCoords = vec3(uv, animatedTextureIndex); + vec4 color = texture(textureSampler, textureCoords); + if (color.a < 0.5) discard; +} diff --git a/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert b/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert new file mode 100644 index 0000000000..ee0a159656 --- /dev/null +++ b/assets/cubyz/shaders/chunks/chunk_depth_vertex.vert @@ -0,0 +1,97 @@ +#version 460 + +layout(location = 1) out vec3 direction; +layout(location = 2) out vec2 uv; +layout(location = 3) flat out vec3 normal; +layout(location = 4) flat out int textureIndex; +layout(location = 5) flat out int isBackFace; +layout(location = 7) flat out int opaqueInLod; + +layout(location = 1) uniform mat4 projectionMatrix; +layout(location = 2) uniform mat4 viewMatrix; +layout(location = 3) uniform ivec3 playerPositionInteger; +layout(location = 4) uniform vec3 playerPositionFraction; + +struct FaceData { + int encodedPositionAndLightIndex; + int textureAndQuad; +}; +layout(std430, binding = 3) buffer _faceData +{ + FaceData faceData[]; +}; + +struct QuadInfo { + vec3 normal; + float corners[4][3]; + vec2 cornerUV[4]; + uint textureSlot; + int opaqueInLod; +}; + +layout(std430, binding = 4) buffer _quads +{ + QuadInfo quads[]; +}; + +layout(std430, binding = 10) buffer _lightData +{ + uint lightData[]; +}; + +struct ChunkData { + ivec4 position; + vec4 minPos; + vec4 maxPos; + int voxelSize; + uint lightStart; + uint vertexStartOpaque; + uint faceCountsByNormalOpaque[14]; + uint vertexStartTransparent; + uint vertexCountTransparent; + uint visibilityState; + uint oldVisibilityState; + uint visibilityStateDepth; + uint oldVisibilityStateDepth; +}; + +layout(std430, binding = 6) buffer _chunks +{ + ChunkData chunks[]; +}; + +vec3 square(vec3 x) { + return x*x; +} + +void main() { + int faceID = gl_VertexID >> 2; + int vertexID = gl_VertexID & 3; + int chunkID = gl_BaseInstance; + int voxelSize = chunks[chunkID].voxelSize; + int encodedPositionAndLightIndex = faceData[faceID].encodedPositionAndLightIndex; + int textureAndQuad = faceData[faceID].textureAndQuad; + isBackFace = encodedPositionAndLightIndex>>15 & 1; + + textureIndex = textureAndQuad & 65535; + int quadIndex = textureAndQuad >> 16; + + vec3 position = vec3( + encodedPositionAndLightIndex & 31, + encodedPositionAndLightIndex >> 5 & 31, + encodedPositionAndLightIndex >> 10 & 31 + ); + + normal = quads[quadIndex].normal; + + position += vec3(quads[quadIndex].corners[vertexID][0], quads[quadIndex].corners[vertexID][1], quads[quadIndex].corners[vertexID][2]); + position *= voxelSize; + position += vec3(chunks[chunkID].position.xyz - playerPositionInteger); + position -= playerPositionFraction; + + direction = position; + + gl_Position = projectionMatrix*viewMatrix*vec4(position, 1); + uv = quads[quadIndex].cornerUV[vertexID]*voxelSize; + opaqueInLod = quads[quadIndex].opaqueInLod; +} diff --git a/assets/cubyz/shaders/chunks/chunk_fragment.frag b/assets/cubyz/shaders/chunks/chunk_fragment.frag index 8515a0bc84..c105967436 100644 --- a/assets/cubyz/shaders/chunks/chunk_fragment.frag +++ b/assets/cubyz/shaders/chunks/chunk_fragment.frag @@ -2,13 +2,17 @@ layout(location = 0) in vec3 mvVertexPos; layout(location = 1) in vec3 direction; -layout(location = 2) in vec3 light; -layout(location = 3) in vec2 uv; -layout(location = 4) flat in vec3 normal; -layout(location = 5) flat in int textureIndex; -layout(location = 6) flat in int isBackFace; -layout(location = 7) flat in float distanceForLodCheck; -layout(location = 8) flat in int opaqueInLod; +layout(location = 2) in vec3 sunLight; +layout(location = 3) in vec3 blockLight; +layout(location = 4) in vec2 uv; +layout(location = 5) in vec3 shadowPos; +layout(location = 6) flat in vec3 normal; +layout(location = 7) flat in int textureIndex; +layout(location = 8) flat in int isBackFace; +layout(location = 9) flat in float distanceForLodCheck; +layout(location = 10) flat in int opaqueInLod; +layout(location = 11) flat in mat4 worldToQuad; +layout(location = 15) flat in mat3 uvTransform; layout(location = 0) out vec4 fragColor; @@ -17,16 +21,24 @@ layout(binding = 1) uniform sampler2DArray emissionSampler; layout(binding = 2) uniform sampler2DArray reflectivityAndAbsorptionSampler; layout(binding = 4) uniform samplerCube reflectionMap; layout(binding = 5) uniform sampler2D ditherTexture; +layout(binding = 6) uniform sampler2D shadowMap; layout(location = 5) uniform float reflectionMapSize; layout(location = 6) uniform float contrast; layout(location = 7) uniform float lodDistance; +layout(location = 8) uniform mat4 lightProjectionMatrix; +layout(location = 9) uniform mat4 lightViewMatrix; +layout(location = 42) uniform vec3 lightDir; layout(std430, binding = 1) buffer _animatedTexture { float animatedTexture[]; }; +vec3 square(vec3 x) { + return x*x; +} + float lightVariation(vec3 normal) { const vec3 directionalPart = vec3(0, contrast/2, contrast); const float baseLighting = 1 - contrast; @@ -51,6 +63,26 @@ vec4 fixedCubeMapLookup(vec3 v) { // Taken from http://the-witness.net/news/2012 return texture(reflectionMap, v); } +float shadowCalculation() { + if (dot(lightDir, normal) > 0.0) return 1.0; + + vec3 shadowPosUV = uvTransform * (worldToQuad * lightViewMatrix * vec4(shadowPos, 1.0)).xyz; + shadowPosUV.xy = ceil(shadowPosUV.xy * 16.0) / 16.0; + vec4 shadowPosSnapped = inverse(worldToQuad) * vec4(inverse(uvTransform) * shadowPosUV, 1.0); + + vec4 lightPos = lightProjectionMatrix * shadowPosSnapped; + vec3 projCoords = lightPos.xyz; + projCoords = projCoords * 0.5 + 0.5; + float closestDepth = texture(shadowMap, projCoords.xy).r; + float currentDepth = projCoords.z; + currentDepth += 0.0001; + float shadow = currentDepth > closestDepth ? 1.0 : 0.0; + if(projCoords.z > 1.0) { + shadow = 0.0; + } + return shadow; +} + void main() { float animatedTextureIndex = animatedTexture[textureIndex]; float normalVariation = lightVariation(normal); @@ -63,6 +95,11 @@ void main() { reflectivity = reflectivity*fixedCubeMapLookup(reflect(direction, normal)).x; reflectivity = reflectivity*(1 - fresnelReflection) + fresnelReflection; + vec3 shadowColor = vec3(0.5, 0.5, 0.35); + + float shadow = shadowCalculation(); + vec3 light = min(sqrt(square((1.0 - shadow*shadowColor)*sunLight) + square(blockLight)), vec3(31))/31; + vec3 pixelLight = max(light*normalVariation, texture(emissionSampler, textureCoords).r*4); fragColor = texture(textureSampler, textureCoords)*vec4(pixelLight, 1); fragColor.rgb += reflectivity*pixelLight; diff --git a/assets/cubyz/shaders/chunks/chunk_vertex.vert b/assets/cubyz/shaders/chunks/chunk_vertex.vert index 252ddf2f30..a55a4fc86a 100644 --- a/assets/cubyz/shaders/chunks/chunk_vertex.vert +++ b/assets/cubyz/shaders/chunks/chunk_vertex.vert @@ -2,19 +2,25 @@ layout(location = 0) out vec3 mvVertexPos; layout(location = 1) out vec3 direction; -layout(location = 2) out vec3 light; -layout(location = 3) out vec2 uv; -layout(location = 4) flat out vec3 normal; -layout(location = 5) flat out int textureIndex; -layout(location = 6) flat out int isBackFace; -layout(location = 7) flat out float distanceForLodCheck; -layout(location = 8) flat out int opaqueInLod; +layout(location = 2) out vec3 sunLight; +layout(location = 3) out vec3 blockLight; +layout(location = 4) out vec2 uv; +layout(location = 5) out vec3 shadowPos; +layout(location = 6) flat out vec3 normal; +layout(location = 7) flat out int textureIndex; +layout(location = 8) flat out int isBackFace; +layout(location = 9) flat out float distanceForLodCheck; +layout(location = 10) flat out int opaqueInLod; +layout(location = 11) flat out mat4 worldToQuad; +layout(location = 15) flat out mat3 uvTransform; layout(location = 0) uniform vec3 ambientLight; layout(location = 1) uniform mat4 projectionMatrix; layout(location = 2) uniform mat4 viewMatrix; layout(location = 3) uniform ivec3 playerPositionInteger; layout(location = 4) uniform vec3 playerPositionFraction; +layout(location = 8) uniform mat4 lightProjectionMatrix; +layout(location = 9) uniform mat4 lightViewMatrix; #ifdef ENTITY layout(location = 14) uniform mat4 modelMatrix; @@ -62,17 +68,16 @@ void main() { int textureAndQuad = faceData[faceID].textureAndQuad; uint lightIndex = chunks[chunkID].lightStart + 4*(encodedPositionAndLightIndex >> 16); uint fullLight = lightData[lightIndex + vertexID]; - vec3 sunLight = vec3( + sunLight = vec3( fullLight >> 25 & 31u, fullLight >> 20 & 31u, fullLight >> 15 & 31u - ); - vec3 blockLight = vec3( + ) * ambientLight; + blockLight = vec3( fullLight >> 10 & 31u, fullLight >> 5 & 31u, fullLight >> 0 & 31u ); - light = min(sqrt(square(sunLight*ambientLight) + square(blockLight)), vec3(31))/31; isBackFace = encodedPositionAndLightIndex>>15 & 1; textureIndex = textureAndQuad & 65535; @@ -103,4 +108,34 @@ void main() { distanceForLodCheck = length(mvPos.xyz) + voxelSize; uv = quads[quadIndex].cornerUV[vertexID]*voxelSize; opaqueInLod = quads[quadIndex].opaqueInLod; + + shadowPos = position + normal * 0.03; + + vec3 p0 = vec3(quads[quadIndex].corners[0][0], quads[quadIndex].corners[0][1], quads[quadIndex].corners[0][2]); + vec3 p1 = vec3(quads[quadIndex].corners[1][0], quads[quadIndex].corners[1][1], quads[quadIndex].corners[1][2]); + vec3 p3 = vec3(quads[quadIndex].corners[3][0], quads[quadIndex].corners[3][1], quads[quadIndex].corners[3][2]); + + vec2 uv0 = vec2(quads[quadIndex].cornerUV[0][0], quads[quadIndex].cornerUV[0][1]); + vec2 uv1 = vec2(quads[quadIndex].cornerUV[1][0], quads[quadIndex].cornerUV[1][1]); + vec2 uv3 = vec2(quads[quadIndex].cornerUV[3][0], quads[quadIndex].cornerUV[3][1]); + + vec3 u = p1 - p0; + vec3 v = p3 - p0; + mat3 invBasis = inverse(mat3(u, v, cross(u,v))); + + vec2 du = uv1 - uv0; + vec2 dv = uv3 - uv0; + + uvTransform = mat3( + vec3(du, 0.0), + vec3(dv, 0.0), + vec3(uv0, 1.0) + ); + + worldToQuad = mat4( + vec4(invBasis[0], 0), + vec4(invBasis[1], 0), + vec4(invBasis[2], 0), + vec4(-(invBasis * p0),1) + ); } diff --git a/assets/cubyz/shaders/chunks/fillIndirectBuffer.comp b/assets/cubyz/shaders/chunks/fillIndirectBuffer.comp index c400402841..f325baea8a 100644 --- a/assets/cubyz/shaders/chunks/fillIndirectBuffer.comp +++ b/assets/cubyz/shaders/chunks/fillIndirectBuffer.comp @@ -34,6 +34,8 @@ layout(location = 5) uniform ivec3 playerPositionInteger; layout(location = 6) uniform float lodDistance; +layout(location = 7) uniform bool isDepth; + bool isVisible(int dir, ivec3 playerDist) { switch(dir) { case 0: // dirUp @@ -72,14 +74,31 @@ void main() { uint commandIndexEnd = commandIndex + 8; uint groupFaceOffset = 0; uint groupFaceCount = 0; - uint oldoldvisibilityState = chunks[chunkID].oldVisibilityState; + uint oldoldvisibilityState; + if(isDepth) { + oldoldvisibilityState = chunks[chunkID].oldVisibilityStateDepth; + } else { + oldoldvisibilityState = chunks[chunkID].oldVisibilityState; + } ivec3 playerDist = playerPositionInteger - chunks[chunkID].position.xyz; if(playerDist.x > 0) playerDist.x = max(0, playerDist.x - 32*chunks[chunkID].voxelSize); if(playerDist.y > 0) playerDist.y = max(0, playerDist.y - 32*chunks[chunkID].voxelSize); if(playerDist.z > 0) playerDist.z = max(0, playerDist.z - 32*chunks[chunkID].voxelSize); float playerDistSquare = dot(playerDist, playerDist); - if((onlyDrawPreviouslyInvisible && chunks[chunkID].oldVisibilityState == 0 && chunks[chunkID].visibilityState != 0) || (chunks[chunkID].oldVisibilityState != 0 && !onlyDrawPreviouslyInvisible)) { + uint oldVisibilityState; + if(isDepth) { + oldVisibilityState = chunks[chunkID].oldVisibilityStateDepth; + } else { + oldVisibilityState = chunks[chunkID].oldVisibilityState; + } + uint visibilityState; + if(isDepth) { + visibilityState = chunks[chunkID].visibilityStateDepth; + } else { + visibilityState = chunks[chunkID].visibilityState; + } + if((onlyDrawPreviouslyInvisible && oldVisibilityState == 0 && visibilityState != 0) || (oldVisibilityState != 0 && !onlyDrawPreviouslyInvisible)) { for(int i = 0; i < 14; i++) { if(playerDistSquare >= lodDistance*lodDistance && i == 7) break; uint faceCount = chunks[chunkID].faceCountsByNormalOpaque[i]; @@ -97,8 +116,15 @@ void main() { } } if(onlyDrawPreviouslyInvisible) { - chunks[chunkID].oldVisibilityState = chunks[chunkID].visibilityState; - chunks[chunkID].visibilityState = 0; + if(isDepth) { + chunks[chunkID].oldVisibilityStateDepth = chunks[chunkID].visibilityStateDepth; + chunks[chunkID].visibilityStateDepth = 0; + oldVisibilityState = chunks[chunkID].oldVisibilityStateDepth; + } else { + chunks[chunkID].oldVisibilityState = chunks[chunkID].visibilityState; + chunks[chunkID].visibilityState = 0; + oldVisibilityState = chunks[chunkID].oldVisibilityState; + } } if(groupFaceCount != 0) { commands[commandIndex] = addCommand(6*groupFaceCount, chunks[chunkID].vertexStartOpaque + 4*groupFaceOffset, chunkID); @@ -106,7 +132,7 @@ void main() { } for(; commandIndex < commandIndexEnd; commandIndex++) { - commands[commandIndex] = DrawElementsIndirectCommand(0, 0, 0, 0, oldoldvisibilityState << 1 | chunks[chunkID].oldVisibilityState); + commands[commandIndex] = DrawElementsIndirectCommand(0, 0, 0, 0, oldoldvisibilityState << 1 | oldVisibilityState); } } } diff --git a/assets/cubyz/shaders/chunks/occlusionTestFragment.frag b/assets/cubyz/shaders/chunks/occlusionTestFragment.frag index 8dd510224d..97b0fdcf59 100644 --- a/assets/cubyz/shaders/chunks/occlusionTestFragment.frag +++ b/assets/cubyz/shaders/chunks/occlusionTestFragment.frag @@ -6,6 +6,12 @@ layout(location = 0) flat in uint chunkID; #include "chunk_data.glsl" +layout(location = 4) uniform bool isDepth; + void main() { - chunks[chunkID].visibilityState = 1; + if(isDepth) { + chunks[chunkID].visibilityStateDepth = 1; + } else { + chunks[chunkID].visibilityState = 1; + } } diff --git a/assets/cubyz/shaders/chunks/occlusionTestVertex.vert b/assets/cubyz/shaders/chunks/occlusionTestVertex.vert index 6c64c7cd39..096c0d3c44 100644 --- a/assets/cubyz/shaders/chunks/occlusionTestVertex.vert +++ b/assets/cubyz/shaders/chunks/occlusionTestVertex.vert @@ -45,6 +45,7 @@ layout(location = 0) uniform mat4 projectionMatrix; layout(location = 1) uniform mat4 viewMatrix; layout(location = 2) uniform ivec3 playerPositionInteger; layout(location = 3) uniform vec3 playerPositionFraction; +layout(location = 4) uniform bool isDepth; void main() { uint chunkIDID = uint(gl_VertexID)/24u; @@ -53,7 +54,11 @@ void main() { vec3 modelPosition = vec3(chunks[chunkID].position.xyz - playerPositionInteger) - playerPositionFraction; vec3 margin = vec3(1); // Avoid near plane clipping when the player is at the edge of chunks if(all(lessThan(modelPosition + chunks[chunkID].minPos.xyz*chunks[chunkID].voxelSize, margin)) && all(greaterThan(modelPosition + chunks[chunkID].maxPos.xyz*chunks[chunkID].voxelSize, -margin))) { - chunks[chunkID].visibilityState = 1; + if(isDepth) { + chunks[chunkID].visibilityStateDepth = 1; + } else { + chunks[chunkID].visibilityState = 1; + } gl_Position = vec4(-2, -2, -2, 1); return; } diff --git a/assets/cubyz/shaders/chunks/transparent_fragment.frag b/assets/cubyz/shaders/chunks/transparent_fragment.frag index 51b47e6b20..a22292d19f 100644 --- a/assets/cubyz/shaders/chunks/transparent_fragment.frag +++ b/assets/cubyz/shaders/chunks/transparent_fragment.frag @@ -2,13 +2,15 @@ layout(location = 0) in vec3 mvVertexPos; layout(location = 1) in vec3 direction; -layout(location = 2) in vec3 light; -layout(location = 3) in vec2 uv; -layout(location = 4) flat in vec3 normal; -layout(location = 5) flat in int textureIndex; -layout(location = 6) flat in int isBackFace; -layout(location = 7) flat in float distanceForLodCheck; -layout(location = 8) flat in int opaqueInLod; +layout(location = 2) in vec3 sunLight; +layout(location = 3) in vec3 blockLight; +layout(location = 4) in vec2 uv; +layout(location = 5) in vec3 shadowPos; +layout(location = 6) flat in vec3 normal; +layout(location = 7) flat in int textureIndex; +layout(location = 8) flat in int isBackFace; +layout(location = 9) flat in float distanceForLodCheck; +layout(location = 10) flat in int opaqueInLod; layout(location = 0, index = 0) out vec4 fragColor; layout(location = 0, index = 1) out vec4 blendColor; @@ -25,8 +27,8 @@ layout(location = 4) uniform vec3 playerPositionFraction; layout(location = 5) uniform float reflectionMapSize; layout(location = 6) uniform float contrast; -layout(location = 8) uniform float zNear; -layout(location = 9) uniform float zFar; +layout(location = 10) uniform float zNear; +layout(location = 11) uniform float zFar; struct Fog { vec3 color; @@ -35,7 +37,7 @@ struct Fog { float fogHigher; }; -layout(location = 10) uniform Fog fog; +layout(location = 12) uniform Fog fog; layout(std430, binding = 1) buffer _animatedTexture { @@ -52,6 +54,10 @@ layout(std430, binding = 7) buffer _fogData FogData fogData[]; }; +vec3 square(vec3 x) { + return x*x; +} + float lightVariation(vec3 normal) { const vec3 directionalPart = vec3(0, contrast/2, contrast); const float baseLighting = 1 - contrast; @@ -139,6 +145,7 @@ void main() { float fogDistance = calculateFogDistance(dist, densityAdjustment, playerPositionFraction.z, normalize(direction).z, fogData[int(animatedTextureIndex)].fogDensity, 1e10, 1e10); float airFogDistance = calculateFogDistance(dist, densityAdjustment, playerPositionFraction.z, normalize(direction).z, fog.density, fog.fogLower - playerPositionInteger.z, fog.fogHigher - playerPositionInteger.z); vec3 fogColor = unpackColor(fogData[int(animatedTextureIndex)].fogColor); + vec3 light = min(sqrt(square(sunLight) + square(blockLight)), vec3(31))/31; vec3 pixelLight = max(light*normalVariation, texture(emissionSampler, textureCoords).r*4); vec4 textureColor = texture(textureSampler, textureCoords)*vec4(pixelLight, 1); diff --git a/assets/cubyz/shaders/include/chunk_data.glsl b/assets/cubyz/shaders/include/chunk_data.glsl index 996e30403f..ae01791ab1 100644 --- a/assets/cubyz/shaders/include/chunk_data.glsl +++ b/assets/cubyz/shaders/include/chunk_data.glsl @@ -10,6 +10,8 @@ struct ChunkData { uint vertexCountTransparent; uint visibilityState; uint oldVisibilityState; + uint visibilityStateDepth; + uint oldVisibilityStateDepth; }; layout(std430, binding = 6) buffer _chunks diff --git a/src/block_entity.zig b/src/block_entity.zig index 073bfee1ca..626d334e6b 100644 --- a/src/block_entity.zig +++ b/src/block_entity.zig @@ -496,7 +496,7 @@ pub const BlockEntityTypes = struct { // MARK: BlockEntityTypes defer c.glViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]); var finalFrameBuffer: graphics.FrameBuffer = undefined; - finalFrameBuffer.init(false, c.GL_NEAREST, c.GL_REPEAT); + finalFrameBuffer.init(true, false, c.GL_NEAREST, c.GL_REPEAT); finalFrameBuffer.updateSize(textureWidth, textureHeight, c.GL_RGBA8); finalFrameBuffer.bind(); finalFrameBuffer.clear(.{0, 0, 0, 0}); diff --git a/src/graphics.zig b/src/graphics.zig index f1bd7b85f8..2560394400 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -1669,14 +1669,16 @@ pub fn LargeBuffer(comptime Entry: type) type { // MARK: LargerBuffer pub const FrameBuffer = struct { // MARK: FrameBuffer frameBuffer: c_uint, + hasTexture: bool, texture: c_uint, hasDepthTexture: bool, depthTexture: c_uint, - pub fn init(self: *FrameBuffer, hasDepthTexture: bool, textureFilter: c_int, textureWrap: c_int) void { + pub fn init(self: *FrameBuffer, hasTexture: bool, hasDepthTexture: bool, textureFilter: c_int, textureWrap: c_int) void { self.* = FrameBuffer{ .frameBuffer = undefined, .texture = undefined, + .hasTexture = hasTexture, .depthTexture = undefined, .hasDepthTexture = hasDepthTexture, }; @@ -1689,16 +1691,21 @@ pub const FrameBuffer = struct { // MARK: FrameBuffer 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.glTexParameterfv(c.GL_TEXTURE_2D, c.GL_TEXTURE_BORDER_COLOR, @ptrCast(&[4]f32{1.0, 1.0, 1.0, 1.0})); 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); - 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); - + if (hasTexture) { + c.glGenTextures(1, &self.texture); + c.glBindTexture(c.GL_TEXTURE_2D, self.texture); + 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); + } else { + c.glDrawBuffer(c.GL_NONE); + c.glReadBuffer(c.GL_NONE); + } c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0); } @@ -1707,10 +1714,12 @@ pub const FrameBuffer = struct { // MARK: FrameBuffer if (self.hasDepthTexture) { c.glDeleteRenderbuffers(1, &self.depthTexture); } - c.glDeleteTextures(1, &self.texture); + if (self.hasTexture) { + c.glDeleteTextures(1, &self.texture); + } } - pub fn updateSize(self: *FrameBuffer, _width: u31, _height: u31, internalFormat: c_int) void { + pub fn updateSize(self: *FrameBuffer, _width: u31, _height: u31, internalFormat: ?c_int) void { const width = @max(_width, 1); const height = @max(_height, 1); c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.frameBuffer); @@ -1718,9 +1727,10 @@ pub const FrameBuffer = struct { // MARK: FrameBuffer 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); - c.glTexImage2D(c.GL_TEXTURE_2D, 0, internalFormat, width, height, 0, c.GL_RGBA, c.GL_UNSIGNED_BYTE, null); + if (self.hasTexture) { + c.glBindTexture(c.GL_TEXTURE_2D, self.texture); + c.glTexImage2D(c.GL_TEXTURE_2D, 0, internalFormat.?, width, height, 0, c.GL_RGBA, c.GL_UNSIGNED_BYTE, null); + } } pub fn clear(_: FrameBuffer, clearColor: Vec4f) void { @@ -1742,6 +1752,7 @@ pub const FrameBuffer = struct { // MARK: FrameBuffer } pub fn bindTexture(self: *const FrameBuffer, target: c_uint) void { + std.debug.assert(self.hasTexture); c.glActiveTexture(target); c.glBindTexture(c.GL_TEXTURE_2D, self.texture); } @@ -2223,7 +2234,7 @@ pub fn generateBlockTexture(blockType: u16) Texture { var frameBuffer: FrameBuffer = undefined; - frameBuffer.init(false, c.GL_NEAREST, c.GL_REPEAT); + frameBuffer.init(true, false, c.GL_NEAREST, c.GL_REPEAT); defer frameBuffer.deinit(); frameBuffer.updateSize(textureSize, textureSize, c.GL_RGBA16F); frameBuffer.bind(); @@ -2285,14 +2296,16 @@ pub fn generateBlockTexture(blockType: u16) Texture { .vertexCountTransparent = undefined, .visibilityState = 0, .oldVisibilityState = 0, + .visibilityStateDepth = 0, + .oldVisibilityStateDepth = 0, }}, &chunkAllocation); defer main.renderer.chunk_meshing.chunkBuffer.free(chunkAllocation); if (block.transparent()) { c.glBlendEquation(c.GL_FUNC_ADD); c.glBlendFunc(c.GL_ONE, c.GL_SRC1_COLOR); - main.renderer.chunk_meshing.bindTransparentShaderAndUniforms(projMatrix, .{1, 1, 1}, .{x, y, z}); + main.renderer.chunk_meshing.bindTransparentShaderAndUniforms(projMatrix, Mat4f.identity(), Mat4f.identity(), .{0, 0, 0}, .{1, 1, 1}, .{x, y, z}); } else { - main.renderer.chunk_meshing.bindShaderAndUniforms(projMatrix, .{1, 1, 1}, .{x, y, z}); + main.renderer.chunk_meshing.bindShaderAndUniforms(projMatrix, Mat4f.identity(), Mat4f.identity(), .{0, 0, 0}, .{1, 1, 1}, .{x, y, z}); } c.glUniform1f(uniforms.contrast, 0.25); c.glActiveTexture(c.GL_TEXTURE0); @@ -2307,7 +2320,7 @@ pub fn generateBlockTexture(blockType: u16) Texture { c.glDisable(c.GL_CULL_FACE); var finalFrameBuffer: FrameBuffer = undefined; - finalFrameBuffer.init(false, c.GL_NEAREST, c.GL_REPEAT); + finalFrameBuffer.init(true, false, c.GL_NEAREST, c.GL_REPEAT); finalFrameBuffer.updateSize(textureSize, textureSize, c.GL_RGBA8); finalFrameBuffer.bind(); const texture = Texture{.textureID = finalFrameBuffer.texture}; diff --git a/src/gui/windows/gpu_performance_measuring.zig b/src/gui/windows/gpu_performance_measuring.zig index 092d407670..229f00b3a0 100644 --- a/src/gui/windows/gpu_performance_measuring.zig +++ b/src/gui/windows/gpu_performance_measuring.zig @@ -14,6 +14,9 @@ const GuiComponent = gui.GuiComponent; pub const Samples = enum(u8) { screenbuffer_clear, + depth_framebuffer_clear, + depth_framebuffer_chunk_rendering_preparation, + depth_framebuffer_chunk_rendering, clear, skybox, animation, @@ -33,6 +36,9 @@ pub const Samples = enum(u8) { const names = [_][]const u8{ "Screenbuffer clear", + "Depth Framebuffer clear", + "Depth Framebuffer Chunk Rendering Preparation", + "Depth Framebuffer Chunk Rendering", "Clear", "Skybox", "Pre-processing Block Animations", diff --git a/src/renderer.zig b/src/renderer.zig index b194bd3e27..4dac82317e 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -62,6 +62,9 @@ pub var activeFrameBuffer: c_uint = 0; pub const reflectionCubeMapSize = 64; var reflectionCubeMap: graphics.CubeMapTexture = undefined; +pub const shadowMapResolution = 2048.0; +var depthFrameBuffer: graphics.FrameBuffer = undefined; + pub fn init() void { deferredRenderPassPipeline = graphics.Pipeline.init( "assets/cubyz/shaders/deferred_render_pass.vert", @@ -85,7 +88,7 @@ pub fn init() void { .{.depthTest = false, .depthWrite = false}, .{.attachments = &.{.noBlending}}, ); - worldFrameBuffer.init(true, c.GL_NEAREST, c.GL_CLAMP_TO_EDGE); + worldFrameBuffer.init(true, true, c.GL_NEAREST, c.GL_CLAMP_TO_EDGE); worldFrameBuffer.updateSize(Window.width, Window.height, c.GL_RGB16F); Bloom.init(); MeshSelection.init(); @@ -96,6 +99,8 @@ pub fn init() void { reflectionCubeMap = .init(); reflectionCubeMap.generate(reflectionCubeMapSize, reflectionCubeMapSize); initReflectionCubeMap(); + depthFrameBuffer.init(false, true, c.GL_NEAREST, c.GL_CLAMP_TO_BORDER); + depthFrameBuffer.updateSize(shadowMapResolution, shadowMapResolution, null); } pub fn deinit() void { @@ -109,12 +114,13 @@ pub fn deinit() void { mesh_storage.deinit(); chunk_meshing.deinit(); reflectionCubeMap.deinit(); + depthFrameBuffer.deinit(); } fn initReflectionCubeMap() void { c.glViewport(0, 0, reflectionCubeMapSize, reflectionCubeMapSize); var framebuffer: graphics.FrameBuffer = undefined; - framebuffer.init(false, c.GL_LINEAR, c.GL_CLAMP_TO_EDGE); + framebuffer.init(true, false, c.GL_LINEAR, c.GL_CLAMP_TO_EDGE); defer framebuffer.deinit(); framebuffer.bind(); fakeReflectionPipeline.bind(null); @@ -187,15 +193,68 @@ pub fn crosshairDirection(rotationMatrix: Mat4f, fovY: f32, width: u31, height: } pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPos: Vec3d) void { // MARK: renderWorld() + depthFrameBuffer.bind(); + c.glViewport(0, 0, shadowMapResolution, shadowMapResolution); + gpu_performance_measuring.startQuery(.depth_framebuffer_clear); + c.glClear(c.GL_DEPTH_BUFFER_BIT); + gpu_performance_measuring.stopQuery(); + + const lightOffset: Vec3f = Vec3f{@floatCast(@mod(playerPos[0], 1)), @floatCast(@mod(playerPos[1], 1)), @floatCast(@mod(playerPos[2], 1))}; + + const xRot = std.math.pi*0.8; + const zRot = std.math.pi*0.1; + + const lightDir = vec.rotateZ(vec.rotateX(Vec3f{0.0, 0.0, 1.0}, xRot), zRot); + const xR = lightDir[0]; + const yR = lightDir[1]; + const zR = lightDir[2]; + + const shadowMapSize = shadowMapResolution/16.0; + + const far = shadowMapSize; + const near = -shadowMapSize; + const lightProjection = Mat4f.scale(.{2.0/shadowMapSize, 2.0/shadowMapSize, 1.0}).mul(.{.rows = [4]Vec4f{ + Vec4f{1, 0, xR/zR, 0.0}, + Vec4f{0, 1, yR/zR, 0.0}, + Vec4f{0, 0, 1.0/(far - near), -near/(far - near)}, + Vec4f{0, 0, 0, 1}, + }}).mul(.scale(.{1, 1, -1})); + + const lightView: Mat4f = Mat4f.identity().mul(.translation(lightOffset)); + + const depthFrustum = Frustum.initFromView(lightProjection.mul(lightView), playerPos); + + game.camera.updateViewMatrix(); + + chunk_meshing.quadsDrawn = 0; + chunk_meshing.transparentQuadsDrawn = 0; + const depthMeshes = mesh_storage.updateAndGetRenderChunks(world.conn, &depthFrustum, playerPos, settings.renderDistance); + + gpu_performance_measuring.startQuery(.depth_framebuffer_chunk_rendering_preparation); + chunk_meshing.beginRender(); + + var depthChunkLists: [main.settings.highestSupportedLod + 1]main.ListManaged(u32) = @splat(main.ListManaged(u32).init(main.stackAllocator)); + defer for (depthChunkLists) |list| list.deinit(); + for (depthMeshes) |mesh| { + mesh.prepareRendering(&depthChunkLists); + } + gpu_performance_measuring.stopQuery(); + + // Rebind block textures back to their original slots + c.glActiveTexture(c.GL_TEXTURE0); + blocks.meshes.blockTextureArray.bind(); + + gpu_performance_measuring.startQuery(.depth_framebuffer_chunk_rendering); + chunk_meshing.drawChunksIndirect(&depthChunkLists, lightProjection, lightProjection, lightView, lightDir, ambientLight, playerPos, .depth); + gpu_performance_measuring.stopQuery(); + + chunk_meshing.endRender(); + worldFrameBuffer.bind(); c.glViewport(0, 0, lastWidth, lastHeight); gpu_performance_measuring.startQuery(.clear); worldFrameBuffer.clear(Vec4f{skyColor[0], skyColor[1], skyColor[2], 1}); gpu_performance_measuring.stopQuery(); - game.camera.updateViewMatrix(); - - // Uses FrustumCulling on the chunks. - const frustum = Frustum.init(Vec3f{0, 0, 0}, game.camera.viewMatrix, lastFov, lastWidth, lastHeight); const time: u32 = @intCast(main.timestamp().toMilliseconds() & std.math.maxInt(u32)); @@ -208,7 +267,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo gpu_performance_measuring.stopQuery(); // Update the uniforms. The uniforms are needed to render the replacement meshes. - chunk_meshing.bindShaderAndUniforms(game.projectionMatrix, ambientLight, playerPos); + chunk_meshing.bindShaderAndUniforms(game.projectionMatrix, lightProjection, lightView, lightDir, ambientLight, playerPos); c.glActiveTexture(c.GL_TEXTURE0); blocks.meshes.blockTextureArray.bind(); @@ -219,6 +278,14 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo c.glActiveTexture(c.GL_TEXTURE5); blocks.meshes.ditherTexture.bind(); reflectionCubeMap.bindTo(4); + depthFrameBuffer.bindDepthTexture(c.GL_TEXTURE6); + + chunk_meshing.quadsDrawn = 0; + chunk_meshing.transparentQuadsDrawn = 0; + + // Uses FrustumCulling on the chunks. + const frustum = Frustum.init(Vec3f{0, 0, 0}, game.camera.viewMatrix, lastFov, lastWidth, lastHeight); + game.camera.updateViewMatrix(); chunk_meshing.quadsDrawn = 0; chunk_meshing.transparentQuadsDrawn = 0; @@ -236,8 +303,10 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo mesh.prepareRendering(&chunkLists); } gpu_performance_measuring.stopQuery(); + chunk_meshing.beginRender(); + gpu_performance_measuring.startQuery(.chunk_rendering); - chunk_meshing.drawChunksIndirect(&chunkLists, game.projectionMatrix, ambientLight, playerPos, false); + chunk_meshing.drawChunksIndirect(&chunkLists, game.projectionMatrix, lightProjection, lightView, lightDir, ambientLight, playerPos, .regular); gpu_performance_measuring.stopQuery(); gpu_performance_measuring.startQuery(.entity_rendering); @@ -278,7 +347,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo } gpu_performance_measuring.stopQuery(); gpu_performance_measuring.startQuery(.transparent_rendering); - chunk_meshing.drawChunksIndirect(&chunkLists, game.projectionMatrix, ambientLight, playerPos, true); + chunk_meshing.drawChunksIndirect(&chunkLists, game.projectionMatrix, lightProjection, lightView, lightDir, ambientLight, playerPos, .transparent); gpu_performance_measuring.stopQuery(); } @@ -356,8 +425,8 @@ const Bloom = struct { // MARK: Bloom } = undefined; pub fn init() void { - buffer1.init(false, c.GL_LINEAR, c.GL_CLAMP_TO_EDGE); - buffer2.init(false, c.GL_LINEAR, c.GL_CLAMP_TO_EDGE); + buffer1.init(true, false, c.GL_LINEAR, c.GL_CLAMP_TO_EDGE); + buffer2.init(true, false, c.GL_LINEAR, c.GL_CLAMP_TO_EDGE); emptyBuffer = .init(); emptyBuffer.generate(graphics.Image.emptyImage); firstPassPipeline = graphics.Pipeline.init( @@ -633,7 +702,7 @@ pub const MenuBackGround = struct { defer updateViewport(Window.width, Window.height); var buffer: graphics.FrameBuffer = undefined; - buffer.init(true, c.GL_NEAREST, c.GL_REPEAT); + buffer.init(true, true, c.GL_NEAREST, c.GL_REPEAT); defer buffer.deinit(); buffer.updateSize(size, size, c.GL_RGBA8); @@ -840,6 +909,7 @@ pub const Frustum = struct { // MARK: Frustum norm: Vec3f, }; planes: [4]Plane, // Who cares about the near/far plane anyways? + isPerspective: bool, pub fn init(cameraPos: Vec3f, rotationMatrix: Mat4f, fovY: f32, width: u31, height: u31) Frustum { const invRotationMatrix = rotationMatrix.transpose(); @@ -855,6 +925,27 @@ pub const Frustum = struct { // MARK: Frustum self.planes[1] = Plane{.pos = cameraPos, .norm = vec.cross(cameraDir - cameraRight*@as(Vec3f, @splat(halfHSide)), cameraUp)}; // left self.planes[2] = Plane{.pos = cameraPos, .norm = vec.cross(cameraRight, cameraDir - cameraUp*@as(Vec3f, @splat(halfVSide)))}; // top self.planes[3] = Plane{.pos = cameraPos, .norm = vec.cross(cameraDir + cameraUp*@as(Vec3f, @splat(halfVSide)), cameraRight)}; // bottom + self.isPerspective = true; + return self; + } + + pub fn initFromView(view: Mat4f, playerPos: Vec3d) Frustum { + const invView = view.inverse(); + var planes: [4]Plane = undefined; + planes[0] = Plane{.pos = .{0, 0, 0}, .norm = .{1, 0, 0}}; + planes[1] = Plane{.pos = .{0, 0, 0}, .norm = .{0, 1, 0}}; + planes[2] = Plane{.pos = .{1, 0, 0}, .norm = .{-1, 0, 0}}; + planes[3] = Plane{.pos = .{0, 1, 0}, .norm = .{0, -1, 0}}; + + var self: Frustum = undefined; + inline for (0.., planes) |i, plane| { + const newPos = vec.xyz(invView.mulVec(vec.combine(plane.pos, 1.0))); + const normPos = plane.pos + plane.norm; + const newNormPos = vec.xyz(invView.mulVec(vec.combine(normPos, 1.0))); + const newNorm = vec.normalize(newNormPos - newPos); + self.planes[i] = Plane{.pos = @as(Vec3f, @floatCast(playerPos)) + newPos, .norm = newNorm}; + } + self.isPerspective = false; return self; } diff --git a/src/renderer/chunk_meshing.zig b/src/renderer/chunk_meshing.zig index 00250f1daf..36f1665546 100644 --- a/src/renderer/chunk_meshing.zig +++ b/src/renderer/chunk_meshing.zig @@ -28,6 +28,7 @@ const mesh_storage = @import("mesh_storage.zig"); var pipeline: graphics.Pipeline = undefined; var transparentPipeline: graphics.Pipeline = undefined; +var depthPipeline: graphics.Pipeline = undefined; const UniformStruct = struct { projectionMatrix: c_int, viewMatrix: c_int, @@ -44,9 +45,22 @@ const UniformStruct = struct { lodDistance: c_int, zNear: c_int, zFar: c_int, + lightProjectionMatrix: c_int, + lightViewMatrix: c_int, + lightDir: c_int, }; pub var uniforms: UniformStruct = undefined; pub var transparentUniforms: UniformStruct = undefined; +const DepthUniformStruct = struct { + projectionMatrix: c_int, + viewMatrix: c_int, + playerPositionInteger: c_int, + playerPositionFraction: c_int, + lodDistance: c_int, + zNear: c_int, + zFar: c_int, +}; +pub var depthUniforms: DepthUniformStruct = undefined; pub var commandPipeline: graphics.ComputePipeline = undefined; pub var commandUniforms: struct { chunkIDIndex: c_int, @@ -56,6 +70,7 @@ pub var commandUniforms: struct { playerPositionInteger: c_int, onlyDrawPreviouslyInvisible: c_int, lodDistance: c_int, + isDepth: c_int, } = undefined; pub var occlusionTestPipeline: graphics.Pipeline = undefined; pub var occlusionTestUniforms: struct { @@ -63,6 +78,7 @@ pub var occlusionTestUniforms: struct { viewMatrix: c_int, playerPositionInteger: c_int, playerPositionFraction: c_int, + isDepth: c_int, } = undefined; pub var vao: graphics.VertexArray = undefined; pub var faceBuffers: [settings.highestSupportedLod + 1]graphics.LargeBuffer(FaceData) = undefined; @@ -104,6 +120,17 @@ pub fn init() void { .alphaBlendOp = .add, }}}, ); + depthPipeline = graphics.Pipeline.init( + "assets/cubyz/shaders/chunks/chunk_depth_vertex.vert", + "assets/cubyz/shaders/chunks/chunk_depth_fragment.frag", + "", + &depthUniforms, + graphics.VertexArray.EmptyVertex, + &.{}, + .{.cullMode = .none}, + .{.depthTest = true, .depthWrite = true, .depthCompare = .lessOrEqual}, + .{.attachments = &.{.noBlending}}, + ); commandPipeline = graphics.ComputePipeline.init("assets/cubyz/shaders/chunks/fillIndirectBuffer.comp", "", &commandUniforms); occlusionTestPipeline = graphics.Pipeline.init( "assets/cubyz/shaders/chunks/occlusionTestVertex.vert", @@ -146,6 +173,7 @@ pub fn init() void { pub fn deinit() void { pipeline.deinit(); transparentPipeline.deinit(); + depthPipeline.deinit(); occlusionTestPipeline.deinit(); commandPipeline.deinit(); vao.deinit(); @@ -178,7 +206,7 @@ pub fn endRender() void { chunkIDBuffer.endRender(); } -fn bindCommonUniforms(locations: *UniformStruct, projMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d) void { +fn bindCommonUniforms(locations: *UniformStruct, projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, lightDir: Vec3f, ambient: Vec3f, playerPos: Vec3d) void { c.glUniformMatrix4fv(locations.projectionMatrix, 1, c.GL_TRUE, @ptrCast(&projMatrix)); c.glUniform1f(locations.reflectionMapSize, renderer.reflectionCubeMapSize); @@ -196,17 +224,21 @@ fn bindCommonUniforms(locations: *UniformStruct, projMatrix: Mat4f, ambient: Vec c.glUniform3i(locations.playerPositionInteger, @floor(playerPos[0]), @floor(playerPos[1]), @floor(playerPos[2])); c.glUniform3f(locations.playerPositionFraction, @floatCast(@mod(playerPos[0], 1)), @floatCast(@mod(playerPos[1], 1)), @floatCast(@mod(playerPos[2], 1))); + + c.glUniformMatrix4fv(locations.lightProjectionMatrix, 1, c.GL_TRUE, @ptrCast(&lightProjMatrix)); + c.glUniformMatrix4fv(locations.lightViewMatrix, 1, c.GL_TRUE, @ptrCast(&lightViewMatrix)); + c.glUniform3fv(locations.lightDir, 1, @ptrCast(&lightDir)); } -pub fn bindShaderAndUniforms(projMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d) void { +pub fn bindShaderAndUniforms(projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, lightDir: Vec3f, ambient: Vec3f, playerPos: Vec3d) void { pipeline.bind(null); - bindCommonUniforms(&uniforms, projMatrix, ambient, playerPos); + bindCommonUniforms(&uniforms, projMatrix, lightProjMatrix, lightViewMatrix, lightDir, ambient, playerPos); vao.bind(); } -pub fn bindTransparentShaderAndUniforms(projMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d) void { +pub fn bindTransparentShaderAndUniforms(projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, lightDir: Vec3f, ambient: Vec3f, playerPos: Vec3d) void { transparentPipeline.bind(null); c.glUniform3fv(transparentUniforms.@"fog.color", 1, @ptrCast(&game.world.?.dayTime.fog.fogColor)); @@ -214,7 +246,25 @@ pub fn bindTransparentShaderAndUniforms(projMatrix: Mat4f, ambient: Vec3f, playe c.glUniform1f(transparentUniforms.@"fog.fogLower", game.world.?.dayTime.fog.fogLower); c.glUniform1f(transparentUniforms.@"fog.fogHigher", game.world.?.dayTime.fog.fogHigher); - bindCommonUniforms(&transparentUniforms, projMatrix, ambient, playerPos); + bindCommonUniforms(&transparentUniforms, projMatrix, lightProjMatrix, lightViewMatrix, lightDir, ambient, playerPos); + + vao.bind(); +} + +pub fn bindDepthShaderAndUniforms(projMatrix: Mat4f, viewMatrix: Mat4f, _: Vec3f, cameraPos: Vec3d) void { + depthPipeline.bind(null); + + c.glUniformMatrix4fv(depthUniforms.projectionMatrix, 1, c.GL_TRUE, @ptrCast(&projMatrix)); + + c.glUniform1f(depthUniforms.lodDistance, main.settings.@"lod0.5Distance"); + + c.glUniformMatrix4fv(depthUniforms.viewMatrix, 1, c.GL_TRUE, @ptrCast(&viewMatrix)); + + c.glUniform1f(depthUniforms.zNear, renderer.zNear); + c.glUniform1f(depthUniforms.zFar, renderer.zFar); + + c.glUniform3i(depthUniforms.playerPositionInteger, @floor(cameraPos[0]), @floor(cameraPos[1]), @floor(cameraPos[2])); + c.glUniform3f(depthUniforms.playerPositionFraction, @floatCast(@mod(cameraPos[0], 1)), @floatCast(@mod(cameraPos[1], 1)), @floatCast(@mod(cameraPos[2], 1))); vao.bind(); } @@ -224,17 +274,23 @@ fn bindBuffers(lod: usize) void { lightBuffers[lod].ssbo.bind(lightBuffers[lod].binding); } -pub fn drawChunksIndirect(chunkIds: *const [main.settings.highestSupportedLod + 1]main.ListManaged(u32), projMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d, transparent: bool) void { +pub const DrawMode = enum { + regular, + transparent, + depth, +}; + +pub fn drawChunksIndirect(chunkIds: *const [main.settings.highestSupportedLod + 1]main.ListManaged(u32), projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, lightDir: Vec3f, ambient: Vec3f, playerPos: Vec3d, mode: DrawMode) void { for (0..chunkIds.len) |i| { - const lod = if (transparent) main.settings.highestSupportedLod - i else i; + const lod = if (mode == .transparent) main.settings.highestSupportedLod - i else i; bindBuffers(lod); - drawChunksOfLod(chunkIds[lod].items, projMatrix, ambient, playerPos, transparent); + drawChunksOfLod(chunkIds[lod].items, projMatrix, lightProjMatrix, lightViewMatrix, lightDir, ambient, playerPos, mode); } } -fn drawChunksOfLod(chunkIDs: []const u32, projMatrix: Mat4f, ambient: Vec3f, playerPos: Vec3d, transparent: bool) void { +fn drawChunksOfLod(chunkIDs: []const u32, projMatrix: Mat4f, lightProjMatrix: Mat4f, lightViewMatrix: Mat4f, lightDir: Vec3f, ambient: Vec3f, playerPos: Vec3d, mode: DrawMode) void { if (chunkIDs.len == 0) return; - const drawCallsEstimate: u31 = @intCast(if (transparent) chunkIDs.len else chunkIDs.len*8); + const drawCallsEstimate: u31 = @intCast(if (mode == .transparent) chunkIDs.len else chunkIDs.len*8); var chunkIDAllocation: main.graphics.SubAllocation = .{.start = 0, .len = 0}; chunkIDBuffer.uploadData(chunkIDs, &chunkIDAllocation); defer chunkIDBuffer.free(chunkIDAllocation); @@ -245,17 +301,18 @@ fn drawChunksOfLod(chunkIDs: []const u32, projMatrix: Mat4f, ambient: Vec3f, pla c.glUniform1ui(commandUniforms.chunkIDIndex, chunkIDAllocation.start); c.glUniform1ui(commandUniforms.commandIndexStart, allocation.start); c.glUniform1ui(commandUniforms.size, @intCast(chunkIDs.len)); - c.glUniform1i(commandUniforms.isTransparent, @intFromBool(transparent)); + c.glUniform1i(commandUniforms.isTransparent, @intFromBool(mode == .transparent)); + c.glUniform1i(commandUniforms.isDepth, @intFromBool(mode == .depth)); c.glUniform3i(commandUniforms.playerPositionInteger, @floor(playerPos[0]), @floor(playerPos[1]), @floor(playerPos[2])); - if (!transparent) { + if (mode != .transparent) { 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); - if (transparent) { - bindTransparentShaderAndUniforms(projMatrix, ambient, playerPos); + if (mode == .depth) { + bindDepthShaderAndUniforms(lightProjMatrix, lightViewMatrix, lightDir, playerPos); } else { - bindShaderAndUniforms(projMatrix, ambient, playerPos); + bindShaderAndUniforms(projMatrix, lightProjMatrix, lightViewMatrix, lightDir, ambient, playerPos); } 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); @@ -266,7 +323,8 @@ fn drawChunksOfLod(chunkIDs: []const u32, projMatrix: Mat4f, ambient: Vec3f, pla c.glUniform3i(occlusionTestUniforms.playerPositionInteger, @floor(playerPos[0]), @floor(playerPos[1]), @floor(playerPos[2])); c.glUniform3f(occlusionTestUniforms.playerPositionFraction, @floatCast(@mod(playerPos[0], 1)), @floatCast(@mod(playerPos[1], 1)), @floatCast(@mod(playerPos[2], 1))); c.glUniformMatrix4fv(occlusionTestUniforms.projectionMatrix, 1, c.GL_TRUE, @ptrCast(&projMatrix)); - c.glUniformMatrix4fv(occlusionTestUniforms.viewMatrix, 1, c.GL_TRUE, @ptrCast(&game.camera.viewMatrix)); + c.glUniformMatrix4fv(occlusionTestUniforms.viewMatrix, 1, c.GL_TRUE, @ptrCast(&if (mode == .depth) lightViewMatrix else game.camera.viewMatrix)); + c.glUniform1i(occlusionTestUniforms.isDepth, @intFromBool(mode == .depth)); vao.bind(); c.glDrawElementsBaseVertex(c.GL_TRIANGLES, @intCast(6*6*chunkIDs.len), c.GL_UNSIGNED_INT, null, chunkIDAllocation.start*24); c.glMemoryBarrier(c.GL_SHADER_STORAGE_BARRIER_BIT); @@ -277,10 +335,10 @@ fn drawChunksOfLod(chunkIDs: []const u32, projMatrix: Mat4f, ambient: Vec3f, pla 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); - if (transparent) { - bindTransparentShaderAndUniforms(projMatrix, ambient, playerPos); - } else { - bindShaderAndUniforms(projMatrix, ambient, playerPos); + switch (mode) { + .regular => bindShaderAndUniforms(projMatrix, lightProjMatrix, lightViewMatrix, lightDir, ambient, playerPos), + .transparent => bindTransparentShaderAndUniforms(projMatrix, lightProjMatrix, lightViewMatrix, lightDir, ambient, playerPos), + .depth => bindDepthShaderAndUniforms(lightProjMatrix, lightViewMatrix, lightDir, playerPos), } 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); @@ -319,6 +377,8 @@ pub const ChunkData = extern struct { vertexCountTransparent: u32, visibilityState: u32, oldVisibilityState: u32, + visibilityStateDepth: u32, + oldVisibilityStateDepth: u32, }; pub const IndirectData = extern struct { @@ -1454,6 +1514,8 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh .max = self.max, .visibilityState = 0, .oldVisibilityState = 0, + .visibilityStateDepth = 0, + .oldVisibilityStateDepth = 0, }}, &self.chunkAllocation); } diff --git a/src/renderer/mesh_storage.zig b/src/renderer/mesh_storage.zig index 30f562ffb4..2a5d91ba29 100644 --- a/src/renderer/mesh_storage.zig +++ b/src/renderer/mesh_storage.zig @@ -601,7 +601,6 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, frustum: *co while (searchList.popFront()) |node| { std.debug.assert(node.finishedMeshing); std.debug.assert(node.active); - if (!node.active) continue; node.active = false; const pos = node.pos; @@ -610,6 +609,7 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, frustum: *co const chunkSizeVector: Vec3i = @splat(chunk.chunkSize*pos.voxelSize); + // TODO: This seems like a good place for a comment, could someone please add one? if (pos.voxelSize == @as(i32, 1) << settings.highestLod) { for (chunk.Neighbor.iterable) |neighbor| { const component = neighbor.extractDirectionComponent(relPos); @@ -650,7 +650,8 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, frustum: *co if (dz == 1) nextPos.wz ^= lowerLodBit; const node2 = getNodePointer(nextPos); const relNextPos: Vec3d = @as(Vec3d, @floatFromInt(Vec3i{nextPos.wx, nextPos.wy, nextPos.wz})) - playerPos; - if (!frustum.testAAB(@floatCast(relNextPos), @floatFromInt(chunkSizeVector))) continue; + if (frustum.isPerspective and !frustum.testAAB(@floatCast(relNextPos), @splat(@floatFromInt(chunk.chunkSize*nextPos.voxelSize)))) + continue; std.debug.assert(node2.finishedMeshing); node2.active = true; node2.rendered = true; diff --git a/src/vec.zig b/src/vec.zig index a62bc52fc7..c14fc31ee9 100644 --- a/src/vec.zig +++ b/src/vec.zig @@ -244,7 +244,7 @@ pub const Mat4f = struct { // MARK: Mat4f return @as(T, @bitCast(v0u & v1u)); // andps } - // copied from zmath library (MIT Liscence) : https://github.com/zig-gamedev/zmath/blob/3a5955b2b72cd081563fbb084eff05bffd1e3fbb/src/root.zig#L2726 + // copied from zmath library (MIT License) : https://github.com/zig-gamedev/zmath/blob/3a5955b2b72cd081563fbb084eff05bffd1e3fbb/src/root.zig#L2726 pub fn rotationQuat(quat: Quat) Mat4f { const f32x4_mask3: Vec4f = Vec4f{ @as(f32, @bitCast(@as(u32, 0xffff_ffff))), @@ -296,6 +296,111 @@ pub const Mat4f = struct { // MARK: Mat4f return m; } + // copied from zmath library (MIT License) : https://github.com/zig-gamedev/zmath/blob/3a5955b2b72cd081563fbb084eff05bffd1e3fbb/src/root.zig#L2536 + pub fn inverse(m: Mat4f) Mat4f { + const mt = transpose(m); + var v0: [4]Vec4f = undefined; + var v1: [4]Vec4f = undefined; + + v0[0] = swizzle(mt.rows[2], .x, .x, .y, .y); + v1[0] = swizzle(mt.rows[3], .z, .w, .z, .w); + v0[1] = swizzle(mt.rows[0], .x, .x, .y, .y); + v1[1] = swizzle(mt.rows[1], .z, .w, .z, .w); + v0[2] = @shuffle(f32, mt.rows[2], mt.rows[0], [4]i32{ 0, 2, ~@as(i32, 0), ~@as(i32, 2) }); + v1[2] = @shuffle(f32, mt.rows[3], mt.rows[1], [4]i32{ 1, 3, ~@as(i32, 1), ~@as(i32, 3) }); + + var d0 = v0[0] * v1[0]; + var d1 = v0[1] * v1[1]; + var d2 = v0[2] * v1[2]; + + v0[0] = swizzle(mt.rows[2], .z, .w, .z, .w); + v1[0] = swizzle(mt.rows[3], .x, .x, .y, .y); + v0[1] = swizzle(mt.rows[0], .z, .w, .z, .w); + v1[1] = swizzle(mt.rows[1], .x, .x, .y, .y); + v0[2] = @shuffle(f32, mt.rows[2], mt.rows[0], [4]i32{ 1, 3, ~@as(i32, 1), ~@as(i32, 3) }); + v1[2] = @shuffle(f32, mt.rows[3], mt.rows[1], [4]i32{ 0, 2, ~@as(i32, 0), ~@as(i32, 2) }); + + d0 = -v0[0] * v1[0] + d0; + d1 = -v0[1] * v1[1] + d1; + d2 = -v0[2] * v1[2] + d2; + + v0[0] = swizzle(mt.rows[1], .y, .z, .x, .y); + v1[0] = @shuffle(f32, d0, d2, [4]i32{ ~@as(i32, 1), 1, 3, 0 }); + v0[1] = swizzle(mt.rows[0], .z, .x, .y, .x); + v1[1] = @shuffle(f32, d0, d2, [4]i32{ 3, ~@as(i32, 1), 1, 2 }); + v0[2] = swizzle(mt.rows[3], .y, .z, .x, .y); + v1[2] = @shuffle(f32, d1, d2, [4]i32{ ~@as(i32, 3), 1, 3, 0 }); + v0[3] = swizzle(mt.rows[2], .z, .x, .y, .x); + v1[3] = @shuffle(f32, d1, d2, [4]i32{ 3, ~@as(i32, 3), 1, 2 }); + + var c0 = v0[0] * v1[0]; + var c2 = v0[1] * v1[1]; + var c4 = v0[2] * v1[2]; + var c6 = v0[3] * v1[3]; + + v0[0] = swizzle(mt.rows[1], .z, .w, .y, .z); + v1[0] = @shuffle(f32, d0, d2, [4]i32{ 3, 0, 1, ~@as(i32, 0) }); + v0[1] = swizzle(mt.rows[0], .w, .z, .w, .y); + v1[1] = @shuffle(f32, d0, d2, [4]i32{ 2, 1, ~@as(i32, 0), 0 }); + v0[2] = swizzle(mt.rows[3], .z, .w, .y, .z); + v1[2] = @shuffle(f32, d1, d2, [4]i32{ 3, 0, 1, ~@as(i32, 2) }); + v0[3] = swizzle(mt.rows[2], .w, .z, .w, .y); + v1[3] = @shuffle(f32, d1, d2, [4]i32{ 2, 1, ~@as(i32, 2), 0 }); + + c0 = -v0[0] * v1[0] + c0; + c2 = -v0[1] * v1[1] + c2; + c4 = -v0[2] * v1[2] + c4; + c6 = -v0[3] * v1[3] + c6; + + v0[0] = swizzle(mt.rows[1], .w, .x, .w, .x); + v1[0] = @shuffle(f32, d0, d2, [4]i32{ 2, ~@as(i32, 1), ~@as(i32, 0), 2 }); + v0[1] = swizzle(mt.rows[0], .y, .w, .x, .z); + v1[1] = @shuffle(f32, d0, d2, [4]i32{ ~@as(i32, 1), 0, 3, ~@as(i32, 0) }); + v0[2] = swizzle(mt.rows[3], .w, .x, .w, .x); + v1[2] = @shuffle(f32, d1, d2, [4]i32{ 2, ~@as(i32, 3), ~@as(i32, 2), 2 }); + v0[3] = swizzle(mt.rows[2], .y, .w, .x, .z); + v1[3] = @shuffle(f32, d1, d2, [4]i32{ ~@as(i32, 3), 0, 3, ~@as(i32, 2) }); + + const c1 = -v0[0] * v1[0] + c0; + const c3 = v0[1] * v1[1] + c2; + const c5 = -v0[2] * v1[2] + c4; + const c7 = v0[3] * v1[3] + c6; + + c0 = v0[0] * v1[0] + c0; + c2 = -v0[1] * v1[1] + c2; + c4 = v0[2] * v1[2] + c4; + c6 = -v0[3] * v1[3] + c6; + + var mr = Mat4f{ + .rows = [4]Vec4f{ + Vec4f{c0[0], c1[1], c0[2], c1[3]}, + Vec4f{c2[0], c3[1], c2[2], c3[3]}, + Vec4f{c4[0], c5[1], c4[2], c5[3]}, + Vec4f{c6[0], c7[1], c6[2], c7[3]}, + } + }; + + const det = dot(mr.rows[0], mt.rows[0]); + + if (std.math.approxEqAbs(f64, det, 0.0, std.math.floatEps(f64))) { + return Mat4f{ + .rows = [4]Vec4f{ + Vec4f{0.0, 0.0, 0.0, 0.0}, + Vec4f{0.0, 0.0, 0.0, 0.0}, + Vec4f{0.0, 0.0, 0.0, 0.0}, + Vec4f{0.0, 0.0, 0.0, 0.0}, + } + }; + } + + const scaleFactor = @as(Vec4f, @splat(1.0 / det)); + mr.rows[0] *= scaleFactor; + mr.rows[1] *= scaleFactor; + mr.rows[2] *= scaleFactor; + mr.rows[3] *= scaleFactor; + return mr; + } + pub fn perspective(fovY: f32, aspect: f32, near: f32, far: f32) Mat4f { // zig fmt: off const tanY = std.math.tan(fovY*0.5); const tanX = aspect*tanY;