mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-06-22 02:27:01 +08:00
feat: lighting effects (#18)
* feat(rendering): add basic diffuse and ambient lighting to block rendering * feat(world): add day/night cycle with server tick system * feat(renderer): make ambient strength adjustable via dev panel * fix(game_time): use unsigned tick type and enforce positive tick speed * feat(renderer): add shadow mapping with PCF soft shadows Introduce shadow mapping using a dedicated depth framebuffer and shader. The block fragment shader now performs percentage-closer filtering (PCF) with Poisson disk sampling and random rotation for soft shadows. The vertex shader outputs light-space coordinates. A new depth shader pair handles rendering from the light's perspective, discarding transparent fragments. The renderer sets up the light projection based on the camera position and sun direction, and applies the shadow factor to diffuse lighting. Day/night cycle can now be toggled off in the world server thread. * perf(shadow): increase depth map resolution and refine PCF sampling * feat(dev-panel): add tick freeze toggle and fix TickType sign Add checkbox to freeze tick advancement in dev panel. Change TickType from unsigned long long to signed long long to prevent underflow. * chore(world): add missing <numbers> include * feat(renderer): add runtime shader and shadow mode controls Introduce user-controllable shader on/off, shadow mode (rotated Poisson disk, 3x3 grid, or off), light cull face, and discard transparent in depth pass. Expose all settings in new dev panel "shader" tab. Move ambient strength slider to the new tab. * fix(texture): set texture wrap mode to clamp to edge * feat(renderer): smooth shadow sun direction transitions using quantized directions and slerp * feat(renderer): add PCSS shadow mode with configurable samples and softness Implement Percentage Closer Soft Shadows (PCSS) as shadow mode 3. Add a FindBlocker function and three Poisson disk arrays (8, 16, 32 samples). Expose new uniforms (lightSizeUV, minRadius, maxRadius, samples) and provide slider/combo controls in the dev panel for sample count, light size, and penumbra radius limits. * feat(renderer): add roughness-based specular lighting to blocks * feat(renderer): compute ambient and sunlight colors based on sun height * feat(renderer): add moonlight and smooth day/night light blending * refactor(renderer): extract day/night calculation and add billboard shaders Add a new ParallelLight struct to encapsulate lighting parameters. Move day-night cycle computation from render_world() to a new day_night_calculation() method. Update sky shaders to use procedural colors based on sun position. Add separate billboard shaders for sun/moon. * feat(sky): add animated clouds to sky shader with speed control * feat(renderer): add configurable cloud thresholds and white mix
This commit is contained in:
@@ -111,6 +111,15 @@ bool BlockManager::is_transitional(BlockType id) {
|
||||
}
|
||||
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() {
|
||||
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_blend = safe_get_value(block, "is_blend", 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,
|
||||
*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(
|
||||
m_datas.begin(), m_datas.end(),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -84,6 +84,7 @@ void DevPanel::render() {
|
||||
show_world_tab_item();
|
||||
show_player_tab_item();
|
||||
show_items_tab_item();
|
||||
show_shader_tab_item();
|
||||
show_about_table_bar();
|
||||
|
||||
ImGui::EndTabBar();
|
||||
@@ -107,6 +108,7 @@ void DevPanel::show_about_table_bar() {
|
||||
ImGui::Text("FreeType");
|
||||
ImGui::Text("toml++");
|
||||
ImGui::Text("Dear ImGui");
|
||||
ImGui::Text("Tbb");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Special Thanks");
|
||||
ImGui::Text("TANGERIME");
|
||||
@@ -263,6 +265,31 @@ void DevPanel::show_biome_table_bar() {
|
||||
}
|
||||
}
|
||||
|
||||
void DevPanel::show_time_table_bar() {
|
||||
World& world = m_app.world();
|
||||
ImGui::Text("Game Tick %llu", world.game_tick());
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Day Tick %llu", world.day_tick());
|
||||
m_tick_frezze = !world.is_tick_running();
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Checkbox("Tick Frezze", &m_tick_frezze)) {
|
||||
world.tick_running(!m_tick_frezze);
|
||||
}
|
||||
if (ImGui::SliderInt("SetDayTick", &m_pre_set_day_tick, 0, DAY_TIME)) {
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Set##DayTick")) {
|
||||
world.day_tick(static_cast<TickType>(m_pre_set_day_tick));
|
||||
}
|
||||
ImGui::Text("MSPT %d", world.per_tick_time());
|
||||
if (ImGui::SliderInt("SetMSPT", &m_pre_set_tick_speed, 1, 200)) {
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Set##MSPT")) {
|
||||
world.per_tick_time(m_pre_set_tick_speed);
|
||||
}
|
||||
}
|
||||
|
||||
void DevPanel::show_cave_table_bar() {
|
||||
auto& cave_carcer = m_app.world().cave_carcer();
|
||||
|
||||
@@ -336,6 +363,7 @@ void DevPanel::show_settings_tab_item() {
|
||||
static_cast<double>(m_config.mouse_sensitivity));
|
||||
m_player->hot_reload();
|
||||
}
|
||||
|
||||
if (ImGui::SliderInt("Distance", &m_config.rendering_distance, 2,
|
||||
128)) {
|
||||
Config::get().set("world.rendering_distance",
|
||||
@@ -457,6 +485,10 @@ void DevPanel::show_world_tab_item() {
|
||||
ImGui::Text("Chunk Build Progress\n");
|
||||
ImGui::ProgressBar(m_app.world().chunk_gen_fraction());
|
||||
if (ImGui::BeginTabBar("World Settings")) {
|
||||
if (ImGui::BeginTabItem("Time")) {
|
||||
show_time_table_bar();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Cave")) {
|
||||
show_cave_table_bar();
|
||||
ImGui::EndTabItem();
|
||||
@@ -577,6 +609,58 @@ void DevPanel::show_items_tab_item() {
|
||||
}
|
||||
}
|
||||
|
||||
void DevPanel::show_shader_tab_item() {
|
||||
|
||||
static const char* shader_mode[] = {
|
||||
"Rotated Poisson Disk PCF", "3x3 Square Grid PCF", "PCF off", "PCSS"};
|
||||
static const char* cull_face_mode[] = {"Front", "Back"};
|
||||
static const char* samples[] = {"8", "16", "32"};
|
||||
if (ImGui::BeginTabItem("shader")) {
|
||||
ImGui::Checkbox("Shader", &m_app.renderer().shader_on());
|
||||
if (ImGui::SliderFloat("AmbientStrength",
|
||||
&m_app.renderer().ambient_strength(), 0.0f,
|
||||
0.35f))
|
||||
;
|
||||
ImGui::SliderFloat("SpecularStrength",
|
||||
&m_app.renderer().specular_strength(), 0.0f, 2.0f);
|
||||
ImGui::Checkbox("Discard Transparent",
|
||||
&m_app.renderer().discard_transparent());
|
||||
ImGui::Combo("ShaderMode", &m_app.renderer().shadow_mode(), shader_mode,
|
||||
IM_ARRAYSIZE(shader_mode));
|
||||
ImGui::Combo("LightCullFaceMode", &m_app.renderer().light_cull_face(),
|
||||
cull_face_mode, IM_ARRAYSIZE(cull_face_mode));
|
||||
if (ImGui::Combo("samples", &m_samples_idx, samples,
|
||||
IM_ARRAYSIZE(samples))) {
|
||||
if (m_samples_idx == 0) {
|
||||
m_app.renderer().samples() = 8;
|
||||
} else if (m_samples_idx == 1) {
|
||||
m_app.renderer().samples() = 16;
|
||||
} else if (m_samples_idx == 2) {
|
||||
m_app.renderer().samples() = 32;
|
||||
} else {
|
||||
Logger::warn("Samples Index {} is invaild", m_samples_idx);
|
||||
m_app.renderer().samples() = 16;
|
||||
}
|
||||
}
|
||||
if (m_app.renderer().shadow_mode() == 3) {
|
||||
ImGui::SliderInt("LightSizeUV", &m_app.renderer().light_size_uv(),
|
||||
0, 800);
|
||||
ImGui::SliderFloat("MinRaduis", &m_app.renderer().min_radius(),
|
||||
0.0f, 20.0f);
|
||||
ImGui::SliderFloat("MaxRadius", &m_app.renderer().max_radius(),
|
||||
0.0f, 100.0f);
|
||||
}
|
||||
ImGui::SliderFloat("Cloud Speed", &m_app.renderer().cloud_speed(), 1.0f,
|
||||
100.0f);
|
||||
ImGui::SliderFloat("Cloud Threshold Low",
|
||||
&m_app.renderer().cloud_threshold_low(), 0.0f, 1.0f);
|
||||
ImGui::SliderFloat("Cloud Threshold High",
|
||||
&m_app.renderer().cloud_threshold_high(), 0.0f,
|
||||
1.0f);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
|
||||
void DevPanel::update_config_view() {
|
||||
auto config = Config::get();
|
||||
m_config.fov =
|
||||
|
||||
@@ -366,13 +366,19 @@ void Chunk::gen_vertices(const OptionalBlockVectorArray& neighbor_block) {
|
||||
cur_id);
|
||||
}
|
||||
for (int i = 0; i < 6; i++) {
|
||||
Vertex vex = {
|
||||
Vertex3D vex = {
|
||||
VERTICES_POS[face][i][0] + (float)world_x * 1.0f,
|
||||
VERTICES_POS[face][i][1] + (float)world_y * 1.0f,
|
||||
VERTICES_POS[face][i][2] + (float)world_z * 1.0f,
|
||||
TEX_COORDS[face][i][0],
|
||||
TEX_COORDS[face][i][1],
|
||||
static_cast<float>(cur_id * 6 + face)
|
||||
|
||||
static_cast<float>(cur_id * 6 + face),
|
||||
|
||||
NORMALS[face][i][0],
|
||||
NORMALS[face][i][1],
|
||||
NORMALS[face][i][2],
|
||||
BlockManager::roughness(cur_id)
|
||||
|
||||
};
|
||||
if (BlockManager::is_transparent(cur_id)) {
|
||||
@@ -413,13 +419,17 @@ void Chunk::gen_cross_plane_vertices(int world_x, int world_y, int world_z,
|
||||
}
|
||||
for (int face = 0; face < 2; face++) {
|
||||
for (int i = 0; i < 6; i++) {
|
||||
Vertex vex = {
|
||||
Vertex3D 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)),
|
||||
CROSS_NORMALS[face][i][0],
|
||||
CROSS_NORMALS[face][i][1],
|
||||
CROSS_NORMALS[face][i][2],
|
||||
BlockManager::roughness(id)
|
||||
|
||||
};
|
||||
m_vertex_data[1].m_vertices.emplace_back(vex);
|
||||
|
||||
@@ -41,18 +41,23 @@ void VertexData::upload() {
|
||||
}
|
||||
glBindVertexArray(m_vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, m_vertices.size() * sizeof(Vertex),
|
||||
glBufferData(GL_ARRAY_BUFFER, m_vertices.size() * sizeof(Vertex3D),
|
||||
m_vertices.data(), GL_DYNAMIC_DRAW);
|
||||
|
||||
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));
|
||||
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), (void*)0);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex3D),
|
||||
(void*)offsetof(Vertex3D, s));
|
||||
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex3D),
|
||||
(void*)offsetof(Vertex3D, layer));
|
||||
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3D),
|
||||
(void*)offsetof(Vertex3D, nx));
|
||||
glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex3D),
|
||||
(void*)offsetof(Vertex3D, roughness));
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glEnableVertexAttribArray(2);
|
||||
glEnableVertexAttribArray(3);
|
||||
glEnableVertexAttribArray(4);
|
||||
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
#include "Cubed/tools/cubed_hash.hpp"
|
||||
|
||||
#include <execution>
|
||||
#include <glm/gtc/constants.hpp>
|
||||
#include <numbers>
|
||||
using namespace std::chrono;
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
@@ -18,6 +21,7 @@ World::World() {}
|
||||
|
||||
World::~World() {
|
||||
stop_gen_thread();
|
||||
stop_server_thread();
|
||||
m_chunks.clear();
|
||||
{
|
||||
std::lock_guard lk(m_delete_vbo_mutex);
|
||||
@@ -86,6 +90,8 @@ void World::init_world() {
|
||||
auto d = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1);
|
||||
Logger::info("Chunk Block Init Finish, Time Consuming: {}", d);
|
||||
|
||||
start_server_thread();
|
||||
|
||||
Logger::info("TestPlayer Create Finish");
|
||||
}
|
||||
void World::init_chunks() {
|
||||
@@ -727,6 +733,11 @@ void World::start_gen_thread() {
|
||||
});
|
||||
}
|
||||
|
||||
void World::start_server_thread() {
|
||||
m_server_thread = std::thread(
|
||||
[this]() { serever_run(m_server_stop_source.get_token()); });
|
||||
}
|
||||
|
||||
void World::stop_gen_thread() {
|
||||
m_gen_running = false;
|
||||
m_gen_cv.notify_all();
|
||||
@@ -736,6 +747,25 @@ void World::stop_gen_thread() {
|
||||
Logger::info("Gen Thread Stopped");
|
||||
}
|
||||
|
||||
void World::stop_server_thread() {
|
||||
m_server_stop_source.request_stop();
|
||||
if (m_server_thread.joinable()) {
|
||||
m_server_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
if (m_tick_running) {
|
||||
++m_game_ticks;
|
||||
m_day_tick = (m_day_tick + 1) % DAY_TIME;
|
||||
}
|
||||
}
|
||||
Logger::info("Server Thread Stopped!");
|
||||
}
|
||||
|
||||
void World::need_gen() {
|
||||
if (!m_could_gen) {
|
||||
Logger::warn("It is generating or consuming new chunks");
|
||||
@@ -1006,4 +1036,49 @@ std::vector<glm::vec4>& World::planes() { return m_planes; }
|
||||
std::vector<ChunkRenderSnapshot>& World::render_snapshots() {
|
||||
return m_render_snapshots;
|
||||
};
|
||||
/*
|
||||
glm::vec3 World::sunlight_dir() const {
|
||||
float t = static_cast<float>(m_day_tick) / DAY_TIME;
|
||||
|
||||
float azimuth = glm::radians(90.0f - t * 360.0f);
|
||||
|
||||
float altitude =
|
||||
glm::half_pi<float>() * sin((t - 0.25f) * glm::two_pi<float>());
|
||||
|
||||
glm::vec3 dir{cos(altitude) * cos(azimuth), sin(altitude),
|
||||
cos(altitude) * sin(azimuth)};
|
||||
|
||||
return glm::normalize(-dir);
|
||||
}
|
||||
*/
|
||||
|
||||
glm::vec3 World::sunlight_dir() const {
|
||||
float altitude = sin((m_day_tick - 6 * PER_HOUR) /
|
||||
static_cast<float>(DAY_TIME / 2) * std::numbers::pi) *
|
||||
90.0f;
|
||||
|
||||
float t = static_cast<float>(m_day_tick) / DAY_TIME;
|
||||
float azimuth = 90.0f - 360.0f * (t - 0.25f);
|
||||
|
||||
float alt = glm::radians(altitude);
|
||||
float az = glm::radians(azimuth);
|
||||
glm::vec3 dir;
|
||||
dir.x = cos(alt) * sin(az);
|
||||
dir.y = sin(alt);
|
||||
dir.z = cos(alt) * cos(az);
|
||||
|
||||
return glm::normalize(-dir);
|
||||
}
|
||||
|
||||
TickType World::game_tick() const { return m_game_ticks.load(); }
|
||||
TickType World::day_tick() const { return m_day_tick.load(); }
|
||||
void World::day_tick(TickType tick) {
|
||||
tick %= DAY_TIME;
|
||||
m_day_tick = tick;
|
||||
}
|
||||
int World::per_tick_time() const { return m_per_tick_time.load(); }
|
||||
void World::per_tick_time(int ms) { m_per_tick_time = ms; }
|
||||
|
||||
bool World::is_tick_running() const { return m_tick_running.load(); }
|
||||
void World::tick_running(bool run) { m_tick_running = run; }
|
||||
} // namespace Cubed
|
||||
380
src/renderer.cpp
380
src/renderer.cpp
@@ -18,6 +18,7 @@
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <format>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
Renderer::Renderer(const Camera& camera, World& world,
|
||||
@@ -42,6 +43,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 +80,10 @@ 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"};
|
||||
Shader billboard{"billboard", "shaders/billboard_v_shader.glsl",
|
||||
"shaders/billboard_f_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 +94,8 @@ 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)});
|
||||
m_shaders.insert({billboard.hash(), std::move(billboard)});
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
|
||||
@@ -192,27 +202,56 @@ void Renderer::init_text() {
|
||||
}
|
||||
|
||||
void Renderer::render() {
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
|
||||
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
day_night_calculation();
|
||||
render_sky();
|
||||
render_world();
|
||||
render_outline();
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
render_underwater();
|
||||
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
render_ui();
|
||||
render_text();
|
||||
render_dev_panel();
|
||||
}
|
||||
|
||||
void Renderer::day_night_calculation() {
|
||||
m_parallel_light.sundir = glm::normalize(m_world.sunlight_dir());
|
||||
m_parallel_light.sun_height = (-m_parallel_light.sundir).y;
|
||||
m_parallel_light.lightdir = m_parallel_light.sundir;
|
||||
|
||||
m_parallel_light.day_light =
|
||||
glm::smoothstep(0.15f, 0.3f, m_parallel_light.sun_height);
|
||||
|
||||
m_parallel_light.sun_color = mix(SUNSET_SUNLIGHT_COLOR, NOON_SUNLIGHT_COLOR,
|
||||
m_parallel_light.day_light);
|
||||
|
||||
glm::vec3 ambient_color = mix(SUNSET_AMBIENT_COLOR, NOON_AMBIENT_COLOR,
|
||||
m_parallel_light.day_light);
|
||||
|
||||
m_parallel_light.day_factor =
|
||||
glm::smoothstep(-0.15f, 0.05f, m_parallel_light.sun_height);
|
||||
auto day_factor = m_parallel_light.day_factor;
|
||||
float light_intensity =
|
||||
glm::smoothstep(moon_intensity, sun_intensity, day_factor);
|
||||
m_parallel_light.directional_light_color =
|
||||
glm::mix(MOON_COLOR, m_parallel_light.sun_color, day_factor) *
|
||||
light_intensity;
|
||||
m_parallel_light.finnal_ambient_color =
|
||||
glm::mix(NIGHT_AMBIENT_COLOR, ambient_color, day_factor);
|
||||
|
||||
m_ambient_strength = glm::mix(0.45f, 0.25f, day_factor);
|
||||
}
|
||||
|
||||
void Renderer::render_outline() {
|
||||
const auto& shader = get_shader("outline");
|
||||
shader.use();
|
||||
@@ -244,26 +283,114 @@ void Renderer::render_outline() {
|
||||
|
||||
void Renderer::render_sky() {
|
||||
|
||||
const auto& shader = get_shader("sky");
|
||||
glm::vec3 zenith = {0.20f, 0.45f, 0.95f};
|
||||
|
||||
shader.use();
|
||||
m_mv_loc = shader.loc("mv_matrix");
|
||||
m_proj_loc = shader.loc("proj_matrix");
|
||||
glm::vec3 horizon = {0.55f, 0.75f, 1.00f};
|
||||
|
||||
glm::vec3 sunset_zenith = {0.05f, 0.10f, 0.25f};
|
||||
|
||||
glm::vec3 sunset_horizon = {1.0f, 0.35f, 0.10f};
|
||||
|
||||
glm::vec3 night_zenith = {0.018f, 0.023f, 0.048f};
|
||||
glm::vec3 night_horizon = {0.022f, 0.027f, 0.052f};
|
||||
|
||||
constexpr float NIGHT_SHARPNESS = 0.35f;
|
||||
constexpr float SUNSET_SHARPNESS = 0.6f;
|
||||
constexpr float NOON_SHARPNESS = 0.35f;
|
||||
|
||||
constexpr float NIGHT_CLOUD_MIX = 0.3f;
|
||||
constexpr float SUNSET_CLOUD_MIX = 0.4f;
|
||||
constexpr float NOON_CLOUD_MIX = 0.7;
|
||||
|
||||
glm::vec3 day_top = mix(sunset_zenith, zenith, m_parallel_light.day_light);
|
||||
glm::vec3 day_bottom =
|
||||
mix(sunset_horizon, horizon, m_parallel_light.day_light);
|
||||
|
||||
glm::vec3 sky_top = mix(night_zenith, day_top, m_parallel_light.day_factor);
|
||||
glm::vec3 sky_bottom =
|
||||
mix(night_horizon, day_bottom, m_parallel_light.day_factor);
|
||||
|
||||
float day_sharpness =
|
||||
glm::mix(SUNSET_SHARPNESS, NOON_SHARPNESS, m_parallel_light.day_light);
|
||||
|
||||
float horizon_sharpness =
|
||||
glm::mix(NIGHT_SHARPNESS, day_sharpness, m_parallel_light.day_factor);
|
||||
|
||||
float day_cloud_mix =
|
||||
glm::mix(SUNSET_CLOUD_MIX, NOON_CLOUD_MIX, m_parallel_light.day_light);
|
||||
float cloud_white_mix =
|
||||
glm::mix(NIGHT_CLOUD_MIX, day_cloud_mix, m_parallel_light.day_factor);
|
||||
|
||||
m_cloud_time += m_delta_time * m_cloud_speed;
|
||||
|
||||
const auto& sky_shader = get_shader("sky");
|
||||
|
||||
sky_shader.use();
|
||||
m_mv_loc = sky_shader.loc("mv_matrix");
|
||||
m_proj_loc = sky_shader.loc("proj_matrix");
|
||||
|
||||
m_m_mat = glm::translate(glm::mat4(1.0f), m_camera.get_camera_pos() -
|
||||
glm::vec3(0.5f, 0.5f, 0.5f));
|
||||
m_v_mat = m_camera.get_camera_lookat();
|
||||
m_mv_mat = m_v_mat * m_m_mat;
|
||||
|
||||
glm::vec3 sun_dir_view = (-m_parallel_light.sundir);
|
||||
|
||||
glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat));
|
||||
glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat));
|
||||
|
||||
glUniform3fv(sky_shader.loc("skyTop"), 1, glm::value_ptr(sky_top));
|
||||
glUniform3fv(sky_shader.loc("skyBottom"), 1, glm::value_ptr(sky_bottom));
|
||||
glUniform3fv(sky_shader.loc("sunDir"), 1, glm::value_ptr(sun_dir_view));
|
||||
glUniform3fv(sky_shader.loc("sunColor"), 1,
|
||||
glm::value_ptr(m_parallel_light.directional_light_color));
|
||||
glUniform1f(sky_shader.loc("horizonSharpness"), horizon_sharpness);
|
||||
glUniform1f(sky_shader.loc("time"), m_cloud_time);
|
||||
glUniform1f(sky_shader.loc("cloudWhiteMix"), cloud_white_mix);
|
||||
glUniform1f(sky_shader.loc("cloudThresholdLow"), m_cloud_threshold_low);
|
||||
glUniform1f(sky_shader.loc("cloudThresholdHigh"), m_cloud_threshold_high);
|
||||
glBindVertexArray(m_vao[1]);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 36);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
// draw sun and moon
|
||||
const auto& billboard = get_shader("billboard");
|
||||
billboard.use();
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
glBindVertexArray(m_vao[0]);
|
||||
// draw sun
|
||||
glm::vec3 sun_pos = m_camera.get_camera_pos() +
|
||||
normalize(-m_world.sunlight_dir()) * (FAR_PLANE * 0.9f);
|
||||
glm::vec3 sun_view_pos = glm::vec3(m_v_mat * glm::vec4(sun_pos, 1.0f));
|
||||
m_mv_mat = glm::translate(glm::mat4(1.0f), sun_view_pos) *
|
||||
glm::scale(glm::mat4(1.0f), glm::vec3(SUN_SIZE)) *
|
||||
glm::translate(glm::mat4(1.0f), glm::vec3(-0.5f, -0.5f, 0.0f));
|
||||
|
||||
m_mv_loc = billboard.loc("mv_matrix");
|
||||
m_proj_loc = billboard.loc("proj_matrix");
|
||||
|
||||
glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat));
|
||||
glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat));
|
||||
glUniform3fv(billboard.loc("color"), 1, glm::value_ptr(SUN_COLOR));
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
|
||||
glm::vec3 moon_pos = m_camera.get_camera_pos() +
|
||||
normalize(m_world.sunlight_dir()) * (FAR_PLANE * 0.9f);
|
||||
glm::vec3 moon_view_pos = glm::vec3(m_v_mat * glm::vec4(moon_pos, 1.0f));
|
||||
m_mv_mat = glm::translate(glm::mat4(1.0f), moon_view_pos) *
|
||||
glm::scale(glm::mat4(1.0f), glm::vec3(MOON_SIZE)) *
|
||||
glm::translate(glm::mat4(1.0f), glm::vec3(-0.5f, -0.5f, 0.0f));
|
||||
glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat));
|
||||
glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat));
|
||||
glUniform3fv(billboard.loc("color"), 1, glm::value_ptr(MOON_COLOR));
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
||||
void Renderer::render_text() {
|
||||
@@ -343,7 +470,8 @@ void Renderer::update_fov(float fov) {
|
||||
|
||||
void Renderer::update_proj_matrix(float aspect, float width, float height) {
|
||||
m_aspect = aspect;
|
||||
m_p_mat = glm::perspective(glm::radians(m_fov), aspect, 0.1f, 1000.0f);
|
||||
m_p_mat =
|
||||
glm::perspective(glm::radians(m_fov), aspect, NEAR_PLANE, FAR_PLANE);
|
||||
m_ui_proj = glm::ortho(0.0f, width, height, 0.0f, -1.0f, 1.0f);
|
||||
// scale and then translate
|
||||
m_ui_m_matrix =
|
||||
@@ -423,11 +551,150 @@ 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_COMPONENT32F, DEPTH_MAP_SIZE,
|
||||
DEPTH_MAP_SIZE, 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;
|
||||
}
|
||||
|
||||
#pragma region render_world
|
||||
void Renderer::render_world() {
|
||||
// shader map
|
||||
glm::mat4 light_space_matrix;
|
||||
auto& m_render_snapshots = m_world.render_snapshots();
|
||||
auto& camera_pos = m_camera.get_camera_pos();
|
||||
float texels_per_unit = 0.0f;
|
||||
|
||||
const auto& lightdir = m_parallel_light.lightdir;
|
||||
|
||||
if (m_shader_on) {
|
||||
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 = 128.0f;
|
||||
|
||||
glm::vec3 center = cam_pos + cam_fwd * (half_extent * 0.5f);
|
||||
|
||||
glm::vec3 raw_shadow_lightdir =
|
||||
quantize_sun_direction(lightdir, ANGLE_STEP_DEG);
|
||||
glm::vec3 shadow_lightdir =
|
||||
get_smoothed_shadow_lightdir(raw_shadow_lightdir, m_delta_time);
|
||||
glm::vec3 up = fabs(shadow_lightdir.y) > 0.99f ? glm::vec3(0, 0, 1)
|
||||
: glm::vec3(0, 1, 0);
|
||||
|
||||
glm::mat4 light_basis =
|
||||
glm::lookAt(glm::vec3(0.0f), shadow_lightdir, up);
|
||||
texels_per_unit = DEPTH_MAP_SIZE / (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 = half_extent * 1.5f;
|
||||
float near_plane = 1.0f;
|
||||
float far_plane = distance + half_extent * 2.0f;
|
||||
glm::vec3 light_pos = snapped_center - shadow_lightdir * 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,
|
||||
near_plane, far_plane);
|
||||
|
||||
light_space_matrix = light_projection * light_view;
|
||||
glUniformMatrix4fv(depth_shader.loc("lightSpaceMatrix"), 1, GL_FALSE,
|
||||
glm::value_ptr(light_space_matrix));
|
||||
glUniform1i(depth_shader.loc("is_discard_tranparent"),
|
||||
m_discard_tranparent);
|
||||
glViewport(0, 0, DEPTH_MAP_SIZE, DEPTH_MAP_SIZE);
|
||||
if (m_light_cull_face == 0) {
|
||||
glCullFace(GL_FRONT);
|
||||
} else if (m_light_cull_face == 1) {
|
||||
glCullFace(GL_BACK);
|
||||
} else {
|
||||
Logger::warn("Light Cull Face {} Over The Max Selection",
|
||||
m_light_cull_face);
|
||||
glCullFace(GL_BACK);
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, m_depth_map_fbo);
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
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();
|
||||
|
||||
@@ -435,17 +702,43 @@ 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();
|
||||
m_mv_mat = m_v_mat * m_m_mat;
|
||||
m_norm_mat = glm::transpose(glm::inverse(m_mv_mat));
|
||||
glm::vec3 light_dir_view = glm::normalize(glm::mat3(m_v_mat) * lightdir);
|
||||
|
||||
glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat));
|
||||
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(m_parallel_light.directional_light_color));
|
||||
glUniform3fv(normal_block_shader.loc("ambientColor"), 1,
|
||||
glm::value_ptr(m_parallel_light.finnal_ambient_color));
|
||||
glUniform3fv(normal_block_shader.loc("sunlightDir"), 1,
|
||||
glm::value_ptr(light_dir_view));
|
||||
glUniform1i(normal_block_shader.loc("shadowMode"), m_shadow_mode);
|
||||
glUniform1i(normal_block_shader.loc("shader_on"), m_shader_on);
|
||||
glUniform1f(normal_block_shader.loc("texelsPerUnit"), texels_per_unit);
|
||||
glUniform1f(normal_block_shader.loc("lightSizeUV"),
|
||||
static_cast<GLfloat>(m_light_size_uv));
|
||||
glUniform1f(normal_block_shader.loc("minRadius"), m_min_radius);
|
||||
glUniform1f(normal_block_shader.loc("maxRadius"), m_max_radius);
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
@@ -526,6 +819,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)) {
|
||||
@@ -564,11 +858,71 @@ void Renderer::render_world() {
|
||||
DebugCollector::get().report(
|
||||
"rendered_chunk", "Rendered Chunk: " + std::to_string(rendered_sum));
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
void Renderer::render_dev_panel() {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
m_dev_panel.render();
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
glm::vec3 Renderer::quantize_sun_direction(const glm::vec3& lightdir,
|
||||
float angle_step_deg) const {
|
||||
float elevation = std::asin(glm::clamp(lightdir.y, -1.0f, 1.0f));
|
||||
float azimuth = std::atan2(lightdir.z, lightdir.x);
|
||||
|
||||
float step = glm::radians(angle_step_deg);
|
||||
|
||||
float quantized_elevation = std::round(elevation / step) * step;
|
||||
float quantized_azimuth = std::round(azimuth / step) * step;
|
||||
|
||||
glm::vec3 quantized_dir;
|
||||
quantized_dir.x =
|
||||
std::cos(quantized_elevation) * std::cos(quantized_azimuth);
|
||||
quantized_dir.z =
|
||||
std::cos(quantized_elevation) * std::sin(quantized_azimuth);
|
||||
quantized_dir.y = std::sin(quantized_elevation);
|
||||
|
||||
return glm::normalize(quantized_dir);
|
||||
}
|
||||
|
||||
glm::vec3
|
||||
Renderer::get_smoothed_shadow_lightdir(const glm::vec3& raw_shadow_lightdir,
|
||||
float dt) {
|
||||
if (!m_blend_initialized) {
|
||||
|
||||
m_blend_from_lightdir = raw_shadow_lightdir;
|
||||
m_blend_to_lightdir = raw_shadow_lightdir;
|
||||
m_blend_t = 1.0f;
|
||||
m_blend_initialized = true;
|
||||
return raw_shadow_lightdir;
|
||||
}
|
||||
|
||||
if (raw_shadow_lightdir != m_blend_to_lightdir) {
|
||||
glm::vec3 current_displayed = glm::normalize(
|
||||
Math::slerp(m_blend_from_lightdir, m_blend_to_lightdir, m_blend_t));
|
||||
|
||||
m_blend_from_lightdir = current_displayed;
|
||||
m_blend_to_lightdir = raw_shadow_lightdir;
|
||||
m_blend_t = 0.0f;
|
||||
}
|
||||
|
||||
m_blend_t = glm::min(m_blend_t + dt / BLEND_DURATION, 1.0f);
|
||||
|
||||
return glm::normalize(
|
||||
Math::slerp(m_blend_from_lightdir, m_blend_to_lightdir, m_blend_t));
|
||||
}
|
||||
|
||||
float& Renderer::ambient_strength() { return m_ambient_strength; }
|
||||
bool& Renderer::discard_transparent() { return m_discard_tranparent; }
|
||||
bool& Renderer::shader_on() { return m_shader_on; }
|
||||
int& Renderer::shadow_mode() { return m_shadow_mode; }
|
||||
int& Renderer::light_cull_face() { return m_light_cull_face; }
|
||||
int& Renderer::light_size_uv() { return m_light_size_uv; }
|
||||
float& Renderer::min_radius() { return m_min_radius; }
|
||||
float& Renderer::max_radius() { return m_max_radius; }
|
||||
int& Renderer::samples() { return m_samples; }
|
||||
float& Renderer::specular_strength() { return m_specular_strength; }
|
||||
float& Renderer::cloud_speed() { return m_cloud_speed; }
|
||||
float& Renderer::cloud_threshold_low() { return m_cloud_threshold_low; }
|
||||
float& Renderer::cloud_threshold_high() { return m_cloud_threshold_high; }
|
||||
} // namespace Cubed
|
||||
@@ -164,6 +164,8 @@ void TextureManager::init_block() {
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER,
|
||||
GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
|
||||
if (m_aniso >= 1) {
|
||||
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_ANISOTROPY,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#include "Cubed/tools/math_tools.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
namespace Math {
|
||||
@@ -65,6 +67,39 @@ float deterministic_random(int x, int z, uint64_t seed) {
|
||||
h = h * 6364136223846793005ULL + (uint64_t)z;
|
||||
return (float)(h >> 40) / (float)(1 << 24);
|
||||
}
|
||||
|
||||
glm::vec3 slerp(const glm::vec3& from, const glm::vec3& to, float t) {
|
||||
|
||||
float cos_theta = glm::clamp(glm::dot(from, to), -1.0f, 1.0f);
|
||||
|
||||
if (cos_theta > 0.9995f) {
|
||||
return glm::normalize(glm::mix(from, to, t));
|
||||
}
|
||||
|
||||
if (cos_theta < -0.9995f) {
|
||||
|
||||
glm::vec3 axis = (std::fabs(from.x) < 0.9f)
|
||||
? glm::vec3(1.0f, 0.0f, 0.0f)
|
||||
: glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
glm::vec3 ortho = glm::normalize(glm::cross(from, axis));
|
||||
|
||||
float angle = glm::pi<float>() * t;
|
||||
|
||||
glm::vec3 rotated =
|
||||
from * std::cos(angle) + glm::cross(ortho, from) * std::sin(angle);
|
||||
|
||||
return glm::normalize(rotated);
|
||||
}
|
||||
|
||||
float theta = std::acos(cos_theta);
|
||||
float sin_theta = std::sin(theta);
|
||||
|
||||
float a = std::sin((1.0f - t) * theta) / sin_theta;
|
||||
float b = std::sin(t * theta) / sin_theta;
|
||||
|
||||
return glm::normalize(a * from + b * to);
|
||||
}
|
||||
|
||||
} // namespace Math
|
||||
|
||||
} // namespace Cubed
|
||||
Reference in New Issue
Block a user