feat(rendering): separate transparent blocks into discard and blend modes

This commit is contained in:
2026-06-05 13:09:49 +08:00
parent aa3805448f
commit 32b703a0f4
18 changed files with 176 additions and 91 deletions

View File

@@ -1,5 +1,7 @@
id = 0
is_blend = false
is_cross_plane = false
is_discard = true
is_gas = true
is_liquid = false
is_passable = true

View File

@@ -1,5 +1,7 @@
id = 2
is_blend = false
is_cross_plane = false
is_discard = false
is_gas = false
is_liquid = false
is_passable = false

View File

@@ -1,5 +1,7 @@
id = 9
is_blend = false
is_cross_plane = true
is_discard = true
is_gas = false
is_liquid = false
is_passable = true

View File

@@ -1,5 +1,7 @@
id = 1
is_blend = false
is_cross_plane = false
is_discard = false
is_gas = false
is_liquid = false
is_passable = false

View File

@@ -1,5 +1,7 @@
id = 6
is_blend = false
is_cross_plane = false
is_discard = true
is_gas = false
is_liquid = false
is_passable = false

View File

@@ -1,5 +1,7 @@
id = 5
is_blend = false
is_cross_plane = false
is_discard = false
is_gas = false
is_liquid = false
is_passable = false

View File

@@ -1,5 +1,7 @@
id = 4
is_blend = false
is_cross_plane = false
is_discard = false
is_gas = false
is_liquid = false
is_passable = false

View File

@@ -1,5 +1,7 @@
id = 8
is_blend = false
is_cross_plane = false
is_discard = false
is_gas = false
is_liquid = false
is_passable = false

View File

@@ -1,5 +1,7 @@
id = 3
is_blend = false
is_cross_plane = false
is_discard = false
is_gas = false
is_liquid = false
is_passable = false

View File

@@ -5,3 +5,5 @@ is_gas = false
is_passable = false
is_cross_plane = false
is_transparent = false
is_discard = false
is_blend = false

View File

@@ -1,5 +1,7 @@
id = 7
is_blend = true
is_cross_plane = false
is_discard = false
is_gas = false
is_liquid = true
is_passable = true

View File

@@ -8,7 +8,7 @@ layout (binding = 0) uniform sampler2DArray samp;
void main(void) {
color = texture(samp, vec3(tc, tex_layer));
if (color.a < 0.1) {
if (color.a < 0.45) {
discard;
}
//color = varyingColor;

View File

@@ -47,11 +47,16 @@ struct BlockData {
bool is_passable = false;
bool is_cross_plane = false;
bool is_transparent = false;
bool is_discard = false;
bool is_blend = false;
BlockData(BlockType b_id, std::string_view b_name, bool liquid,
bool passable, bool cross_plane, bool transparent, bool gas)
bool passable, bool cross_plane, bool transparent, bool gas,
bool discard, bool blend)
: name(b_name), id(b_id), is_liquid(liquid), is_gas(gas),
is_passable(passable), is_cross_plane(cross_plane),
is_transparent(transparent) {}
is_transparent(transparent), is_discard(discard), is_blend(blend) {}
};
class BlockManager {
@@ -69,6 +74,10 @@ public:
static bool is_cross_plane(BlockType id);
static bool is_transparent(BlockType id);
static bool is_passable(BlockType id);
static bool is_discard(BlockType id);
static bool is_blend(BlockType id);
static BlockType cross_plane_index(BlockType id);
private:

View File

@@ -17,7 +17,7 @@ private:
static constexpr int SIZE_X = CHUNK_SIZE;
static constexpr int SIZE_Y = WORLD_SIZE_Y;
static constexpr int SIZE_Z = CHUNK_SIZE;
static constexpr int VERTEX_DATA_SUM = 4;
std::atomic<bool> m_dirty{false};
std::atomic<bool> m_need_upload{true};
std::atomic<bool> m_is_on_gen_vertex_data{false};
@@ -35,7 +35,8 @@ private:
/*
0 - normal
1 - cross_plane
2 - transparent
2 - normal_discard
3 - transparent and blend
*/
std::vector<VertexData> m_vertex_data;
float frequency = 0.01f;
@@ -45,9 +46,10 @@ private:
BiomeConditions m_conditions;
void clear_dirty();
void gen_normal_vertices(
void gen_vertices(
const std::array<const std::vector<BlockType>*, 4>& neighbor_block);
void gen_cross_plane_vertices();
void gen_cross_plane_vertices(int world_x, int world_y, int world_z,
BlockType id);
public:
Chunk(World& world, ChunkPos chunk_pos);
@@ -105,8 +107,11 @@ public:
GLuint get_cross_vbo() const;
size_t get_cross_vertices_sum() const;
GLuint get_transparent_vbo() const;
size_t get_transparent_vertices_sum() const;
GLuint get_normal_discard_vbo() const;
size_t get_normal_discard_vertices_sum() const;
GLuint get_normal_blend_vbo() const;
size_t get_normal_blend_vertices_sum() const;
bool is_dirty() const;
void mark_dirty();

View File

@@ -19,8 +19,10 @@ struct ChunkRenderSnapshot {
size_t normal_vertices_count;
GLuint cross_vbo;
size_t cross_vertices_count;
GLuint transparent_vbo;
size_t transparent_vertices_count;
GLuint normal_discard_vbo;
size_t normal_discard_vertices_count;
GLuint normal_blend_vbo;
size_t normal_blend_vertices_count;
glm::vec3 center;
glm::vec3 half_extents;
};

View File

@@ -90,6 +90,21 @@ bool BlockManager::is_passable(BlockType id) {
return m_datas[id].is_passable;
}
bool BlockManager::is_discard(BlockType id) {
if (id >= sums()) {
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
return m_datas[0].is_discard;
}
return m_datas[id].is_discard;
}
bool BlockManager::is_blend(BlockType id) {
if (id >= sums()) {
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
return m_datas[0].is_blend;
}
return m_datas[id].is_blend;
}
void BlockManager::init() {
fs::path data_path{block_data_dir};
@@ -125,8 +140,11 @@ void BlockManager::init() {
auto is_cross_plane = safe_get_value(block, "is_cross_plane", false);
auto is_transparent = safe_get_value(block, "is_transparent", false);
auto is_gas = safe_get_value(block, "is_gas", false);
auto is_discard = safe_get_value(block, "is_discard", false);
auto is_blend = safe_get_value(block, "is_blend", false);
m_datas.emplace_back(*id, *name, *is_liquid, *is_passable,
*is_cross_plane, *is_transparent, *is_gas);
*is_cross_plane, *is_transparent, *is_gas,
*is_discard, *is_blend);
}
std::sort(
m_datas.begin(), m_datas.end(),

View File

@@ -10,7 +10,7 @@ namespace Cubed {
Chunk::Chunk(World& world, ChunkPos chunk_pos)
: m_chunk_pos(chunk_pos), m_world(world) {
for (int i = 0; i < 3; i++) {
for (int i = 0; i < VERTEX_DATA_SUM; i++) {
m_vertex_data.emplace_back(m_world);
}
}
@@ -115,8 +115,7 @@ void Chunk::gen_vertex_data(
data.m_vertices.clear();
}
gen_normal_vertices(neighbor_block);
gen_cross_plane_vertices();
gen_vertices(neighbor_block);
for (auto& data : m_vertex_data) {
data.update_sum();
}
@@ -138,11 +137,16 @@ size_t Chunk::get_cross_vertices_sum() const {
return m_vertex_data[1].m_sum.load();
}
GLuint Chunk::get_transparent_vbo() const { return m_vertex_data[2].m_vbo; }
size_t Chunk::get_transparent_vertices_sum() const {
GLuint Chunk::get_normal_discard_vbo() const { return m_vertex_data[2].m_vbo; }
size_t Chunk::get_normal_discard_vertices_sum() const {
return m_vertex_data[2].m_sum.load();
}
GLuint Chunk::get_normal_blend_vbo() const { return m_vertex_data[3].m_vbo; }
size_t Chunk::get_normal_blend_vertices_sum() const {
return m_vertex_data[3].m_sum.load();
}
void Chunk::gen_phase_one() {
m_generator = std::make_unique<ChunkGenerator>(*this);
if (!m_generator) {
@@ -258,7 +262,7 @@ unsigned Chunk::seed() const {
BiomeConditions& Chunk::conditions() { return m_conditions; }
void Chunk::gen_normal_vertices(
void Chunk::gen_vertices(
const std::array<const std::vector<BlockType>*, 4>& neighbor_block) {
static const glm::ivec3 DIR[6] = {{0, 0, 1}, {1, 0, 0}, {0, 0, -1},
{-1, 0, 0}, {0, 1, 0}, {0, -1, 0}};
@@ -355,6 +359,10 @@ void Chunk::gen_normal_vertices(
if (neighbor_culled) {
continue;
}
if (BlockManager::is_cross_plane(cur_id)) {
gen_cross_plane_vertices(world_x, world_y, world_z,
cur_id);
}
for (int i = 0; i < 6; i++) {
Vertex vex = {
VERTICES_POS[face][i][0] + (float)world_x * 1.0f,
@@ -366,7 +374,17 @@ void Chunk::gen_normal_vertices(
};
if (BlockManager::is_transparent(cur_id)) {
if (BlockManager::is_discard(cur_id)) {
m_vertex_data[2].m_vertices.emplace_back(vex);
} else if (BlockManager::is_blend(cur_id)) {
m_vertex_data[3].m_vertices.emplace_back(vex);
} else {
Logger::warn("Id {} is transparent but not "
"discard or blend",
cur_id);
m_vertex_data[3].m_vertices.emplace_back(vex);
}
} else {
m_vertex_data[0].m_vertices.emplace_back(vex);
}
@@ -376,40 +394,30 @@ void Chunk::gen_normal_vertices(
}
}
}
void Chunk::gen_cross_plane_vertices() {
for (int x = 0; x < SIZE_X; x++) {
for (int y = 0; y < SIZE_Y; y++) {
for (int z = 0; z < SIZE_Z; z++) {
int world_x = x + m_chunk_pos.x * CHUNK_SIZE;
int world_z = z + m_chunk_pos.z * CHUNK_SIZE;
int world_y = y;
int id = m_blocks[index(x, y, z)];
void Chunk::gen_cross_plane_vertices(int world_x, int world_y, int world_z,
BlockType id) {
if (!BlockManager::is_cross_plane(id)) {
continue;
Logger::warn("Block {} {} {} id {} is not cross plane", world_x,
world_y, world_z, id);
return;
}
for (int face = 0; face < 2; face++) {
for (int i = 0; i < 6; i++) {
Vertex vex = {CROSS_VERTICES_POS[face][i][0] +
(float)world_x * 1.0f,
CROSS_VERTICES_POS[face][i][1] +
(float)world_y * 1.0f,
CROSS_VERTICES_POS[face][i][2] +
(float)world_z * 1.0f,
Vertex vex = {
CROSS_VERTICES_POS[face][i][0] + (float)world_x * 1.0f,
CROSS_VERTICES_POS[face][i][1] + (float)world_y * 1.0f,
CROSS_VERTICES_POS[face][i][2] + (float)world_z * 1.0f,
CROSS_TEX_COORDS[face][i][0],
CROSS_TEX_COORDS[face][i][1],
static_cast<float>(
BlockManager::cross_plane_index(id))
static_cast<float>(BlockManager::cross_plane_index(id))
};
m_vertex_data[1].m_vertices.emplace_back(vex);
}
}
}
}
}
// Logger::info("Cross Sum {}", m_cross_vertices_sum.load());
}
// Logger::info("Cross Sum {}", m_cross_vertices_sum.load());
} // namespace Cubed

View File

@@ -319,46 +319,17 @@ void World::render(const glm::mat4& mvp_matrix,
rendered_sum++;
}
}
glDepthMask(GL_FALSE);
struct SortableSnapshot {
const ChunkRenderSnapshot* snapshot;
float distance;
};
std::vector<SortableSnapshot> cross_list;
std::vector<SortableSnapshot> transparent_list;
// cross_plane and normal discard
for (const auto& snapshot : m_render_snapshots) {
if (!is_aabb_in_frustum(snapshot.center, snapshot.half_extents)) {
continue;
}
float dist = glm::distance(camera_pos, snapshot.center);
glm::vec2 camera_pos_xz{camera_pos.x, camera_pos.z};
if (snapshot.cross_vertices_count != 0) {
glm::vec2 center_xz{snapshot.center.x, snapshot.center.z};
float dist2d = glm::distance(camera_pos_xz, center_xz);
if (dist2d <= CROSS_PLANE_DISTANCE * 16) {
cross_list.push_back({&snapshot, dist});
}
}
if (snapshot.transparent_vertices_count != 0) {
transparent_list.push_back({&snapshot, dist});
}
}
std::sort(transparent_list.begin(), transparent_list.end(),
[](const SortableSnapshot& a, const SortableSnapshot& b) {
return a.distance > b.distance;
});
std::sort(cross_list.begin(), cross_list.end(),
[](const SortableSnapshot& a, const SortableSnapshot& b) {
return a.distance > b.distance;
});
for (const auto& item : cross_list) {
const auto& snapshot = *item.snapshot;
glBindTexture(GL_TEXTURE_2D_ARRAY,
texture_manager.get_cross_plane_array());
glBindBuffer(GL_ARRAY_BUFFER, snapshot.cross_vbo);
@@ -376,11 +347,11 @@ void World::render(const glm::mat4& mvp_matrix,
glDrawArrays(GL_TRIANGLES, 0, snapshot.cross_vertices_count);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
for (const auto& item : transparent_list) {
const auto& snapshot = *item.snapshot;
glBindTexture(GL_TEXTURE_2D_ARRAY, texture_manager.get_texture_array());
glBindBuffer(GL_ARRAY_BUFFER, snapshot.transparent_vbo);
}
if (snapshot.normal_discard_vertices_count != 0) {
glBindTexture(GL_TEXTURE_2D_ARRAY,
texture_manager.get_texture_array());
glBindBuffer(GL_ARRAY_BUFFER, snapshot.normal_discard_vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
(void*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
@@ -392,7 +363,55 @@ void World::render(const glm::mat4& mvp_matrix,
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glDrawArrays(GL_TRIANGLES, 0, snapshot.transparent_vertices_count);
glDrawArrays(GL_TRIANGLES, 0,
snapshot.normal_discard_vertices_count);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
}
// blend block
glDepthMask(GL_FALSE);
struct SortableSnapshot {
const ChunkRenderSnapshot* snapshot;
float distance;
};
std::vector<SortableSnapshot> blend_list;
for (const auto& snapshot : m_render_snapshots) {
if (!is_aabb_in_frustum(snapshot.center, snapshot.half_extents)) {
continue;
}
float dist = glm::distance(camera_pos, snapshot.center);
if (snapshot.normal_blend_vertices_count != 0) {
blend_list.push_back({&snapshot, dist});
}
}
std::sort(blend_list.begin(), blend_list.end(),
[](const SortableSnapshot& a, const SortableSnapshot& b) {
return a.distance > b.distance;
});
for (const auto& item : blend_list) {
const auto& snapshot = *item.snapshot;
glBindTexture(GL_TEXTURE_2D_ARRAY, texture_manager.get_texture_array());
glBindBuffer(GL_ARRAY_BUFFER, snapshot.normal_blend_vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
(void*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
(void*)offsetof(Vertex, s));
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex),
(void*)offsetof(Vertex, layer));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glDrawArrays(GL_TRIANGLES, 0, snapshot.normal_blend_vertices_count);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
@@ -966,8 +985,10 @@ void World::update(float delta_time) {
m_render_snapshots.push_back(
{chunk.get_normal_vbo(), chunk.get_normal_vertices_sum(),
chunk.get_cross_vbo(), chunk.get_cross_vertices_sum(),
chunk.get_transparent_vbo(),
chunk.get_transparent_vertices_sum(),
chunk.get_normal_discard_vbo(),
chunk.get_normal_discard_vertices_sum(),
chunk.get_normal_blend_vbo(),
chunk.get_normal_blend_vertices_sum(),
glm::vec3(static_cast<float>(pos.x * CHUNK_SIZE) +
static_cast<float>(CHUNK_SIZE / 2),
static_cast<float>(WORLD_SIZE_Y / 2),