WIP: eevee-next-world-irradiance #108304
|
@ -187,6 +187,8 @@ static void lightprobe_grid_cache_frame_blend_read(BlendDataReader *reader,
|
|||
cache->baking.L1_a = nullptr;
|
||||
cache->baking.L1_b = nullptr;
|
||||
cache->baking.L1_c = nullptr;
|
||||
cache->surfels = nullptr;
|
||||
cache->surfels_len = 0;
|
||||
|
||||
if (cache->irradiance.L0 != nullptr) {
|
||||
BLO_read_float3_array(reader, sample_count, (float **)&cache->irradiance.L0);
|
||||
|
@ -257,6 +259,7 @@ void BKE_lightprobe_grid_cache_frame_free(LightProbeGridCacheFrame *cache)
|
|||
spherical_harmonic_free(cache->irradiance);
|
||||
spherical_harmonic_free(cache->visibility);
|
||||
MEM_SAFE_FREE(cache->connectivity.bitmask);
|
||||
MEM_SAFE_FREE(cache->surfels);
|
||||
|
||||
MEM_SAFE_FREE(cache);
|
||||
}
|
||||
|
|
|
@ -107,7 +107,6 @@
|
|||
#include "BLO_read_write.h"
|
||||
|
||||
#include "engines/eevee/eevee_lightcache.h"
|
||||
#include "engines/eevee_next/eevee_lightcache.h"
|
||||
|
||||
#include "PIL_time.h"
|
||||
|
||||
|
@ -1444,10 +1443,7 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id)
|
|||
}
|
||||
}
|
||||
|
||||
RenderEngineType *engine_type = RE_engines_find(sce->r.engine);
|
||||
bool use_eevee_next = STREQ(engine_type->idname, "BLENDER_EEVEE_NEXT");
|
||||
|
||||
(use_eevee_next ? EEVEE_NEXT_lightcache_info_update : EEVEE_lightcache_info_update)(&sce->eevee);
|
||||
EEVEE_lightcache_info_update(&sce->eevee);
|
||||
|
||||
BKE_screen_view3d_shading_blend_read_data(reader, &sce->display.shading);
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
|
||||
#include "PIL_time.h"
|
||||
|
||||
#include "../eevee_next/eevee_lightcache.h"
|
||||
#include "eevee_lightcache.h"
|
||||
#include "eevee_private.h"
|
||||
|
||||
|
@ -502,11 +501,6 @@ static void eevee_lightbake_readback_reflections(LightCache *lcache)
|
|||
|
||||
void EEVEE_lightcache_free(LightCache *lcache)
|
||||
{
|
||||
if (lcache->version == LIGHTCACHE_NEXT_STATIC_VERSION) {
|
||||
EEVEE_NEXT_lightcache_free(lcache);
|
||||
return;
|
||||
}
|
||||
|
||||
DRW_TEXTURE_FREE_SAFE(lcache->cube_tx.tex);
|
||||
MEM_SAFE_FREE(lcache->cube_tx.data);
|
||||
DRW_TEXTURE_FREE_SAFE(lcache->grid_tx.tex);
|
||||
|
@ -545,10 +539,6 @@ static void write_lightcache_texture(BlendWriter *writer, LightCacheTexture *tex
|
|||
|
||||
void EEVEE_lightcache_blend_write(BlendWriter *writer, LightCache *cache)
|
||||
{
|
||||
if (cache->version == LIGHTCACHE_NEXT_STATIC_VERSION) {
|
||||
EEVEE_NEXT_lightcache_blend_write(writer, cache);
|
||||
}
|
||||
|
||||
write_lightcache_texture(writer, &cache->grid_tx);
|
||||
write_lightcache_texture(writer, &cache->cube_tx);
|
||||
|
||||
|
@ -589,10 +579,6 @@ static void direct_link_lightcache_texture(BlendDataReader *reader, LightCacheTe
|
|||
|
||||
void EEVEE_lightcache_blend_read_data(BlendDataReader *reader, LightCache *cache)
|
||||
{
|
||||
if (cache->version == LIGHTCACHE_NEXT_STATIC_VERSION) {
|
||||
EEVEE_NEXT_lightcache_blend_read_data(reader, cache);
|
||||
}
|
||||
|
||||
cache->flag &= ~LIGHTCACHE_NOT_USABLE;
|
||||
direct_link_lightcache_texture(reader, &cache->cube_tx);
|
||||
direct_link_lightcache_texture(reader, &cache->grid_tx);
|
||||
|
|
|
@ -78,9 +78,7 @@ void Camera::sync()
|
|||
|
||||
CameraData &data = data_;
|
||||
|
||||
/* WORKAROUND(fclem): This is an abomination that should be remove ASAP. */
|
||||
bool is_first_baking_sync = inst_.light_probes.grids.size() == 0;
|
||||
if (inst_.is_baking() && !is_first_baking_sync) {
|
||||
if (inst_.is_baking()) {
|
||||
/* Any view so that shadows and light culling works during irradiance bake. */
|
||||
draw::View &view = inst_.irradiance_cache.bake.view_z_;
|
||||
data.viewmat = view.viewmat();
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
|
||||
#include "eevee_engine.h"
|
||||
#include "eevee_instance.hh"
|
||||
#include "eevee_lightcache.h"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
|
@ -333,51 +332,6 @@ void Instance::render_read_result(RenderLayer *render_layer, const char *view_na
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Light Bake
|
||||
* \{ */
|
||||
|
||||
LightCache *Instance::light_cache_create(Vector<IrradianceGrid> grids,
|
||||
Vector<ReflectionCube> cubes)
|
||||
{
|
||||
LightCache *light_cache = EEVEE_NEXT_lightcache_create();
|
||||
light_cache->flag = LIGHTCACHE_BAKING;
|
||||
|
||||
light_cache->grid_len = grids.size();
|
||||
light_cache->grids = MEM_cnew_array<LightCacheIrradianceGrid>(grids.size(),
|
||||
"LightCacheIrradianceGrid");
|
||||
|
||||
for (const auto i : grids.index_range()) {
|
||||
const IrradianceGrid &grid = grids[i];
|
||||
size_t sample_count = grid.resolution.x * grid.resolution.y * grid.resolution.z;
|
||||
size_t grid_texture_sample_size = sizeof(uint16_t) * 4;
|
||||
if (sample_count * grid_texture_sample_size > INT_MAX) {
|
||||
/* The size of the texture doesn't fit on a 32bit system. */
|
||||
light_cache->flag |= LIGHTCACHE_INVALID;
|
||||
info = "Scene contains an irradiance grid with too many samples points";
|
||||
return nullptr;
|
||||
}
|
||||
LightCacheIrradianceGrid &irradiance_grid = light_cache->grids[i];
|
||||
float4x4 world_to_grid = math::invert(grid.transform);
|
||||
copy_m4_m4(irradiance_grid.world_to_grid, world_to_grid.ptr());
|
||||
irradiance_grid.resolution[0] = grid.resolution.x;
|
||||
irradiance_grid.resolution[1] = grid.resolution.y;
|
||||
irradiance_grid.resolution[2] = grid.resolution.z;
|
||||
}
|
||||
|
||||
light_cache->cube_len = cubes.size();
|
||||
// light_cache->cubes = MEM_cnew_array<LightCacheIrradianceGrid>(cubes.size(),
|
||||
// "LightCacheIrradianceGrid");
|
||||
|
||||
// for (const auto i : cubes.index_range()) {
|
||||
/* TODO */
|
||||
// }
|
||||
|
||||
return light_cache;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Interface
|
||||
* \{ */
|
||||
|
@ -498,12 +452,12 @@ void Instance::update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view
|
|||
EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL);
|
||||
}
|
||||
|
||||
void Instance::light_bake_irradiance(LightCache *&r_light_cache,
|
||||
void Instance::light_bake_irradiance(Object &probe,
|
||||
std::function<void()> context_enable,
|
||||
std::function<void()> context_disable)
|
||||
std::function<void()> context_disable,
|
||||
std::function<void(LightProbeGridCacheFrame *)> result_update)
|
||||
{
|
||||
BLI_assert(is_baking());
|
||||
BLI_assert(r_light_cache == nullptr);
|
||||
|
||||
auto custom_pipeline_wrapper = [&](std::function<void()> callback) {
|
||||
context_enable();
|
||||
|
@ -519,67 +473,47 @@ void Instance::light_bake_irradiance(LightCache *&r_light_cache,
|
|||
context_disable();
|
||||
};
|
||||
|
||||
/* Count probes. */
|
||||
/* TODO(fclem): Ideally, this should only iterate the despgraph and not do a full sync. */
|
||||
custom_pipeline_wrapper([&]() {
|
||||
GPU_debug_capture_begin();
|
||||
irradiance_cache.bake.surfel_raster_views_sync(probe);
|
||||
/* TODO: lightprobe visibility group option. */
|
||||
manager->begin_sync();
|
||||
render_sync();
|
||||
manager->end_sync();
|
||||
|
||||
irradiance_cache.bake.surfels_create(probe);
|
||||
irradiance_cache.bake.surfels_lights_eval();
|
||||
GPU_debug_capture_end();
|
||||
});
|
||||
/* Allocate CPU storage. */
|
||||
r_light_cache = this->light_cache_create(light_probes.grids, light_probes.cubes);
|
||||
|
||||
if (r_light_cache->flag & LIGHTCACHE_INVALID) {
|
||||
/* Something happened and the light cache couldn't be created. */
|
||||
// stop = true;
|
||||
// do_update = true;
|
||||
r_light_cache->flag &= ~LIGHTCACHE_BAKING;
|
||||
return;
|
||||
}
|
||||
int bounce_len = scene->eevee.gi_diffuse_bounces;
|
||||
/* Start at bounce 1 as 0 bounce is no indirect lighting. */
|
||||
for (int bounce = 1; bounce <= bounce_len; bounce++) {
|
||||
sampling.init(scene);
|
||||
while (!sampling.finished()) {
|
||||
context_wrapper([&]() {
|
||||
/* Batch ray cast by pack of 16 to avoid too much overhead of
|
||||
* the update function & context switch. */
|
||||
for (int i = 0; i < 16 && !sampling.finished(); i++) {
|
||||
sampling.step();
|
||||
irradiance_cache.bake.propagate_light_sample();
|
||||
}
|
||||
if (sampling.finished()) {
|
||||
irradiance_cache.bake.accumulate_bounce();
|
||||
}
|
||||
|
||||
for (auto i : light_probes.grids.index_range()) {
|
||||
custom_pipeline_wrapper([&]() {
|
||||
GPU_debug_capture_begin();
|
||||
irradiance_cache.bake.surfel_raster_views_sync(light_probes.grids[i]);
|
||||
/* TODO: lightprobe visibility group option. */
|
||||
manager->begin_sync();
|
||||
render_sync();
|
||||
manager->end_sync();
|
||||
|
||||
irradiance_cache.bake.surfels_create(light_probes.grids[i]);
|
||||
irradiance_cache.bake.surfels_lights_eval();
|
||||
GPU_debug_capture_end();
|
||||
});
|
||||
|
||||
int bounce_len = scene->eevee.gi_diffuse_bounces;
|
||||
/* Start at bounce 1 as 0 bounce is no indirect lighting. */
|
||||
for (int bounce = 1; bounce <= bounce_len; bounce++) {
|
||||
sampling.init(scene);
|
||||
while (!sampling.finished()) {
|
||||
context_wrapper([&]() {
|
||||
/* Batch ray cast by pack of 16 to avoid too much overhead of
|
||||
* the update function & context switch. */
|
||||
for (int i = 0; i < 16 && !sampling.finished(); i++) {
|
||||
sampling.step();
|
||||
irradiance_cache.bake.propagate_light_sample();
|
||||
}
|
||||
if (sampling.finished()) {
|
||||
irradiance_cache.bake.accumulate_bounce();
|
||||
}
|
||||
irradiance_cache.bake.read_result(r_light_cache->grids[i]);
|
||||
});
|
||||
// do_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (bounce_len == 0) {
|
||||
/* Still read result for debugging surfel direct lighting. */
|
||||
context_wrapper([&]() { irradiance_cache.bake.read_result(r_light_cache->grids[i]); });
|
||||
LightProbeGridCacheFrame *cache_frame;
|
||||
if (bounce != bounce_len) {
|
||||
/* TODO(fclem): Only do this read-back if needed. But it might be tricky to know when. */
|
||||
cache_frame = irradiance_cache.bake.read_result_unpacked();
|
||||
}
|
||||
else {
|
||||
cache_frame = irradiance_cache.bake.read_result_packed();
|
||||
}
|
||||
result_update(cache_frame);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
r_light_cache->flag |= LIGHTCACHE_BAKED;
|
||||
r_light_cache->flag &= ~LIGHTCACHE_BAKING;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -146,9 +146,10 @@ class Instance {
|
|||
/* Light bake. */
|
||||
|
||||
void init_light_bake(Depsgraph *depsgraph, draw::Manager *manager);
|
||||
void light_bake_irradiance(LightCache *&r_light_cache,
|
||||
void light_bake_irradiance(Object &probe,
|
||||
std::function<void()> context_enable,
|
||||
std::function<void()> context_disable);
|
||||
std::function<void()> context_disable,
|
||||
std::function<void(LightProbeGridCacheFrame *)> result_update);
|
||||
|
||||
static void update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer);
|
||||
|
||||
|
@ -196,13 +197,6 @@ class Instance {
|
|||
void scene_sync();
|
||||
void mesh_sync(Object *ob, ObjectHandle &ob_handle);
|
||||
|
||||
/**
|
||||
* Create a light cache big enough to fit all light-probes inside.
|
||||
* IMPORTANT: Can return nullptr on failure, in which case, the `Instance::info` will be set
|
||||
* to an error message.
|
||||
*/
|
||||
LightCache *light_cache_create(Vector<IrradianceGrid> grids, Vector<ReflectionCube> cubes);
|
||||
|
||||
void update_eval_members();
|
||||
|
||||
void set_time(float time);
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "DNA_lightprobe_types.h"
|
||||
|
||||
#include "BKE_lightprobe.h"
|
||||
|
||||
#include "GPU_capabilities.h"
|
||||
#include "GPU_debug.h"
|
||||
|
||||
|
@ -17,15 +19,15 @@ namespace blender::eevee {
|
|||
/** \name Interface
|
||||
* \{ */
|
||||
|
||||
void IrradianceCache::init() {}
|
||||
void IrradianceCache::init()
|
||||
{
|
||||
display_grids_enabled_ = DRW_state_draw_support() &&
|
||||
inst_.scene->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE;
|
||||
}
|
||||
|
||||
void IrradianceCache::sync()
|
||||
{
|
||||
if (!inst_.is_baking()) {
|
||||
debug_pass_sync();
|
||||
display_pass_sync();
|
||||
}
|
||||
else {
|
||||
if (inst_.is_baking()) {
|
||||
bake.sync();
|
||||
}
|
||||
}
|
||||
|
@ -38,42 +40,6 @@ void IrradianceCache::viewport_draw(View &view, GPUFrameBuffer *view_fb)
|
|||
}
|
||||
}
|
||||
|
||||
void IrradianceCache::debug_pass_sync()
|
||||
{
|
||||
if (!ELEM(inst_.debug_mode,
|
||||
eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL,
|
||||
eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LightCache *light_cache = inst_.scene->eevee.light_cache_data;
|
||||
if (light_cache == nullptr || light_cache->version != LIGHTCACHE_NEXT_STATIC_VERSION ||
|
||||
light_cache->grids == nullptr || light_cache->grid_len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
debug_surfels_ps_.init();
|
||||
debug_surfels_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
|
||||
DRW_STATE_DEPTH_LESS_EQUAL);
|
||||
debug_surfels_ps_.shader_set(inst_.shaders.static_shader_get(DEBUG_SURFELS));
|
||||
debug_surfels_ps_.push_constant("surfel_radius", 1.5f / 4.0f);
|
||||
debug_surfels_ps_.push_constant("debug_mode", static_cast<int>(inst_.debug_mode));
|
||||
|
||||
surfels_buf_.clear();
|
||||
for (auto i : IndexRange(light_cache->grid_len)) {
|
||||
LightCacheIrradianceGrid &grid = light_cache->grids[i];
|
||||
if (grid.surfels_len > 0 && grid.surfels != nullptr) {
|
||||
Span<Surfel> grid_surfels(static_cast<Surfel *>(grid.surfels), grid.surfels_len);
|
||||
for (const Surfel &surfel : grid_surfels) {
|
||||
surfels_buf_.append(surfel);
|
||||
}
|
||||
}
|
||||
}
|
||||
surfels_buf_.push_update();
|
||||
debug_surfels_ps_.bind_ssbo("surfels_buf", surfels_buf_);
|
||||
debug_surfels_ps_.draw_procedural(GPU_PRIM_TRI_STRIP, surfels_buf_.size(), 4);
|
||||
}
|
||||
|
||||
void IrradianceCache::debug_pass_draw(View &view, GPUFrameBuffer *view_fb)
|
||||
{
|
||||
switch (inst_.debug_mode) {
|
||||
|
@ -88,66 +54,105 @@ void IrradianceCache::debug_pass_draw(View &view, GPUFrameBuffer *view_fb)
|
|||
return;
|
||||
}
|
||||
|
||||
GPU_framebuffer_bind(view_fb);
|
||||
inst_.manager->submit(debug_surfels_ps_, view);
|
||||
}
|
||||
|
||||
void IrradianceCache::display_pass_sync()
|
||||
{
|
||||
LightCache *light_cache = inst_.scene->eevee.light_cache_data;
|
||||
|
||||
display_grids_enabled_ = light_cache && light_cache->grid_len > 0 && light_cache->grids &&
|
||||
DRW_state_draw_support() &&
|
||||
inst_.scene->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE;
|
||||
if (!display_grids_enabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
display_grids_ps_.init();
|
||||
display_grids_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
|
||||
DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK);
|
||||
display_grids_ps_.shader_set(inst_.shaders.static_shader_get(DISPLAY_PROBE_GRID));
|
||||
|
||||
for (auto i : IndexRange(light_cache->grid_len)) {
|
||||
LightCacheIrradianceGrid &grid = light_cache->grids[i];
|
||||
|
||||
if (grid.irradiance_L0_L1_a.data == nullptr) {
|
||||
for (const IrradianceGrid &grid : inst_.light_probes.grid_map_.values()) {
|
||||
if (grid.cache == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto load_texture = [&](const char *name, LightCacheTexture &cache_texture) {
|
||||
if ((light_cache->flag & LIGHTCACHE_BAKED) && cache_texture.tex != nullptr) {
|
||||
return;
|
||||
}
|
||||
if (light_cache->flag & LIGHTCACHE_BAKING) {
|
||||
GPU_TEXTURE_FREE_SAFE(cache_texture.tex);
|
||||
}
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ;
|
||||
cache_texture.tex = GPU_texture_create_3d(
|
||||
name, UNPACK3(cache_texture.tex_size), 1, GPU_RGBA16F, usage, cache_texture.data);
|
||||
};
|
||||
load_texture("grid.irradiance_L0_L1_a", grid.irradiance_L0_L1_a);
|
||||
load_texture("grid.irradiance_L0_L1_b", grid.irradiance_L0_L1_b);
|
||||
load_texture("grid.irradiance_L0_L1_c", grid.irradiance_L0_L1_c);
|
||||
LightProbeGridCacheFrame *cache = grid.cache->grid_static_cache;
|
||||
|
||||
display_grids_ps_.push_constant("sphere_radius", 0.3f);
|
||||
display_grids_ps_.push_constant("grid_resolution", int3(grid.resolution));
|
||||
float4x4 grid_to_world = math::invert(float4x4(grid.world_to_grid));
|
||||
display_grids_ps_.push_constant("grid_to_world", grid_to_world);
|
||||
display_grids_ps_.bind_texture("irradiance_a_tx", grid.irradiance_L0_L1_a.tex);
|
||||
display_grids_ps_.bind_texture("irradiance_b_tx", grid.irradiance_L0_L1_b.tex);
|
||||
display_grids_ps_.bind_texture("irradiance_c_tx", grid.irradiance_L0_L1_c.tex);
|
||||
if (cache->surfels == nullptr || cache->surfels_len == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int cell_count = grid.resolution[0] * grid.resolution[1] * grid.resolution[2];
|
||||
display_grids_ps_.draw_procedural(GPU_PRIM_TRIS, 1, cell_count * 3 * 2);
|
||||
debug_surfels_ps_.init();
|
||||
debug_surfels_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
|
||||
DRW_STATE_DEPTH_LESS_EQUAL);
|
||||
display_grids_ps_.framebuffer_set(&view_fb);
|
||||
debug_surfels_ps_.shader_set(inst_.shaders.static_shader_get(DEBUG_SURFELS));
|
||||
debug_surfels_ps_.push_constant("surfel_radius", 1.5f / 4.0f);
|
||||
debug_surfels_ps_.push_constant("debug_mode", static_cast<int>(inst_.debug_mode));
|
||||
|
||||
debug_surfels_buf_.resize(cache->surfels_len);
|
||||
/* TODO(fclem): Cleanup: Could have a function in draw::StorageArrayBuffer that takes an input
|
||||
* data. */
|
||||
Span<Surfel> grid_surfels(static_cast<Surfel *>(cache->surfels), cache->surfels_len);
|
||||
MutableSpan<Surfel>(debug_surfels_buf_.data(), cache->surfels_len).copy_from(grid_surfels);
|
||||
debug_surfels_buf_.push_update();
|
||||
|
||||
debug_surfels_ps_.bind_ssbo("surfels_buf", debug_surfels_buf_);
|
||||
debug_surfels_ps_.draw_procedural(GPU_PRIM_TRI_STRIP, cache->surfels_len, 4);
|
||||
|
||||
inst_.manager->submit(debug_surfels_ps_, view);
|
||||
}
|
||||
}
|
||||
|
||||
void IrradianceCache::display_pass_draw(View &view, GPUFrameBuffer *view_fb)
|
||||
{
|
||||
if (display_grids_enabled_) {
|
||||
GPU_framebuffer_bind(view_fb);
|
||||
if (!display_grids_enabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const IrradianceGrid &grid : inst_.light_probes.grid_map_.values()) {
|
||||
if (grid.cache == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LightProbeGridCacheFrame *cache = grid.cache->grid_static_cache;
|
||||
|
||||
if (cache == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Display texture. Updated for each individual light grid to avoid increasing VRAM usage. */
|
||||
draw::Texture irradiance_a_tx_ = {"irradiance_a_tx"};
|
||||
draw::Texture irradiance_b_tx_ = {"irradiance_b_tx"};
|
||||
draw::Texture irradiance_c_tx_ = {"irradiance_c_tx"};
|
||||
draw::Texture irradiance_d_tx_ = {"irradiance_d_tx"};
|
||||
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ;
|
||||
int3 grid_size = int3(cache->size);
|
||||
if (cache->baking.L0) {
|
||||
irradiance_a_tx_.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L0);
|
||||
irradiance_b_tx_.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_a);
|
||||
irradiance_c_tx_.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_b);
|
||||
irradiance_d_tx_.ensure_3d(GPU_RGBA16F, grid_size, usage, (float *)cache->baking.L1_c);
|
||||
}
|
||||
else if (cache->irradiance.L0) {
|
||||
irradiance_a_tx_.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L0);
|
||||
irradiance_b_tx_.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_a);
|
||||
irradiance_c_tx_.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_b);
|
||||
irradiance_d_tx_.ensure_3d(GPU_RGB16F, grid_size, usage, (float *)cache->irradiance.L1_c);
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
|
||||
display_grids_ps_.init();
|
||||
display_grids_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
|
||||
DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK);
|
||||
display_grids_ps_.framebuffer_set(&view_fb);
|
||||
display_grids_ps_.shader_set(inst_.shaders.static_shader_get(DISPLAY_PROBE_GRID));
|
||||
|
||||
display_grids_ps_.push_constant("sphere_radius", 0.3f); /* TODO property */
|
||||
display_grids_ps_.push_constant("grid_resolution", grid_size);
|
||||
display_grids_ps_.push_constant("grid_to_world", grid.object_to_world);
|
||||
|
||||
display_grids_ps_.bind_texture("irradiance_a_tx", &irradiance_a_tx_);
|
||||
display_grids_ps_.bind_texture("irradiance_b_tx", &irradiance_b_tx_);
|
||||
display_grids_ps_.bind_texture("irradiance_c_tx", &irradiance_c_tx_);
|
||||
display_grids_ps_.bind_texture("irradiance_d_tx", &irradiance_d_tx_);
|
||||
|
||||
int sample_count = static_cast<int>(BKE_lightprobe_grid_cache_frame_sample_count(cache));
|
||||
int triangle_count = sample_count * 2;
|
||||
display_grids_ps_.draw_procedural(GPU_PRIM_TRIS, 1, triangle_count * 3);
|
||||
|
||||
inst_.manager->submit(display_grids_ps_, view);
|
||||
|
||||
irradiance_a_tx_.free();
|
||||
irradiance_b_tx_.free();
|
||||
irradiance_c_tx_.free();
|
||||
irradiance_d_tx_.free();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,9 +220,10 @@ void IrradianceBake::sync()
|
|||
pass.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_);
|
||||
pass.bind_ssbo("list_start_buf", &list_start_buf_);
|
||||
pass.bind_ssbo("list_info_buf", &list_info_buf_);
|
||||
pass.bind_image("irradiance_L0_L1_a_img", &irradiance_L0_L1_a_tx_);
|
||||
pass.bind_image("irradiance_L0_L1_b_img", &irradiance_L0_L1_b_tx_);
|
||||
pass.bind_image("irradiance_L0_L1_c_img", &irradiance_L0_L1_c_tx_);
|
||||
pass.bind_image("irradiance_L0_img", &irradiance_L0_tx_);
|
||||
pass.bind_image("irradiance_L1_a_img", &irradiance_L1_a_tx_);
|
||||
pass.bind_image("irradiance_L1_b_img", &irradiance_L1_b_tx_);
|
||||
pass.bind_image("irradiance_L1_c_img", &irradiance_L1_c_tx_);
|
||||
pass.barrier(GPU_BARRIER_SHADER_STORAGE | GPU_BARRIER_SHADER_IMAGE_ACCESS);
|
||||
pass.dispatch(&dispatch_per_grid_sample_);
|
||||
}
|
||||
|
@ -232,10 +238,10 @@ void IrradianceBake::sync()
|
|||
}
|
||||
}
|
||||
|
||||
void IrradianceBake::surfel_raster_views_sync(const IrradianceGrid &grid)
|
||||
void IrradianceBake::surfel_raster_views_sync(const Object &probe_object)
|
||||
{
|
||||
using namespace blender::math;
|
||||
const float4x4 transform(grid.transform);
|
||||
const float4x4 transform(probe_object.object_to_world);
|
||||
|
||||
float3 scale;
|
||||
math::to_loc_rot_scale(transform, grid_location_, grid_orientation_, scale);
|
||||
|
@ -258,7 +264,7 @@ void IrradianceBake::surfel_raster_views_sync(const IrradianceGrid &grid)
|
|||
sync_view(view_z_, basis_z_);
|
||||
}
|
||||
|
||||
void IrradianceBake::surfels_create(const IrradianceGrid &grid)
|
||||
void IrradianceBake::surfels_create(const Object &probe_object)
|
||||
{
|
||||
/**
|
||||
* We rasterize the scene along the 3 axes. Each generated fragment will write a surface element
|
||||
|
@ -267,9 +273,14 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid)
|
|||
*/
|
||||
using namespace blender::math;
|
||||
|
||||
dispatch_per_grid_sample_ = math::divide_ceil(grid.resolution, int3(IRRADIANCE_GRID_GROUP_SIZE));
|
||||
capture_info_buf_.irradiance_grid_size = grid.resolution;
|
||||
capture_info_buf_.irradiance_grid_local_to_world = grid.transform;
|
||||
const ::LightProbe *lightprobe = static_cast<::LightProbe *>(probe_object.data);
|
||||
|
||||
int3 grid_resolution = int3(&lightprobe->grid_resolution_x);
|
||||
float4x4 grid_local_to_world = invert(float4x4(probe_object.world_to_object));
|
||||
|
||||
dispatch_per_grid_sample_ = math::divide_ceil(grid_resolution, int3(IRRADIANCE_GRID_GROUP_SIZE));
|
||||
capture_info_buf_.irradiance_grid_size = grid_resolution;
|
||||
capture_info_buf_.irradiance_grid_local_to_world = grid_local_to_world;
|
||||
capture_info_buf_.irradiance_accum_solid_angle = 0.0f;
|
||||
/* Divide by twice the sample count because each ray is evaluated in both directions. */
|
||||
capture_info_buf_.irradiance_sample_solid_angle = 4.0f * float(M_PI) /
|
||||
|
@ -280,14 +291,14 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid)
|
|||
|
||||
/* 32bit float is needed here otherwise we loose too much energy from rounding error during the
|
||||
* accumulation when the sample count is above 500. */
|
||||
irradiance_L0_L1_a_tx_.ensure_3d(GPU_RGBA32F, grid.resolution, texture_usage);
|
||||
irradiance_L0_L1_b_tx_.ensure_3d(GPU_RGBA32F, grid.resolution, texture_usage);
|
||||
irradiance_L0_L1_c_tx_.ensure_3d(GPU_RGBA32F, grid.resolution, texture_usage);
|
||||
irradiance_L0_L1_a_tx_.clear(float4(0.0f));
|
||||
irradiance_L0_L1_b_tx_.clear(float4(0.0f));
|
||||
irradiance_L0_L1_c_tx_.clear(float4(0.0f));
|
||||
|
||||
const float4x4 transform(grid.transform);
|
||||
irradiance_L0_tx_.ensure_3d(GPU_RGBA32F, grid_resolution, texture_usage);
|
||||
irradiance_L1_a_tx_.ensure_3d(GPU_RGBA32F, grid_resolution, texture_usage);
|
||||
irradiance_L1_b_tx_.ensure_3d(GPU_RGBA32F, grid_resolution, texture_usage);
|
||||
irradiance_L1_c_tx_.ensure_3d(GPU_RGBA32F, grid_resolution, texture_usage);
|
||||
irradiance_L0_tx_.clear(float4(0.0f));
|
||||
irradiance_L1_a_tx_.clear(float4(0.0f));
|
||||
irradiance_L1_b_tx_.clear(float4(0.0f));
|
||||
irradiance_L1_c_tx_.clear(float4(0.0f));
|
||||
|
||||
/* Extract bounding box. Order is arbitrary as it is not important for our usage. */
|
||||
const std::array<float3, 8> bbox_corners({float3{+1, +1, +1},
|
||||
|
@ -300,7 +311,7 @@ void IrradianceBake::surfels_create(const IrradianceGrid &grid)
|
|||
float3{-1, -1, -1}});
|
||||
grid_bbox_vertices.clear();
|
||||
for (const float3 &point : bbox_corners) {
|
||||
grid_bbox_vertices.append(transform_point(transform, point));
|
||||
grid_bbox_vertices.append(transform_point(grid_local_to_world, point));
|
||||
}
|
||||
|
||||
DRW_stats_group_start("IrradianceBake.SurfelsCount");
|
||||
|
@ -428,35 +439,85 @@ void IrradianceBake::accumulate_bounce()
|
|||
inst_.manager->submit(surfel_light_bounce_ps_);
|
||||
}
|
||||
|
||||
void IrradianceBake::read_result(LightCacheIrradianceGrid &light_cache_grid)
|
||||
void IrradianceBake::read_surfels(LightProbeGridCacheFrame *cache_frame)
|
||||
{
|
||||
auto read_texture = [&](LightCacheTexture &cache_texture, draw::Texture &texture) {
|
||||
MEM_SAFE_FREE(cache_texture.data);
|
||||
cache_texture.data = (char *)texture.read<float4>(GPU_DATA_FLOAT);
|
||||
copy_v3_v3_int(cache_texture.tex_size, light_cache_grid.resolution);
|
||||
cache_texture.data_type = LIGHTCACHETEX_FLOAT;
|
||||
cache_texture.components = 4;
|
||||
};
|
||||
GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
|
||||
read_texture(light_cache_grid.irradiance_L0_L1_a, irradiance_L0_L1_a_tx_);
|
||||
read_texture(light_cache_grid.irradiance_L0_L1_b, irradiance_L0_L1_b_tx_);
|
||||
read_texture(light_cache_grid.irradiance_L0_L1_c, irradiance_L0_L1_c_tx_);
|
||||
|
||||
switch (inst_.debug_mode) {
|
||||
case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL:
|
||||
case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE:
|
||||
GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE);
|
||||
capture_info_buf_.read();
|
||||
surfels_buf_.read();
|
||||
light_cache_grid.surfels_len = capture_info_buf_.surfel_len;
|
||||
/* TODO(fclem): This isn't threadsafe. */
|
||||
MEM_SAFE_FREE(light_cache_grid.surfels);
|
||||
light_cache_grid.surfels = MEM_dupallocN(surfels_buf_.data());
|
||||
break;
|
||||
default:
|
||||
/* Nothing to display. */
|
||||
return;
|
||||
if (!ELEM(inst_.debug_mode,
|
||||
eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL,
|
||||
eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE);
|
||||
capture_info_buf_.read();
|
||||
surfels_buf_.read();
|
||||
|
||||
cache_frame->surfels_len = capture_info_buf_.surfel_len;
|
||||
cache_frame->surfels = MEM_malloc_arrayN(cache_frame->surfels_len, sizeof(Surfel), __func__);
|
||||
|
||||
MutableSpan<Surfel> surfels_dst((Surfel *)cache_frame->surfels, cache_frame->surfels_len);
|
||||
Span<Surfel> surfels_src(surfels_buf_.data(), cache_frame->surfels_len);
|
||||
surfels_dst.copy_from(surfels_src);
|
||||
}
|
||||
|
||||
LightProbeGridCacheFrame *IrradianceBake::read_result_unpacked()
|
||||
{
|
||||
LightProbeGridCacheFrame *cache_frame = BKE_lightprobe_grid_cache_frame_create();
|
||||
|
||||
read_surfels(cache_frame);
|
||||
|
||||
cache_frame->size[0] = irradiance_L0_tx_.width();
|
||||
cache_frame->size[1] = irradiance_L0_tx_.height();
|
||||
cache_frame->size[2] = irradiance_L0_tx_.depth();
|
||||
|
||||
GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
|
||||
|
||||
cache_frame->baking.L0 = (float(*)[4])irradiance_L0_tx_.read<float4>(GPU_DATA_FLOAT);
|
||||
cache_frame->baking.L1_a = (float(*)[4])irradiance_L1_a_tx_.read<float4>(GPU_DATA_FLOAT);
|
||||
cache_frame->baking.L1_b = (float(*)[4])irradiance_L1_b_tx_.read<float4>(GPU_DATA_FLOAT);
|
||||
cache_frame->baking.L1_c = (float(*)[4])irradiance_L1_c_tx_.read<float4>(GPU_DATA_FLOAT);
|
||||
|
||||
/* TODO(fclem): Connectivity. */
|
||||
// cache_frame->connectivity.bitmask = connectivity_tx_.read<uint8_t>(GPU_DATA_FLOAT);
|
||||
|
||||
return cache_frame;
|
||||
}
|
||||
|
||||
LightProbeGridCacheFrame *IrradianceBake::read_result_packed()
|
||||
{
|
||||
LightProbeGridCacheFrame *cache_frame = BKE_lightprobe_grid_cache_frame_create();
|
||||
|
||||
read_surfels(cache_frame);
|
||||
|
||||
cache_frame->size[0] = irradiance_L0_tx_.width();
|
||||
cache_frame->size[1] = irradiance_L0_tx_.height();
|
||||
cache_frame->size[2] = irradiance_L0_tx_.depth();
|
||||
|
||||
GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
|
||||
|
||||
/* TODO(fclem): Temp. */
|
||||
cache_frame->baking.L0 = (float(*)[4])irradiance_L0_tx_.read<float4>(GPU_DATA_FLOAT);
|
||||
cache_frame->baking.L1_a = (float(*)[4])irradiance_L1_a_tx_.read<float4>(GPU_DATA_FLOAT);
|
||||
cache_frame->baking.L1_b = (float(*)[4])irradiance_L1_b_tx_.read<float4>(GPU_DATA_FLOAT);
|
||||
cache_frame->baking.L1_c = (float(*)[4])irradiance_L1_c_tx_.read<float4>(GPU_DATA_FLOAT);
|
||||
|
||||
/* TODO(fclem): Create packed format texture and swizzle on gpu. */
|
||||
// cache_frame->irradiance.L0 = (float(*)[4])irradiance_only_L0_tx_.read<float4>(GPU_DATA_FLOAT);
|
||||
// cache_frame->irradiance.L1_a =
|
||||
// (float(*)[4])irradiance_only_L1_a_tx_.read<float4>(GPU_DATA_FLOAT);
|
||||
// cache_frame->irradiance.L1_b =
|
||||
// (float(*)[4])irradiance_only_L1_b_tx_.read<float4>(GPU_DATA_FLOAT);
|
||||
// cache_frame->irradiance.L1_c =
|
||||
// (float(*)[4])irradiance_only_L1_c_tx_.read<float4>(GPU_DATA_FLOAT);
|
||||
|
||||
// cache_frame->visibility.L0 = irradiance_only_L0_tx_.read<uint8_t>(GPU_DATA_UBYTE);
|
||||
// cache_frame->visibility.L1_a = irradiance_only_L1_a_tx_.read<uint8_t>(GPU_DATA_UBYTE);
|
||||
// cache_frame->visibility.L1_b = irradiance_only_L1_b_tx_.read<uint8_t>(GPU_DATA_UBYTE);
|
||||
// cache_frame->visibility.L1_c = irradiance_only_L1_c_tx_.read<uint8_t>(GPU_DATA_UBYTE);
|
||||
|
||||
/* TODO(fclem): Connectivity. */
|
||||
// cache_frame->connectivity.bitmask = connectivity_tx_.read<uint8_t>(GPU_DATA_FLOAT);
|
||||
|
||||
return cache_frame;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -77,9 +77,10 @@ class IrradianceBake {
|
|||
int3 dispatch_per_grid_sample_ = int3(1);
|
||||
|
||||
/** Irradiance textures for baking. Only represents one grid in there. */
|
||||
Texture irradiance_L0_L1_a_tx_ = {"irradiance_L0_L1_a_tx_"};
|
||||
Texture irradiance_L0_L1_b_tx_ = {"irradiance_L0_L1_b_tx_"};
|
||||
Texture irradiance_L0_L1_c_tx_ = {"irradiance_L0_L1_c_tx_"};
|
||||
Texture irradiance_L0_tx_ = {"irradiance_L0_tx_"};
|
||||
Texture irradiance_L1_a_tx_ = {"irradiance_L1_a_tx_"};
|
||||
Texture irradiance_L1_b_tx_ = {"irradiance_L1_b_tx_"};
|
||||
Texture irradiance_L1_c_tx_ = {"irradiance_L1_c_tx_"};
|
||||
|
||||
/* Orientation of the irradiance grid being baked. */
|
||||
math::Quaternion grid_orientation_;
|
||||
|
@ -96,9 +97,9 @@ class IrradianceBake {
|
|||
void sync();
|
||||
|
||||
/** Create the views used to rasterize the scene into surfel representation. */
|
||||
void surfel_raster_views_sync(const IrradianceGrid &grid);
|
||||
/** Create a surfel representation of the scene from the \a grid using the capture pipeline. */
|
||||
void surfels_create(const IrradianceGrid &grid);
|
||||
void surfel_raster_views_sync(const Object &probe_object);
|
||||
/** Create a surfel representation of the scene from the probe using the capture pipeline. */
|
||||
void surfels_create(const Object &probe_object);
|
||||
/** Evaluate direct lighting (and also clear the surfels radiance). */
|
||||
void surfels_lights_eval();
|
||||
/** Propagate light from surfel to surfel in a random direction over the sphere. */
|
||||
|
@ -106,8 +107,14 @@ class IrradianceBake {
|
|||
/** Accumulate light inside `surfel.radiance_bounce` to `surfel.radiance`. */
|
||||
void accumulate_bounce();
|
||||
|
||||
/** Read grid final irradiance back to CPU into \a light_cache_grid . */
|
||||
void read_result(LightCacheIrradianceGrid &light_cache_grid);
|
||||
/** Read grid unpacked irradiance back to CPU and returns as a #LightProbeGridCacheFrame. */
|
||||
LightProbeGridCacheFrame *read_result_unpacked();
|
||||
/** Read grid packed irradiance back to CPU and returns as a #LightProbeGridCacheFrame. */
|
||||
LightProbeGridCacheFrame *read_result_packed();
|
||||
|
||||
private:
|
||||
/** Read surfel data back to CPU into \a cache_frame . */
|
||||
void read_surfels(LightProbeGridCacheFrame *cache_frame);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -121,10 +128,12 @@ class IrradianceCache {
|
|||
private:
|
||||
Instance &inst_;
|
||||
|
||||
/** Display surfel debug data. */
|
||||
PassSimple debug_surfels_ps_ = {"IrradianceCache.Debug"};
|
||||
/** Debug surfel elements copied from the light cache. */
|
||||
draw::StorageVectorBuffer<Surfel> surfels_buf_;
|
||||
draw::StorageArrayBuffer<Surfel> debug_surfels_buf_;
|
||||
|
||||
/** Display grid cache data. */
|
||||
bool display_grids_enabled_ = false;
|
||||
PassSimple display_grids_ps_ = {"IrradianceCache.Display Grids"};
|
||||
|
||||
|
@ -137,9 +146,7 @@ class IrradianceCache {
|
|||
void viewport_draw(View &view, GPUFrameBuffer *view_fb);
|
||||
|
||||
private:
|
||||
void debug_pass_sync();
|
||||
void debug_pass_draw(View &view, GPUFrameBuffer *view_fb);
|
||||
void display_pass_sync();
|
||||
void display_pass_draw(View &view, GPUFrameBuffer *view_fb);
|
||||
};
|
||||
|
||||
|
|
|
@ -9,8 +9,10 @@
|
|||
#include "DRW_render.h"
|
||||
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_lightprobe.h"
|
||||
|
||||
#include "DNA_lightprobe_types.h"
|
||||
|
||||
#include "BLI_endian_switch.h"
|
||||
#include "BLI_threads.h"
|
||||
|
||||
#include "DEG_depsgraph_build.h"
|
||||
|
@ -24,14 +26,12 @@
|
|||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "BLO_read_write.h"
|
||||
|
||||
#include "wm_window.h"
|
||||
|
||||
#include "eevee_engine.h"
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
#include "eevee_lightcache.h"
|
||||
#include "eevee_lightcache.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Light Probe Baking
|
||||
|
@ -64,18 +64,29 @@ class LightBake {
|
|||
/** Manager used for command submission. Created and freed in the worker thread. */
|
||||
draw::Manager *manager_ = nullptr;
|
||||
|
||||
/** Lightprobe original objects to bake. */
|
||||
Vector<Object *> original_probes_;
|
||||
/** Frame to copy to original objects during update. This is needed to avoid race conditions. */
|
||||
Vector<LightProbeGridCacheFrame *> bake_result_;
|
||||
std::mutex result_mutex_;
|
||||
|
||||
public:
|
||||
LightBake(struct Main *bmain,
|
||||
struct ViewLayer *view_layer,
|
||||
struct Scene *scene,
|
||||
Span<Object *> probes,
|
||||
bool run_as_job,
|
||||
int frame,
|
||||
int delay_ms = 0)
|
||||
: depsgraph_(DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER)),
|
||||
frame_(frame),
|
||||
delay_ms_(delay_ms)
|
||||
delay_ms_(delay_ms),
|
||||
original_probes_(probes)
|
||||
{
|
||||
BLI_assert(BLI_thread_is_main());
|
||||
bake_result_.resize(probes.size());
|
||||
bake_result_.fill(nullptr);
|
||||
|
||||
if (run_as_job && !GPU_use_main_context_workaround()) {
|
||||
/* This needs to happen in main thread. */
|
||||
gl_context_ = WM_opengl_context_create();
|
||||
|
@ -98,20 +109,28 @@ class LightBake {
|
|||
void update()
|
||||
{
|
||||
BLI_assert(BLI_thread_is_main());
|
||||
Scene *original_scene = DEG_get_input_scene(depsgraph_);
|
||||
LightCache *&scene_light_cache = original_scene->eevee.light_cache_data;
|
||||
|
||||
if (scene_light_cache != light_cache_) {
|
||||
if (scene_light_cache != nullptr) {
|
||||
/* Delete old data if existing. */
|
||||
EEVEE_NEXT_lightcache_free(scene_light_cache);
|
||||
for (auto i : bake_result_.index_range()) {
|
||||
if (bake_result_[i] == nullptr) {
|
||||
continue;
|
||||
}
|
||||
scene_light_cache = light_cache_;
|
||||
Object *orig_ob = original_probes_[i];
|
||||
|
||||
{
|
||||
std::scoped_lock lock(result_mutex_);
|
||||
|
||||
LightProbeObjectCache *cache = orig_ob->lightprobe_cache;
|
||||
/* Delete any existing cache. */
|
||||
if (cache->grid_static_cache != nullptr) {
|
||||
BKE_lightprobe_grid_cache_frame_free(cache->grid_static_cache);
|
||||
}
|
||||
/* Pass ownership to original object. */
|
||||
cache->grid_static_cache = bake_result_[i];
|
||||
bake_result_[i] = nullptr;
|
||||
}
|
||||
/* Propagate the cache to evaluated object. */
|
||||
DEG_id_tag_update(&orig_ob->id, ID_RECALC_COPY_ON_WRITE);
|
||||
}
|
||||
|
||||
EEVEE_NEXT_lightcache_info_update(&original_scene->eevee);
|
||||
|
||||
DEG_id_tag_update(&original_scene->id, ID_RECALC_COPY_ON_WRITE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,18 +143,36 @@ class LightBake {
|
|||
DEG_graph_relations_update(depsgraph_);
|
||||
DEG_evaluate_on_framechange(depsgraph_, frame_);
|
||||
|
||||
if (delay_ms_ > 0) {
|
||||
PIL_sleep_ms(delay_ms_);
|
||||
}
|
||||
|
||||
context_enable();
|
||||
manager_ = new draw::Manager();
|
||||
instance_ = new eevee::Instance();
|
||||
instance_->init_light_bake(depsgraph_, manager_);
|
||||
context_disable();
|
||||
|
||||
if (delay_ms_ > 0) {
|
||||
PIL_sleep_ms(delay_ms_);
|
||||
}
|
||||
for (auto i : original_probes_.index_range()) {
|
||||
Object *eval_ob = DEG_get_evaluated_object(depsgraph_, original_probes_[i]);
|
||||
|
||||
instance_->light_bake_irradiance(
|
||||
light_cache_, [this]() { context_enable(); }, [this]() { context_disable(); });
|
||||
instance_->light_bake_irradiance(
|
||||
*eval_ob,
|
||||
[this]() { context_enable(); },
|
||||
[this]() { context_disable(); },
|
||||
[&](LightProbeGridCacheFrame *cache_frame) {
|
||||
{
|
||||
std::scoped_lock lock(result_mutex_);
|
||||
/* Delete any existing cache that wasn't transferred to the original object. */
|
||||
if (bake_result_[i] != nullptr) {
|
||||
BKE_lightprobe_grid_cache_frame_free(bake_result_[i]);
|
||||
}
|
||||
bake_result_[i] = cache_frame;
|
||||
}
|
||||
*do_update = true;
|
||||
/* TODO: Update progress. */
|
||||
});
|
||||
}
|
||||
|
||||
delete_resources();
|
||||
}
|
||||
|
@ -226,8 +263,6 @@ class LightBake {
|
|||
|
||||
} // namespace blender::eevee
|
||||
|
||||
extern "C" {
|
||||
|
||||
using namespace blender::eevee;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -239,6 +274,7 @@ wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm,
|
|||
struct Main *bmain,
|
||||
struct ViewLayer *view_layer,
|
||||
struct Scene *scene,
|
||||
blender::Vector<struct Object *> original_probes,
|
||||
int delay_ms,
|
||||
int frame)
|
||||
{
|
||||
|
@ -257,7 +293,8 @@ wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm,
|
|||
WM_JOB_EXCL_RENDER | WM_JOB_PRIORITY | WM_JOB_PROGRESS,
|
||||
WM_JOB_TYPE_LIGHT_BAKE);
|
||||
|
||||
LightBake *bake = new LightBake(bmain, view_layer, scene, true, frame, delay_ms);
|
||||
LightBake *bake = new LightBake(
|
||||
bmain, view_layer, scene, original_probes, true, frame, delay_ms);
|
||||
|
||||
WM_jobs_customdata_set(wm_job, bake, EEVEE_NEXT_lightbake_job_data_free);
|
||||
WM_jobs_timer(wm_job, 0.4, NC_SCENE | NA_EDITED, 0);
|
||||
|
@ -275,12 +312,10 @@ wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm,
|
|||
void *EEVEE_NEXT_lightbake_job_data_alloc(struct Main *bmain,
|
||||
struct ViewLayer *view_layer,
|
||||
struct Scene *scene,
|
||||
bool run_as_job,
|
||||
blender::Vector<struct Object *> original_probes,
|
||||
int frame)
|
||||
{
|
||||
/* This should only be used for exec job. Eventually, remove `run_as_job` parameter later. */
|
||||
BLI_assert(run_as_job == false);
|
||||
LightBake *bake = new LightBake(bmain, view_layer, scene, run_as_job, frame);
|
||||
LightBake *bake = new LightBake(bmain, view_layer, scene, original_probes, false, frame);
|
||||
/* TODO(fclem): Can remove this cast once we remove the previous EEVEE light cache. */
|
||||
return reinterpret_cast<void *>(bake);
|
||||
}
|
||||
|
@ -301,184 +336,3 @@ void EEVEE_NEXT_lightbake_job(void *job_data, bool *stop, bool *do_update, float
|
|||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Light Cache Create / Delete
|
||||
* \{ */
|
||||
|
||||
LightCache *EEVEE_NEXT_lightcache_create()
|
||||
{
|
||||
LightCache *light_cache = (LightCache *)MEM_callocN(sizeof(LightCache), "LightCache");
|
||||
|
||||
light_cache->version = LIGHTCACHE_NEXT_STATIC_VERSION;
|
||||
light_cache->type = LIGHTCACHE_TYPE_STATIC;
|
||||
|
||||
return light_cache;
|
||||
}
|
||||
|
||||
void EEVEE_NEXT_lightcache_free(LightCache *light_cache)
|
||||
{
|
||||
if (light_cache->version < LIGHTCACHE_NEXT_STATIC_VERSION) {
|
||||
/* Allow deleting old EEVEE cache. */
|
||||
DRW_TEXTURE_FREE_SAFE(light_cache->cube_tx.tex);
|
||||
MEM_SAFE_FREE(light_cache->cube_tx.data);
|
||||
DRW_TEXTURE_FREE_SAFE(light_cache->grid_tx.tex);
|
||||
MEM_SAFE_FREE(light_cache->grid_tx.data);
|
||||
if (light_cache->cube_mips) {
|
||||
for (int i = 0; i < light_cache->mips_len; i++) {
|
||||
MEM_SAFE_FREE(light_cache->cube_mips[i].data);
|
||||
}
|
||||
MEM_SAFE_FREE(light_cache->cube_mips);
|
||||
}
|
||||
MEM_SAFE_FREE(light_cache->cube_data);
|
||||
MEM_SAFE_FREE(light_cache->grid_data);
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < light_cache->grid_len; i++) {
|
||||
MEM_SAFE_FREE(light_cache->grids[i].surfels);
|
||||
MEM_SAFE_FREE(light_cache->grids[i].irradiance_L0_L1_a.data);
|
||||
MEM_SAFE_FREE(light_cache->grids[i].irradiance_L0_L1_b.data);
|
||||
MEM_SAFE_FREE(light_cache->grids[i].irradiance_L0_L1_c.data);
|
||||
DRW_TEXTURE_FREE_SAFE(light_cache->grids[i].irradiance_L0_L1_a.tex);
|
||||
DRW_TEXTURE_FREE_SAFE(light_cache->grids[i].irradiance_L0_L1_b.tex);
|
||||
DRW_TEXTURE_FREE_SAFE(light_cache->grids[i].irradiance_L0_L1_c.tex);
|
||||
}
|
||||
MEM_SAFE_FREE(light_cache->grids);
|
||||
}
|
||||
MEM_freeN(light_cache);
|
||||
}
|
||||
|
||||
void EEVEE_NEXT_lightcache_info_update(SceneEEVEE *eevee)
|
||||
{
|
||||
LightCache *light_cache = eevee->light_cache_data;
|
||||
|
||||
if (light_cache == nullptr) {
|
||||
BLI_strncpy(eevee->light_cache_info,
|
||||
TIP_("No light cache in this scene"),
|
||||
sizeof(eevee->light_cache_info));
|
||||
return;
|
||||
}
|
||||
|
||||
if (light_cache->version != LIGHTCACHE_NEXT_STATIC_VERSION) {
|
||||
BLI_strncpy(eevee->light_cache_info,
|
||||
TIP_("Incompatible Light cache version, please bake again"),
|
||||
sizeof(eevee->light_cache_info));
|
||||
return;
|
||||
}
|
||||
|
||||
if (light_cache->flag & LIGHTCACHE_INVALID) {
|
||||
BLI_strncpy(eevee->light_cache_info,
|
||||
TIP_("Error: Light cache dimensions not supported by the GPU"),
|
||||
sizeof(eevee->light_cache_info));
|
||||
return;
|
||||
}
|
||||
|
||||
if (light_cache->flag & LIGHTCACHE_BAKING) {
|
||||
BLI_strncpy(
|
||||
eevee->light_cache_info, TIP_("Baking light cache"), sizeof(eevee->light_cache_info));
|
||||
return;
|
||||
}
|
||||
|
||||
int irradiance_sample_len = 0;
|
||||
for (const LightCacheIrradianceGrid &grid :
|
||||
blender::Span<LightCacheIrradianceGrid>(light_cache->grids, light_cache->grid_len)) {
|
||||
irradiance_sample_len += grid.resolution[0] * grid.resolution[1] * grid.resolution[2];
|
||||
}
|
||||
|
||||
size_t size_in_bytes = irradiance_sample_len * sizeof(uint16_t) * 4 * 3;
|
||||
|
||||
char formatted_mem[BLI_STR_FORMAT_INT64_BYTE_UNIT_SIZE];
|
||||
BLI_str_format_byte_unit(formatted_mem, size_in_bytes, false);
|
||||
|
||||
BLI_snprintf(eevee->light_cache_info,
|
||||
sizeof(eevee->light_cache_info),
|
||||
TIP_("%d Ref. Cubemaps, %d Irr. Samples (%s in memory)"),
|
||||
light_cache->cube_len,
|
||||
irradiance_sample_len,
|
||||
formatted_mem);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Light Cache Read/Write to file
|
||||
* \{ */
|
||||
|
||||
void EEVEE_NEXT_lightcache_blend_write(BlendWriter *writer, LightCache *light_cache)
|
||||
{
|
||||
auto write_lightcache_texture = [&](LightCacheTexture &tex) {
|
||||
if (tex.data) {
|
||||
size_t data_size = tex.components * tex.tex_size[0] * tex.tex_size[1] * tex.tex_size[2];
|
||||
if (tex.data_type == LIGHTCACHETEX_FLOAT) {
|
||||
data_size *= sizeof(float);
|
||||
}
|
||||
else if (tex.data_type == LIGHTCACHETEX_UINT) {
|
||||
data_size *= sizeof(uint);
|
||||
}
|
||||
|
||||
/* FIXME: We can't save more than what 32bit systems can handle.
|
||||
* The solution would be to split the texture but it is too late for 2.90. (see #78529) */
|
||||
if (data_size < INT_MAX) {
|
||||
BLO_write_raw(writer, data_size, tex.data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BLO_write_struct_array(
|
||||
writer, LightCacheIrradianceGrid, light_cache->grid_len, light_cache->grids);
|
||||
|
||||
for (int i = 0; i < light_cache->grid_len; i++) {
|
||||
LightCacheIrradianceGrid &grid = light_cache->grids[i];
|
||||
/* Surfels are runtime data. Not stored in the blend file. */
|
||||
write_lightcache_texture(grid.irradiance_L0_L1_a);
|
||||
write_lightcache_texture(grid.irradiance_L0_L1_b);
|
||||
write_lightcache_texture(grid.irradiance_L0_L1_c);
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_NEXT_lightcache_blend_read_data(BlendDataReader *reader, LightCache *light_cache)
|
||||
{
|
||||
|
||||
if (light_cache->grids) {
|
||||
BLO_read_data_address(reader, &light_cache->grids);
|
||||
|
||||
auto direct_link_lightcache_texture = [&](LightCacheTexture &lctex) {
|
||||
/* Runtime data. Not stored in the blend file. */
|
||||
lctex.tex = nullptr;
|
||||
|
||||
if (lctex.data) {
|
||||
BLO_read_data_address(reader, &lctex.data);
|
||||
if (lctex.data && BLO_read_requires_endian_switch(reader)) {
|
||||
int data_size = lctex.components * lctex.tex_size[0] * lctex.tex_size[1] *
|
||||
lctex.tex_size[2];
|
||||
|
||||
if (lctex.data_type == LIGHTCACHETEX_FLOAT) {
|
||||
BLI_endian_switch_float_array((float *)lctex.data, data_size * sizeof(float));
|
||||
}
|
||||
else if (lctex.data_type == LIGHTCACHETEX_UINT) {
|
||||
BLI_endian_switch_uint32_array((uint *)lctex.data, data_size * sizeof(uint));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lctex.data == nullptr) {
|
||||
zero_v3_int(lctex.tex_size);
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < light_cache->grid_len; i++) {
|
||||
LightCacheIrradianceGrid &grid = light_cache->grids[i];
|
||||
/* Runtime data. Not stored in the blend file. */
|
||||
grid.surfels_len = 0;
|
||||
grid.surfels = nullptr;
|
||||
direct_link_lightcache_texture(grid.irradiance_L0_L1_a);
|
||||
direct_link_lightcache_texture(grid.irradiance_L0_L1_b);
|
||||
direct_link_lightcache_texture(grid.irradiance_L0_L1_c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -6,9 +6,15 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
struct wmWindowManager;
|
||||
struct wmWindow;
|
||||
struct Main;
|
||||
struct ViewLayer;
|
||||
struct Scene;
|
||||
struct Object;
|
||||
struct wmJob;
|
||||
|
||||
/** Opaque type hiding eevee::LightBake. */
|
||||
typedef struct EEVEE_NEXT_LightBake EEVEE_NEXT_LightBake;
|
||||
|
@ -21,7 +27,6 @@ typedef struct EEVEE_NEXT_LightBake EEVEE_NEXT_LightBake;
|
|||
* Create the job description.
|
||||
* This is called for async (modal) bake operator.
|
||||
* The actual work will be done by `EEVEE_NEXT_lightbake_job()`.
|
||||
* Will internally call `EEVEE_NEXT_lightbake_job_data_alloc()`.
|
||||
* IMPORTANT: Must run on the main thread because of potential GPUContext creation.
|
||||
*/
|
||||
struct wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm,
|
||||
|
@ -29,6 +34,7 @@ struct wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm,
|
|||
struct Main *bmain,
|
||||
struct ViewLayer *view_layer,
|
||||
struct Scene *scene,
|
||||
blender::Vector<struct Object *> original_probes,
|
||||
int delay_ms,
|
||||
int frame);
|
||||
|
||||
|
@ -43,7 +49,7 @@ struct wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm,
|
|||
void *EEVEE_NEXT_lightbake_job_data_alloc(struct Main *bmain,
|
||||
struct ViewLayer *view_layer,
|
||||
struct Scene *scene,
|
||||
bool run_as_job,
|
||||
blender::Vector<struct Object *> original_probes,
|
||||
int frame);
|
||||
|
||||
/**
|
||||
|
@ -69,38 +75,3 @@ void EEVEE_NEXT_lightbake_job(void *job_data /* EEVEE_NEXT_LightBake */,
|
|||
float *progress);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Light Cache Create / Delete
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Create an empty light-cache.
|
||||
*/
|
||||
struct LightCache *EEVEE_NEXT_lightcache_create(void);
|
||||
|
||||
/**
|
||||
* Free a light-cache and its associated data.
|
||||
*/
|
||||
void EEVEE_NEXT_lightcache_free(struct LightCache *lcache);
|
||||
|
||||
/**
|
||||
* Update the UI message in the render panel about the state of the cache.
|
||||
*/
|
||||
void EEVEE_NEXT_lightcache_info_update(struct SceneEEVEE *eevee);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Light Cache Read/Write to file
|
||||
* \{ */
|
||||
|
||||
void EEVEE_NEXT_lightcache_blend_write(struct BlendWriter *writer, struct LightCache *light_cache);
|
||||
void EEVEE_NEXT_lightcache_blend_read_data(struct BlendDataReader *reader,
|
||||
struct LightCache *light_cache);
|
||||
|
||||
/** \} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -19,40 +19,28 @@ void LightProbeModule::begin_sync()
|
|||
{
|
||||
auto_bake_enabled_ = inst_.is_viewport() &&
|
||||
(inst_.scene->eevee.flag & SCE_EEVEE_GI_AUTOBAKE) != 0;
|
||||
grid_update_ = false;
|
||||
cube_update_ = false;
|
||||
|
||||
grids.clear();
|
||||
cubes.clear();
|
||||
}
|
||||
|
||||
void LightProbeModule::sync_grid(const Object *ob, ObjectHandle &handle)
|
||||
{
|
||||
LightProbe &grid = grid_map_.lookup_or_add_default(handle.object_key);
|
||||
IrradianceGrid &grid = grid_map_.lookup_or_add_default(handle.object_key);
|
||||
grid.used = true;
|
||||
if (handle.recalc != 0 || grid.initialized == false) {
|
||||
grid.initialized = true;
|
||||
grid_update_ = true;
|
||||
}
|
||||
|
||||
if (inst_.is_baking()) {
|
||||
const ::LightProbe *light_probe = (const ::LightProbe *)ob->data;
|
||||
grids.append({float4x4(ob->object_to_world), &light_probe->grid_resolution_x});
|
||||
grid.updated = true;
|
||||
grid.object_to_world = float4x4(ob->object_to_world);
|
||||
grid.cache = ob->lightprobe_cache;
|
||||
}
|
||||
}
|
||||
|
||||
void LightProbeModule::sync_cube(ObjectHandle &handle)
|
||||
{
|
||||
LightProbe &cube = cube_map_.lookup_or_add_default(handle.object_key);
|
||||
ReflectionCube &cube = cube_map_.lookup_or_add_default(handle.object_key);
|
||||
cube.used = true;
|
||||
if (handle.recalc != 0 || cube.initialized == false) {
|
||||
cube.initialized = true;
|
||||
cube_update_ = true;
|
||||
}
|
||||
|
||||
if (inst_.is_baking()) {
|
||||
cubes.append({});
|
||||
}
|
||||
}
|
||||
|
||||
void LightProbeModule::sync_probe(const Object *ob, ObjectHandle &handle)
|
||||
|
@ -75,10 +63,14 @@ void LightProbeModule::sync_probe(const Object *ob, ObjectHandle &handle)
|
|||
void LightProbeModule::end_sync()
|
||||
{
|
||||
{
|
||||
/* Check for deleted grid. */
|
||||
/* Check for deleted or updated grid. */
|
||||
grid_update_ = false;
|
||||
auto it_end = grid_map_.items().end();
|
||||
for (auto it = grid_map_.items().begin(); it != it_end; ++it) {
|
||||
LightProbe &grid = (*it).value;
|
||||
IrradianceGrid &grid = (*it).value;
|
||||
if (grid.updated) {
|
||||
grid_update_ = true;
|
||||
}
|
||||
if (!grid.used) {
|
||||
grid_map_.remove(it);
|
||||
grid_update_ = true;
|
||||
|
@ -89,10 +81,14 @@ void LightProbeModule::end_sync()
|
|||
}
|
||||
}
|
||||
{
|
||||
/* Check for deleted cube. */
|
||||
/* Check for deleted or updated cube. */
|
||||
cube_update_ = false;
|
||||
auto it_end = cube_map_.items().end();
|
||||
for (auto it = cube_map_.items().begin(); it != it_end; ++it) {
|
||||
LightProbe &cube = (*it).value;
|
||||
ReflectionCube &cube = (*it).value;
|
||||
if (cube.updated) {
|
||||
cube_update_ = true;
|
||||
}
|
||||
if (!cube.used) {
|
||||
cube_map_.remove(it);
|
||||
cube_update_ = true;
|
||||
|
|
|
@ -16,30 +16,37 @@
|
|||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
|
||||
struct IrradianceGrid {
|
||||
float4x4 transform;
|
||||
int3 resolution;
|
||||
};
|
||||
|
||||
struct ReflectionCube {};
|
||||
class IrradianceCache;
|
||||
|
||||
struct LightProbe {
|
||||
bool used = false;
|
||||
bool initialized = false;
|
||||
bool updated = false;
|
||||
};
|
||||
|
||||
struct IrradianceGrid : public LightProbe {
|
||||
/** Reference to the light-cache data. Should be refreshed every sync. */
|
||||
float4x4 object_to_world;
|
||||
/**
|
||||
* Reference to the light-cache data.
|
||||
* Do not try to dereference it before LightProbeModule::end_sync() as the grid could
|
||||
* already have been freed (along with its cache). It is only safe to dereference after the
|
||||
* pruning have been done.
|
||||
*/
|
||||
const struct LightProbeObjectCache *cache = nullptr;
|
||||
};
|
||||
|
||||
struct ReflectionCube : public LightProbe {};
|
||||
|
||||
class LightProbeModule {
|
||||
public:
|
||||
/** Synced probe data. Only valid if the `eevee::Instance` is a baking instance. */
|
||||
Vector<IrradianceGrid> grids;
|
||||
Vector<ReflectionCube> cubes;
|
||||
friend class IrradianceCache;
|
||||
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
/** Light Probe map to detect deletion. */
|
||||
Map<ObjectKey, LightProbe> grid_map_, cube_map_;
|
||||
/** Light Probe map to detect deletion and store associated data. */
|
||||
Map<ObjectKey, IrradianceGrid> grid_map_;
|
||||
Map<ObjectKey, ReflectionCube> cube_map_;
|
||||
/** True if a grid update was detected. It will trigger a bake if auto bake is enabled. */
|
||||
bool grid_update_;
|
||||
/** True if a grid update was detected. It will trigger a bake if auto bake is enabled. */
|
||||
|
|
|
@ -11,13 +11,15 @@ void main()
|
|||
return;
|
||||
}
|
||||
|
||||
SphericalHarmonicL1 sh = spherical_harmonics_unpack(texelFetch(irradiance_a_tx, cell, 0),
|
||||
texelFetch(irradiance_b_tx, cell, 0),
|
||||
texelFetch(irradiance_c_tx, cell, 0));
|
||||
SphericalHarmonicL1 sh;
|
||||
sh.L0.M0 = texelFetch(irradiance_a_tx, cell, 0);
|
||||
sh.L1.Mn1 = texelFetch(irradiance_b_tx, cell, 0);
|
||||
sh.L1.M0 = texelFetch(irradiance_c_tx, cell, 0);
|
||||
sh.L1.Mp1 = texelFetch(irradiance_d_tx, cell, 0);
|
||||
|
||||
vec3 vN = vec3(lP, sqrt(max(0.0, 1.0 - dist_sqr)));
|
||||
vec3 N = normal_view_to_world(vN);
|
||||
|
||||
vec3 irradiance = spherical_harmonics_evaluate_lambert(N, sh);
|
||||
out_color = vec4(irradiance, 0.0);
|
||||
vec4 irradiance = spherical_harmonics_evaluate_lambert(N, sh);
|
||||
out_color = vec4(irradiance.rgb, 0.0);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
void irradiance_capture(vec3 L, vec3 irradiance, inout SphericalHarmonicL1 sh)
|
||||
{
|
||||
spherical_harmonics_encode_signal_sample(
|
||||
L, irradiance * capture_info_buf.irradiance_sample_solid_angle, sh);
|
||||
L, vec4(irradiance, 1.0) * capture_info_buf.irradiance_sample_solid_angle, sh);
|
||||
}
|
||||
|
||||
void irradiance_capture(Surfel surfel_emitter, vec3 P, inout SphericalHarmonicL1 sh)
|
||||
|
@ -59,10 +59,11 @@ void main()
|
|||
|
||||
vec3 sky_L = cameraVec(P);
|
||||
|
||||
SphericalHarmonicL1 sh = spherical_harmonics_unpack(
|
||||
imageLoad(irradiance_L0_L1_a_img, grid_coord),
|
||||
imageLoad(irradiance_L0_L1_b_img, grid_coord),
|
||||
imageLoad(irradiance_L0_L1_c_img, grid_coord));
|
||||
SphericalHarmonicL1 sh;
|
||||
sh.L0.M0 = imageLoad(irradiance_L0_img, grid_coord);
|
||||
sh.L1.Mn1 = imageLoad(irradiance_L1_a_img, grid_coord);
|
||||
sh.L1.M0 = imageLoad(irradiance_L1_b_img, grid_coord);
|
||||
sh.L1.Mp1 = imageLoad(irradiance_L1_c_img, grid_coord);
|
||||
|
||||
if (surfel_next > -1) {
|
||||
irradiance_capture(surfel_buf[surfel_next], P, sh);
|
||||
|
@ -80,9 +81,8 @@ void main()
|
|||
irradiance_capture(-sky_L, vec3(0.0), sh);
|
||||
}
|
||||
|
||||
vec4 sh_a, sh_b, sh_c;
|
||||
spherical_harmonics_pack(sh, sh_a, sh_b, sh_c);
|
||||
imageStore(irradiance_L0_L1_a_img, grid_coord, sh_a);
|
||||
imageStore(irradiance_L0_L1_b_img, grid_coord, sh_b);
|
||||
imageStore(irradiance_L0_L1_c_img, grid_coord, sh_c);
|
||||
imageStore(irradiance_L0_img, grid_coord, sh.L0.M0);
|
||||
imageStore(irradiance_L1_a_img, grid_coord, sh.L1.Mn1);
|
||||
imageStore(irradiance_L1_b_img, grid_coord, sh.L1.M0);
|
||||
imageStore(irradiance_L1_c_img, grid_coord, sh.L1.Mp1);
|
||||
}
|
||||
|
|
|
@ -64,21 +64,21 @@ float spherical_harmonics_L2_Mp2(vec3 v)
|
|||
* \{ */
|
||||
|
||||
struct SphericalHarmonicBandL0 {
|
||||
vec3 M0;
|
||||
vec4 M0;
|
||||
};
|
||||
|
||||
struct SphericalHarmonicBandL1 {
|
||||
vec3 Mn1;
|
||||
vec3 M0;
|
||||
vec3 Mp1;
|
||||
vec4 Mn1;
|
||||
vec4 M0;
|
||||
vec4 Mp1;
|
||||
};
|
||||
|
||||
struct SphericalHarmonicBandL2 {
|
||||
vec3 Mn2;
|
||||
vec3 Mn1;
|
||||
vec3 M0;
|
||||
vec3 Mp1;
|
||||
vec3 Mp2;
|
||||
vec4 Mn2;
|
||||
vec4 Mn1;
|
||||
vec4 M0;
|
||||
vec4 Mp1;
|
||||
vec4 Mp2;
|
||||
};
|
||||
|
||||
struct SphericalHarmonicL0 {
|
||||
|
@ -106,14 +106,14 @@ struct SphericalHarmonicL2 {
|
|||
* \{ */
|
||||
|
||||
void spherical_harmonics_L0_encode_signal_sample(vec3 direction,
|
||||
vec3 amplitude,
|
||||
vec4 amplitude,
|
||||
inout SphericalHarmonicBandL0 r_L0)
|
||||
{
|
||||
r_L0.M0 += spherical_harmonics_L0_M0(direction) * amplitude;
|
||||
}
|
||||
|
||||
void spherical_harmonics_L1_encode_signal_sample(vec3 direction,
|
||||
vec3 amplitude,
|
||||
vec4 amplitude,
|
||||
inout SphericalHarmonicBandL1 r_L1)
|
||||
{
|
||||
r_L1.Mn1 += spherical_harmonics_L1_Mn1(direction) * amplitude;
|
||||
|
@ -122,7 +122,7 @@ void spherical_harmonics_L1_encode_signal_sample(vec3 direction,
|
|||
}
|
||||
|
||||
void spherical_harmonics_L2_encode_signal_sample(vec3 direction,
|
||||
vec3 amplitude,
|
||||
vec4 amplitude,
|
||||
inout SphericalHarmonicBandL2 r_L2)
|
||||
{
|
||||
r_L2.Mn2 += spherical_harmonics_L2_Mn2(direction) * amplitude;
|
||||
|
@ -133,14 +133,14 @@ void spherical_harmonics_L2_encode_signal_sample(vec3 direction,
|
|||
}
|
||||
|
||||
void spherical_harmonics_encode_signal_sample(vec3 direction,
|
||||
vec3 amplitude,
|
||||
vec4 amplitude,
|
||||
inout SphericalHarmonicL0 sh)
|
||||
{
|
||||
spherical_harmonics_L0_encode_signal_sample(direction, amplitude, sh.L0);
|
||||
}
|
||||
|
||||
void spherical_harmonics_encode_signal_sample(vec3 direction,
|
||||
vec3 amplitude,
|
||||
vec4 amplitude,
|
||||
inout SphericalHarmonicL1 sh)
|
||||
{
|
||||
spherical_harmonics_L0_encode_signal_sample(direction, amplitude, sh.L0);
|
||||
|
@ -148,7 +148,7 @@ void spherical_harmonics_encode_signal_sample(vec3 direction,
|
|||
}
|
||||
|
||||
void spherical_harmonics_encode_signal_sample(vec3 direction,
|
||||
vec3 amplitude,
|
||||
vec4 amplitude,
|
||||
inout SphericalHarmonicL2 sh)
|
||||
{
|
||||
spherical_harmonics_L0_encode_signal_sample(direction, amplitude, sh.L0);
|
||||
|
@ -164,19 +164,19 @@ void spherical_harmonics_encode_signal_sample(vec3 direction,
|
|||
* Evaluate an encoded signal in a given unit vector direction.
|
||||
* \{ */
|
||||
|
||||
vec3 spherical_harmonics_L0_evaluate(vec3 direction, SphericalHarmonicBandL0 L0)
|
||||
vec4 spherical_harmonics_L0_evaluate(vec3 direction, SphericalHarmonicBandL0 L0)
|
||||
{
|
||||
return spherical_harmonics_L0_M0(direction) * L0.M0;
|
||||
}
|
||||
|
||||
vec3 spherical_harmonics_L1_evaluate(vec3 direction, SphericalHarmonicBandL1 L1)
|
||||
vec4 spherical_harmonics_L1_evaluate(vec3 direction, SphericalHarmonicBandL1 L1)
|
||||
{
|
||||
return spherical_harmonics_L1_Mn1(direction) * L1.Mn1 +
|
||||
spherical_harmonics_L1_M0(direction) * L1.M0 +
|
||||
spherical_harmonics_L1_Mp1(direction) * L1.Mp1;
|
||||
}
|
||||
|
||||
vec3 spherical_harmonics_L2_evaluate(vec3 direction, SphericalHarmonicBandL2 L2)
|
||||
vec4 spherical_harmonics_L2_evaluate(vec3 direction, SphericalHarmonicBandL2 L2)
|
||||
{
|
||||
return spherical_harmonics_L2_Mn2(direction) * L2.Mn2 +
|
||||
spherical_harmonics_L2_Mn1(direction) * L2.Mn1 +
|
||||
|
@ -197,16 +197,16 @@ vec3 spherical_harmonics_L2_evaluate(vec3 direction, SphericalHarmonicBandL2 L2)
|
|||
* 2/3 and 1/4. See this reference for more explanation:
|
||||
* https://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
|
||||
*/
|
||||
vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL0 sh)
|
||||
vec4 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL0 sh)
|
||||
{
|
||||
return spherical_harmonics_L0_evaluate(N, sh.L0);
|
||||
}
|
||||
vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL1 sh)
|
||||
vec4 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL1 sh)
|
||||
{
|
||||
return spherical_harmonics_L0_evaluate(N, sh.L0) +
|
||||
spherical_harmonics_L1_evaluate(N, sh.L1) * (2.0 / 3.0);
|
||||
}
|
||||
vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL2 sh)
|
||||
vec4 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL2 sh)
|
||||
{
|
||||
return spherical_harmonics_L0_evaluate(N, sh.L0) +
|
||||
spherical_harmonics_L1_evaluate(N, sh.L1) * (2.0 / 3.0) +
|
||||
|
@ -221,27 +221,36 @@ vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL2 sh)
|
|||
* This section define the compression scheme of spherical harmonic data.
|
||||
* \{ */
|
||||
|
||||
SphericalHarmonicL1 spherical_harmonics_unpack(vec4 L0_L1_a, vec4 L0_L1_b, vec4 L0_L1_c)
|
||||
SphericalHarmonicL1 spherical_harmonics_unpack(vec4 L0_L1_a,
|
||||
vec4 L0_L1_b,
|
||||
vec4 L0_L1_c,
|
||||
vec4 L0_L1_vis)
|
||||
{
|
||||
SphericalHarmonicL1 sh;
|
||||
sh.L0.M0 = L0_L1_a.xyz;
|
||||
sh.L1.Mn1 = L0_L1_b.xyz;
|
||||
sh.L1.M0 = L0_L1_c.xyz;
|
||||
sh.L1.Mp1 = vec3(L0_L1_a.w, L0_L1_b.w, L0_L1_c.w);
|
||||
sh.L0.M0.xyz = L0_L1_a.xyz;
|
||||
sh.L1.Mn1.xyz = L0_L1_b.xyz;
|
||||
sh.L1.M0.xyz = L0_L1_c.xyz;
|
||||
sh.L1.Mp1.xyz = vec3(L0_L1_a.w, L0_L1_b.w, L0_L1_c.w);
|
||||
sh.L0.M0.w = L0_L1_vis.x;
|
||||
sh.L1.Mn1.w = L0_L1_vis.y;
|
||||
sh.L1.M0.w = L0_L1_vis.z;
|
||||
sh.L1.Mp1.w = L0_L1_vis.w;
|
||||
return sh;
|
||||
}
|
||||
|
||||
void spherical_harmonics_pack(SphericalHarmonicL1 sh,
|
||||
out vec4 L0_L1_a,
|
||||
out vec4 L0_L1_b,
|
||||
out vec4 L0_L1_c)
|
||||
out vec4 L0_L1_c,
|
||||
out vec4 L0_L1_vis)
|
||||
{
|
||||
L0_L1_a.xyz = sh.L0.M0;
|
||||
L0_L1_b.xyz = sh.L1.Mn1;
|
||||
L0_L1_c.xyz = sh.L1.M0;
|
||||
L0_L1_a.xyz = sh.L0.M0.xyz;
|
||||
L0_L1_b.xyz = sh.L1.Mn1.xyz;
|
||||
L0_L1_c.xyz = sh.L1.M0.xyz;
|
||||
L0_L1_a.w = sh.L1.Mp1.x;
|
||||
L0_L1_b.w = sh.L1.Mp1.y;
|
||||
L0_L1_c.w = sh.L1.Mp1.z;
|
||||
L0_L1_vis = vec4(sh.L0.M0.w, sh.L1.Mn1.w, sh.L1.M0.w, sh.L1.Mp1.w);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -77,6 +77,7 @@ GPU_SHADER_CREATE_INFO(eevee_display_probe_grid)
|
|||
.sampler(0, ImageType::FLOAT_3D, "irradiance_a_tx")
|
||||
.sampler(1, ImageType::FLOAT_3D, "irradiance_b_tx")
|
||||
.sampler(2, ImageType::FLOAT_3D, "irradiance_c_tx")
|
||||
.sampler(3, ImageType::FLOAT_3D, "irradiance_d_tx")
|
||||
.do_static_compilation(true);
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_ray)
|
||||
|
@ -86,8 +87,9 @@ GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_ray)
|
|||
.additional_info("eevee_shared", "eevee_surfel_common", "draw_view")
|
||||
.storage_buf(0, Qualifier::READ, "int", "list_start_buf[]")
|
||||
.storage_buf(6, Qualifier::READ, "SurfelListInfoData", "list_info_buf")
|
||||
.image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L0_L1_a_img")
|
||||
.image(1, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L0_L1_b_img")
|
||||
.image(2, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L0_L1_c_img")
|
||||
.image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L0_img")
|
||||
.image(1, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L1_a_img")
|
||||
.image(2, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L1_b_img")
|
||||
.image(3, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_L1_c_img")
|
||||
.compute_source("eevee_lightprobe_irradiance_ray_comp.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
#include "RE_pipeline.h"
|
||||
|
||||
#include "engines/eevee/eevee_lightcache.h"
|
||||
#include "engines/eevee_next/eevee_lightcache.h"
|
||||
#include "engines/eevee_next/eevee_lightcache.hh"
|
||||
|
||||
#include "render_intern.hh" /* own include */
|
||||
|
||||
|
@ -1401,23 +1401,17 @@ static int light_cache_bake_exec(bContext *C, wmOperator *op)
|
|||
|
||||
G.is_break = false;
|
||||
|
||||
RenderEngineType *engine_type = RE_engines_find(scene->r.engine);
|
||||
bool use_eevee_next = STREQ(engine_type->idname, "BLENDER_EEVEE_NEXT");
|
||||
|
||||
/* TODO: abort if selected engine is not eevee. */
|
||||
void *rj = ((use_eevee_next) ?
|
||||
EEVEE_NEXT_lightbake_job_data_alloc :
|
||||
EEVEE_lightbake_job_data_alloc)(bmain, view_layer, scene, false, scene->r.cfra);
|
||||
void *rj = EEVEE_lightbake_job_data_alloc(bmain, view_layer, scene, false, scene->r.cfra);
|
||||
|
||||
light_cache_bake_tag_cache(scene, op);
|
||||
|
||||
bool stop = false, do_update;
|
||||
float progress; /* Not actually used. */
|
||||
/* Do the job. */
|
||||
((use_eevee_next) ? EEVEE_NEXT_lightbake_job :
|
||||
EEVEE_lightbake_job)(rj, &stop, &do_update, &progress);
|
||||
EEVEE_lightbake_job(rj, &stop, &do_update, &progress);
|
||||
/* Free baking data. Result is already stored in the scene data. */
|
||||
((use_eevee_next) ? EEVEE_NEXT_lightbake_job_data_free : EEVEE_lightbake_job_data_free)(rj);
|
||||
EEVEE_lightbake_job_data_free(rj);
|
||||
|
||||
/* No redraw needed, we leave state as we entered it. */
|
||||
ED_update_for_newframe(bmain, CTX_data_depsgraph_pointer(C));
|
||||
|
@ -1436,11 +1430,7 @@ static int light_cache_bake_invoke(bContext *C, wmOperator *op, const wmEvent *
|
|||
Scene *scene = CTX_data_scene(C);
|
||||
int delay = RNA_int_get(op->ptr, "delay");
|
||||
|
||||
RenderEngineType *engine_type = RE_engines_find(scene->r.engine);
|
||||
bool use_eevee_next = STREQ(engine_type->idname, "BLENDER_EEVEE_NEXT");
|
||||
|
||||
wmJob *wm_job = ((use_eevee_next) ? EEVEE_NEXT_lightbake_job_create :
|
||||
EEVEE_lightbake_job_create)(
|
||||
wmJob *wm_job = EEVEE_lightbake_job_create(
|
||||
wm, win, bmain, view_layer, scene, delay, scene->r.cfra);
|
||||
|
||||
if (!wm_job) {
|
||||
|
@ -1514,7 +1504,7 @@ void SCENE_OT_light_cache_bake(wmOperatorType *ot)
|
|||
|
||||
/* NOTE: New version destined to replace the old lightcache bake operator. */
|
||||
|
||||
static void lightprobe_cache_bake_start(bContext *C, wmOperator *op)
|
||||
static blender::Vector<Object *> lightprobe_cache_bake_start(bContext *C, wmOperator *op)
|
||||
{
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
|
@ -1524,10 +1514,13 @@ static void lightprobe_cache_bake_start(bContext *C, wmOperator *op)
|
|||
static_cast<LightProbe *>(ob->data)->type == LIGHTPROBE_TYPE_GRID;
|
||||
};
|
||||
|
||||
auto irradiance_volume_setup = [](Object *ob) {
|
||||
blender::Vector<Object *> probes;
|
||||
|
||||
auto irradiance_volume_setup = [&](Object *ob) {
|
||||
BKE_lightprobe_cache_free(ob);
|
||||
BKE_lightprobe_cache_create(ob);
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
|
||||
probes.append(ob);
|
||||
};
|
||||
|
||||
int subset = RNA_enum_get(op->ptr, "subset");
|
||||
|
@ -1576,21 +1569,37 @@ static void lightprobe_cache_bake_start(bContext *C, wmOperator *op)
|
|||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
|
||||
return probes;
|
||||
}
|
||||
|
||||
static int lightprobe_cache_bake_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
int delay = RNA_int_get(op->ptr, "delay");
|
||||
|
||||
lightprobe_cache_bake_start(C, op);
|
||||
blender::Vector<Object *> probes = lightprobe_cache_bake_start(C, op);
|
||||
|
||||
if (probes.is_empty()) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
wmJob *wm_job = EEVEE_NEXT_lightbake_job_create(
|
||||
wm, win, bmain, view_layer, scene, probes, scene->r.cfra, delay);
|
||||
|
||||
WM_event_add_modal_handler(C, op);
|
||||
|
||||
/* store actual owner of job, so modal operator could check for it,
|
||||
/* Store actual owner of job, so modal operator could check for it,
|
||||
* the reason of this is that active scene could change when rendering
|
||||
* several layers from compositor #31800. */
|
||||
op->customdata = scene;
|
||||
|
||||
WM_jobs_start(wm, wm_job);
|
||||
|
||||
WM_cursor_wait(false);
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
|
@ -1625,7 +1634,18 @@ static void lightprobe_cache_bake_cancel(bContext *C, wmOperator *op)
|
|||
/* Executes blocking bake. */
|
||||
static int lightprobe_cache_bake_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
lightprobe_cache_bake_start(C, op);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
|
||||
blender::Vector<Object *> probes = lightprobe_cache_bake_start(C, op);
|
||||
|
||||
/* TODO: abort if selected engine is not eevee. */
|
||||
void *rj = EEVEE_NEXT_lightbake_job_data_alloc(bmain, view_layer, scene, probes, scene->r.cfra);
|
||||
/* Do the job. */
|
||||
EEVEE_NEXT_lightbake_job(rj, nullptr, nullptr, nullptr);
|
||||
/* Free baking data. Result is already stored in the scene data. */
|
||||
EEVEE_NEXT_lightbake_job_data_free(rj);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
@ -1703,11 +1723,7 @@ static int light_cache_free_exec(bContext *C, wmOperator * /*op*/)
|
|||
EEVEE_lightcache_free(scene->eevee.light_cache_data);
|
||||
scene->eevee.light_cache_data = nullptr;
|
||||
|
||||
RenderEngineType *engine_type = RE_engines_find(scene->r.engine);
|
||||
bool use_eevee_next = STREQ(engine_type->idname, "BLENDER_EEVEE_NEXT");
|
||||
|
||||
((use_eevee_next) ? EEVEE_NEXT_lightcache_info_update :
|
||||
EEVEE_lightcache_info_update)(&scene->eevee);
|
||||
EEVEE_lightcache_info_update(&scene->eevee);
|
||||
|
||||
DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
|
||||
|
||||
|
|
|
@ -139,21 +139,6 @@ typedef struct LightCacheTexture {
|
|||
char _pad[2];
|
||||
} LightCacheTexture;
|
||||
|
||||
typedef struct LightCacheIrradianceGrid {
|
||||
/** Transform to local space [0..resolution-1] range. */
|
||||
float world_to_grid[4][4];
|
||||
/** Number of samples in each dimension. */
|
||||
int resolution[3];
|
||||
/** Number of surfels in the cache. */
|
||||
int surfels_len;
|
||||
/** Currently only used at runtime for debugging the baking process. Not written to file. */
|
||||
void *surfels;
|
||||
/** Irradiance Data. Stored as spherical harmonic. */
|
||||
LightCacheTexture irradiance_L0_L1_a;
|
||||
LightCacheTexture irradiance_L0_L1_b;
|
||||
LightCacheTexture irradiance_L0_L1_c;
|
||||
} LightCacheIrradianceGrid;
|
||||
|
||||
typedef struct LightCache {
|
||||
int flag;
|
||||
/** Version number to know if the cache data is compatible with this version of blender. */
|
||||
|
@ -179,17 +164,10 @@ typedef struct LightCache {
|
|||
/* All lightprobes data contained in the cache. */
|
||||
LightProbeCache *cube_data;
|
||||
LightGridCache *grid_data;
|
||||
|
||||
/* ---- EEVEE-Next ---- */
|
||||
LightCacheIrradianceGrid *grids;
|
||||
// LightCacheReflectionCube *cubes;
|
||||
} LightCache;
|
||||
|
||||
/* Bump the version number for lightcache data structure changes. */
|
||||
#define LIGHTCACHE_STATIC_VERSION 2
|
||||
/* Cache generated by EEVEE-Next. Should be removed and be replaced by bumped
|
||||
* LIGHTCACHE_STATIC_VERSION once EEVEE-Next is made default. */
|
||||
#define LIGHTCACHE_NEXT_STATIC_VERSION 3
|
||||
|
||||
/* LightCache->type */
|
||||
enum {
|
||||
|
@ -306,6 +284,13 @@ typedef struct LightProbeGridCacheFrame {
|
|||
LightProbeIrradianceData irradiance;
|
||||
LightProbeVisibilityData visibility;
|
||||
LightProbeConnectivityData connectivity;
|
||||
|
||||
char _pad[4];
|
||||
|
||||
/** Number of debug surfels. */
|
||||
int surfels_len;
|
||||
/** Debug surfels used to visualize the baking process. */
|
||||
void *surfels;
|
||||
} LightProbeGridCacheFrame;
|
||||
|
||||
/** #LightProbeGridCacheFrame.data_layout (int) */
|
||||
|
|
Loading…
Reference in New Issue