diff --git a/assets/shaders/block_f_shader.glsl b/assets/shaders/block_f_shader.glsl index d13036c..c93651e 100644 --- a/assets/shaders/block_f_shader.glsl +++ b/assets/shaders/block_f_shader.glsl @@ -3,15 +3,62 @@ in vec2 tc; in vec3 normal; in vec3 vert_pos; +in vec4 FragPosLightSpace; flat in int tex_layer; out vec4 color; - -layout (binding = 0) uniform sampler2DArray samp; +layout (binding = 0) uniform sampler2D shadowMap; +layout (binding = 1) uniform sampler2DArray samp; uniform float ambientStrength; uniform vec3 sunlightColor; uniform vec3 sunlightDir; +const vec2 poissonDisk[8] = vec2[]( + vec2( 0.1440, 0.7659), vec2(-0.5761, 0.4479), + vec2(-0.3220, -0.6058), vec2( 0.5693, -0.4048), + vec2(-0.1276, 0.1657), vec2(-0.0649, -0.0165), + vec2( 0.2773, -0.0305), vec2(-0.1134, -0.2122) +); + +float random(vec3 seed) { + return fract(sin(dot(seed, vec3(12.9898,78.233,45.5432))) * 43758.5453); +} + +float ShadowCalculation(vec4 fragPosLightSpace, vec3 norm, vec3 lightDir) +{ + + vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; + + projCoords = projCoords * 0.5 + 0.5; + if (projCoords.x < 0.0 || projCoords.x > 1.0 || + projCoords.y < 0.0 || projCoords.y > 1.0 || + projCoords.z < 0.0 || projCoords.z > 1.0) { + return 0.0; + } + float currentDepth = projCoords.z; + vec2 texelSize = 1.0 / vec2(textureSize(shadowMap, 0)); + float bias = 0.0002; + bias += max(0.005 * texelSize.x * (1.0 - dot(norm, lightDir)), 0.0); + + float angle = random(gl_FragCoord.xyy) * 6.2831853; // 2*PI + float s = sin(angle), c = cos(angle); + mat2 rot = mat2(c, -s, s, c); + + float radius = 1.5; + + float shadow = 0.0; + const int samples = 8; + for (int i = 0; i < samples; ++i) { + vec2 offset = rot * poissonDisk[i] * radius * texelSize; + float pcfDepth = texture(shadowMap, projCoords.xy + offset).r; + shadow += (currentDepth - bias > pcfDepth ? 1.0 : 0.0); + } + shadow /= float(samples); + + return shadow; +} + + void main(void) { vec4 objectColor = texture(samp, vec3(tc, tex_layer)); @@ -28,8 +75,10 @@ void main(void) { float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = diff * sunlightColor; - - color = vec4((ambient + diffuse) * objectColor.rgb, objectColor.a); + + float shadow = ShadowCalculation(FragPosLightSpace, norm, lightDir); + + color = vec4((ambient + (1.0 - shadow) * (diffuse)) * objectColor.rgb, objectColor.a); //color = varyingColor; } diff --git a/assets/shaders/block_v_shader.glsl b/assets/shaders/block_v_shader.glsl index 971fd4f..f3c4d7d 100644 --- a/assets/shaders/block_v_shader.glsl +++ b/assets/shaders/block_v_shader.glsl @@ -8,6 +8,7 @@ out vec2 tc; out vec3 normal; out vec3 vert_pos; flat out int tex_layer; +out vec4 FragPosLightSpace; mat4 buildRotateX(float rad); mat4 buildRotateY(float rad); @@ -17,19 +18,19 @@ mat4 buildTranslate(float x, float y, float z); uniform mat4 mv_matrix; uniform mat4 proj_matrix; uniform mat4 norm_matrix; - +uniform mat4 lightSpaceMatrix; void main(void) { vec4 viewPos = mv_matrix * vec4(pos, 1.0); - vert_pos = viewPos.xyz; + vert_pos = pos; tc = texCoord; tex_layer = int(layer); normal = mat3(norm_matrix) * aNormal; - + FragPosLightSpace = lightSpaceMatrix * vec4(pos, 1.0); gl_Position = proj_matrix * viewPos; } diff --git a/assets/shaders/depth_fragment_shader.glsl b/assets/shaders/depth_fragment_shader.glsl new file mode 100644 index 0000000..2c249e5 --- /dev/null +++ b/assets/shaders/depth_fragment_shader.glsl @@ -0,0 +1,12 @@ +#version 460 + +in vec2 tc; +flat in int tex_layer; +layout (binding = 1) uniform sampler2DArray samp; + +void main() { + vec4 texColor = texture(samp, vec3(tc, tex_layer)); + if (texColor.a < 0.8) + discard; + //gl_FragDepth = gl_FragCoord.z; +} \ No newline at end of file diff --git a/assets/shaders/depth_shader.glsl b/assets/shaders/depth_shader.glsl new file mode 100644 index 0000000..5aab56a --- /dev/null +++ b/assets/shaders/depth_shader.glsl @@ -0,0 +1,12 @@ +#version 460 +layout (location = 0) in vec3 pos; +layout (location = 1) in vec2 texCoord; +layout (location = 2) in float layer; +uniform mat4 lightSpaceMatrix; +out vec2 tc; +flat out int tex_layer; +void main() { + tc = texCoord; + tex_layer = int(layer); + gl_Position = lightSpaceMatrix * vec4(pos, 1.0); +} \ No newline at end of file diff --git a/include/Cubed/camera.hpp b/include/Cubed/camera.hpp index 2abb3fc..d831a13 100644 --- a/include/Cubed/camera.hpp +++ b/include/Cubed/camera.hpp @@ -32,6 +32,7 @@ public: const glm::vec3& get_camera_pos() const; bool is_under_water() const; + glm::vec3 get_camera_front() const; }; } // namespace Cubed diff --git a/include/Cubed/gameplay/world.hpp b/include/Cubed/gameplay/world.hpp index 046623b..7913903 100644 --- a/include/Cubed/gameplay/world.hpp +++ b/include/Cubed/gameplay/world.hpp @@ -69,6 +69,7 @@ private: std::atomic m_is_rebuilding{false}; std::atomic m_chunk_gen_finished{false}; std::atomic m_could_gen{true}; + std::atomic m_day_night_cycle{true}; std::atomic m_rendering_distance{24}; std::atomic m_chunk_gen_fraction{0.0f}; diff --git a/include/Cubed/renderer.hpp b/include/Cubed/renderer.hpp index 2182f20..f3453fa 100644 --- a/include/Cubed/renderer.hpp +++ b/include/Cubed/renderer.hpp @@ -39,7 +39,8 @@ private: static constexpr float NEAR_PLANE = 0.1f; static constexpr float SUN_SIZE = 50.0f; static constexpr float MOON_SIZE = 50.0f; - + static constexpr float DEPTH_MAP_WIDTH = 2048.0f; + static constexpr float DEPTH_MAP_HEIGHT = 2048.0f; float m_ambient_strength = 0.1f; const Camera& m_camera; @@ -74,6 +75,10 @@ private: GLuint m_accum_texture = 0; GLuint m_reveal_texture = 0; GLuint m_oit_depth_render_buffer = 0; + + GLuint m_depth_map_fbo = 0; + GLuint m_depth_map_texture = 0; + GLuint m_quad_vbo = 0; glm::mat4 m_ui_proj; diff --git a/src/camera.cpp b/src/camera.cpp index 224f91e..22562a2 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -60,4 +60,6 @@ const glm::vec3& Camera::get_camera_pos() const { return m_camera_pos; } bool Camera::is_under_water() const { return m_under_water; } +glm::vec3 Camera::get_camera_front() const { return m_player->get_front(); } + } // namespace Cubed diff --git a/src/gameplay/world.cpp b/src/gameplay/world.cpp index ca57225..837cf65 100644 --- a/src/gameplay/world.cpp +++ b/src/gameplay/world.cpp @@ -757,8 +757,10 @@ void World::serever_run(std::stop_token stoken) { Logger::info("Server Thread Started!"); while (!stoken.stop_requested()) { std::this_thread::sleep_for(milliseconds(m_per_tick_time)); - ++m_game_ticks; - m_day_tick = (++m_day_tick) % DAY_TIME; + if (m_day_night_cycle) { + ++m_game_ticks; + m_day_tick = (++m_day_tick) % DAY_TIME; + } } Logger::info("Server Thread Stopped!"); } diff --git a/src/renderer.cpp b/src/renderer.cpp index 74c9a64..8d55b78 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -42,6 +42,9 @@ Renderer::~Renderer() { glDeleteTextures(1, &m_accum_texture); glDeleteTextures(1, &m_reveal_texture); glDeleteRenderbuffers(1, &m_oit_depth_render_buffer); + + glDeleteFramebuffers(1, &m_depth_map_fbo); + glDeleteTextures(1, &m_depth_map_texture); } void Renderer::hot_reload() { @@ -76,6 +79,8 @@ void Renderer::init() { Shader composite_block_shader{"composite", "shaders/block_composite_v_shader.glsl", "shaders/block_composite_f_shader.glsl"}; + Shader depth_shader{"depth_shader", "shaders/depth_shader.glsl", + "shaders/depth_fragment_shader.glsl"}; m_shaders.insert({world_shader.hash(), std::move(world_shader)}); m_shaders.insert({outline_shader.hash(), std::move(outline_shader)}); m_shaders.insert({sky_shdaer.hash(), std::move(sky_shdaer)}); @@ -86,6 +91,7 @@ void Renderer::init() { m_shaders.insert({accum_shader.hash(), std::move(accum_shader)}); m_shaders.insert( {composite_block_shader.hash(), std::move(composite_block_shader)}); + m_shaders.insert({depth_shader.hash(), std::move(depth_shader)}); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); @@ -456,11 +462,124 @@ void Renderer::updata_framebuffer(int width, int height) { } glBindFramebuffer(GL_FRAMEBUFFER, 0); + // depth map fbo + if (m_depth_map_fbo == 0) { + glGenFramebuffers(1, &m_depth_map_fbo); + } + glDeleteTextures(1, &m_depth_map_texture); + glGenTextures(1, &m_depth_map_texture); + + glBindTexture(GL_TEXTURE_2D, m_depth_map_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, DEPTH_MAP_WIDTH, + DEPTH_MAP_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + float border_color[] = {1.0f, 1.0f, 1.0f, 1.0f}; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color); + // Manually compare shadows + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); + // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, + // GL_COMPARE_REF_TO_TEXTURE); + // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + + glBindFramebuffer(GL_FRAMEBUFFER, m_depth_map_fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, + m_depth_map_texture, 0); + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + Logger::error("FBO incomplete after resize!"); + } else { + Logger::info("Frame Buffer Complete!"); + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); m_width = width; m_height = height; } void Renderer::render_world() { + // shader map + const auto& depth_shader = get_shader("depth_shader"); + depth_shader.use(); + + glm::vec3 cam_pos = m_camera.get_camera_pos(); + glm::vec3 cam_fwd = m_camera.get_camera_front(); + float half_extent = 256.0f; + + glm::vec3 center = cam_pos + cam_fwd * (half_extent * 0.5f); + + glm::vec3 sundir = glm::normalize(m_world.sunlight_dir()); + glm::vec3 up = + fabs(sundir.y) > 0.99f ? glm::vec3(0, 0, 1) : glm::vec3(0, 1, 0); + + glm::mat4 light_basis = glm::lookAt(glm::vec3(0.0f), sundir, up); + float texels_per_unit = DEPTH_MAP_WIDTH / (half_extent * 2.0f); + glm::vec3 ls_center = glm::vec3(light_basis * glm::vec4(center, 1.0f)); + ls_center.x = std::round(ls_center.x * texels_per_unit) / texels_per_unit; + ls_center.y = std::round(ls_center.y * texels_per_unit) / texels_per_unit; + glm::vec3 snapped_center = + glm::vec3(glm::inverse(light_basis) * glm::vec4(ls_center, 1.0f)); + + float distance = 128.0f; + glm::vec3 light_pos = snapped_center - sundir * distance; + glm::mat4 light_view = glm::lookAt(light_pos, snapped_center, up); + glm::mat4 light_projection = glm::ortho( + -half_extent, half_extent, -half_extent, half_extent, 1.0f, 500.0f); + + glm::mat4 light_space_matrix = light_projection * light_view; + glUniformMatrix4fv(depth_shader.loc("lightSpaceMatrix"), 1, GL_FALSE, + glm::value_ptr(light_space_matrix)); + + glViewport(0, 0, DEPTH_MAP_WIDTH, DEPTH_MAP_WIDTH); + glCullFace(GL_FRONT); + + glBindFramebuffer(GL_FRAMEBUFFER, m_depth_map_fbo); + glClear(GL_DEPTH_BUFFER_BIT); + + auto& m_render_snapshots = m_world.render_snapshots(); + auto& camera_pos = m_camera.get_camera_pos(); + glActiveTexture(GL_TEXTURE1); + glEnable(GL_DEPTH_TEST); + for (const auto& snapshot : m_render_snapshots) { + glBindTexture(GL_TEXTURE_2D_ARRAY, + m_texture_manager.get_texture_array()); + glBindVertexArray(snapshot.normal_vao); + + glDrawArrays(GL_TRIANGLES, 0, snapshot.normal_vertices_count); + } + + // cross_plane and discard + + for (const auto& snapshot : m_render_snapshots) { + + 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, + m_texture_manager.get_texture_array()); + glBindVertexArray(snapshot.cross_vao); + + glDrawArrays(GL_TRIANGLES, 0, snapshot.cross_vertices_count); + } + } + if (snapshot.normal_discard_vertices_count != 0) { + glBindTexture(GL_TEXTURE_2D_ARRAY, + m_texture_manager.get_texture_array()); + glBindVertexArray(snapshot.normal_discard_vao); + + glDrawArrays(GL_TRIANGLES, 0, + snapshot.normal_discard_vertices_count); + } + } + + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + + glCullFace(GL_BACK); + glViewport(0, 0, m_width, m_height); const auto& normal_block_shader = get_shader("normal_block"); normal_block_shader.use(); @@ -468,6 +587,8 @@ void Renderer::render_world() { m_proj_loc = normal_block_shader.loc("proj_matrix"); glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_depth_map_texture); + glActiveTexture(GL_TEXTURE1); m_m_mat = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f)); m_v_mat = m_camera.get_camera_lookat(); @@ -480,6 +601,8 @@ void Renderer::render_world() { glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat)); glUniformMatrix4fv(normal_block_shader.loc("norm_matrix"), 1, GL_FALSE, glm::value_ptr(m_norm_mat)); + glUniformMatrix4fv(normal_block_shader.loc("lightSpaceMatrix"), 1, GL_FALSE, + glm::value_ptr(light_space_matrix)); glUniform1f(normal_block_shader.loc("ambientStrength"), m_ambient_strength); glUniform3fv(normal_block_shader.loc("sunlightColor"), 1, glm::value_ptr(SUNLIGHT_COLOR)); @@ -488,9 +611,7 @@ void Renderer::render_world() { m_mvp_mat = m_p_mat * m_mv_mat; - auto& camera_pos = m_camera.get_camera_pos(); auto& m_planes = m_world.planes(); - auto& m_render_snapshots = m_world.render_snapshots(); Math::extract_frustum_planes(m_mvp_mat, m_planes); @@ -571,6 +692,7 @@ void Renderer::render_world() { glBlendFunci(0, GL_ONE, GL_ONE); glBlendFunci(1, GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + glActiveTexture(GL_TEXTURE0); for (const auto& snapshot : m_render_snapshots) { if (!Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents, m_planes)) {