feat(renderer): add roughness-based specular lighting to blocks

This commit is contained in:
2026-06-18 18:40:24 +08:00
parent 8bba170984
commit 83f1077ecd
21 changed files with 105 additions and 20 deletions

View File

@@ -7,4 +7,5 @@ is_liquid = false
is_passable = true is_passable = true
is_transitional = false is_transitional = false
is_transparent = true is_transparent = true
name = 'air' name = 'air'
roughness = 1.0

View File

@@ -7,4 +7,5 @@ is_liquid = false
is_passable = false is_passable = false
is_transitional = true is_transitional = true
is_transparent = false is_transparent = false
name = 'dirt' name = 'dirt'
roughness = 1.0

View File

@@ -7,4 +7,5 @@ is_liquid = false
is_passable = true is_passable = true
is_transitional = false is_transitional = false
is_transparent = true is_transparent = true
name = 'grass' name = 'grass'
roughness = 0.9

View File

@@ -7,4 +7,5 @@ is_liquid = false
is_passable = false is_passable = false
is_transitional = true is_transitional = true
is_transparent = false is_transparent = false
name = 'grass_block' name = 'grass_block'
roughness = 0.9

View File

@@ -7,4 +7,5 @@ is_liquid = false
is_passable = false is_passable = false
is_transitional = false is_transitional = false
is_transparent = true is_transparent = true
name = 'leaf' name = 'leaf'
roughness = 0.7

View File

@@ -7,4 +7,5 @@ is_liquid = false
is_passable = false is_passable = false
is_transitional = false is_transitional = false
is_transparent = false is_transparent = false
name = 'log' name = 'log'
roughness = 0.7

View File

@@ -7,4 +7,5 @@ is_liquid = false
is_passable = false is_passable = false
is_transitional = true is_transitional = true
is_transparent = false is_transparent = false
name = 'sand' name = 'sand'
roughness = 0.8

View File

@@ -7,4 +7,5 @@ is_liquid = false
is_passable = false is_passable = false
is_transitional = true is_transitional = true
is_transparent = false is_transparent = false
name = 'snowy_grass_block' name = 'snowy_grass_block'
roughness = 0.9

View File

@@ -7,4 +7,5 @@ is_liquid = false
is_passable = false is_passable = false
is_transitional = true is_transitional = true
is_transparent = false is_transparent = false
name = 'stone' name = 'stone'
roughness = 0.75

View File

@@ -8,3 +8,4 @@ is_transparent = false
is_discard = false is_discard = false
is_blend = false is_blend = false
is_transitional = false is_transitional = false
roughness = 1.0

View File

@@ -7,4 +7,5 @@ is_liquid = true
is_passable = true is_passable = true
is_transitional = false is_transitional = false
is_transparent = true is_transparent = true
name = 'water' name = 'water'
roughness = 0.02

View File

@@ -4,6 +4,7 @@ in vec2 tc;
in vec3 normal; in vec3 normal;
in vec3 vert_pos; in vec3 vert_pos;
in vec4 FragPosLightSpace; in vec4 FragPosLightSpace;
in float roughness;
flat in int tex_layer; flat in int tex_layer;
out vec4 color; out vec4 color;
layout (binding = 0) uniform sampler2D shadowMap; layout (binding = 0) uniform sampler2D shadowMap;
@@ -12,9 +13,10 @@ layout (binding = 1) uniform sampler2DArray samp;
uniform float ambientStrength; uniform float ambientStrength;
uniform vec3 sunlightColor; uniform vec3 sunlightColor;
uniform vec3 sunlightDir; uniform vec3 sunlightDir;
uniform vec3 cameraPos;
uniform bool shader_on; uniform bool shader_on;
uniform int shadowMode; uniform int shadowMode;
uniform float specularStrength;
uniform float lightSizeUV; uniform float lightSizeUV;
uniform float minRadius; uniform float minRadius;
uniform float maxRadius; uniform float maxRadius;
@@ -271,17 +273,56 @@ void main(void) {
vec3 lightDir = normalize(-sunlightDir); vec3 lightDir = normalize(-sunlightDir);
vec3 ambient = ambientStrength * sunlightColor;
vec3 norm = normalize(normal); vec3 norm = normalize(normal);
vec3 V =
normalize(cameraPos - vert_pos);
vec3 H =
normalize(lightDir + V);
vec3 ambient = ambientStrength * sunlightColor;
float diff = max(dot(norm, lightDir), 0.0); float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * sunlightColor; vec3 diffuse = diff * sunlightColor;
float r =
clamp(roughness, 0.0, 1.0);
float shininess =
mix(
512.0,
4.0,
r
);
float ks =
mix(
0.8,
0.02,
r
);
float spec = 0.0;
if(diff > 0.0)
{
spec =
ks *
pow(
max(dot(norm,H),0.0),
shininess
);
}
vec3 specular = spec * sunlightColor * specularStrength;
float shadow = ShadowCalculation(FragPosLightSpace, norm, lightDir); float shadow = ShadowCalculation(FragPosLightSpace, norm, lightDir);
color = vec4((ambient + (1.0 - shadow) * (diffuse)) * objectColor.rgb, objectColor.a); color = vec4((ambient + (1.0 - shadow) * (diffuse)) * objectColor.rgb + (1.0-shadow) * specular, objectColor.a);
//color = varyingColor; //color = varyingColor;
} }

View File

@@ -4,11 +4,14 @@ layout (location = 0) in vec3 pos;
layout (location = 1) in vec2 texCoord; layout (location = 1) in vec2 texCoord;
layout (location = 2) in float layer; layout (location = 2) in float layer;
layout (location = 3) in vec3 aNormal; layout (location = 3) in vec3 aNormal;
layout (location = 4) in float Roughness;
out vec2 tc; out vec2 tc;
out vec3 normal; out vec3 normal;
out vec3 vert_pos; out vec3 vert_pos;
flat out int tex_layer; flat out int tex_layer;
out vec4 FragPosLightSpace; out vec4 FragPosLightSpace;
out float roughness;
mat4 buildRotateX(float rad); mat4 buildRotateX(float rad);
mat4 buildRotateY(float rad); mat4 buildRotateY(float rad);
@@ -28,7 +31,7 @@ void main(void) {
tc = texCoord; tc = texCoord;
tex_layer = int(layer); tex_layer = int(layer);
roughness = Roughness;
normal = mat3(norm_matrix) * aNormal; normal = mat3(norm_matrix) * aNormal;
FragPosLightSpace = lightSpaceMatrix * vec4(pos, 1.0); FragPosLightSpace = lightSpaceMatrix * vec4(pos, 1.0);
gl_Position = proj_matrix * viewPos; gl_Position = proj_matrix * viewPos;

View File

@@ -51,13 +51,14 @@ struct BlockData {
bool is_discard = false; bool is_discard = false;
bool is_blend = false; bool is_blend = false;
bool is_transitional = false; bool is_transitional = false;
float roughness = 1.0f;
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, bool transitional) bool discard, bool blend, bool transitional, float r)
: 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_discard(discard), is_blend(blend), is_transparent(transparent), is_discard(discard), is_blend(blend),
is_transitional(transitional) {} is_transitional(transitional), roughness(r) {}
}; };
class BlockManager { class BlockManager {
@@ -79,6 +80,7 @@ public:
static bool is_discard(BlockType id); static bool is_discard(BlockType id);
static bool is_blend(BlockType id); static bool is_blend(BlockType id);
static bool is_transitional(BlockType id); static bool is_transitional(BlockType id);
static float roughness(BlockType id);
static BlockType cross_plane_index(BlockType id); static BlockType cross_plane_index(BlockType id);
private: private:

View File

@@ -224,6 +224,7 @@ struct Vertex3D {
float s = 0.0f, t = 0.0f; float s = 0.0f, t = 0.0f;
float layer = 0.0f; float layer = 0.0f;
float nx = 0.0f, ny = 0.0f, nz = 0.0f; float nx = 0.0f, ny = 0.0f, nz = 0.0f;
float roughness = 1.0f;
}; };
struct Vertex2D { struct Vertex2D {

View File

@@ -38,6 +38,7 @@ public:
float& min_radius(); float& min_radius();
float& max_radius(); float& max_radius();
int& samples(); int& samples();
float& specular_strength();
private: private:
static constexpr glm::vec3 SUNLIGHT_COLOR{1.0f, 1.0f, 1.0f}; static constexpr glm::vec3 SUNLIGHT_COLOR{1.0f, 1.0f, 1.0f};
@@ -108,6 +109,9 @@ private:
float m_min_radius = 2.0f; float m_min_radius = 2.0f;
float m_max_radius = 20.0f; float m_max_radius = 20.0f;
int m_samples = 16; int m_samples = 16;
float m_specular_strength = 0.5f;
/* /*
0 - quad vao 0 - quad vao
1 - sky vao 1 - sky vao

View File

@@ -111,6 +111,15 @@ bool BlockManager::is_transitional(BlockType id) {
} }
return m_datas[id].is_transitional; return m_datas[id].is_transitional;
} }
float BlockManager::roughness(BlockType id) {
if (id >= sums()) {
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
return m_datas[0].roughness;
}
return m_datas[id].roughness;
}
void BlockManager::init() { void BlockManager::init() {
fs::path data_path{block_data_dir}; fs::path data_path{block_data_dir};
@@ -149,9 +158,11 @@ void BlockManager::init() {
auto is_discard = safe_get_value(block, "is_discard", false); auto is_discard = safe_get_value(block, "is_discard", false);
auto is_blend = safe_get_value(block, "is_blend", false); auto is_blend = safe_get_value(block, "is_blend", false);
auto is_transitional = safe_get_value(block, "is_transitional", false); auto is_transitional = safe_get_value(block, "is_transitional", false);
auto roughness = safe_get_value(block, "roughness", 1.0);
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, *is_transitional); *is_discard, *is_blend, *is_transitional,
static_cast<float>(*roughness));
} }
std::sort( std::sort(
m_datas.begin(), m_datas.end(), m_datas.begin(), m_datas.end(),

View File

@@ -621,6 +621,8 @@ void DevPanel::show_shader_tab_item() {
&m_app.renderer().ambient_strength(), 0.0f, &m_app.renderer().ambient_strength(), 0.0f,
0.35f)) 0.35f))
; ;
ImGui::SliderFloat("SpecularStrength",
&m_app.renderer().specular_strength(), 0.0f, 2.0f);
ImGui::Checkbox("Discard Transparent", ImGui::Checkbox("Discard Transparent",
&m_app.renderer().discard_transparent()); &m_app.renderer().discard_transparent());
ImGui::Combo("ShaderMode", &m_app.renderer().shadow_mode(), shader_mode, ImGui::Combo("ShaderMode", &m_app.renderer().shadow_mode(), shader_mode,

View File

@@ -377,7 +377,8 @@ void Chunk::gen_vertices(const OptionalBlockVectorArray& neighbor_block) {
NORMALS[face][i][0], NORMALS[face][i][0],
NORMALS[face][i][1], NORMALS[face][i][1],
NORMALS[face][i][2] NORMALS[face][i][2],
BlockManager::roughness(cur_id)
}; };
if (BlockManager::is_transparent(cur_id)) { if (BlockManager::is_transparent(cur_id)) {
@@ -427,7 +428,8 @@ void Chunk::gen_cross_plane_vertices(int world_x, int world_y, int world_z,
static_cast<float>(BlockManager::cross_plane_index(id)), static_cast<float>(BlockManager::cross_plane_index(id)),
CROSS_NORMALS[face][i][0], CROSS_NORMALS[face][i][0],
CROSS_NORMALS[face][i][1], CROSS_NORMALS[face][i][1],
CROSS_NORMALS[face][i][2] CROSS_NORMALS[face][i][2],
BlockManager::roughness(id)
}; };
m_vertex_data[1].m_vertices.emplace_back(vex); m_vertex_data[1].m_vertices.emplace_back(vex);

View File

@@ -51,10 +51,13 @@ void VertexData::upload() {
(void*)offsetof(Vertex3D, layer)); (void*)offsetof(Vertex3D, layer));
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3D),
(void*)offsetof(Vertex3D, nx)); (void*)offsetof(Vertex3D, nx));
glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex3D),
(void*)offsetof(Vertex3D, roughness));
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2); glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3); glEnableVertexAttribArray(3);
glEnableVertexAttribArray(4);
glBindVertexArray(0); glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);

View File

@@ -640,6 +640,10 @@ void Renderer::render_world() {
glUniform1f(normal_block_shader.loc("minRadius"), m_min_radius); glUniform1f(normal_block_shader.loc("minRadius"), m_min_radius);
glUniform1f(normal_block_shader.loc("maxRadius"), m_max_radius); glUniform1f(normal_block_shader.loc("maxRadius"), m_max_radius);
glUniform1i(normal_block_shader.loc("samples"), m_samples); glUniform1i(normal_block_shader.loc("samples"), m_samples);
glUniform1f(normal_block_shader.loc("specularStrength"),
m_specular_strength);
glUniform3fv(normal_block_shader.loc("cameraPos"), 1,
glm::value_ptr(m_camera.get_camera_pos()));
m_mvp_mat = m_p_mat * m_mv_mat; m_mvp_mat = m_p_mat * m_mv_mat;
auto& m_planes = m_world.planes(); auto& m_planes = m_world.planes();
@@ -825,4 +829,5 @@ int& Renderer::light_size_uv() { return m_light_size_uv; }
float& Renderer::min_radius() { return m_min_radius; } float& Renderer::min_radius() { return m_min_radius; }
float& Renderer::max_radius() { return m_max_radius; } float& Renderer::max_radius() { return m_max_radius; }
int& Renderer::samples() { return m_samples; } int& Renderer::samples() { return m_samples; }
float& Renderer::specular_strength() { return m_specular_strength; }
} // namespace Cubed } // namespace Cubed