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 id = 0
is_blend = false
is_cross_plane = false is_cross_plane = false
is_discard = true
is_gas = true is_gas = true
is_liquid = false is_liquid = false
is_passable = true is_passable = true

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -90,6 +90,21 @@ bool BlockManager::is_passable(BlockType id) {
return m_datas[id].is_passable; 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() { void BlockManager::init() {
fs::path data_path{block_data_dir}; 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_cross_plane = safe_get_value(block, "is_cross_plane", false);
auto is_transparent = safe_get_value(block, "is_transparent", false); auto is_transparent = safe_get_value(block, "is_transparent", false);
auto is_gas = safe_get_value(block, "is_gas", 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, 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( std::sort(
m_datas.begin(), m_datas.end(), m_datas.begin(), m_datas.end(),

View File

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

View File

@@ -319,6 +319,58 @@ void World::render(const glm::mat4& mvp_matrix,
rendered_sum++; rendered_sum++;
} }
} }
// cross_plane and normal discard
for (const auto& snapshot : m_render_snapshots) {
if (!is_aabb_in_frustum(snapshot.center, snapshot.half_extents)) {
continue;
}
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) {
glBindTexture(GL_TEXTURE_2D_ARRAY,
texture_manager.get_cross_plane_array());
glBindBuffer(GL_ARRAY_BUFFER, snapshot.cross_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.cross_vertices_count);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
}
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),
(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_discard_vertices_count);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
}
// blend block
glDepthMask(GL_FALSE); glDepthMask(GL_FALSE);
struct SortableSnapshot { struct SortableSnapshot {
@@ -326,8 +378,7 @@ void World::render(const glm::mat4& mvp_matrix,
float distance; float distance;
}; };
std::vector<SortableSnapshot> cross_list; std::vector<SortableSnapshot> blend_list;
std::vector<SortableSnapshot> transparent_list;
for (const auto& snapshot : m_render_snapshots) { for (const auto& snapshot : m_render_snapshots) {
@@ -336,51 +387,19 @@ void World::render(const glm::mat4& mvp_matrix,
} }
float dist = glm::distance(camera_pos, snapshot.center); float dist = glm::distance(camera_pos, snapshot.center);
glm::vec2 camera_pos_xz{camera_pos.x, camera_pos.z}; if (snapshot.normal_blend_vertices_count != 0) {
if (snapshot.cross_vertices_count != 0) { blend_list.push_back({&snapshot, dist});
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(), std::sort(blend_list.begin(), blend_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) { [](const SortableSnapshot& a, const SortableSnapshot& b) {
return a.distance > b.distance; return a.distance > b.distance;
}); });
for (const auto& item : cross_list) { for (const auto& item : blend_list) {
const auto& snapshot = *item.snapshot;
glBindTexture(GL_TEXTURE_2D_ARRAY,
texture_manager.get_cross_plane_array());
glBindBuffer(GL_ARRAY_BUFFER, snapshot.cross_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.cross_vertices_count);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
for (const auto& item : transparent_list) {
const auto& snapshot = *item.snapshot; const auto& snapshot = *item.snapshot;
glBindTexture(GL_TEXTURE_2D_ARRAY, texture_manager.get_texture_array()); glBindTexture(GL_TEXTURE_2D_ARRAY, texture_manager.get_texture_array());
glBindBuffer(GL_ARRAY_BUFFER, snapshot.transparent_vbo); glBindBuffer(GL_ARRAY_BUFFER, snapshot.normal_blend_vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
(void*)0); (void*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
@@ -392,7 +411,7 @@ void World::render(const glm::mat4& mvp_matrix,
glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2); glEnableVertexAttribArray(2);
glDrawArrays(GL_TRIANGLES, 0, snapshot.transparent_vertices_count); glDrawArrays(GL_TRIANGLES, 0, snapshot.normal_blend_vertices_count);
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
} }
@@ -966,8 +985,10 @@ void World::update(float delta_time) {
m_render_snapshots.push_back( m_render_snapshots.push_back(
{chunk.get_normal_vbo(), chunk.get_normal_vertices_sum(), {chunk.get_normal_vbo(), chunk.get_normal_vertices_sum(),
chunk.get_cross_vbo(), chunk.get_cross_vertices_sum(), chunk.get_cross_vbo(), chunk.get_cross_vertices_sum(),
chunk.get_transparent_vbo(), chunk.get_normal_discard_vbo(),
chunk.get_transparent_vertices_sum(), 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) + glm::vec3(static_cast<float>(pos.x * CHUNK_SIZE) +
static_cast<float>(CHUNK_SIZE / 2), static_cast<float>(CHUNK_SIZE / 2),
static_cast<float>(WORLD_SIZE_Y / 2), static_cast<float>(WORLD_SIZE_Y / 2),