Skip to content

Commit

Permalink
Introduce LOD0.5 which renders all leaves opaque.
Browse files Browse the repository at this point in the history
This allows removing a bunch of internal faces, improving frametime greatly when looking at a distant forest.

fixes #579

makes #823 more important, since this doubled the cost
  • Loading branch information
IntegratedQuantum committed Dec 16, 2024
1 parent 3f0f2f7 commit 3bdc1dd
Show file tree
Hide file tree
Showing 13 changed files with 159 additions and 58 deletions.
8 changes: 8 additions & 0 deletions assets/cubyz/shaders/chunks/chunk_fragment.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ flat in vec3 normal;
flat in int textureIndex;
flat in int isBackFace;
flat in int ditherSeed;
flat in float distanceForLodCheck;
flat in int opaqueInLod;

uniform sampler2DArray texture_sampler;
uniform sampler2DArray emissionSampler;
uniform sampler2DArray reflectivityAndAbsorptionSampler;
uniform samplerCube reflectionMap;
uniform float reflectionMapSize;
uniform float contrast;
uniform float lodDistance;

layout(location = 0) out vec4 fragColor;

Expand Down Expand Up @@ -53,6 +56,11 @@ ivec2 random1to2(int v) {
}

bool passDitherTest(float alpha) {
if(opaqueInLod != 0) {
if(distanceForLodCheck > lodDistance) return true;
float factor = max(0, distanceForLodCheck - (lodDistance - 32.0))/32.0;
alpha = alpha*(1 - factor) + factor;
}
ivec2 screenPos = ivec2(gl_FragCoord.xy);
screenPos += random1to2(ditherSeed);
screenPos &= 3;
Expand Down
7 changes: 6 additions & 1 deletion assets/cubyz/shaders/chunks/chunk_vertex.vs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ flat out vec3 normal;
flat out int textureIndex;
flat out int isBackFace;
flat out int ditherSeed;
flat out float distanceForLodCheck;
flat out int opaqueInLod;

uniform vec3 ambientLight;
uniform mat4 projectionMatrix;
Expand All @@ -29,6 +31,7 @@ struct QuadInfo {
vec3 corners[4];
vec2 cornerUV[4];
uint textureSlot;
int opaqueInLod;
};

layout(std430, binding = 4) buffer _quads
Expand All @@ -47,7 +50,7 @@ struct ChunkData {
vec4 maxPos;
int voxelSize;
uint vertexStartOpaque;
uint faceCountsByNormalOpaque[7];
uint faceCountsByNormalOpaque[14];
uint lightStartOpaque;
uint vertexStartTransparent;
uint vertexCountTransparent;
Expand Down Expand Up @@ -109,5 +112,7 @@ void main() {
vec4 mvPos = viewMatrix*vec4(position, 1);
gl_Position = projectionMatrix*mvPos;
mvVertexPos = mvPos.xyz;
distanceForLodCheck = length(mvPos.xyz) + voxelSize;
uv = quads[quadIndex].cornerUV[vertexID]*voxelSize;
opaqueInLod = quads[quadIndex].opaqueInLod;
}
33 changes: 21 additions & 12 deletions assets/cubyz/shaders/chunks/fillIndirectBuffer.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct ChunkData {
vec4 maxPos;
int voxelSize;
uint vertexStartOpaque;
uint faceCountsByNormalOpaque[7];
uint faceCountsByNormalOpaque[14];
uint lightStartOpaque;
uint vertexStartTransparent;
uint vertexCountTransparent;
Expand Down Expand Up @@ -48,20 +48,22 @@ uniform bool isTransparent;
uniform bool onlyDrawPreviouslyInvisible;
uniform ivec3 playerPositionInteger;

bool isVisible(int dir, ivec3 relativePlayerPos, int voxelSize) {
uniform float lodDistance;

bool isVisible(int dir, ivec3 playerDist) {
switch(dir) {
case 0: // dirUp
return relativePlayerPos.z >= 0;
return playerDist.z >= 0;
case 1: // dirDown
return relativePlayerPos.z < 32*voxelSize;
return playerDist.z <= 0;
case 2: // dirPosX
return relativePlayerPos.x >= 0;
return playerDist.x >= 0;
case 3: // dirNegX
return relativePlayerPos.x < 32*voxelSize;
return playerDist.x <= 0;
case 4: // dirPosY
return relativePlayerPos.y >= 0;
return playerDist.y >= 0;
case 5: // dirNegY
return relativePlayerPos.y < 32*voxelSize;
return playerDist.y <= 0;
}
return true;
}
Expand All @@ -82,15 +84,22 @@ void main() {
}
chunks[chunkID].visibilityState = 0;
} else {
uint commandIndex = commandIndexStart + gl_GlobalInvocationID.x*4;
uint commandIndexEnd = commandIndex + 4;
uint commandIndex = commandIndexStart + gl_GlobalInvocationID.x*8;
uint commandIndexEnd = commandIndex + 8;
uint groupFaceOffset = 0;
uint groupFaceCount = 0;
uint 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)) {
for(int i = 0; i < 7; i++) {
for(int i = 0; i < 14; i++) {
if(playerDistSquare >= lodDistance*lodDistance && i == 7) break;
uint faceCount = chunks[chunkID].faceCountsByNormalOpaque[i];
if(isVisible(i, playerPositionInteger - chunks[chunkID].position.xyz, chunks[chunkID].voxelSize) || faceCount == 0) {
if(isVisible(i%7, playerDist) || faceCount == 0) {
groupFaceCount += faceCount;
} else {
if(groupFaceCount != 0) {
Expand Down
2 changes: 1 addition & 1 deletion assets/cubyz/shaders/chunks/occlusionTestFragment.fs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ struct ChunkData {
vec4 maxPos;
int voxelSize;
uint vertexStartOpaque;
uint faceCountsByNormalOpaque[7];
uint faceCountsByNormalOpaque[14];
uint lightStartOpaque;
uint vertexStartTransparent;
uint vertexCountTransparent;
Expand Down
2 changes: 1 addition & 1 deletion assets/cubyz/shaders/chunks/occlusionTestVertex.vs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ struct ChunkData {
vec4 maxPos;
int voxelSize;
uint vertexStartOpaque;
uint faceCountsByNormalOpaque[7];
uint faceCountsByNormalOpaque[14];
uint lightStartOpaque;
uint vertexStartTransparent;
uint vertexCountTransparent;
Expand Down
2 changes: 2 additions & 0 deletions assets/cubyz/shaders/chunks/transparent_fragment.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ flat in vec3 normal;
flat in int textureIndex;
flat in int isBackFace;
flat in int ditherSeed;
flat in float distanceForLodCheck;
flat in int opaqueInLod;

uniform sampler2DArray texture_sampler;
uniform sampler2DArray emissionSampler;
Expand Down
1 change: 1 addition & 0 deletions assets/cubyz/shaders/entity_vertex.vs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct QuadInfo {
vec3 corners[4];
vec2 cornerUV[4];
uint textureSlot;
int opaqueInLod;
};

layout(std430, binding = 11) buffer _quads
Expand Down
1 change: 1 addition & 0 deletions assets/cubyz/shaders/item_drop.vs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ struct QuadInfo {
vec3 corners[4];
vec2 cornerUV[4];
uint textureSlot;
int opaqueInLod;
};

layout(std430, binding = 4) buffer _quads
Expand Down
25 changes: 24 additions & 1 deletion src/graphics.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1674,7 +1674,7 @@ pub const TextureArray = struct { // MARK: TextureArray
}

// Calculate the mipmap levels:
for(lodBuffer, 0..) |_, _lod| {
for(0..lodBuffer.len) |_lod| {
const lod: u5 = @intCast(_lod);
const curWidth = maxWidth >> lod;
const curHeight = maxHeight >> lod;
Expand All @@ -1693,6 +1693,29 @@ pub const TextureArray = struct { // MARK: TextureArray
}
}
}
}
// Give the correct color to alpha 0 pixels, to avoid dark pixels:
for(1..lodBuffer.len) |_lod| {
const lod: u5 = @intCast(lodBuffer.len - 1 - _lod);
const curWidth = maxWidth >> lod;
const curHeight = maxHeight >> lod;
for(0..curWidth) |x| {
for(0..curHeight) |y| {
const index = x + y*curWidth;
const index2 = x/2 + y/2*curWidth/2;
if(lodBuffer[lod][index].a == 0) {
lodBuffer[lod][index].r = lodBuffer[lod + 1][index2].r;
lodBuffer[lod][index].g = lodBuffer[lod + 1][index2].g;
lodBuffer[lod][index].b = lodBuffer[lod + 1][index2].b;
}
}
}
}
// Upload:
for(0..lodBuffer.len) |_lod| {
const lod: u5 = @intCast(lodBuffer.len - 1 - _lod);
const curWidth = maxWidth >> lod;
const curHeight = maxHeight >> lod;
c.glTexSubImage3D(c.GL_TEXTURE_2D_ARRAY, lod, 0, 0, @intCast(i), curWidth, curHeight, 1, c.GL_RGBA, c.GL_UNSIGNED_BYTE, lodBuffer[lod].ptr);
}
}
Expand Down
9 changes: 7 additions & 2 deletions src/gui/windows/graphics.zig
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,12 @@ fn fovFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []const
return std.fmt.allocPrint(allocator.allocator, "#ffffffField Of View: {d:.0}°", .{value}) catch unreachable;
}

fn LODFactorCallback(newValue: u16) void {
settings.LODFactor = @as(f32, @floatFromInt(newValue + 1))/2;
fn lodDistanceFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []const u8 {
return std.fmt.allocPrint(allocator.allocator, "#ffffffOpaque leaves distance: {d:.0}", .{@round(value)}) catch unreachable;
}

fn lodDistanceCallback(newValue: f32) void {
settings.@"lod0.5Distance" = @round(newValue);
settings.save();
}

Expand Down Expand Up @@ -103,6 +107,7 @@ pub fn onOpen() void {
list.add(ContinuousSlider.init(.{0, 0}, 128, 10.0, 154.0, @floatFromInt(settings.fpsCap orelse 154), &fpsCapCallback, &fpsCapFormatter));
list.add(DiscreteSlider.init(.{0, 0}, 128, "#ffffffRender Distance: ", "{}", &renderDistances, @min(@max(settings.renderDistance, renderDistances[0]) - renderDistances[0], renderDistances.len - 1), &renderDistanceCallback));
list.add(DiscreteSlider.init(.{0, 0}, 128, "#ffffffLeaves Quality (TODO: requires reload): ", "{}", &leavesQualities, settings.leavesQuality - leavesQualities[0], &leavesQualityCallback));
list.add(ContinuousSlider.init(.{0, 0}, 128, 50.0, 400.0, settings.@"lod0.5Distance", &lodDistanceCallback, &lodDistanceFormatter));
list.add(ContinuousSlider.init(.{0, 0}, 128, 40.0, 120.0, settings.fov, &fovCallback, &fovFormatter));
list.add(CheckBox.init(.{0, 0}, 128, "Bloom", settings.bloom, &bloomCallback));
list.add(CheckBox.init(.{0, 0}, 128, "Vertical Synchronization", settings.vsync, &vsyncCallback));
Expand Down
5 changes: 4 additions & 1 deletion src/models.zig
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub const QuadInfo = extern struct {
corners: [4]Vec3f,
cornerUV: [4]Vec2f,
textureSlot: u32,
opaqueInLod: u32 = 0,
};

const ExtraQuadInfo = struct {
Expand Down Expand Up @@ -415,7 +416,8 @@ pub var models: main.List(Model) = undefined;

var quadDeduplication: std.AutoHashMap([@sizeOf(QuadInfo)]u8, u16) = undefined;

fn addQuad(info: QuadInfo) error{Degenerate}!u16 {
fn addQuad(info_: QuadInfo) error{Degenerate}!u16 {
var info = info_;
if(quadDeduplication.get(std.mem.toBytes(info))) |id| {
return id;
}
Expand All @@ -428,6 +430,7 @@ fn addQuad(info: QuadInfo) error{Degenerate}!u16 {
}
if(cornerEqualities >= 2) return error.Degenerate; // One corner equality is fine, since then the quad degenerates to a triangle, which has a non-zero area.
const index: u16 = @intCast(quads.items.len);
info.opaqueInLod = @intFromBool(Model.getFaceNeighbor(&info) != null);
quads.append(info);
quadDeduplication.put(std.mem.toBytes(info), index) catch unreachable;

Expand Down
Loading

0 comments on commit 3bdc1dd

Please sign in to comment.