mirror of
https://github.com/blender/blender
synced 2026-09-26 16:15:47 +03:00
EEVEE-Next: Refactor world spherical harmonic extraction
This uses parallel reduction when doing the octahedral map re-mapping. The goal is not the speedup but the accuracy of the computation (temporal stability) and to pave the way for sunlight extraction. This weight each individual samples using texel solid angle for correct energy. After optimization, the cost is not so expensive (1024px² octahedral map): - new: 263µs remap + 12µs sum - old: 75µs remap + 180µs irradiance update We could optimize it more, but that feels unecessary given that the first two filter pass are 7ms and a more pressing optimization. The old irradiance update was fast because it was using the mip2 which was already pre-filtered and using way less pixels (which already yield a temporally stable output). This new implementation does consider all pixel in the LOD0 which will allow for more precise sunlight extraction. This also comes with a cleanup of the update tagging. Pull Request: https://projects.blender.org/blender/blender/pulls/119537
This commit is contained in:
parent
f20d9fe5a5
commit
f646f4c2b4
18 changed files with 367 additions and 156 deletions
|
|
@ -529,6 +529,7 @@ set(GLSL_SRC
|
|||
engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_lightprobe_irradiance_offset_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_lightprobe_irradiance_load_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_lightprobe_irradiance_world_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_lightprobe_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_lightprobe_volume_eval_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_lookdev_display_frag.glsl
|
||||
|
|
@ -558,11 +559,11 @@ set(GLSL_SRC
|
|||
engines/eevee_next/shaders/eevee_ray_types_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_convolve_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_eval_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_irradiance_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_mapping_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_remap_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_select_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_update_irradiance_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_renderpass_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_sampling_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_debug_frag.glsl
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@
|
|||
# pragma once
|
||||
#endif
|
||||
|
||||
#ifndef SQUARE
|
||||
# define SQUARE(x) ((x) * (x))
|
||||
#endif
|
||||
|
||||
/* Look Up Tables. */
|
||||
#define LUT_WORKGROUP_SIZE 16
|
||||
|
||||
|
|
@ -30,14 +34,17 @@
|
|||
#define CULLING_TILE_GROUP_SIZE 256
|
||||
|
||||
/* Reflection Probes. */
|
||||
#define SPHERE_PROBE_REMAP_GROUP_SIZE 32
|
||||
#define SPHERE_PROBE_GROUP_SIZE 16
|
||||
#define SPHERE_PROBE_SELECT_GROUP_SIZE 64
|
||||
#define SPHERE_PROBE_MIPMAP_LEVELS 5
|
||||
#define SPHERE_PROBE_SH_GROUP_SIZE 512
|
||||
#define SPHERE_PROBE_SH_GROUP_SIZE 256
|
||||
#define SPHERE_PROBE_SH_SAMPLES_PER_GROUP 64
|
||||
/* Must be power of two for correct partitioning. */
|
||||
#define SPHERE_PROBE_ATLAS_MAX_SUBDIV 10
|
||||
#define SPHERE_PROBE_ATLAS_RES (1 << SPHERE_PROBE_ATLAS_MAX_SUBDIV)
|
||||
/* Maximum number of thread-groups dispatched for remapping a probe to octahedral mapping. */
|
||||
#define SPHERE_PROBE_MAX_HARMONIC SQUARE(SPHERE_PROBE_ATLAS_RES / SPHERE_PROBE_REMAP_GROUP_SIZE)
|
||||
/* Start and end value for mixing sphere probe and volume probes. */
|
||||
#define SPHERE_PROBE_MIX_START_ROUGHNESS 0.7
|
||||
#define SPHERE_PROBE_MIX_END_ROUGHNESS 0.9
|
||||
|
|
|
|||
|
|
@ -70,8 +70,6 @@ void VolumeProbeModule::init()
|
|||
/* Clear the pool to avoid any interpolation to undefined values. */
|
||||
irradiance_atlas_tx_.clear(float4(0.0f));
|
||||
}
|
||||
|
||||
inst_.sphere_probes.tag_world_irradiance_for_update();
|
||||
}
|
||||
|
||||
if (irradiance_atlas_tx_.is_valid() == false) {
|
||||
|
|
@ -188,6 +186,7 @@ void VolumeProbeModule::set_view(View & /*view*/)
|
|||
}
|
||||
|
||||
/* Then create brick & grid infos UBOs content. */
|
||||
int world_grid_index = 0;
|
||||
{
|
||||
/* Stable sorting of grids. */
|
||||
std::sort(
|
||||
|
|
@ -226,6 +225,8 @@ void VolumeProbeModule::set_view(View & /*view*/)
|
|||
}
|
||||
|
||||
/* Insert world grid last. */
|
||||
world_grid_index = grids_len++;
|
||||
|
||||
VolumeProbeData grid;
|
||||
grid.world_to_grid_transposed = float3x4::identity();
|
||||
grid.grid_size = int3(1);
|
||||
|
|
@ -233,7 +234,8 @@ void VolumeProbeModule::set_view(View & /*view*/)
|
|||
grid.normal_bias = 0.0f;
|
||||
grid.view_bias = 0.0f;
|
||||
grid.facing_bias = 0.0f;
|
||||
grids_infos_buf_[grids_len++] = grid;
|
||||
grids_infos_buf_[world_grid_index] = grid;
|
||||
|
||||
bricks_infos_buf_.append(world_brick_index_);
|
||||
|
||||
if (grids_len < IRRADIANCE_GRID_MAX) {
|
||||
|
|
@ -245,6 +247,25 @@ void VolumeProbeModule::set_view(View & /*view*/)
|
|||
grids_infos_buf_.push_update();
|
||||
}
|
||||
|
||||
/* Upload data for world. */
|
||||
if (do_update_world_) {
|
||||
grid_upload_ps_.init();
|
||||
grid_upload_ps_.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_WORLD));
|
||||
grid_upload_ps_.bind_ssbo("harmonic_buf", &inst_.sphere_probes.spherical_harmonics_buf());
|
||||
grid_upload_ps_.bind_ubo("grids_infos_buf", &grids_infos_buf_);
|
||||
grid_upload_ps_.bind_ssbo("bricks_infos_buf", &bricks_infos_buf_);
|
||||
grid_upload_ps_.push_constant("grid_index", world_grid_index);
|
||||
grid_upload_ps_.bind_image("irradiance_atlas_img", &irradiance_atlas_tx_);
|
||||
/* Sync with extraction. */
|
||||
grid_upload_ps_.barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
/* Only upload one brick. */
|
||||
grid_upload_ps_.dispatch(int3(1));
|
||||
/* Sync with next load. */
|
||||
grid_upload_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH);
|
||||
|
||||
inst_.manager->submit(grid_upload_ps_);
|
||||
}
|
||||
|
||||
/* Upload data for each grid that need to be inserted in the atlas.
|
||||
* Upload by order of dependency. */
|
||||
/* Start at world index to not load any other grid (+1 because we decrement at loop start). */
|
||||
|
|
|
|||
|
|
@ -192,10 +192,6 @@ class VolumeProbeModule {
|
|||
public:
|
||||
IrradianceBake bake;
|
||||
|
||||
/** True if world irradiance need to be updated. */
|
||||
/* TODO(fclem): move to private once world irradiance extraction is moved to irradiance cache. */
|
||||
bool do_update_world_ = true;
|
||||
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
|
|
@ -222,12 +218,22 @@ class VolumeProbeModule {
|
|||
bool display_grids_enabled_ = false;
|
||||
PassSimple display_grids_ps_ = {"VolumeProbeModule.Display Grids"};
|
||||
|
||||
/** True if world irradiance need to be updated. */
|
||||
bool do_update_world_ = true;
|
||||
|
||||
public:
|
||||
VolumeProbeModule(Instance &inst) : bake(inst), inst_(inst){};
|
||||
~VolumeProbeModule(){};
|
||||
|
||||
void init();
|
||||
void sync();
|
||||
|
||||
/* Tag all grids for reupload in set_view and composite them with the world irradiance. */
|
||||
void update_world_irradiance()
|
||||
{
|
||||
do_update_world_ = true;
|
||||
}
|
||||
|
||||
void set_view(View &view);
|
||||
void viewport_draw(View &view, GPUFrameBuffer *view_fb);
|
||||
|
||||
|
|
|
|||
|
|
@ -210,7 +210,6 @@ void LightProbeModule::sync_world(const ::World *world, bool has_update)
|
|||
|
||||
if (has_update) {
|
||||
world_sphere_.do_render = true;
|
||||
sph_module.tag_world_irradiance_for_update();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,12 +37,16 @@ void SphereProbeModule::begin_sync()
|
|||
const RaytraceEEVEE &options = instance_.scene->eevee.ray_tracing_options;
|
||||
float probe_brightness_clamp = (options.sample_clamp > 0.0) ? options.sample_clamp : 1e20;
|
||||
|
||||
GPUShader *shader = instance_.shaders.static_shader_get(SPHERE_PROBE_REMAP);
|
||||
|
||||
PassSimple &pass = remap_ps_;
|
||||
pass.init();
|
||||
pass.shader_set(instance_.shaders.static_shader_get(SPHERE_PROBE_REMAP));
|
||||
pass.specialize_constant(shader, "extract_sh", &extract_sh_);
|
||||
pass.shader_set(shader);
|
||||
pass.bind_texture("cubemap_tx", &cubemap_tx_);
|
||||
pass.bind_texture("atlas_tx", &probes_tx_);
|
||||
pass.bind_image("atlas_img", &probes_tx_);
|
||||
pass.bind_ssbo("out_sh", &tmp_spherical_harmonics_);
|
||||
pass.push_constant("probe_coord_packed", reinterpret_cast<int4 *>(&probe_sampling_coord_));
|
||||
pass.push_constant("write_coord_packed", reinterpret_cast<int4 *>(&probe_write_coord_));
|
||||
pass.push_constant("world_coord_packed", reinterpret_cast<int4 *>(&world_data.atlas_coord));
|
||||
|
|
@ -64,13 +68,14 @@ void SphereProbeModule::begin_sync()
|
|||
pass.dispatch(&dispatch_probe_convolve_);
|
||||
}
|
||||
{
|
||||
PassSimple &pass = update_irradiance_ps_;
|
||||
PassSimple &pass = sum_sh_ps_;
|
||||
pass.init();
|
||||
pass.shader_set(instance_.shaders.static_shader_get(SPHERE_PROBE_UPDATE_IRRADIANCE));
|
||||
pass.push_constant("world_coord_packed", reinterpret_cast<int4 *>(&world_data.atlas_coord));
|
||||
pass.bind_image("irradiance_atlas_img", &instance_.volume_probes.irradiance_atlas_tx_);
|
||||
pass.bind_texture("reflection_probes_tx", &probes_tx_);
|
||||
pass.dispatch(int2(1, 1));
|
||||
pass.shader_set(instance_.shaders.static_shader_get(SPHERE_PROBE_IRRADIANCE));
|
||||
pass.push_constant("probe_remap_dispatch_size", &dispatch_probe_pack_);
|
||||
pass.bind_ssbo("in_sh", &tmp_spherical_harmonics_);
|
||||
pass.bind_ssbo("out_sh", &spherical_harmonics_);
|
||||
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
pass.dispatch(1);
|
||||
}
|
||||
{
|
||||
PassSimple &pass = select_ps_;
|
||||
|
|
@ -155,7 +160,7 @@ void SphereProbeModule::ensure_cubemap_render_target(int resolution)
|
|||
/* TODO(fclem): deallocate it. */
|
||||
}
|
||||
|
||||
SphereProbeModule::UpdateInfo SphereProbeModule::update_info_from_probe(const SphereProbe &probe)
|
||||
SphereProbeModule::UpdateInfo SphereProbeModule::update_info_from_probe(SphereProbe &probe)
|
||||
{
|
||||
SphereProbeModule::UpdateInfo info = {};
|
||||
info.atlas_coord = probe.atlas_coord;
|
||||
|
|
@ -163,22 +168,21 @@ SphereProbeModule::UpdateInfo SphereProbeModule::update_info_from_probe(const Sp
|
|||
info.clipping_distances = probe.clipping_distances;
|
||||
info.probe_pos = probe.location;
|
||||
info.do_render = probe.do_render;
|
||||
info.do_world_irradiance_update = false;
|
||||
|
||||
probe.do_render = false;
|
||||
probe.use_for_render = true;
|
||||
|
||||
ensure_cubemap_render_target(info.cube_target_extent);
|
||||
return info;
|
||||
}
|
||||
|
||||
std::optional<SphereProbeModule::UpdateInfo> SphereProbeModule::world_update_info_pop()
|
||||
{
|
||||
SphereProbe &world_probe = instance_.light_probes.world_sphere_;
|
||||
if (!world_probe.do_render && !do_world_irradiance_update) {
|
||||
return std::nullopt;
|
||||
if (world_probe.do_render) {
|
||||
return update_info_from_probe(world_probe);
|
||||
}
|
||||
SphereProbeModule::UpdateInfo info = update_info_from_probe(world_probe);
|
||||
info.do_world_irradiance_update = do_world_irradiance_update;
|
||||
world_probe.do_render = false;
|
||||
do_world_irradiance_update = false;
|
||||
ensure_cubemap_render_target(info.cube_target_extent);
|
||||
return info;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<SphereProbeModule::UpdateInfo> SphereProbeModule::probe_update_info_pop()
|
||||
|
|
@ -192,23 +196,22 @@ std::optional<SphereProbeModule::UpdateInfo> SphereProbeModule::probe_update_inf
|
|||
if (!probe.do_render) {
|
||||
continue;
|
||||
}
|
||||
SphereProbeModule::UpdateInfo info = update_info_from_probe(probe);
|
||||
probe.do_render = false;
|
||||
probe.use_for_render = true;
|
||||
ensure_cubemap_render_target(info.cube_target_extent);
|
||||
return info;
|
||||
return update_info_from_probe(probe);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void SphereProbeModule::remap_to_octahedral_projection(const SphereProbeAtlasCoord &atlas_coord)
|
||||
void SphereProbeModule::remap_to_octahedral_projection(const SphereProbeAtlasCoord &atlas_coord,
|
||||
bool extract_spherical_harmonics)
|
||||
{
|
||||
/* Update shader parameters that change per dispatch. */
|
||||
probe_sampling_coord_ = atlas_coord.as_sampling_coord();
|
||||
probe_write_coord_ = atlas_coord.as_write_coord(0);
|
||||
int resolution = probe_write_coord_.extent;
|
||||
dispatch_probe_pack_ = int3(int2(ceil_division(resolution, SPHERE_PROBE_GROUP_SIZE)), 1);
|
||||
dispatch_probe_pack_ = int3(
|
||||
int2(math::divide_ceil(int2(resolution), int2(SPHERE_PROBE_REMAP_GROUP_SIZE))), 1);
|
||||
extract_sh_ = extract_spherical_harmonics;
|
||||
instance_.manager->submit(remap_ps_);
|
||||
|
||||
/* Populate the mip levels */
|
||||
|
|
@ -219,20 +222,21 @@ void SphereProbeModule::remap_to_octahedral_projection(const SphereProbeAtlasCoo
|
|||
probe_read_coord_ = atlas_coord.as_write_coord(i);
|
||||
probe_write_coord_ = atlas_coord.as_write_coord(i + 1);
|
||||
int out_mip_res = probe_write_coord_.extent;
|
||||
dispatch_probe_convolve_ = int3(int2(ceil_division(out_mip_res, SPHERE_PROBE_GROUP_SIZE)), 1);
|
||||
dispatch_probe_convolve_ = int3(
|
||||
math::divide_ceil(int2(out_mip_res), int2(SPHERE_PROBE_GROUP_SIZE)), 1);
|
||||
instance_.manager->submit(convolve_ps_);
|
||||
}
|
||||
|
||||
if (extract_spherical_harmonics) {
|
||||
instance_.manager->submit(sum_sh_ps_);
|
||||
/* All volume probe that needs to composite the world probe need to be updated. */
|
||||
instance_.volume_probes.update_world_irradiance();
|
||||
}
|
||||
|
||||
/* Sync with atlas usage for shading. */
|
||||
GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
|
||||
}
|
||||
|
||||
void SphereProbeModule::update_world_irradiance()
|
||||
{
|
||||
instance_.manager->submit(update_irradiance_ps_);
|
||||
/* All volume probe that needs to composite the world probe need to be updated. */
|
||||
instance_.volume_probes.do_update_world_ = true;
|
||||
}
|
||||
|
||||
void SphereProbeModule::set_view(View & /*view*/)
|
||||
{
|
||||
Vector<SphereProbe *> probe_active;
|
||||
|
|
|
|||
|
|
@ -44,8 +44,8 @@ class SphereProbeModule {
|
|||
|
||||
/** Copy the rendered cube-map to the atlas texture. */
|
||||
PassSimple remap_ps_ = {"Probe.CubemapToOctahedral"};
|
||||
/** Extract irradiance information from the world. */
|
||||
PassSimple update_irradiance_ps_ = {"Probe.UpdateIrradiance"};
|
||||
/** Sum irradiance information optionally extracted during `remap_ps_`. */
|
||||
PassSimple sum_sh_ps_ = {"Probe.SumSphericalHarmonics"};
|
||||
/** Copy volume probe irradiance for the center of sphere probes. */
|
||||
PassSimple select_ps_ = {"Probe.Select"};
|
||||
/** Convolve the octahedral map to fill the Mip-map levels. */
|
||||
|
|
@ -55,6 +55,8 @@ class SphereProbeModule {
|
|||
/** Output mip level for the convolution. */
|
||||
GPUTexture *convolve_output_ = nullptr;
|
||||
int convolve_lod_ = 0;
|
||||
/* True if we extract spherical harmonic during `remap_ps_`. */
|
||||
bool extract_sh_ = false;
|
||||
|
||||
int3 dispatch_probe_pack_ = int3(1);
|
||||
int3 dispatch_probe_convolve_ = int3(1);
|
||||
|
|
@ -78,6 +80,12 @@ class SphereProbeModule {
|
|||
/** Number of the probe to process in the select phase. */
|
||||
int reflection_probe_count_ = 0;
|
||||
|
||||
/** Intermediate buffer to store spherical harmonics. */
|
||||
StorageArrayBuffer<SphereProbeHarmonic, SPHERE_PROBE_MAX_HARMONIC, true>
|
||||
tmp_spherical_harmonics_ = {"tmp_spherical_harmonics_"};
|
||||
/** Final buffer containing the spherical harmonics for the world. */
|
||||
StorageBuffer<SphereProbeHarmonic, true> spherical_harmonics_ = {"spherical_harmonics_"};
|
||||
|
||||
/**
|
||||
* True if the next redraw will trigger a light-probe sphere update.
|
||||
* As syncing the draw passes for rendering has a significant overhead,
|
||||
|
|
@ -120,9 +128,9 @@ class SphereProbeModule {
|
|||
*/
|
||||
int probe_render_extent() const;
|
||||
|
||||
void tag_world_irradiance_for_update()
|
||||
StorageBuffer<SphereProbeHarmonic, true> &spherical_harmonics_buf()
|
||||
{
|
||||
do_world_irradiance_update = true;
|
||||
return spherical_harmonics_;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -154,10 +162,9 @@ class SphereProbeModule {
|
|||
SphereProbeAtlasCoord atlas_coord;
|
||||
|
||||
bool do_render;
|
||||
bool do_world_irradiance_update;
|
||||
};
|
||||
|
||||
UpdateInfo update_info_from_probe(const SphereProbe &probe);
|
||||
UpdateInfo update_info_from_probe(SphereProbe &probe);
|
||||
|
||||
/**
|
||||
* Pop the next reflection probe that requires to be updated.
|
||||
|
|
@ -166,10 +173,13 @@ class SphereProbeModule {
|
|||
std::optional<UpdateInfo> probe_update_info_pop();
|
||||
|
||||
/**
|
||||
* Internal processing passes.
|
||||
* Remap the rendered cube-map `cubemap_tx_` to a octahedral map inside the atlas at the given
|
||||
* coordinate.
|
||||
* If `extract_spherical_harmonics` is true, it will extract the spherical harmonics into
|
||||
* `spherical_harmonics_`.
|
||||
*/
|
||||
void remap_to_octahedral_projection(const SphereProbeAtlasCoord &atlas_coord);
|
||||
void update_world_irradiance();
|
||||
void remap_to_octahedral_projection(const SphereProbeAtlasCoord &atlas_coord,
|
||||
bool extract_spherical_harmonics);
|
||||
|
||||
void sync_display(Vector<SphereProbe *> &probe_active);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -215,12 +215,14 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
|
|||
return "eevee_lightprobe_irradiance_ray";
|
||||
case LIGHTPROBE_IRRADIANCE_LOAD:
|
||||
return "eevee_lightprobe_irradiance_load";
|
||||
case LIGHTPROBE_IRRADIANCE_WORLD:
|
||||
return "eevee_lightprobe_irradiance_world";
|
||||
case SPHERE_PROBE_CONVOLVE:
|
||||
return "eevee_reflection_probe_convolve";
|
||||
case SPHERE_PROBE_REMAP:
|
||||
return "eevee_reflection_probe_remap";
|
||||
case SPHERE_PROBE_UPDATE_IRRADIANCE:
|
||||
return "eevee_reflection_probe_update_irradiance";
|
||||
case SPHERE_PROBE_IRRADIANCE:
|
||||
return "eevee_reflection_probe_irradiance";
|
||||
case SPHERE_PROBE_SELECT:
|
||||
return "eevee_reflection_probe_select";
|
||||
case SHADOW_CLIPMAP_CLEAR:
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ enum eShaderType {
|
|||
LIGHTPROBE_IRRADIANCE_OFFSET,
|
||||
LIGHTPROBE_IRRADIANCE_RAY,
|
||||
LIGHTPROBE_IRRADIANCE_LOAD,
|
||||
LIGHTPROBE_IRRADIANCE_WORLD,
|
||||
|
||||
LOOKDEV_DISPLAY,
|
||||
|
||||
|
|
@ -107,7 +108,7 @@ enum eShaderType {
|
|||
SPHERE_PROBE_CONVOLVE,
|
||||
SPHERE_PROBE_REMAP,
|
||||
SPHERE_PROBE_SELECT,
|
||||
SPHERE_PROBE_UPDATE_IRRADIANCE,
|
||||
SPHERE_PROBE_IRRADIANCE,
|
||||
|
||||
SHADOW_CLIPMAP_CLEAR,
|
||||
SHADOW_DEBUG,
|
||||
|
|
|
|||
|
|
@ -1105,6 +1105,16 @@ struct SphereProbeDisplayData {
|
|||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(SphereProbeDisplayData, 16)
|
||||
|
||||
/* Used for sphere probe spherical harmonics extraction. Output one for each thread-group
|
||||
* and do a sum afterward. Reduces bandwidth usage. */
|
||||
struct SphereProbeHarmonic {
|
||||
float4 L0_M0;
|
||||
float4 L1_Mn1;
|
||||
float4 L1_M0;
|
||||
float4 L1_Mp1;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(SphereProbeHarmonic, 16)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
@ -1613,6 +1623,5 @@ using VelocityIndexBuf = draw::StorageArrayBuffer<VelocityIndex, 16>;
|
|||
using VelocityObjectBuf = draw::StorageArrayBuffer<float4x4, 16>;
|
||||
using CryptomatteObjectBuf = draw::StorageArrayBuffer<float2, 16>;
|
||||
using ClipPlaneBuf = draw::UniformBuffer<ClipPlaneData>;
|
||||
|
||||
} // namespace blender::eevee
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -287,11 +287,7 @@ void CaptureView::render_world()
|
|||
inst_.pipelines.world.render(view);
|
||||
}
|
||||
|
||||
inst_.sphere_probes.remap_to_octahedral_projection(update_info->atlas_coord);
|
||||
}
|
||||
|
||||
if (update_info->do_world_irradiance_update) {
|
||||
inst_.sphere_probes.update_world_irradiance();
|
||||
inst_.sphere_probes.remap_to_octahedral_projection(update_info->atlas_coord, true);
|
||||
}
|
||||
|
||||
GPU_debug_group_end();
|
||||
|
|
@ -349,7 +345,7 @@ void CaptureView::render_probes()
|
|||
inst_.render_buffers.release();
|
||||
inst_.gbuffer.release();
|
||||
GPU_debug_group_end();
|
||||
inst_.sphere_probes.remap_to_octahedral_projection(update_info->atlas_coord);
|
||||
inst_.sphere_probes.remap_to_octahedral_projection(update_info->atlas_coord, false);
|
||||
}
|
||||
|
||||
if (inst_.pipelines.data.is_probe_reflection) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* Load the extracted spherical harmonics from the world into the probe volume atlas.
|
||||
*
|
||||
* The whole thread group will load the same data and write a brick worth of data.
|
||||
*/
|
||||
|
||||
void atlas_store(vec4 sh_coefficient, ivec2 atlas_coord, int layer)
|
||||
{
|
||||
imageStore(irradiance_atlas_img,
|
||||
ivec3(atlas_coord, layer * IRRADIANCE_GRID_BRICK_SIZE) + ivec3(gl_LocalInvocationID),
|
||||
sh_coefficient);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
int brick_index = grids_infos_buf[grid_index].brick_offset;
|
||||
|
||||
/* Brick coordinate in the destination atlas. */
|
||||
IrradianceBrick brick = irradiance_brick_unpack(bricks_infos_buf[brick_index]);
|
||||
ivec2 output_coord = ivec2(brick.atlas_coord);
|
||||
|
||||
atlas_store(harmonic_buf.L0_M0, output_coord, 0);
|
||||
atlas_store(harmonic_buf.L1_Mn1, output_coord, 1);
|
||||
atlas_store(harmonic_buf.L1_M0, output_coord, 2);
|
||||
atlas_store(harmonic_buf.L1_Mp1, output_coord, 3);
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/* Sum all spherical harmonic coefficients extracting during remapping to octahedral map.
|
||||
* Dispatch only one thread-group that sums. */
|
||||
|
||||
#pragma BLENDER_REQUIRE(eevee_reflection_probe_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_reflection_probe_mapping_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
|
||||
|
||||
shared vec4 local_sh_coefs[gl_WorkGroupSize.x][4];
|
||||
|
||||
void spherical_harmonic_lds_store(uint index, SphericalHarmonicL1 sh)
|
||||
{
|
||||
local_sh_coefs[index][0] = sh.L0.M0;
|
||||
local_sh_coefs[index][1] = sh.L1.Mn1;
|
||||
local_sh_coefs[index][2] = sh.L1.M0;
|
||||
local_sh_coefs[index][3] = sh.L1.Mp1;
|
||||
}
|
||||
|
||||
SphericalHarmonicL1 spherical_harmonic_lds_load(uint index)
|
||||
{
|
||||
SphericalHarmonicL1 sh;
|
||||
sh.L0.M0 = local_sh_coefs[index][0];
|
||||
sh.L1.Mn1 = local_sh_coefs[index][1];
|
||||
sh.L1.M0 = local_sh_coefs[index][2];
|
||||
sh.L1.Mp1 = local_sh_coefs[index][3];
|
||||
return sh;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
SphericalHarmonicL1 sh;
|
||||
sh.L0.M0 = vec4(0.0);
|
||||
sh.L1.Mn1 = vec4(0.0);
|
||||
sh.L1.M0 = vec4(0.0);
|
||||
sh.L1.Mp1 = vec4(0.0);
|
||||
|
||||
/* First sum onto the local memory. */
|
||||
uint valid_data_len = probe_remap_dispatch_size.x * probe_remap_dispatch_size.y;
|
||||
const uint iter_count = uint(SPHERE_PROBE_MAX_HARMONIC) / gl_WorkGroupSize.x;
|
||||
for (uint i = 0; i < iter_count; i++) {
|
||||
uint index = gl_WorkGroupSize.x * i + gl_LocalInvocationIndex;
|
||||
if (index >= valid_data_len) {
|
||||
break;
|
||||
}
|
||||
SphericalHarmonicL1 sh_sample;
|
||||
sh_sample.L0.M0 = in_sh[index].L0_M0;
|
||||
sh_sample.L1.Mn1 = in_sh[index].L1_Mn1;
|
||||
sh_sample.L1.M0 = in_sh[index].L1_M0;
|
||||
sh_sample.L1.Mp1 = in_sh[index].L1_Mp1;
|
||||
sh = spherical_harmonics_add(sh, sh_sample);
|
||||
}
|
||||
|
||||
/* Then sum across invocations. */
|
||||
const uint local_index = gl_LocalInvocationIndex;
|
||||
local_sh_coefs[local_index][0] = sh.L0.M0;
|
||||
local_sh_coefs[local_index][1] = sh.L1.Mn1;
|
||||
local_sh_coefs[local_index][2] = sh.L1.M0;
|
||||
local_sh_coefs[local_index][3] = sh.L1.Mp1;
|
||||
|
||||
/* Parallel sum. */
|
||||
const uint group_size = gl_WorkGroupSize.x * gl_WorkGroupSize.y;
|
||||
for (uint stride = group_size / 2; stride > 0; stride /= 2) {
|
||||
barrier();
|
||||
if (local_index < stride) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
local_sh_coefs[local_index][i] += local_sh_coefs[local_index + stride][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
barrier();
|
||||
if (gl_LocalInvocationIndex == 0u) {
|
||||
out_sh.L0_M0 = local_sh_coefs[0][0];
|
||||
out_sh.L1_Mn1 = local_sh_coefs[0][1];
|
||||
out_sh.L1_M0 = local_sh_coefs[0][2];
|
||||
out_sh.L1_Mp1 = local_sh_coefs[0][3];
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,63 @@
|
|||
|
||||
#pragma BLENDER_REQUIRE(eevee_reflection_probe_mapping_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_colorspace_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
|
||||
|
||||
shared vec4 local_radiance[gl_WorkGroupSize.x * gl_WorkGroupSize.y];
|
||||
|
||||
float triangle_solid_angle(vec3 A, vec3 B, vec3 C)
|
||||
{
|
||||
return 2.0 * atan(abs(dot(A, cross(B, C))), (1.0 + dot(B, C) + dot(A, C) + dot(A, B)));
|
||||
}
|
||||
|
||||
float quad_solid_angle(vec3 A, vec3 B, vec3 C, vec3 D)
|
||||
{
|
||||
return triangle_solid_angle(A, B, C) + triangle_solid_angle(C, B, D);
|
||||
}
|
||||
|
||||
float octahedral_texel_solid_angle(ivec2 local_texel,
|
||||
SphereProbePixelArea write_co,
|
||||
SphereProbeUvArea sample_co)
|
||||
{
|
||||
if (any(equal(local_texel, ivec2(write_co.extent - 1)))) {
|
||||
/* Do not weight these border pixels that are redundant. */
|
||||
return 0.0;
|
||||
}
|
||||
/* Since we are puting texel centers on the edges of the octahedron, the shape of a texel can be
|
||||
* anything from a simple quad (at the Z=0 poles), to a 4 pointed start (at the Z=+-1 poles)
|
||||
* passing by arrow tail shapes (at the X=0 and Y=0 edges). So while it would be more correct to
|
||||
* account for all these shapes (using 8 triangles), it proves to be quite involved with all the
|
||||
* corner cases. Instead, we compute the area as if the texels were not aligned with the edges.
|
||||
* This simplify things at the cost of making the weighting a tiny bit off for every pixels.
|
||||
* The sum of all texels is still giving 4 PI. */
|
||||
vec3 v00 = sphere_probe_texel_to_direction(local_texel + ivec2(-1, -1), write_co, sample_co);
|
||||
vec3 v10 = sphere_probe_texel_to_direction(local_texel + ivec2(+0, -1), write_co, sample_co);
|
||||
vec3 v20 = sphere_probe_texel_to_direction(local_texel + ivec2(-1, -1), write_co, sample_co);
|
||||
vec3 v01 = sphere_probe_texel_to_direction(local_texel + ivec2(-1, +0), write_co, sample_co);
|
||||
vec3 v11 = sphere_probe_texel_to_direction(local_texel + ivec2(+0, +0), write_co, sample_co);
|
||||
vec3 v21 = sphere_probe_texel_to_direction(local_texel + ivec2(+1, +0), write_co, sample_co);
|
||||
vec3 v02 = sphere_probe_texel_to_direction(local_texel + ivec2(-1, +1), write_co, sample_co);
|
||||
vec3 v12 = sphere_probe_texel_to_direction(local_texel + ivec2(+0, +1), write_co, sample_co);
|
||||
vec3 v22 = sphere_probe_texel_to_direction(local_texel + ivec2(+1, +1), write_co, sample_co);
|
||||
/* The solid angle functions expect normalized vectors. */
|
||||
v00 = normalize(v00);
|
||||
v10 = normalize(v10);
|
||||
v20 = normalize(v20);
|
||||
v01 = normalize(v01);
|
||||
v11 = normalize(v11);
|
||||
v21 = normalize(v21);
|
||||
v02 = normalize(v02);
|
||||
v12 = normalize(v12);
|
||||
v22 = normalize(v22);
|
||||
#if 0 /* Has artifacts, is marginaly more correct. */
|
||||
/* For some reason quad_solid_angle(v10, v20, v11, v21) gives some strange artifacts at Z=0. */
|
||||
return 0.25 * (quad_solid_angle(v00, v10, v01, v11) + quad_solid_angle(v10, v20, v11, v21) +
|
||||
quad_solid_angle(v01, v11, v02, v12) + quad_solid_angle(v11, v21, v12, v22));
|
||||
#else
|
||||
/* Choosing the positive quad (0,0) > (+1,+1) for stability. */
|
||||
return quad_solid_angle(v11, v21, v12, v22);
|
||||
#endif
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
|
|
@ -16,11 +73,6 @@ void main()
|
|||
/* Texel in probe. */
|
||||
ivec2 local_texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
|
||||
/* Exit when pixel being written doesn't fit in the area reserved for the probe. */
|
||||
if (any(greaterThanEqual(local_texel, ivec2(write_coord.extent)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
vec2 wrapped_uv;
|
||||
vec3 direction = sphere_probe_texel_to_direction(
|
||||
local_texel, write_coord, sample_coord, wrapped_uv);
|
||||
|
|
@ -30,7 +82,7 @@ void main()
|
|||
float opacity = 1.0 - radiance_and_transmittance.a;
|
||||
|
||||
/* Composite world into reflection probes. */
|
||||
bool is_world = all(equal(write_coord_packed, world_coord_packed));
|
||||
bool is_world = all(equal(probe_coord_packed, world_coord_packed));
|
||||
if (!is_world && opacity != 1.0) {
|
||||
vec2 world_uv = wrapped_uv * world_coord.scale + world_coord.offset;
|
||||
vec4 world_radiance = textureLod(atlas_tx, vec3(world_uv, world_coord.layer), 0.0);
|
||||
|
|
@ -39,6 +91,53 @@ void main()
|
|||
|
||||
radiance = colorspace_brightness_clamp_max(radiance, probe_brightness_clamp);
|
||||
|
||||
ivec3 texel = ivec3(local_texel + write_coord.offset, write_coord.layer);
|
||||
imageStore(atlas_img, texel, vec4(radiance, 1.0));
|
||||
if (!any(greaterThanEqual(local_texel, ivec2(write_coord.extent)))) {
|
||||
ivec3 texel = ivec3(local_texel + write_coord.offset, write_coord.layer);
|
||||
imageStore(atlas_img, texel, vec4(radiance, 1.0));
|
||||
}
|
||||
|
||||
if (extract_sh) {
|
||||
float sample_weight = octahedral_texel_solid_angle(local_texel, write_coord, sample_coord);
|
||||
|
||||
const uint local_index = gl_LocalInvocationIndex;
|
||||
const uint group_size = gl_WorkGroupSize.x * gl_WorkGroupSize.y;
|
||||
|
||||
/* Parallel sum. Result is stored inside local_radiance[0]. */
|
||||
local_radiance[local_index] = radiance.xyzz * sample_weight;
|
||||
for (uint stride = group_size / 2; stride > 0; stride /= 2) {
|
||||
barrier();
|
||||
if (local_index < stride) {
|
||||
local_radiance[local_index] += local_radiance[local_index + stride];
|
||||
}
|
||||
}
|
||||
|
||||
barrier();
|
||||
if (gl_LocalInvocationIndex == 0u) {
|
||||
/* Find the middle point of the whole thread-group. Use it as light vector.
|
||||
* Note that this is an approximation since the footprint of a thread-group is not
|
||||
* necessarily a convex polygons (with center of gravity at midpoint).
|
||||
* But the actual error introduce by this approximation is not perceivable. */
|
||||
ivec2 max_group_texel = local_texel + ivec2(gl_WorkGroupSize.xy);
|
||||
/* Min direction is the local direction since this is only ran by thread 0. */
|
||||
vec3 min_direction = normalize(direction);
|
||||
vec3 max_direction = normalize(
|
||||
sphere_probe_texel_to_direction(max_group_texel, write_coord, sample_coord));
|
||||
vec3 L = normalize(min_direction + max_direction);
|
||||
/* Convert radiance to spherical harmonics. */
|
||||
SphericalHarmonicL1 sh;
|
||||
sh.L0.M0 = vec4(0.0);
|
||||
sh.L1.Mn1 = vec4(0.0);
|
||||
sh.L1.M0 = vec4(0.0);
|
||||
sh.L1.Mp1 = vec4(0.0);
|
||||
/* TODO(fclem): Cleanup: Should spherical_harmonics_encode_signal_sample return a new sh
|
||||
* instead of adding to it? */
|
||||
spherical_harmonics_encode_signal_sample(L, local_radiance[0], sh);
|
||||
/* Outputs one SH for each threadgroup. */
|
||||
uint work_group_index = gl_NumWorkGroups.x * gl_WorkGroupID.y + gl_WorkGroupID.x;
|
||||
out_sh[work_group_index].L0_M0 = sh.L0.M0;
|
||||
out_sh[work_group_index].L1_Mn1 = sh.L1.Mn1;
|
||||
out_sh[work_group_index].L1_M0 = sh.L1.M0;
|
||||
out_sh[work_group_index].L1_Mp1 = sh.L1.Mp1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,76 +0,0 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/* Shader to extract spherical harmonics cooefs from octahedral mapped reflection probe. */
|
||||
|
||||
#pragma BLENDER_REQUIRE(eevee_reflection_probe_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_reflection_probe_mapping_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
|
||||
|
||||
void atlas_store(vec4 sh_coefficient, ivec2 atlas_coord, int layer)
|
||||
{
|
||||
for (int x = 0; x < IRRADIANCE_GRID_BRICK_SIZE; x++) {
|
||||
for (int y = 0; y < IRRADIANCE_GRID_BRICK_SIZE; y++) {
|
||||
for (int z = 0; z < IRRADIANCE_GRID_BRICK_SIZE; z++) {
|
||||
ivec3 brick_coord = ivec3(x, y, z);
|
||||
imageStore(irradiance_atlas_img,
|
||||
ivec3(atlas_coord, layer * IRRADIANCE_GRID_BRICK_SIZE) + brick_coord,
|
||||
sh_coefficient);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shared vec4 cooefs[gl_WorkGroupSize.x][4];
|
||||
|
||||
void main()
|
||||
{
|
||||
SphericalHarmonicL1 cooef;
|
||||
cooef.L0.M0 = vec4(0.0);
|
||||
cooef.L1.Mn1 = vec4(0.0);
|
||||
cooef.L1.M0 = vec4(0.0);
|
||||
cooef.L1.Mp1 = vec4(0.0);
|
||||
|
||||
SphereProbeUvArea atlas_coord = reinterpret_as_atlas_coord(world_coord_packed);
|
||||
float layer_mipmap = 2;
|
||||
/* Perform multiple sample. */
|
||||
uint store_index = gl_LocalInvocationID.x;
|
||||
float total_samples = float(gl_WorkGroupSize.x * SPHERE_PROBE_SH_SAMPLES_PER_GROUP);
|
||||
float sample_weight = 4.0 * M_PI / total_samples;
|
||||
float sample_offset = float(gl_LocalInvocationID.x * SPHERE_PROBE_SH_SAMPLES_PER_GROUP);
|
||||
for (int sample_index = 0; sample_index < SPHERE_PROBE_SH_SAMPLES_PER_GROUP; sample_index++) {
|
||||
vec2 rand = fract(hammersley_2d(sample_index + sample_offset, total_samples));
|
||||
vec3 direction = sample_sphere(rand);
|
||||
vec4 light = reflection_probes_sample(direction, layer_mipmap, atlas_coord);
|
||||
spherical_harmonics_encode_signal_sample(direction, light * sample_weight, cooef);
|
||||
}
|
||||
cooefs[store_index][0] = cooef.L0.M0;
|
||||
cooefs[store_index][1] = cooef.L1.Mn1;
|
||||
cooefs[store_index][2] = cooef.L1.M0;
|
||||
cooefs[store_index][3] = cooef.L1.Mp1;
|
||||
|
||||
barrier();
|
||||
if (gl_LocalInvocationIndex == 0u) {
|
||||
/* Join results */
|
||||
vec4 result[4];
|
||||
result[0] = vec4(0.0);
|
||||
result[1] = vec4(0.0);
|
||||
result[2] = vec4(0.0);
|
||||
result[3] = vec4(0.0);
|
||||
|
||||
for (uint i = 0; i < gl_WorkGroupSize.x; i++) {
|
||||
result[0] += cooefs[i][0];
|
||||
result[1] += cooefs[i][1];
|
||||
result[2] += cooefs[i][2];
|
||||
result[3] += cooefs[i][3];
|
||||
}
|
||||
|
||||
ivec2 atlas_coord = ivec2(0, 0);
|
||||
atlas_store(result[0], atlas_coord, 0);
|
||||
atlas_store(result[1], atlas_coord, 1);
|
||||
atlas_store(result[2], atlas_coord, 2);
|
||||
atlas_store(result[3], atlas_coord, 3);
|
||||
}
|
||||
}
|
||||
|
|
@ -169,6 +169,20 @@ GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_offset)
|
|||
/** \name Runtime
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_world)
|
||||
.local_group_size(IRRADIANCE_GRID_BRICK_SIZE,
|
||||
IRRADIANCE_GRID_BRICK_SIZE,
|
||||
IRRADIANCE_GRID_BRICK_SIZE)
|
||||
.define("IRRADIANCE_GRID_UPLOAD")
|
||||
.additional_info("eevee_shared")
|
||||
.push_constant(Type::INT, "grid_index")
|
||||
.storage_buf(0, Qualifier::READ, "uint", "bricks_infos_buf[]")
|
||||
.storage_buf(1, Qualifier::READ, "SphereProbeHarmonic", "harmonic_buf")
|
||||
.uniform_buf(0, "VolumeProbeData", "grids_infos_buf[IRRADIANCE_GRID_MAX]")
|
||||
.image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_atlas_img")
|
||||
.compute_source("eevee_lightprobe_irradiance_world_comp.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_load)
|
||||
.local_group_size(IRRADIANCE_GRID_BRICK_SIZE,
|
||||
IRRADIANCE_GRID_BRICK_SIZE,
|
||||
|
|
|
|||
|
|
@ -18,29 +18,28 @@ GPU_SHADER_CREATE_INFO(eevee_reflection_probe_data)
|
|||
|
||||
/* Sample cubemap and remap into an octahedral texture. */
|
||||
GPU_SHADER_CREATE_INFO(eevee_reflection_probe_remap)
|
||||
.local_group_size(SPHERE_PROBE_GROUP_SIZE, SPHERE_PROBE_GROUP_SIZE)
|
||||
.local_group_size(SPHERE_PROBE_REMAP_GROUP_SIZE, SPHERE_PROBE_REMAP_GROUP_SIZE)
|
||||
.specialization_constant(Type::BOOL, "extract_sh", true)
|
||||
.push_constant(Type::IVEC4, "probe_coord_packed")
|
||||
.push_constant(Type::IVEC4, "write_coord_packed")
|
||||
.push_constant(Type::IVEC4, "world_coord_packed")
|
||||
.push_constant(Type::FLOAT, "probe_brightness_clamp")
|
||||
.sampler(0, ImageType::FLOAT_CUBE, "cubemap_tx")
|
||||
.sampler(1, ImageType::FLOAT_2D_ARRAY, "atlas_tx")
|
||||
.storage_buf(0, Qualifier::WRITE, "SphereProbeHarmonic", "out_sh[SPHERE_PROBE_MAX_HARMONIC]")
|
||||
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "atlas_img")
|
||||
.compute_source("eevee_reflection_probe_remap_comp.glsl")
|
||||
.additional_info("eevee_shared")
|
||||
.do_static_compilation(true);
|
||||
|
||||
/* Extract spherical harmonics band L0 + L1 from octahedral mapped reflection probe and update the
|
||||
* world brick of the irradiance cache. */
|
||||
GPU_SHADER_CREATE_INFO(eevee_reflection_probe_update_irradiance)
|
||||
.local_group_size(SPHERE_PROBE_SH_GROUP_SIZE, 1)
|
||||
.define("SPHERE_PROBE")
|
||||
.push_constant(Type::IVEC4, "world_coord_packed")
|
||||
.sampler(0, ImageType::FLOAT_2D_ARRAY, "reflection_probes_tx")
|
||||
.image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_atlas_img")
|
||||
GPU_SHADER_CREATE_INFO(eevee_reflection_probe_irradiance)
|
||||
.local_group_size(SPHERE_PROBE_SH_GROUP_SIZE)
|
||||
.push_constant(Type::IVEC3, "probe_remap_dispatch_size")
|
||||
.storage_buf(0, Qualifier::READ, "SphereProbeHarmonic", "in_sh[SPHERE_PROBE_MAX_HARMONIC]")
|
||||
.storage_buf(1, Qualifier::WRITE, "SphereProbeHarmonic", "out_sh")
|
||||
.additional_info("eevee_shared")
|
||||
.compute_source("eevee_reflection_probe_update_irradiance_comp.glsl")
|
||||
.do_static_compilation(true);
|
||||
.do_static_compilation(true)
|
||||
.compute_source("eevee_reflection_probe_irradiance_comp.glsl");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_reflection_probe_select)
|
||||
.local_group_size(SPHERE_PROBE_SELECT_GROUP_SIZE)
|
||||
|
|
|
|||
|
|
@ -276,6 +276,7 @@ class PassBase {
|
|||
/**
|
||||
* Record a compute dispatch call.
|
||||
*/
|
||||
void dispatch(int group_len);
|
||||
void dispatch(int2 group_len);
|
||||
void dispatch(int3 group_len);
|
||||
void dispatch(int3 *group_len);
|
||||
|
|
@ -802,6 +803,12 @@ inline void PassBase<T>::draw_procedural_indirect(
|
|||
/** \name Compute Dispatch Implementation
|
||||
* \{ */
|
||||
|
||||
template<class T> inline void PassBase<T>::dispatch(int group_len)
|
||||
{
|
||||
BLI_assert(shader_);
|
||||
create_command(Type::Dispatch).dispatch = {int3(group_len, 1, 1)};
|
||||
}
|
||||
|
||||
template<class T> inline void PassBase<T>::dispatch(int2 group_len)
|
||||
{
|
||||
BLI_assert(shader_);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue