Browse Source

Add luminance cubemap filtering to light probe stage

master
C. J. Howard 10 months ago
parent
commit
d7660503f5
12 changed files with 408 additions and 147 deletions
  1. +2
    -0
      src/engine/gl/texture-cube.cpp
  2. +2
    -1
      src/engine/gl/texture.cpp
  3. +19
    -10
      src/engine/gl/texture.hpp
  4. +47
    -37
      src/engine/render/passes/material-pass.cpp
  5. +8
    -81
      src/engine/render/passes/sky-pass.cpp
  6. +0
    -3
      src/engine/render/passes/sky-pass.hpp
  7. +232
    -8
      src/engine/render/stages/light-probe-stage.cpp
  8. +66
    -4
      src/engine/render/stages/light-probe-stage.hpp
  9. +21
    -0
      src/engine/scene/light-probe.cpp
  10. +8
    -0
      src/engine/scene/light-probe.hpp
  11. +2
    -2
      src/game/states/main-menu-state.cpp
  12. +1
    -1
      src/game/states/nest-selection-state.cpp

+ 2
- 0
src/engine/gl/texture-cube.cpp View File

@ -18,6 +18,7 @@
*/
#include <engine/gl/texture-cube.hpp>
#include <cmath>
namespace gl {
@ -103,6 +104,7 @@ void texture_cube::resized()
const auto h = get_height();
const auto layout = infer_cube_map_layout(w, h);
m_face_size = infer_cube_map_face_size(layout, w, h);
m_mip_count = 1 + static_cast<std::uint16_t>(std::log2(m_face_size));
}
} // namespace gl

+ 2
- 1
src/engine/gl/texture.cpp View File

@ -202,7 +202,7 @@ void texture::set_max_level(std::uint8_t level)
glTexParameteri(m_gl_texture_target, GL_TEXTURE_MAX_LEVEL, static_cast<GLint>(m_max_level));
}
void texture::set_mipmap_range(std::uint8_t base_level, std::uint8_t max_level)
void texture::set_mip_range(std::uint8_t base_level, std::uint8_t max_level)
{
m_base_level = base_level;
m_max_level = max_level;
@ -323,6 +323,7 @@ void texture::resize(std::uint16_t width, std::uint16_t height, std::uint16_t de
break;
}
m_mip_count = 1 + static_cast<std::uint16_t>(std::log2(std::max<std::uint16_t>(std::max<std::uint16_t>(width, height), depth)));
glGenerateMipmap(m_gl_texture_target);
glTexParameteriv(m_gl_texture_target, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle_mask);

+ 19
- 10
src/engine/gl/texture.hpp View File

@ -84,26 +84,26 @@ public:
void set_mag_filter(texture_mag_filter filter);
/**
* Sets the index of lowest mipmap level.
* Sets the index of lowest accessible mip level.
*
* @param level Index of the lowest mipmap level.
* @param level Index of the lowest accessible mip level.
*/
void set_base_level(std::uint8_t level);
/**
* Sets the index of highest mipmap level.
* Sets the index of highest accessible mip level.
*
* @param level Index of the highest mipmap level.
* @param level Index of the highest accessible mip level.
*/
void set_max_level(std::uint8_t level);
/**
* Sets the range of mipmap levels.
* Sets the range of accessible mip levels.
*
* @param base Index of the lowest mipmap level
* @param max Index of the highest mipmap level.
* @param base Index of the lowest accessible mip level.
* @param max Index of the highest accessible mip level.
*/
void set_mipmap_range(std::uint8_t base_level, std::uint8_t max_level);
void set_mip_range(std::uint8_t base_level, std::uint8_t max_level);
/**
* Sets the maximum anisotropy.
@ -169,13 +169,19 @@ public:
return m_filters;
}
/// Returns the index of the lowest mipmap level.
/// Returns the number of available mip levels.
[[nodiscard]] inline std::uint16_t get_mip_count() const noexcept
{
return m_mip_count;
}
/// Returns the index of the lowest accessible mip level.
[[nodiscard]] inline std::uint8_t get_base_level() const noexcept
{
return m_base_level;
}
/// Returns the index of the highest mipmap level.
/// Returns the index of the highest accessible mip level.
[[nodiscard]] inline std::uint8_t get_max_level() const noexcept
{
return m_max_level;
@ -259,6 +265,9 @@ private:
std::uint8_t m_base_level{};
std::uint8_t m_max_level{255};
float m_max_anisotropy{};
protected:
std::uint16_t m_mip_count{};
};
} // namespace gl

+ 47
- 37
src/engine/render/passes/material-pass.cpp View File

@ -546,23 +546,7 @@ void material_pass::build_shader_command_buffer(std::vector
command_buffer.emplace_back([&, clip_depth_var](){clip_depth_var->update(clip_depth);});
}
// LTC variables
if (auto ltc_lut_1_var = shader_program.variable("ltc_lut_1"))
{
if (auto ltc_lut_2_var = shader_program.variable("ltc_lut_2"))
{
command_buffer.emplace_back
(
[&, ltc_lut_1_var, ltc_lut_2_var]()
{
ltc_lut_1_var->update(*ltc_lut_1);
ltc_lut_2_var->update(*ltc_lut_2);
}
);
}
}
// IBL variables
// Update IBL variables
if (auto brdf_lut_var = shader_program.variable("brdf_lut"))
{
command_buffer.emplace_back
@ -588,6 +572,17 @@ void material_pass::build_shader_command_buffer(std::vector
);
}
if (auto light_probe_luminance_mip_scale_var = shader_program.variable("light_probe_luminance_mip_scale"))
{
command_buffer.emplace_back
(
[&, light_probe_luminance_mip_scale_var]()
{
light_probe_luminance_mip_scale_var->update(std::max<float>(static_cast<float>(light_probe_luminance_texture->get_mip_count()) - 4.0f, 0.0f));
}
);
}
if (auto light_probe_illuminance_texture_var = shader_program.variable("light_probe_illuminance_texture"))
{
command_buffer.emplace_back
@ -600,6 +595,41 @@ void material_pass::build_shader_command_buffer(std::vector
}
}
// Update LTC variables
if (auto ltc_lut_1_var = shader_program.variable("ltc_lut_1"))
{
if (auto ltc_lut_2_var = shader_program.variable("ltc_lut_2"))
{
command_buffer.emplace_back
(
[&, ltc_lut_1_var, ltc_lut_2_var]()
{
ltc_lut_1_var->update(*ltc_lut_1);
ltc_lut_2_var->update(*ltc_lut_2);
}
);
}
}
if (rectangle_light_count)
{
if (auto rectangle_light_colors_var = shader_program.variable("rectangle_light_colors"))
{
auto rectangle_light_corners_var = shader_program.variable("rectangle_light_corners");
if (rectangle_light_corners_var)
{
command_buffer.emplace_back
(
[&, rectangle_light_colors_var, rectangle_light_corners_var]()
{
rectangle_light_colors_var->update(std::span<const float3>{rectangle_light_colors.data(), rectangle_light_count});
rectangle_light_corners_var->update(std::span<const float3>{rectangle_light_corners.data(), rectangle_light_count * 4});
}
);
}
}
}
// Update directional light variables
if (directional_light_count)
{
@ -696,26 +726,6 @@ void material_pass::build_shader_command_buffer(std::vector
}
}
if (rectangle_light_count)
{
if (auto rectangle_light_colors_var = shader_program.variable("rectangle_light_colors"))
{
auto rectangle_light_corners_var = shader_program.variable("rectangle_light_corners");
if (rectangle_light_corners_var)
{
command_buffer.emplace_back
(
[&, rectangle_light_colors_var, rectangle_light_corners_var]()
{
rectangle_light_colors_var->update(std::span<const float3>{rectangle_light_colors.data(), rectangle_light_count});
rectangle_light_corners_var->update(std::span<const float3>{rectangle_light_corners.data(), rectangle_light_count * 4});
}
);
}
}
}
// Update time variable
if (auto time_var = shader_program.variable("time"))
{

+ 8
- 81
src/engine/render/passes/sky-pass.cpp View File

@ -183,17 +183,6 @@ sky_pass::sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffe
debug::log::warning("{}", m_sky_probe_shader_template->configure(gl::shader_stage::vertex));
}
// Load cubemap downsample shader template
m_cubemap_downsample_shader_template = resource_manager->load<gl::shader_template>("cubemap-downsample.glsl");
// Build cubemap downsample shader program
m_cubemap_downsample_shader_program = m_cubemap_downsample_shader_template->build({});
if (!m_cubemap_downsample_shader_program->linked())
{
debug::log::error("Failed to build cubemap downsample shader program: {}", m_cubemap_downsample_shader_program->info());
debug::log::warning("{}", m_cubemap_downsample_shader_template->configure(gl::shader_stage::vertex));
}
// Load moon textures
m_moon_albedo_map = resource_manager->load<gl::texture_2d>("moon-albedo.tex");
m_moon_normal_map = resource_manager->load<gl::texture_2d>("moon-normal.tex");
@ -408,8 +397,6 @@ void sky_pass::render(render::context& ctx)
rasterizer->use_program(*star_shader_program);
if (star_model_view_projection_var)
star_model_view_projection_var->update(model_view_projection);
if (star_distance_var)
star_distance_var->update(star_distance);
if (star_exposure_var)
star_exposure_var->update(camera_exposure);
if (star_inv_resolution_var)
@ -645,7 +632,6 @@ void sky_pass::set_stars_model(std::shared_ptr model)
if (star_shader_program->linked())
{
star_model_view_projection_var = star_shader_program->variable("model_view_projection");
star_distance_var = star_shader_program->variable("star_distance");
star_exposure_var = star_shader_program->variable("camera_exposure");
star_inv_resolution_var = star_shader_program->variable("inv_resolution");
}
@ -1145,6 +1131,10 @@ void sky_pass::rebuild_sky_probe_command_buffer()
{
m_sky_probe_command_buffer.emplace_back([&, light_direction_var](){light_direction_var->update(dominant_light_direction);});
}
if (auto light_illuminance_var = m_sky_probe_shader_program->variable("light_illuminance"))
{
m_sky_probe_command_buffer.emplace_back([&, light_illuminance_var](){light_illuminance_var->update(dominant_light_illuminance);});
}
if (auto observer_position_var = m_sky_probe_shader_program->variable("observer_position"))
{
m_sky_probe_command_buffer.emplace_back([&, observer_position_var](){observer_position_var->update(observer_position);});
@ -1153,6 +1143,10 @@ void sky_pass::rebuild_sky_probe_command_buffer()
{
m_sky_probe_command_buffer.emplace_back([&, atmosphere_radii_var](){atmosphere_radii_var->update(atmosphere_radii);});
}
if (auto ground_albedo_var = m_sky_probe_shader_program->variable("ground_albedo"))
{
m_sky_probe_command_buffer.emplace_back([&, ground_albedo_var](){ground_albedo_var->update(m_ground_albedo);});
}
// Draw point
m_sky_probe_command_buffer.emplace_back
@ -1164,73 +1158,6 @@ void sky_pass::rebuild_sky_probe_command_buffer()
m_sky_probe->set_illuminance_outdated(true);
}
);
if (!m_cubemap_downsample_shader_program->linked())
{
return;
}
auto cubemap_var = m_cubemap_downsample_shader_program->variable("cubemap");
if (!cubemap_var)
{
return;
}
m_sky_probe_command_buffer.emplace_back
(
[&, cubemap_var]()
{
// Bind downsample shader program
rasterizer->use_program(*m_cubemap_downsample_shader_program);
// Change texture filter mode to linear to prevent undefined access when reading and writing to different mip levels of the same texture
// m_sky_probe->get_luminance_texture()->set_min_filter(gl::texture_min_filter::linear);
// Update cubemap shader variable
cubemap_var->update(*m_sky_probe->get_luminance_texture());
}
);
for (std::size_t i = 1; i < m_sky_probe_framebuffers.size(); ++i)
{
const auto resolution = m_sky_probe->get_luminance_texture()->get_face_size() >> i;
// Bind sky probe mip framebuffer
m_sky_probe_command_buffer.emplace_back
(
[&, resolution, i]()
{
rasterizer->set_viewport(0, 0, resolution, resolution);
// Restrict mipmap range
const std::uint8_t base_mip_level = static_cast<std::uint8_t>(i - 1);
m_sky_probe->get_luminance_texture()->set_mipmap_range(base_mip_level, base_mip_level);
rasterizer->use_framebuffer(*m_sky_probe_framebuffers[i]);
rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::points, 0, 1);
}
);
}
// Restore mipmap range
m_sky_probe_command_buffer.emplace_back
(
[&]()
{
m_sky_probe->get_luminance_texture()->set_mipmap_range(0, 255);
}
);
// Restore texture filter mode
// m_sky_probe_command_buffer.emplace_back
// (
// [&]()
// {
// m_sky_probe->get_luminance_texture()->set_min_filter(gl::texture_min_filter::linear_mipmap_linear);
// }
// );
}
} // namespace render

+ 0
- 3
src/engine/render/passes/sky-pass.hpp View File

@ -262,8 +262,6 @@ private:
std::vector<std::unique_ptr<gl::framebuffer>> m_sky_probe_framebuffers;
std::shared_ptr<gl::shader_template> m_sky_probe_shader_template;
std::unique_ptr<gl::shader_program> m_sky_probe_shader_program;
std::shared_ptr<gl::shader_template> m_cubemap_downsample_shader_template;
std::unique_ptr<gl::shader_program> m_cubemap_downsample_shader_program;
std::vector<std::function<void()>> m_sky_probe_command_buffer;
float3 dominant_light_direction;
@ -326,7 +324,6 @@ private:
std::unique_ptr<gl::shader_program> star_shader_program;
const gl::shader_variable* star_model_view_projection_var;
const gl::shader_variable* star_exposure_var;
const gl::shader_variable* star_distance_var;
const gl::shader_variable* star_inv_resolution_var;
float2 mouse_position;

+ 232
- 8
src/engine/render/stages/light-probe-stage.cpp View File

@ -60,18 +60,135 @@ light_probe_stage::light_probe_stage(gl::rasterizer& rasterizer, ::resource_mana
m_quad_vao->bind(render::vertex_attribute::position, position_attribute);
}
// Load cubemap to spherical harmonics shader template
// Load cubemap to spherical harmonics shader template and build shader program
m_cubemap_to_sh_shader_template = resource_manager.load<gl::shader_template>("cubemap-to-sh.glsl");
// Build cubemap to spherical harmonics shader program
rebuild_cubemap_to_sh_shader_program();
// Load cubemap downsample shader template and build shader program
m_cubemap_downsample_shader_template = resource_manager.load<gl::shader_template>("cubemap-downsample.glsl");
rebuild_cubemap_downsample_shader_program();
// Load cubemap filter LUT shader template and build shader program
m_cubemap_filter_lut_shader_template = resource_manager.load<gl::shader_template>("cubemap-filter-lut.glsl");
rebuild_cubemap_filter_lut_shader_program();
// Allocate cubemap filter LUT texture
m_cubemap_filter_lut_texture = std::make_unique<gl::texture_2d>(static_cast<std::uint16_t>(m_cubemap_filter_sample_count), static_cast<std::uint16_t>(m_cubemap_filter_mip_count - 1), gl::pixel_type::float_32, gl::pixel_format::rgba);
// Allocate cubemap filter LUT framebuffer and attach LUT texture
m_cubemap_filter_lut_framebuffer = std::make_unique<gl::framebuffer>();
m_cubemap_filter_lut_framebuffer->attach(gl::framebuffer_attachment_type::color, m_cubemap_filter_lut_texture.get());
// Build cubemap filter LUT texture
rebuild_cubemap_filter_lut_texture();
// Load cubemap filter shader template and build shader program
m_cubemap_filter_shader_template = resource_manager.load<gl::shader_template>("cubemap-filter.glsl");
rebuild_cubemap_filter_shader_program();
}
void light_probe_stage::execute(render::context& ctx)
{
// Get all light probes in the collection
const auto& light_probes = ctx.collection->get_objects(scene::light_probe::object_type_id);
if (light_probes.empty())
{
return;
}
update_light_probes_luminance(light_probes);
update_light_probes_illuminance(light_probes);
}
void light_probe_stage::update_light_probes_luminance(const std::vector<scene::object_base*>& light_probes)
{
bool state_bound = false;
// Downsample cubemaps
std::for_each
(
std::execution::seq,
std::begin(light_probes),
std::end(light_probes),
[&](scene::object_base* object)
{
scene::light_probe& light_probe = static_cast<scene::light_probe&>(*object);
if (!light_probe.is_luminance_outdated() && !m_refilter_cubemaps)
{
return;
}
// Bind state, if unbound
if (!state_bound)
{
glDisable(GL_BLEND);
state_bound = true;
}
// Bind cubemap downsample shader program
m_rasterizer->use_program(*m_cubemap_downsample_shader_program);
// Update cubemap shader variable with light probe luminance texture
m_cubemap_downsample_cubemap_var->update(*light_probe.get_luminance_texture());
// Get resolution of cubemap face for base mip level
const std::uint16_t base_mip_face_size = light_probe.get_luminance_texture()->get_face_size();
// Downsample mip chain
for (std::size_t i = 1; i < light_probe.get_luminance_framebuffers().size(); ++i)
{
// Set viewport to resolution of cubemap face size for current mip level
const std::uint16_t current_mip_face_size = base_mip_face_size >> i;
m_rasterizer->set_viewport(0, 0, current_mip_face_size, current_mip_face_size);
// Restrict cubemap mipmap range to parent mip level
const std::uint8_t parent_mip_level = static_cast<std::uint8_t>(i - 1);
light_probe.get_luminance_texture()->set_mip_range(parent_mip_level, parent_mip_level);
// Bind framebuffer of current cubemap mip level
m_rasterizer->use_framebuffer(*light_probe.get_luminance_framebuffers()[i]);
// Downsample
m_rasterizer->draw_arrays(*m_quad_vao, gl::drawing_mode::points, 0, 1);
}
// Bind cubemap filter shader program
m_rasterizer->use_program(*m_cubemap_filter_shader_program);
// Pass cubemap and filter lut textures to cubemap filter shader program
m_cubemap_filter_cubemap_var->update(*light_probe.get_luminance_texture());
m_cubemap_filter_filter_lut_var->update(*m_cubemap_filter_lut_texture);
// Filter mip chain
for (int i = 1; i < static_cast<int>(light_probe.get_luminance_framebuffers().size()) - 2; ++i)
{
// Update mip level shader variable
m_cubemap_filter_mip_level_var->update(static_cast<int>(i));
// Set viewport to resolution of cubemap face size for current mip level
const std::uint16_t current_mip_face_size = base_mip_face_size >> i;
m_rasterizer->set_viewport(0, 0, current_mip_face_size, current_mip_face_size);
// Restrict cubemap mipmap range to descendent levels
light_probe.get_luminance_texture()->set_mip_range(static_cast<std::uint8_t>(i + 1), 255);
// Bind framebuffer of current cubemap mip level
m_rasterizer->use_framebuffer(*light_probe.get_luminance_framebuffers()[i]);
// Filter
m_rasterizer->draw_arrays(*m_quad_vao, gl::drawing_mode::points, 0, 1);
}
// Restore cubemap mipmap range
light_probe.get_luminance_texture()->set_mip_range(0, 255);
// Mark light probe luminance as current
light_probe.set_luminance_outdated(false);
}
);
}
void light_probe_stage::update_light_probes_illuminance(const std::vector<scene::object_base*>& light_probes)
{
bool state_bound = false;
// For each light probe
@ -101,7 +218,7 @@ void light_probe_stage::execute(render::context& ctx)
m_rasterizer->use_framebuffer(*light_probe.get_illuminance_framebuffer());
// Update cubemap to spherical harmonics cubemap variable with light probe luminance texture
m_cubemap_var->update(*light_probe.get_luminance_texture());
m_cubemap_to_sh_cubemap_var->update(*light_probe.get_luminance_texture());
// Draw quad
m_rasterizer->draw_arrays(*m_quad_vao, gl::drawing_mode::triangle_strip, 0, 4);
@ -123,6 +240,24 @@ void light_probe_stage::set_sh_sample_count(std::size_t count)
}
}
void light_probe_stage::set_cubemap_filter_sample_count(std::size_t count)
{
if (m_cubemap_filter_sample_count != count)
{
m_cubemap_filter_sample_count = count;
cubemap_filter_parameters_changed();
}
}
void light_probe_stage::set_cubemap_filter_mip_bias(float bias)
{
if (m_cubemap_filter_mip_bias != bias)
{
m_cubemap_filter_mip_bias = bias;
cubemap_filter_parameters_changed();
}
}
void light_probe_stage::rebuild_cubemap_to_sh_shader_program()
{
m_cubemap_to_sh_shader_program = m_cubemap_to_sh_shader_template->build({{"SAMPLE_COUNT", std::to_string(m_sh_sample_count)}});
@ -130,24 +265,113 @@ void light_probe_stage::rebuild_cubemap_to_sh_shader_program()
{
debug::log::error("Failed to build cubemap to spherical harmonics shader program: {}", m_cubemap_to_sh_shader_program->info());
debug::log::warning("{}", m_cubemap_to_sh_shader_template->configure(gl::shader_stage::vertex));
m_cubemap_var = nullptr;
m_cubemap_to_sh_cubemap_var = nullptr;
throw std::runtime_error("Failed to build cubemap to spherical harmonics shader program.");
}
else
{
m_cubemap_var = m_cubemap_to_sh_shader_program->variable("cubemap");
if (!m_cubemap_var)
m_cubemap_to_sh_cubemap_var = m_cubemap_to_sh_shader_program->variable("cubemap");
if (!m_cubemap_to_sh_cubemap_var)
{
throw std::runtime_error("Cubemap to spherical harmonics shader program has no `cubemap` variable.");
}
}
}
void light_probe_stage::rebuild_cubemap_downsample_shader_program()
{
m_cubemap_downsample_shader_program = m_cubemap_downsample_shader_template->build({});
if (!m_cubemap_downsample_shader_program->linked())
{
debug::log::error("Failed to build cubemap downsample shader program: {}", m_cubemap_downsample_shader_program->info());
debug::log::warning("{}", m_cubemap_downsample_shader_template->configure(gl::shader_stage::vertex));
m_cubemap_downsample_cubemap_var = nullptr;
throw std::runtime_error("Failed to build cubemap downsample shader program.");
}
else
{
m_cubemap_downsample_cubemap_var = m_cubemap_downsample_shader_program->variable("cubemap");
if (!m_cubemap_downsample_cubemap_var)
{
throw std::runtime_error("Cubemap downsample shader program has no `cubemap` variable.");
}
}
}
void light_probe_stage::rebuild_cubemap_filter_lut_shader_program()
{
m_cubemap_filter_lut_shader_program = m_cubemap_filter_lut_shader_template->build({});
if (!m_cubemap_filter_lut_shader_program->linked())
{
debug::log::error("Failed to build cubemap filter LUT shader program: {}", m_cubemap_filter_lut_shader_program->info());
debug::log::warning("{}", m_cubemap_filter_lut_shader_template->configure(gl::shader_stage::vertex));
m_cubemap_filter_lut_resolution_var = nullptr;
m_cubemap_filter_lut_face_size_var = nullptr;
m_cubemap_filter_lut_mip_bias_var = nullptr;
throw std::runtime_error("Failed to build cubemap filter LUT shader program.");
}
else
{
m_cubemap_filter_lut_resolution_var = m_cubemap_filter_lut_shader_program->variable("resolution");
m_cubemap_filter_lut_face_size_var = m_cubemap_filter_lut_shader_program->variable("face_size");
m_cubemap_filter_lut_mip_bias_var = m_cubemap_filter_lut_shader_program->variable("mip_bias");
if (!m_cubemap_filter_lut_resolution_var || !m_cubemap_filter_lut_face_size_var || !m_cubemap_filter_lut_mip_bias_var)
{
throw std::runtime_error("Cubemap filter LUT shader program is missing one or more required shader variables.");
}
}
}
void light_probe_stage::rebuild_cubemap_filter_lut_texture()
{
glDisable(GL_BLEND);
m_rasterizer->use_framebuffer(*m_cubemap_filter_lut_framebuffer);
m_rasterizer->set_viewport(0, 0, m_cubemap_filter_lut_texture->get_width(), m_cubemap_filter_lut_texture->get_height());
m_rasterizer->use_program(*m_cubemap_filter_lut_shader_program);
m_cubemap_filter_lut_resolution_var->update(math::vector2<float>{static_cast<float>(m_cubemap_filter_lut_texture->get_width()), static_cast<float>(m_cubemap_filter_lut_texture->get_height())});
m_cubemap_filter_lut_face_size_var->update(128.0f);
m_cubemap_filter_lut_mip_bias_var->update(m_cubemap_filter_mip_bias);
m_rasterizer->draw_arrays(*m_quad_vao, gl::drawing_mode::triangle_strip, 0, 4);
}
void light_probe_stage::rebuild_cubemap_filter_shader_program()
{
m_cubemap_filter_shader_program = m_cubemap_filter_shader_template->build({{"SAMPLE_COUNT", std::to_string(m_cubemap_filter_sample_count)}});
if (!m_cubemap_filter_shader_program->linked())
{
debug::log::error("Failed to build cubemap filter shader program: {}", m_cubemap_filter_shader_program->info());
debug::log::warning("{}", m_cubemap_filter_shader_template->configure(gl::shader_stage::vertex));
m_cubemap_filter_cubemap_var = nullptr;
m_cubemap_filter_filter_lut_var = nullptr;
m_cubemap_filter_mip_level_var = nullptr;
throw std::runtime_error("Failed to build cubemap filter shader program.");
}
else
{
m_cubemap_filter_cubemap_var = m_cubemap_filter_shader_program->variable("cubemap");
m_cubemap_filter_filter_lut_var = m_cubemap_filter_shader_program->variable("filter_lut");
m_cubemap_filter_mip_level_var = m_cubemap_filter_shader_program->variable("mip_level");
if (!m_cubemap_filter_cubemap_var || !m_cubemap_filter_filter_lut_var || !m_cubemap_filter_mip_level_var)
{
throw std::runtime_error("Cubemap filter shader program is missing one or more required shader variables.");
}
}
}
void light_probe_stage::sh_parameters_changed()
{
rebuild_cubemap_to_sh_shader_program();
m_reproject_sh = true;
}
void light_probe_stage::cubemap_filter_parameters_changed()
{
m_refilter_cubemaps = true;
}
} // namespace render

+ 66
- 4
src/engine/render/stages/light-probe-stage.hpp View File

@ -48,15 +48,19 @@ public:
*
* @exception std::runtime_error Failed to build cubemap to spherical harmonics shader program.
* @exception std::runtime_error Cubemap to spherical harmonics shader program has no `cubemap` variable.
* @exception std::runtime_error Failed to build cubemap downsample shader program.
* @exception std::runtime_error Cubemap downsample shader program has no `cubemap` variable.
* @exception std::runtime_error Failed to build cubemap filter LUT shader program.
* @exception std::runtime_error Cubemap filter LUT shader program is missing one or more required shader variables.
*/
light_probe_stage(gl::rasterizer& rasterizer, ::resource_manager& resource_manager);
void execute(render::context& ctx) override;
/**
* Sets the number of samples to use when projecting cubemaps into spherical harmonics.
* Sets the number of samples to use when projecting luminance cubemaps into spherical harmonics.
*
* @param count Sample count.
* @param count Spherical harmonics sample count.
*
* @warning Triggers rebuilding of cubemap to spherical harmonics shader program.
* @warning Triggers recalculation of the illuminance of all light probes on next call to `execute()`.
@ -66,24 +70,82 @@ public:
*/
void set_sh_sample_count(std::size_t count);
/// Returns the number of samples used when projecting cubemaps into spherical harmonics.
/**
* Sets the number of samples to use when filtering luminance cubemap mip chains.
*
* @param count Cubemap filter sample count.
*/
void set_cubemap_filter_sample_count(std::size_t count);
/**
* Sets the mip bias to use when filtering luminance cubemap mip chains.
*
* @param bias Cubemap filter mip bias.
*
* @warning Triggers recalculation of the luminance of all light probes on next call to `execute()`.
*
* @exception std::runtime_error Failed to build cubemap filter LUT shader program.
* @exception std::runtime_error Cubemap filter LUT shader program is missing one or more required shader variables.
*/
void set_cubemap_filter_mip_bias(float bias);
/// Returns the number of samples used when projecting luminance cubemaps into spherical harmonics.
[[nodiscard]] inline std::size_t get_sh_sample_count() const noexcept
{
return m_sh_sample_count;
}
/// Returns the number of samples used when filtering luminance cubemaps.
[[nodiscard]] inline std::size_t get_cubemap_filter_sample_count() const noexcept
{
return m_cubemap_filter_sample_count;
}
private:
void rebuild_cubemap_to_sh_shader_program();
void rebuild_cubemap_downsample_shader_program();
void rebuild_cubemap_filter_lut_shader_program();
void rebuild_cubemap_filter_lut_texture();
void rebuild_cubemap_filter_shader_program();
void sh_parameters_changed();
void cubemap_filter_parameters_changed();
void update_light_probes_luminance(const std::vector<scene::object_base*>& light_probes);
void update_light_probes_illuminance(const std::vector<scene::object_base*>& light_probes);
gl::rasterizer* m_rasterizer;
std::unique_ptr<gl::vertex_buffer> m_quad_vbo;
std::unique_ptr<gl::vertex_array> m_quad_vao;
std::shared_ptr<gl::shader_template> m_cubemap_to_sh_shader_template;
std::unique_ptr<gl::shader_program> m_cubemap_to_sh_shader_program;
const gl::shader_variable* m_cubemap_var{};
const gl::shader_variable* m_cubemap_to_sh_cubemap_var{};
std::size_t m_sh_sample_count{1024};
bool m_reproject_sh{true};
std::shared_ptr<gl::shader_template> m_cubemap_downsample_shader_template;
std::unique_ptr<gl::shader_program> m_cubemap_downsample_shader_program;
const gl::shader_variable* m_cubemap_downsample_cubemap_var{};
std::vector<std::unique_ptr<gl::framebuffer>> m_cubemap_downsample_framebuffers;
std::unique_ptr<gl::texture_cube> m_cubemap_downsample_texture;
std::unique_ptr<gl::texture_2d> m_cubemap_filter_lut_texture;
std::unique_ptr<gl::framebuffer> m_cubemap_filter_lut_framebuffer;
std::shared_ptr<gl::shader_template> m_cubemap_filter_lut_shader_template;
std::unique_ptr<gl::shader_program> m_cubemap_filter_lut_shader_program;
const gl::shader_variable* m_cubemap_filter_lut_resolution_var{};
const gl::shader_variable* m_cubemap_filter_lut_face_size_var{};
const gl::shader_variable* m_cubemap_filter_lut_mip_bias_var{};
std::shared_ptr<gl::shader_template> m_cubemap_filter_shader_template;
std::unique_ptr<gl::shader_program> m_cubemap_filter_shader_program;
const gl::shader_variable* m_cubemap_filter_cubemap_var{};
const gl::shader_variable* m_cubemap_filter_filter_lut_var{};
const gl::shader_variable* m_cubemap_filter_mip_level_var{};
std::size_t m_cubemap_filter_sample_count{32};
std::size_t m_cubemap_filter_mip_count{5};
float m_cubemap_filter_mip_bias{1.0f};
bool m_refilter_cubemaps{true};
};
} // namespace render

+ 21
- 0
src/engine/scene/light-probe.cpp View File

@ -47,6 +47,27 @@ void light_probe::set_luminance_texture(std::shared_ptr textur
if (m_luminance_texture != texture)
{
m_luminance_texture = texture;
// Update luminance framebuffers
if (m_luminance_texture)
{
const std::uint8_t mip_count = 1 + static_cast<std::uint8_t>(std::log2(texture->get_face_size()));
for (std::uint8_t i = 0; i < mip_count; ++i)
{
if (i >= m_luminance_framebuffers.size())
{
m_luminance_framebuffers.emplace_back(std::make_shared<gl::framebuffer>());
}
m_luminance_framebuffers[i]->attach(gl::framebuffer_attachment_type::color, m_luminance_texture.get(), i);
}
if (m_luminance_framebuffers.size() > mip_count)
{
m_luminance_framebuffers.resize(mip_count);
}
}
set_luminance_outdated(true);
set_illuminance_outdated(true);
}

+ 8
- 0
src/engine/scene/light-probe.hpp View File

@ -27,6 +27,7 @@
#include <engine/math/matrix.hpp>
#include <memory>
#include <span>
#include <vector>
namespace scene {
@ -76,6 +77,12 @@ public:
return m_luminance_texture;
}
/// Returns the light probe's luminance framebuffers.
[[nodiscard]] inline const std::vector<std::shared_ptr<gl::framebuffer>>& get_luminance_framebuffers() const noexcept
{
return m_luminance_framebuffers;
}
/**
* Returns the light probe's illuminance texture.
*
@ -127,6 +134,7 @@ private:
void transformed() override;
aabb_type m_bounds{};
std::shared_ptr<gl::texture_cube> m_luminance_texture;
std::vector<std::shared_ptr<gl::framebuffer>> m_luminance_framebuffers;
std::shared_ptr<gl::texture_1d> m_illuminance_texture;
std::shared_ptr<gl::framebuffer> m_illuminance_framebuffer;
math::matrix4<float> m_illuminance_matrices[3];

+ 2
- 2
src/game/states/main-menu-state.cpp View File

@ -131,8 +131,8 @@ main_menu_state::main_menu_state(::game& ctx, bool fade_in):
ctx.state_machine.pop();
// ctx.state_machine.emplace(std::make_unique<nuptial_flight_state>(ctx));
// ctx.state_machine.emplace(std::make_unique<collection_menu_state>(ctx));
// ctx.state_machine.emplace(std::make_unique<nest_selection_state>(ctx));
ctx.state_machine.emplace(std::make_unique<nest_view_state>(ctx));
ctx.state_machine.emplace(std::make_unique<nest_selection_state>(ctx));
// ctx.state_machine.emplace(std::make_unique<nest_view_state>(ctx));
}
);
};

+ 1
- 1
src/game/states/nest-selection-state.cpp View File

@ -303,7 +303,7 @@ nest_selection_state::nest_selection_state(::game& ctx):
// Create sphere
auto sphere_eid = ctx.entity_registry->create();
auto sphere_static_mesh = std::make_shared<scene::static_mesh>(ctx.resource_manager->load<render::model>("sphere.mdl"));
auto sphere_static_mesh = std::make_shared<scene::static_mesh>(ctx.resource_manager->load<render::model>("diffuse-spheres.mdl"));
ctx.entity_registry->emplace<scene_component>(sphere_eid, std::move(sphere_static_mesh), std::uint8_t{1});
ctx.entity_registry->patch<scene_component>
(

Loading…
Cancel
Save