WIP: eevee-next-world-irradiance #108304
|
@ -85,8 +85,7 @@ static void eevee_engine_init(void *vedata)
|
|||
}
|
||||
}
|
||||
|
||||
ved->instance->init(
|
||||
size, &rect, nullptr, depsgraph, nullptr, camera, nullptr, default_view, v3d, rv3d);
|
||||
ved->instance->init(size, &rect, nullptr, depsgraph, camera, nullptr, default_view, v3d, rv3d);
|
||||
}
|
||||
|
||||
static void eevee_draw_scene(void *vedata)
|
||||
|
@ -161,7 +160,7 @@ static void eevee_render_to_image(void *vedata,
|
|||
rcti rect;
|
||||
RE_GetViewPlane(render, &view_rect, &rect);
|
||||
|
||||
instance->init(size, &rect, engine, depsgraph, nullptr, camera_original_ob, layer);
|
||||
instance->init(size, &rect, engine, depsgraph, camera_original_ob, layer);
|
||||
instance->render_frame(layer, viewname);
|
||||
|
||||
EEVEE_Data *ved = static_cast<EEVEE_Data *>(vedata);
|
||||
|
|
|
@ -19,7 +19,9 @@
|
|||
#include "DNA_modifier_types.h"
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
#include "eevee_engine.h"
|
||||
#include "eevee_instance.hh"
|
||||
#include "eevee_lightcache.h"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
|
@ -37,14 +39,12 @@ void Instance::init(const int2 &output_res,
|
|||
const rcti *output_rect,
|
||||
RenderEngine *render_,
|
||||
Depsgraph *depsgraph_,
|
||||
const LightProbe *light_probe_,
|
||||
Object *camera_object_,
|
||||
const RenderLayer *render_layer_,
|
||||
const DRWView *drw_view_,
|
||||
const View3D *v3d_,
|
||||
const RegionView3D *rv3d_)
|
||||
{
|
||||
UNUSED_VARS(light_probe_);
|
||||
render = render_;
|
||||
depsgraph = depsgraph_;
|
||||
camera_orig_object = camera_object_;
|
||||
|
@ -73,6 +73,36 @@ void Instance::init(const int2 &output_res,
|
|||
irradiance_cache.init();
|
||||
}
|
||||
|
||||
void Instance::init_light_bake(Depsgraph *depsgraph, draw::Manager *manager)
|
||||
{
|
||||
this->depsgraph = depsgraph;
|
||||
this->manager = manager;
|
||||
camera_orig_object = nullptr;
|
||||
render = nullptr;
|
||||
render_layer = nullptr;
|
||||
drw_view = nullptr;
|
||||
v3d = nullptr;
|
||||
rv3d = nullptr;
|
||||
|
||||
is_light_bake = true;
|
||||
debug_mode = (eDebugMode)G.debug_value;
|
||||
info = "";
|
||||
|
||||
update_eval_members();
|
||||
|
||||
sampling.init(scene);
|
||||
camera.init();
|
||||
/* Film isn't used but init to avoid side effects in other module. */
|
||||
rcti empty_rect{0, 0, 0, 0};
|
||||
film.init(int2(1), &empty_rect);
|
||||
velocity.init();
|
||||
depth_of_field.init();
|
||||
shadows.init();
|
||||
motion_blur.init();
|
||||
main_view.init();
|
||||
irradiance_cache.init();
|
||||
}
|
||||
|
||||
void Instance::set_time(float time)
|
||||
{
|
||||
BLI_assert(render);
|
||||
|
@ -255,8 +285,6 @@ void Instance::render_sample()
|
|||
|
||||
main_view.render();
|
||||
|
||||
irradiance_cache.create_surfels();
|
||||
|
||||
motion_blur.step();
|
||||
}
|
||||
|
||||
|
@ -306,6 +334,50 @@ 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];
|
||||
copy_m4_m4(irradiance_grid.world_to_grid, grid.transform.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
|
||||
* \{ */
|
||||
|
@ -426,6 +498,69 @@ void Instance::update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view
|
|||
EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL);
|
||||
}
|
||||
|
||||
void Instance::light_bake_irradiance(LightCache *&r_light_cache,
|
||||
std::function<void()> context_enable,
|
||||
std::function<void()> context_disable)
|
||||
{
|
||||
BLI_assert(is_baking());
|
||||
BLI_assert(r_light_cache == nullptr);
|
||||
|
||||
auto custom_pipeline_wrapper = [&](std::function<void()> callback) {
|
||||
context_enable();
|
||||
DRW_custom_pipeline_begin(&draw_engine_eevee_next_type, depsgraph);
|
||||
callback();
|
||||
DRW_custom_pipeline_end();
|
||||
context_disable();
|
||||
};
|
||||
|
||||
auto context_wrapper = [&](std::function<void()> callback) {
|
||||
context_enable();
|
||||
callback();
|
||||
context_disable();
|
||||
};
|
||||
|
||||
/* Count probes. */
|
||||
custom_pipeline_wrapper([&]() { render_sync(); });
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* TODO(fclem): Multiple bounce. We need to use the previous bounce result. */
|
||||
for (int bounce = 0; bounce < 1; bounce++) {
|
||||
for (auto i : light_probes.grids.index_range()) {
|
||||
custom_pipeline_wrapper([&]() {
|
||||
/* TODO: lightprobe visibility group option. */
|
||||
render_sync();
|
||||
irradiance_cache.bake.surfels_create(light_probes.grids[i]);
|
||||
irradiance_cache.bake.surfels_lights_eval();
|
||||
});
|
||||
|
||||
sampling.reset();
|
||||
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();
|
||||
}
|
||||
irradiance_cache.bake.read_result(r_light_cache->grids[i]);
|
||||
});
|
||||
// do_update = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r_light_cache->flag &= ~LIGHTCACHE_BAKING;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
|
|
|
@ -73,6 +73,7 @@ class Instance {
|
|||
/** Evaluated IDs. */
|
||||
Scene *scene;
|
||||
ViewLayer *view_layer;
|
||||
/** Camera object if rendering through a camera. nullptr otherwise. */
|
||||
Object *camera_eval_object;
|
||||
Object *camera_orig_object;
|
||||
/** Only available when rendering for final render. */
|
||||
|
@ -85,6 +86,8 @@ class Instance {
|
|||
|
||||
/** True if the grease pencil engine might be running. */
|
||||
bool gpencil_engine_enabled;
|
||||
/** True if the instance is created for light baking. */
|
||||
bool is_light_bake = false;
|
||||
|
||||
/** Info string displayed at the top of the render / viewport. */
|
||||
std::string info = "";
|
||||
|
@ -114,11 +117,12 @@ class Instance {
|
|||
irradiance_cache(*this){};
|
||||
~Instance(){};
|
||||
|
||||
/* Render & Viewport. */
|
||||
/* TODO(fclem): Split for clarity. */
|
||||
void init(const int2 &output_res,
|
||||
const rcti *output_rect,
|
||||
RenderEngine *render,
|
||||
Depsgraph *depsgraph,
|
||||
const LightProbe *light_probe_ = nullptr,
|
||||
Object *camera_object = nullptr,
|
||||
const RenderLayer *render_layer = nullptr,
|
||||
const DRWView *drw_view = nullptr,
|
||||
|
@ -129,17 +133,33 @@ class Instance {
|
|||
void object_sync(Object *ob);
|
||||
void end_sync();
|
||||
|
||||
/* Render. */
|
||||
|
||||
void render_sync();
|
||||
void render_frame(RenderLayer *render_layer, const char *view_name);
|
||||
void store_metadata(RenderResult *render_result);
|
||||
|
||||
/* Viewport. */
|
||||
|
||||
void draw_viewport(DefaultFramebufferList *dfbl);
|
||||
|
||||
/* Light bake. */
|
||||
|
||||
void init_light_bake(Depsgraph *depsgraph, draw::Manager *manager);
|
||||
void light_bake_irradiance(LightCache *&r_light_cache,
|
||||
std::function<void()> context_enable,
|
||||
std::function<void()> context_disable);
|
||||
|
||||
static void update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer);
|
||||
|
||||
bool is_viewport() const
|
||||
{
|
||||
return render == nullptr;
|
||||
return render == nullptr && !is_baking();
|
||||
}
|
||||
|
||||
bool is_baking() const
|
||||
{
|
||||
return is_light_bake;
|
||||
}
|
||||
|
||||
bool overlays_enabled() const
|
||||
|
@ -176,6 +196,13 @@ 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);
|
||||
|
|
|
@ -1,18 +1,30 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "DNA_lightprobe_types.h"
|
||||
#include "GPU_capabilities.h"
|
||||
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
#include "eevee_irradiance_cache.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Interface
|
||||
* \{ */
|
||||
|
||||
void IrradianceCache::init()
|
||||
{
|
||||
}
|
||||
|
||||
void IrradianceCache::sync()
|
||||
{
|
||||
debug_pass_sync();
|
||||
if (inst_.is_baking()) {
|
||||
bake.sync();
|
||||
}
|
||||
else {
|
||||
debug_pass_sync();
|
||||
}
|
||||
}
|
||||
|
||||
void IrradianceCache::debug_pass_sync()
|
||||
|
@ -22,13 +34,32 @@ void IrradianceCache::debug_pass_sync()
|
|||
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) {
|
||||
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_.bind_ssbo("surfels_buf", surfels_buf_);
|
||||
debug_surfels_ps_.push_constant("surfel_radius", 0.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);
|
||||
}
|
||||
|
||||
|
@ -50,7 +81,17 @@ void IrradianceCache::debug_draw(View &view, GPUFrameBuffer *view_fb)
|
|||
inst_.manager->submit(debug_surfels_ps_, view);
|
||||
}
|
||||
|
||||
void IrradianceCache::create_surfels()
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Baking
|
||||
* \{ */
|
||||
|
||||
void IrradianceBake::sync()
|
||||
{
|
||||
}
|
||||
|
||||
void IrradianceBake::surfels_create(const IrradianceGrid & /* grid */)
|
||||
{
|
||||
/**
|
||||
* We rasterize the scene along the 3 axes. Each generated fragment will write a surface element
|
||||
|
@ -60,7 +101,7 @@ void IrradianceCache::create_surfels()
|
|||
using namespace blender::math;
|
||||
|
||||
/* Attachment-less frame-buffer. */
|
||||
empty_raster_fb_.ensure(int2(40 * 4));
|
||||
empty_raster_fb_.ensure(int2(10 * 4));
|
||||
|
||||
/** We could use multi-view rendering here to avoid multiple submissions but it is unlikely to
|
||||
* make any difference. The bottleneck is still the light propagation loop. */
|
||||
|
@ -89,17 +130,15 @@ void IrradianceCache::create_surfels()
|
|||
DRW_stats_group_end();
|
||||
|
||||
/* Allocate surfel pool. */
|
||||
GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE);
|
||||
capture_info_buf_.read();
|
||||
if (capture_info_buf_.surfel_len == 0) {
|
||||
/* Not surfel to allocated. */
|
||||
return;
|
||||
}
|
||||
/* 1000000 for testing. */
|
||||
if (capture_info_buf_.surfel_len * sizeof(Surfel) > 1000000) {
|
||||
/* TODO(fclem): Display error message. */
|
||||
/* Not enough GPU memory to fit all needed surfels. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO(fclem): Check for GL limit and abort if the surfel cache doesn't fit the GPU memory. */
|
||||
std::cout << "Resize " << capture_info_buf_.surfel_len << std::endl;
|
||||
surfels_buf_.resize(capture_info_buf_.surfel_len);
|
||||
|
||||
DRW_stats_group_start("IrradianceBake.SurfelsCreate");
|
||||
|
@ -114,17 +153,37 @@ void IrradianceCache::create_surfels()
|
|||
render_axis(Axis::Z);
|
||||
|
||||
DRW_stats_group_end();
|
||||
|
||||
/* TODO(fclem): Resize at the end of the light propagation. */
|
||||
}
|
||||
|
||||
void IrradianceCache::propagate_light()
|
||||
void IrradianceBake::surfels_lights_eval()
|
||||
{
|
||||
/* Evaluate direct lighting (and also clear the surfels radiance). */
|
||||
/* For every ray direction over the sphere. */
|
||||
/* Create the surfels lists. */
|
||||
}
|
||||
|
||||
void IrradianceBake::propagate_light_sample()
|
||||
{
|
||||
/* Pick random ray direction over the sphere. */
|
||||
/* Project to regular grid and create the surfels lists. */
|
||||
/* Sort the surfels lists. */
|
||||
/* Propagate light. */
|
||||
}
|
||||
|
||||
void IrradianceBake::read_result(LightCacheIrradianceGrid &light_cache_grid)
|
||||
{
|
||||
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);
|
||||
std::cout << "Read " << capture_info_buf_.surfel_len << std::endl;
|
||||
surfels_buf_.read();
|
||||
light_cache_grid.surfels_len = capture_info_buf_.surfel_len;
|
||||
light_cache_grid.surfels = MEM_dupallocN(surfels_buf_.data());
|
||||
break;
|
||||
default:
|
||||
/* Nothing to display. */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_lightprobe_types.h"
|
||||
|
||||
#include "eevee_lightprobe.hh"
|
||||
#include "eevee_shader_shared.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
@ -13,32 +16,63 @@ namespace blender::eevee {
|
|||
class Instance;
|
||||
class CapturePipeline;
|
||||
|
||||
class IrradianceCache {
|
||||
/**
|
||||
* Baking related pass and data. Not used at runtime.
|
||||
*/
|
||||
class IrradianceBake {
|
||||
friend CapturePipeline;
|
||||
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
/** Light cache being baked. */
|
||||
LightCache *light_cache_ = nullptr;
|
||||
/** Surface elements that represent the scene. */
|
||||
SurfelBuf surfels_buf_;
|
||||
/** Capture state. */
|
||||
CaptureInfoBuf capture_info_buf_;
|
||||
|
||||
PassSimple debug_surfels_ps_ = {"IrradianceCache.Debug"};
|
||||
|
||||
/** Framebuffer. */
|
||||
Framebuffer empty_raster_fb_ = {"empty_raster_fb_"};
|
||||
View view_ = {"ortho_raster_view"};
|
||||
|
||||
public:
|
||||
IrradianceCache(Instance &inst) : inst_(inst){};
|
||||
IrradianceBake(Instance &inst) : inst_(inst){};
|
||||
|
||||
void sync();
|
||||
|
||||
/** Create a surfel representation of the scene from the \a grid using the capture pipeline. */
|
||||
void surfels_create(const IrradianceGrid &grid);
|
||||
/** 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. */
|
||||
void propagate_light_sample();
|
||||
|
||||
/** Read grid final irradiance back to CPU into \a light_cache_grid . */
|
||||
void read_result(LightCacheIrradianceGrid &light_cache_grid);
|
||||
};
|
||||
|
||||
/**
|
||||
* Runtime container of diffuse indirect lighting.
|
||||
* Also have debug and baking components.
|
||||
*/
|
||||
class IrradianceCache {
|
||||
public:
|
||||
IrradianceBake bake;
|
||||
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
PassSimple debug_surfels_ps_ = {"IrradianceCache.Debug"};
|
||||
/** Debug surfel elements copied from the light cache. */
|
||||
draw::StorageVectorBuffer<Surfel> surfels_buf_;
|
||||
|
||||
public:
|
||||
IrradianceCache(Instance &inst) : bake(inst), inst_(inst){};
|
||||
~IrradianceCache(){};
|
||||
|
||||
void init();
|
||||
void sync();
|
||||
|
||||
void create_surfels();
|
||||
void propagate_light();
|
||||
|
||||
void debug_pass_sync();
|
||||
void debug_draw(View &view, GPUFrameBuffer *view_fb);
|
||||
};
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "wm_window.h"
|
||||
|
||||
#include "eevee_engine.h"
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
#include "eevee_lightcache.h"
|
||||
|
@ -57,6 +58,11 @@ class LightBake {
|
|||
|
||||
/** Baking instance. Created and freed in the worker thread. */
|
||||
Instance *instance_ = nullptr;
|
||||
/** Light Cache being baked. Create in worker thread and pass ownership to original scene on
|
||||
* first `update()` call. */
|
||||
::LightCache *light_cache_ = nullptr;
|
||||
/** Manager used for command submission. Created and freed in the worker thread. */
|
||||
draw::Manager *manager_ = nullptr;
|
||||
|
||||
public:
|
||||
LightBake(struct Main *bmain,
|
||||
|
@ -93,18 +99,14 @@ class LightBake {
|
|||
{
|
||||
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 (original_scene->eevee.light_cache_data == nullptr) {
|
||||
LightCache *light_cache = EEVEE_NEXT_lightcache_create();
|
||||
|
||||
LightCacheIrradianceGrid *grids = (LightCacheIrradianceGrid *)MEM_callocN(
|
||||
1 * sizeof(LightCacheIrradianceGrid), "LightCacheIrradianceGrid");
|
||||
grids[0].resolution[0] = 1;
|
||||
grids[0].resolution[1] = 2;
|
||||
grids[0].resolution[2] = 3;
|
||||
light_cache->grid_len = 1;
|
||||
light_cache->grids = grids;
|
||||
original_scene->eevee.light_cache_data = light_cache;
|
||||
if (scene_light_cache != light_cache_) {
|
||||
if (scene_light_cache != nullptr) {
|
||||
/* Delete old data if existing. */
|
||||
EEVEE_NEXT_lightcache_free(scene_light_cache);
|
||||
}
|
||||
scene_light_cache = light_cache_;
|
||||
}
|
||||
|
||||
EEVEE_NEXT_lightcache_info_update(&original_scene->eevee);
|
||||
|
@ -120,14 +122,19 @@ class LightBake {
|
|||
DEG_graph_relations_update(depsgraph_);
|
||||
DEG_evaluate_on_framechange(depsgraph_, frame_);
|
||||
|
||||
PIL_sleep_ms(1000);
|
||||
|
||||
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_);
|
||||
}
|
||||
|
||||
instance_->light_bake_irradiance(
|
||||
light_cache_, [this]() { context_enable(); }, [this]() { context_disable(); });
|
||||
|
||||
delete_resources();
|
||||
}
|
||||
|
||||
|
@ -190,8 +197,9 @@ class LightBake {
|
|||
/* Bind context without GPU_render_begin(). */
|
||||
context_enable(false);
|
||||
|
||||
/* Free instance and its resources (Textures, Framebuffers, etc...). */
|
||||
/* Free GPU data (Textures, Framebuffers, etc...). */
|
||||
delete instance_;
|
||||
delete manager_;
|
||||
|
||||
/* Delete / unbind the GL & GPU context. Assumes it is currently bound. */
|
||||
if (GPU_use_main_context_workaround() && !BLI_thread_is_main()) {
|
||||
|
@ -323,8 +331,15 @@ void EEVEE_NEXT_lightcache_free(LightCache *light_cache)
|
|||
MEM_SAFE_FREE(light_cache->cube_data);
|
||||
MEM_SAFE_FREE(light_cache->grid_data);
|
||||
}
|
||||
|
||||
MEM_SAFE_FREE(light_cache->grids);
|
||||
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);
|
||||
}
|
||||
MEM_SAFE_FREE(light_cache->grids);
|
||||
}
|
||||
MEM_freeN(light_cache);
|
||||
}
|
||||
|
||||
|
@ -386,13 +401,76 @@ void EEVEE_NEXT_lightcache_info_update(SceneEEVEE *eevee)
|
|||
|
||||
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)
|
||||
{
|
||||
BLO_read_data_address(reader, &light_cache->grids);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -21,6 +21,9 @@ void LightProbeModule::begin_sync()
|
|||
(inst_.scene->eevee.flag & SCE_EEVEE_GI_AUTOBAKE) != 0;
|
||||
grid_update_ = false;
|
||||
cube_update_ = false;
|
||||
|
||||
grids.clear();
|
||||
cubes.clear();
|
||||
}
|
||||
|
||||
void LightProbeModule::sync_grid(ObjectHandle &handle)
|
||||
|
@ -31,6 +34,10 @@ void LightProbeModule::sync_grid(ObjectHandle &handle)
|
|||
grid.initialized = true;
|
||||
grid_update_ = true;
|
||||
}
|
||||
|
||||
if (inst_.is_baking()) {
|
||||
grids.append({});
|
||||
}
|
||||
}
|
||||
|
||||
void LightProbeModule::sync_cube(ObjectHandle &handle)
|
||||
|
@ -41,6 +48,10 @@ void LightProbeModule::sync_cube(ObjectHandle &handle)
|
|||
cube.initialized = true;
|
||||
cube_update_ = true;
|
||||
}
|
||||
|
||||
if (inst_.is_baking()) {
|
||||
cubes.append({});
|
||||
}
|
||||
}
|
||||
|
||||
void LightProbeModule::sync_probe(const Object *ob, ObjectHandle &handle)
|
||||
|
|
|
@ -17,12 +17,24 @@ namespace blender::eevee {
|
|||
|
||||
class Instance;
|
||||
|
||||
struct IrradianceGrid {
|
||||
float4x4 transform;
|
||||
int3 resolution;
|
||||
};
|
||||
|
||||
struct ReflectionCube {};
|
||||
|
||||
struct LightProbe {
|
||||
bool used = false;
|
||||
bool initialized = false;
|
||||
};
|
||||
|
||||
class LightProbeModule {
|
||||
public:
|
||||
/** Synced probe data. Only valid if the `eevee::Instance` is a baking instance. */
|
||||
Vector<IrradianceGrid> grids;
|
||||
Vector<ReflectionCube> cubes;
|
||||
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
|
|
|
@ -239,14 +239,20 @@ Material &MaterialModule::material_sync(Object *ob,
|
|||
|
||||
Material &mat = material_map_.lookup_or_add_cb(material_key, [&]() {
|
||||
Material mat;
|
||||
/* Order is important for transparent. */
|
||||
mat.prepass = material_pass_get(ob, blender_mat, prepass_pipe, geometry_type);
|
||||
mat.shading = material_pass_get(ob, blender_mat, surface_pipe, geometry_type);
|
||||
if (true) {
|
||||
/* TODO(fclem): This can be expensive since it can trigger a shader compilation. So better
|
||||
* avoid this if we can. */
|
||||
if (inst_.is_baking()) {
|
||||
mat.prepass = MaterialPass();
|
||||
/* TODO(fclem): Still need the shading pass for correct attribute extraction. Would be better
|
||||
* to avoid this shader compilation in another context. */
|
||||
mat.shading = material_pass_get(ob, blender_mat, surface_pipe, geometry_type);
|
||||
mat.capture = material_pass_get(ob, blender_mat, MAT_PIPE_CAPTURE, geometry_type);
|
||||
}
|
||||
else {
|
||||
/* Order is important for transparent. */
|
||||
mat.prepass = material_pass_get(ob, blender_mat, prepass_pipe, geometry_type);
|
||||
mat.shading = material_pass_get(ob, blender_mat, surface_pipe, geometry_type);
|
||||
mat.capture = MaterialPass();
|
||||
}
|
||||
|
||||
if (blender_mat->blend_shadow == MA_BS_NONE) {
|
||||
mat.shadow = MaterialPass();
|
||||
}
|
||||
|
|
|
@ -514,10 +514,10 @@ void CapturePipeline::sync()
|
|||
/* Surfel output is done using a SSBO, so no need for a fragment shader output color or depth. */
|
||||
/* WORKAROUND: Avoid rasterizer discard, but the shaders actually use no fragment output. */
|
||||
surface_ps_.state_set(DRW_STATE_WRITE_STENCIL);
|
||||
surface_ps_.framebuffer_set(&inst_.irradiance_cache.empty_raster_fb_);
|
||||
surface_ps_.framebuffer_set(&inst_.irradiance_cache.bake.empty_raster_fb_);
|
||||
|
||||
surface_ps_.bind_ssbo(SURFEL_BUF_SLOT, &inst_.irradiance_cache.surfels_buf_);
|
||||
surface_ps_.bind_ssbo(CAPTURE_BUF_SLOT, &inst_.irradiance_cache.capture_info_buf_);
|
||||
surface_ps_.bind_ssbo(SURFEL_BUF_SLOT, &inst_.irradiance_cache.bake.surfels_buf_);
|
||||
surface_ps_.bind_ssbo(CAPTURE_BUF_SLOT, &inst_.irradiance_cache.bake.capture_info_buf_);
|
||||
|
||||
surface_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
|
||||
/* TODO(fclem): Remove. There should be no view dependent behavior during capture. */
|
||||
|
|
|
@ -21,7 +21,13 @@ namespace blender::eevee {
|
|||
|
||||
void Sampling::init(const Scene *scene)
|
||||
{
|
||||
sample_count_ = inst_.is_viewport() ? scene->eevee.taa_samples : scene->eevee.taa_render_samples;
|
||||
if (inst_.is_baking()) {
|
||||
sample_count_ = max_ii(1, scene->eevee.gi_irradiance_samples);
|
||||
}
|
||||
else {
|
||||
sample_count_ = inst_.is_viewport() ? scene->eevee.taa_samples :
|
||||
scene->eevee.taa_render_samples;
|
||||
}
|
||||
|
||||
if (sample_count_ == 0) {
|
||||
BLI_assert(inst_.is_viewport());
|
||||
|
|
|
@ -838,6 +838,11 @@ void DRW_custom_pipeline(DrawEngineType *draw_engine_type,
|
|||
struct Depsgraph *depsgraph,
|
||||
void (*callback)(void *vedata, void *user_data),
|
||||
void *user_data);
|
||||
/**
|
||||
* Same as `DRW_custom_pipeline` but allow better code-flow than a callback.
|
||||
*/
|
||||
void DRW_custom_pipeline_begin(DrawEngineType *draw_engine_type, struct Depsgraph *depsgraph);
|
||||
void DRW_custom_pipeline_end(void);
|
||||
|
||||
/**
|
||||
* Used when the render engine want to redo another cache populate inside the same render frame.
|
||||
|
|
|
@ -2094,10 +2094,7 @@ void DRW_render_object_iter(
|
|||
drw_task_graph_deinit();
|
||||
}
|
||||
|
||||
void DRW_custom_pipeline(DrawEngineType *draw_engine_type,
|
||||
struct Depsgraph *depsgraph,
|
||||
void (*callback)(void *vedata, void *user_data),
|
||||
void *user_data)
|
||||
void DRW_custom_pipeline_begin(DrawEngineType *draw_engine_type, struct Depsgraph *depsgraph)
|
||||
{
|
||||
Scene *scene = DEG_get_evaluated_scene(depsgraph);
|
||||
ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
|
||||
|
@ -2124,11 +2121,11 @@ void DRW_custom_pipeline(DrawEngineType *draw_engine_type,
|
|||
DRW_volume_init(DST.vmempool);
|
||||
DRW_smoke_init(DST.vmempool);
|
||||
|
||||
ViewportEngineData *data = DRW_view_data_engine_data_get_ensure(DST.view_data_active,
|
||||
draw_engine_type);
|
||||
DRW_view_data_engine_data_get_ensure(DST.view_data_active, draw_engine_type);
|
||||
}
|
||||
|
||||
/* Execute the callback */
|
||||
callback(data, user_data);
|
||||
void DRW_custom_pipeline_end()
|
||||
{
|
||||
DST.buffer_finish_called = false;
|
||||
|
||||
DRW_smoke_exit(DST.vmempool);
|
||||
|
@ -2147,6 +2144,21 @@ void DRW_custom_pipeline(DrawEngineType *draw_engine_type,
|
|||
drw_manager_exit(&DST);
|
||||
}
|
||||
|
||||
void DRW_custom_pipeline(DrawEngineType *draw_engine_type,
|
||||
struct Depsgraph *depsgraph,
|
||||
void (*callback)(void *vedata, void *user_data),
|
||||
void *user_data)
|
||||
{
|
||||
DRW_custom_pipeline_begin(draw_engine_type, depsgraph);
|
||||
|
||||
ViewportEngineData *data = DRW_view_data_engine_data_get_ensure(DST.view_data_active,
|
||||
draw_engine_type);
|
||||
/* Execute the callback. */
|
||||
callback(data, user_data);
|
||||
|
||||
DRW_custom_pipeline_end();
|
||||
}
|
||||
|
||||
void DRW_cache_restart(void)
|
||||
{
|
||||
DRW_smoke_exit(DST.vmempool);
|
||||
|
|
|
@ -150,11 +150,11 @@ typedef struct LightCacheIrradianceGrid {
|
|||
/** Number of surfels in the cache. */
|
||||
int surfels_len;
|
||||
/** Currently only used at runtime for debugging the baking process. Not written to file. */
|
||||
// struct GPUStorageBuf *surfels;
|
||||
void *surfels;
|
||||
/** Irradiance Data. Stored as spherical harmonic. */
|
||||
// LightCacheTexture *irradiance_L0_L1_a;
|
||||
// LightCacheTexture *irradiance_L0_L1_b;
|
||||
// LightCacheTexture *irradiance_L0_L1_c;
|
||||
LightCacheTexture irradiance_L0_L1_a;
|
||||
LightCacheTexture irradiance_L0_L1_b;
|
||||
LightCacheTexture irradiance_L0_L1_c;
|
||||
} LightCacheIrradianceGrid;
|
||||
|
||||
typedef struct LightCache {
|
||||
|
|
Loading…
Reference in New Issue