WIP: eevee-next-world-irradiance #108304
|
@ -465,7 +465,9 @@ set(GLSL_SRC
|
|||
engines/eevee_next/shaders/eevee_light_eval_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_light_iter_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_light_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_lightprobe_irradiance_load_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_lightprobe_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_ltc_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl
|
||||
|
|
|
@ -71,6 +71,10 @@
|
|||
#define MOTION_BLUR_GROUP_SIZE 32
|
||||
#define MOTION_BLUR_DILATE_GROUP_SIZE 512
|
||||
|
||||
/* Irradiance Cache. */
|
||||
/** Maximum number of entities inside the cache. */
|
||||
#define IRRADIANCE_GRID_MAX 64
|
||||
|
||||
/* Depth Of Field. */
|
||||
#define DOF_TILES_SIZE 8
|
||||
#define DOF_TILES_FLATTEN_GROUP_SIZE DOF_TILES_SIZE
|
||||
|
@ -89,17 +93,20 @@
|
|||
/* IrradianceBake. */
|
||||
#define SURFEL_GROUP_SIZE 256
|
||||
#define SURFEL_LIST_GROUP_SIZE 256
|
||||
#define IRRADIANCE_GRID_GROUP_SIZE 4 /* In each dimension, do 4x4x4 workgroup size. */
|
||||
#define IRRADIANCE_GRID_GROUP_SIZE 4 /* In each dimension, so 4x4x4 workgroup size. */
|
||||
#define IRRADIANCE_GRID_BRICK_SIZE 4 /* In each dimension, so 4x4x4 brick size. */
|
||||
|
||||
/* Resource bindings. */
|
||||
|
||||
/* Texture. */
|
||||
#define IRRADIANCE_ATLAS_TEX_SLOT 11
|
||||
#define SHADOW_TILEMAPS_TEX_SLOT 12
|
||||
/* Only during surface shading. */
|
||||
#define SHADOW_ATLAS_TEX_SLOT 13
|
||||
/* Only during shadow rendering. */
|
||||
#define SHADOW_RENDER_MAP_SLOT 13
|
||||
#define RBUFS_UTILITY_TEX_SLOT 14
|
||||
#define HIZ_TEX_SLOT 15
|
||||
|
||||
/* Images. */
|
||||
#define RBUFS_NORMAL_SLOT 0
|
||||
|
@ -115,6 +122,8 @@
|
|||
#define GBUF_COLOR_SLOT RBUFS_DIFF_COLOR_SLOT
|
||||
|
||||
/* Uniform Buffers. */
|
||||
#define IRRADIANCE_GRID_BUF_SLOT 3
|
||||
#define HIZ_BUF_SLOT 5
|
||||
/* Only during pre-pass. */
|
||||
#define VELOCITY_CAMERA_PREV_BUF 3
|
||||
#define VELOCITY_CAMERA_CURR_BUF 4
|
||||
|
@ -127,6 +136,7 @@
|
|||
#define LIGHT_BUF_SLOT 1
|
||||
#define LIGHT_ZBIN_BUF_SLOT 2
|
||||
#define LIGHT_TILE_BUF_SLOT 3
|
||||
#define IRRADIANCE_BRICK_BUF_SLOT 4
|
||||
/* Only during surface capture. */
|
||||
#define SURFEL_BUF_SLOT 4
|
||||
/* Only during surface capture. */
|
||||
|
|
|
@ -474,7 +474,6 @@ void Instance::light_bake_irradiance(Object &probe,
|
|||
};
|
||||
|
||||
custom_pipeline_wrapper([&]() {
|
||||
GPU_debug_capture_begin();
|
||||
irradiance_cache.bake.surfel_raster_views_sync(probe);
|
||||
/* TODO: lightprobe visibility group option. */
|
||||
manager->begin_sync();
|
||||
|
@ -483,7 +482,6 @@ void Instance::light_bake_irradiance(Object &probe,
|
|||
|
||||
irradiance_cache.bake.surfels_create(probe);
|
||||
irradiance_cache.bake.surfels_lights_eval();
|
||||
GPU_debug_capture_end();
|
||||
});
|
||||
|
||||
int bounce_len = scene->eevee.gi_diffuse_bounces;
|
||||
|
|
|
@ -22,7 +22,44 @@ namespace blender::eevee {
|
|||
void IrradianceCache::init()
|
||||
{
|
||||
display_grids_enabled_ = DRW_state_draw_support() &&
|
||||
inst_.scene->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE;
|
||||
(inst_.scene->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE);
|
||||
|
||||
/* TODO option. */
|
||||
int atlas_byte_size = 1024 * 1024 * 16;
|
||||
/* This might become an option in the future. */
|
||||
bool use_l2_band = false;
|
||||
int sh_coef_len = use_l2_band ? 9 : 4;
|
||||
int texel_byte_size = 8; /* Assumes GPU_RGBA16F. */
|
||||
int3 atlas_extent(IRRADIANCE_GRID_BRICK_SIZE);
|
||||
atlas_extent.z *= sh_coef_len;
|
||||
int atlas_col_count = 256;
|
||||
atlas_extent.x *= atlas_col_count;
|
||||
/* Determine the row count depending on the scene settings. */
|
||||
int row_byte_size = atlas_extent.x * atlas_extent.y * atlas_extent.z * texel_byte_size;
|
||||
int atlas_row_count = divide_ceil_u(atlas_byte_size, row_byte_size);
|
||||
atlas_extent.y *= atlas_row_count;
|
||||
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_WRITE | GPU_TEXTURE_USAGE_SHADER_READ;
|
||||
do_full_update_ = irradiance_atlas_tx_.ensure_3d(GPU_RGBA16F, atlas_extent, usage);
|
||||
|
||||
if (do_full_update_) {
|
||||
/* Delete all references to existing bricks. */
|
||||
for (IrradianceGrid &grid : inst_.light_probes.grid_map_.values()) {
|
||||
grid.bricks.clear();
|
||||
}
|
||||
brick_pool_.clear();
|
||||
/* Fill with all the available bricks. */
|
||||
for (auto i : IndexRange(atlas_row_count * atlas_col_count)) {
|
||||
IrradianceBrick brick;
|
||||
brick.atlas_coord = uint2(i % atlas_col_count, i / atlas_col_count) *
|
||||
IRRADIANCE_GRID_BRICK_SIZE;
|
||||
brick_pool_.append(irradiance_brick_pack(brick));
|
||||
}
|
||||
}
|
||||
|
||||
if (irradiance_atlas_tx_.is_valid() == false) {
|
||||
inst_.info = "Irradiance Atlas texture could not be created";
|
||||
}
|
||||
}
|
||||
|
||||
void IrradianceCache::sync()
|
||||
|
@ -32,6 +69,165 @@ void IrradianceCache::sync()
|
|||
}
|
||||
}
|
||||
|
||||
Vector<IrradianceBrickPacked> IrradianceCache::bricks_alloc(int brick_len)
|
||||
{
|
||||
if (brick_pool_.size() < brick_len) {
|
||||
/* Fail allocation. Not enough brick in the atlas. */
|
||||
return {};
|
||||
}
|
||||
Vector<IrradianceBrickPacked> allocated;
|
||||
allocated.resize(brick_len);
|
||||
/* Copy bricks to return vector. */
|
||||
allocated.as_mutable_span().copy_from(brick_pool_.as_span().take_back(brick_len));
|
||||
/* Remove bricks from the pool. */
|
||||
brick_pool_.resize(brick_pool_.size() - brick_len);
|
||||
|
||||
return allocated;
|
||||
}
|
||||
|
||||
void IrradianceCache::bricks_free(Vector<IrradianceBrickPacked> &bricks)
|
||||
{
|
||||
brick_pool_.extend(bricks.as_span());
|
||||
bricks.clear();
|
||||
}
|
||||
|
||||
void IrradianceCache::set_view(View & /*view*/)
|
||||
{
|
||||
Vector<IrradianceGrid *> grid_updates;
|
||||
|
||||
/* First allocate the needed bricks and populate the brick buffer. */
|
||||
int grids_len = 0;
|
||||
bricks_infos_buf_.clear();
|
||||
for (IrradianceGrid &grid : inst_.light_probes.grid_map_.values()) {
|
||||
LightProbeGridCacheFrame *cache = grid.cache ? grid.cache->grid_static_cache : nullptr;
|
||||
if (cache == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cache->baking.L0 == nullptr && cache->irradiance.L0 == nullptr) {
|
||||
/* No data. */
|
||||
continue;
|
||||
}
|
||||
|
||||
int3 grid_size = int3(cache->size);
|
||||
if (grid_size.x <= 0 || grid_size.y <= 0 || grid_size.z <= 0) {
|
||||
inst_.info = "Error: Malformed irradiance grid data";
|
||||
continue;
|
||||
}
|
||||
|
||||
/* TODO frustum cull and only load visible grids. */
|
||||
|
||||
if (grids_len >= IRRADIANCE_GRID_MAX) {
|
||||
inst_.info = "Error: Too many grid visible";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (grid.bricks.is_empty()) {
|
||||
int3 grid_size_in_bricks = math::divide_ceil(grid_size,
|
||||
int3(IRRADIANCE_GRID_BRICK_SIZE - 1));
|
||||
int brick_len = grid_size_in_bricks.x * grid_size_in_bricks.y * grid_size_in_bricks.z;
|
||||
grid.bricks = bricks_alloc(brick_len);
|
||||
|
||||
if (grid.bricks.is_empty()) {
|
||||
inst_.info = "Error: Irradiance grid allocation failed";
|
||||
continue;
|
||||
}
|
||||
|
||||
grid_updates.append(&grid);
|
||||
}
|
||||
|
||||
grid.brick_offset = bricks_infos_buf_.size();
|
||||
bricks_infos_buf_.extend(grid.bricks);
|
||||
|
||||
if (grid_size.x <= 0 || grid_size.y <= 0 || grid_size.z <= 0) {
|
||||
inst_.info = "Error: Malformed irradiance grid data";
|
||||
continue;
|
||||
}
|
||||
|
||||
float4x4 grid_to_world = grid.object_to_world * math::from_location<float4x4>(float3(-1.0f)) *
|
||||
math::from_scale<float4x4>(float3(2.0f / float3(grid_size))) *
|
||||
math::from_location<float4x4>(float3(0.0f));
|
||||
|
||||
grid.world_to_grid_transposed = float3x4(math::transpose(math::invert(grid_to_world)));
|
||||
grid.grid_size = grid_size;
|
||||
grid.grid_index = grids_len;
|
||||
grids_infos_buf_[grids_len++] = grid;
|
||||
}
|
||||
|
||||
/* TODO(fclem): Insert world grid here. */
|
||||
|
||||
if (grids_len < IRRADIANCE_GRID_MAX) {
|
||||
/* Tag last grid as invalid to stop the iteration. */
|
||||
grids_infos_buf_[grids_len].grid_size = int3(-1);
|
||||
}
|
||||
|
||||
bricks_infos_buf_.push_update();
|
||||
grids_infos_buf_.push_update();
|
||||
|
||||
/* Upload data for each grid that need to be inserted in the atlas. */
|
||||
for (IrradianceGrid *grid : grid_updates) {
|
||||
LightProbeGridCacheFrame *cache = grid->cache->grid_static_cache;
|
||||
|
||||
/* Staging textures are recreated for each 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;
|
||||
}
|
||||
|
||||
if (irradiance_a_tx.is_valid() == false) {
|
||||
inst_.info = "Error: Could not allocate irradiance staging texture";
|
||||
/* Avoid undefined behavior with uninitialized values. */
|
||||
irradiance_a_tx.clear(float4(0.0f));
|
||||
irradiance_b_tx.clear(float4(0.0f));
|
||||
irradiance_c_tx.clear(float4(0.0f));
|
||||
irradiance_d_tx.clear(float4(0.0f));
|
||||
}
|
||||
|
||||
grid_upload_ps_.init();
|
||||
grid_upload_ps_.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_LOAD));
|
||||
|
||||
grid_upload_ps_.push_constant("grid_index", grid->grid_index);
|
||||
grid_upload_ps_.bind_ubo("grids_infos_buf", &grids_infos_buf_);
|
||||
grid_upload_ps_.bind_ssbo("bricks_infos_buf", &bricks_infos_buf_);
|
||||
grid_upload_ps_.bind_texture("irradiance_a_tx", &irradiance_a_tx);
|
||||
grid_upload_ps_.bind_texture("irradiance_b_tx", &irradiance_b_tx);
|
||||
grid_upload_ps_.bind_texture("irradiance_c_tx", &irradiance_c_tx);
|
||||
grid_upload_ps_.bind_texture("irradiance_d_tx", &irradiance_d_tx);
|
||||
grid_upload_ps_.bind_image("irradiance_atlas_img", &irradiance_atlas_tx_);
|
||||
|
||||
/* Note that we take into account the padding border of each brick. */
|
||||
int3 grid_size_in_bricks = math::divide_ceil(grid_size, int3(IRRADIANCE_GRID_BRICK_SIZE - 1));
|
||||
grid_upload_ps_.dispatch(grid_size_in_bricks);
|
||||
|
||||
inst_.manager->submit(grid_upload_ps_);
|
||||
|
||||
irradiance_a_tx.free();
|
||||
irradiance_b_tx.free();
|
||||
irradiance_c_tx.free();
|
||||
irradiance_d_tx.free();
|
||||
}
|
||||
|
||||
do_full_update_ = false;
|
||||
}
|
||||
|
||||
void IrradianceCache::viewport_draw(View &view, GPUFrameBuffer *view_fb)
|
||||
{
|
||||
if (!inst_.is_baking()) {
|
||||
|
@ -105,24 +301,24 @@ void IrradianceCache::display_pass_draw(View &view, GPUFrameBuffer *view_fb)
|
|||
}
|
||||
|
||||
/* 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"};
|
||||
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);
|
||||
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);
|
||||
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;
|
||||
|
@ -139,10 +335,10 @@ void IrradianceCache::display_pass_draw(View &view, GPUFrameBuffer *view_fb)
|
|||
display_grids_ps_.push_constant("grid_to_world", grid.object_to_world);
|
||||
display_grids_ps_.push_constant("world_to_grid", grid.world_to_object);
|
||||
|
||||
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_);
|
||||
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;
|
||||
|
@ -150,10 +346,10 @@ void IrradianceCache::display_pass_draw(View &view, GPUFrameBuffer *view_fb)
|
|||
|
||||
inst_.manager->submit(display_grids_ps_, view);
|
||||
|
||||
irradiance_a_tx_.free();
|
||||
irradiance_b_tx_.free();
|
||||
irradiance_c_tx_.free();
|
||||
irradiance_d_tx_.free();
|
||||
irradiance_a_tx.free();
|
||||
irradiance_b_tx.free();
|
||||
irradiance_c_tx.free();
|
||||
irradiance_d_tx.free();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -128,6 +128,18 @@ class IrradianceCache {
|
|||
private:
|
||||
Instance &inst_;
|
||||
|
||||
/** Atlas 3D texture containing all loaded grid data. */
|
||||
Texture irradiance_atlas_tx_ = {"irradiance_atlas_tx_"};
|
||||
/** Data structure used to index irradiance cache pages inside the atlas. */
|
||||
IrradianceGridDataBuf grids_infos_buf_ = {"grids_infos_buf_"};
|
||||
IrradianceBrickBuf bricks_infos_buf_ = {"bricks_infos_buf_"};
|
||||
/** Pool of atlas regions to allocate to different grids. */
|
||||
Vector<IrradianceBrickPacked> brick_pool_;
|
||||
/** Stream data into the irradiance atlas texture. */
|
||||
PassSimple grid_upload_ps_ = {"IrradianceCache.Upload"};
|
||||
/** If true, will trigger the reupload of all grid data instead of just streaming new ones. */
|
||||
bool do_full_update_ = true;
|
||||
|
||||
/** Display surfel debug data. */
|
||||
PassSimple debug_surfels_ps_ = {"IrradianceCache.Debug"};
|
||||
/** Debug surfel elements copied from the light cache. */
|
||||
|
@ -143,8 +155,19 @@ class IrradianceCache {
|
|||
|
||||
void init();
|
||||
void sync();
|
||||
void set_view(View &view);
|
||||
void viewport_draw(View &view, GPUFrameBuffer *view_fb);
|
||||
|
||||
Vector<IrradianceBrickPacked> bricks_alloc(int brick_len);
|
||||
void bricks_free(Vector<IrradianceBrickPacked> &bricks);
|
||||
|
||||
template<typename T> void bind_resources(draw::detail::PassBase<T> *pass)
|
||||
{
|
||||
pass->bind_ubo(IRRADIANCE_GRID_BUF_SLOT, &grids_infos_buf_);
|
||||
pass->bind_ssbo(IRRADIANCE_BRICK_BUF_SLOT, &bricks_infos_buf_);
|
||||
pass->bind_texture(IRRADIANCE_ATLAS_TEX_SLOT, &irradiance_atlas_tx_);
|
||||
}
|
||||
|
||||
private:
|
||||
void debug_pass_draw(View &view, GPUFrameBuffer *view_fb);
|
||||
void display_pass_draw(View &view, GPUFrameBuffer *view_fb);
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include "eevee_instance.hh"
|
||||
#include "eevee_lightprobe.hh"
|
||||
|
||||
#include "draw_debug.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
void LightProbeModule::begin_sync()
|
||||
|
@ -31,7 +33,10 @@ void LightProbeModule::sync_grid(const Object *ob, ObjectHandle &handle)
|
|||
grid.object_to_world = float4x4(ob->object_to_world);
|
||||
grid.world_to_object = float4x4(
|
||||
math::normalize(math::transpose(float3x3(grid.object_to_world))));
|
||||
|
||||
grid.cache = ob->lightprobe_cache;
|
||||
/* Force reupload. */
|
||||
inst_.irradiance_cache.bricks_free(grid.bricks);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,9 +76,11 @@ void LightProbeModule::end_sync()
|
|||
for (auto it = grid_map_.items().begin(); it != it_end; ++it) {
|
||||
IrradianceGrid &grid = (*it).value;
|
||||
if (grid.updated) {
|
||||
grid.updated = false;
|
||||
grid_update_ = true;
|
||||
}
|
||||
if (!grid.used) {
|
||||
inst_.irradiance_cache.bricks_free(grid.bricks);
|
||||
grid_map_.remove(it);
|
||||
grid_update_ = true;
|
||||
continue;
|
||||
|
@ -89,6 +96,7 @@ void LightProbeModule::end_sync()
|
|||
for (auto it = cube_map_.items().begin(); it != it_end; ++it) {
|
||||
ReflectionCube &cube = (*it).value;
|
||||
if (cube.updated) {
|
||||
cube.updated = false;
|
||||
cube_update_ = true;
|
||||
}
|
||||
if (!cube.used) {
|
||||
|
@ -101,6 +109,7 @@ void LightProbeModule::end_sync()
|
|||
}
|
||||
}
|
||||
|
||||
#if 0 /* TODO make this work with new per object light cache. */
|
||||
/* If light-cache auto-update is enable we tag the relevant part
|
||||
* of the cache to update and fire up a baking job. */
|
||||
if (auto_bake_enabled_ && (grid_update_ || cube_update_)) {
|
||||
|
@ -123,6 +132,7 @@ void LightProbeModule::end_sync()
|
|||
WM_event_add_notifier(DRW_context_state_get()->evil_C, NC_LIGHTPROBE, original_scene);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace blender::eevee
|
||||
|
|
|
@ -24,7 +24,7 @@ struct LightProbe {
|
|||
bool updated = false;
|
||||
};
|
||||
|
||||
struct IrradianceGrid : public LightProbe {
|
||||
struct IrradianceGrid : public LightProbe, IrradianceGridData {
|
||||
/** Copy of the transform matrix. */
|
||||
float4x4 object_to_world;
|
||||
/** Precomputed inverse transform with normalized axes. No position. Used for rotating SH. */
|
||||
|
@ -36,6 +36,10 @@ struct IrradianceGrid : public LightProbe {
|
|||
* pruning have been done.
|
||||
*/
|
||||
const struct LightProbeObjectCache *cache = nullptr;
|
||||
/** List of associated atlas bricks that are used by this grid. */
|
||||
Vector<IrradianceBrickPacked> bricks;
|
||||
/** Index of the grid inside the grid UBO. */
|
||||
int grid_index;
|
||||
};
|
||||
|
||||
struct ReflectionCube : public LightProbe {};
|
||||
|
|
|
@ -263,6 +263,7 @@ void ForwardPipeline::render(View &view,
|
|||
// }
|
||||
|
||||
inst_.shadows.set_view(view);
|
||||
inst_.irradiance_cache.set_view(view);
|
||||
|
||||
GPU_framebuffer_bind(combined_fb);
|
||||
inst_.manager->submit(opaque_ps_, view);
|
||||
|
@ -385,6 +386,7 @@ void DeferredLayer::end_sync()
|
|||
inst_.shadows.bind_resources(&eval_light_ps_);
|
||||
inst_.sampling.bind_resources(&eval_light_ps_);
|
||||
inst_.hiz_buffer.bind_resources(&eval_light_ps_);
|
||||
inst_.irradiance_cache.bind_resources(&eval_light_ps_);
|
||||
|
||||
eval_light_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS);
|
||||
eval_light_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
|
@ -425,6 +427,7 @@ void DeferredLayer::render(View &view,
|
|||
|
||||
inst_.hiz_buffer.set_dirty();
|
||||
inst_.shadows.set_view(view);
|
||||
inst_.irradiance_cache.set_view(view);
|
||||
|
||||
inst_.gbuffer.acquire(extent, closure_bits_);
|
||||
|
||||
|
|
|
@ -150,6 +150,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
|
|||
return "eevee_light_culling_zbin";
|
||||
case LIGHTPROBE_IRRADIANCE_RAY:
|
||||
return "eevee_lightprobe_irradiance_ray";
|
||||
case LIGHTPROBE_IRRADIANCE_LOAD:
|
||||
return "eevee_lightprobe_irradiance_load";
|
||||
case SHADOW_CLIPMAP_CLEAR:
|
||||
return "eevee_shadow_clipmap_clear";
|
||||
case SHADOW_DEBUG:
|
||||
|
|
|
@ -64,6 +64,7 @@ enum eShaderType {
|
|||
LIGHT_CULLING_ZBIN,
|
||||
|
||||
LIGHTPROBE_IRRADIANCE_RAY,
|
||||
LIGHTPROBE_IRRADIANCE_LOAD,
|
||||
|
||||
MOTION_BLUR_GATHER,
|
||||
MOTION_BLUR_TILE_DILATE,
|
||||
|
|
|
@ -892,6 +892,37 @@ struct SurfelListInfoData {
|
|||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(SurfelListInfoData, 16)
|
||||
|
||||
struct IrradianceGridData {
|
||||
/** World to non-normalized local grid space [0..size-1]. Stored transposed for compactness. */
|
||||
float3x4 world_to_grid_transposed;
|
||||
/** Number of bricks for this grid. */
|
||||
int3 grid_size;
|
||||
/** Index in brick descriptor list of the first brick of this grid. */
|
||||
int brick_offset;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(IrradianceGridData, 16)
|
||||
|
||||
struct IrradianceBrick {
|
||||
/* Offset in pixel to the start of the data inside the atlas texture. */
|
||||
uint2 atlas_coord;
|
||||
};
|
||||
/** \note Stored packed as a uint. */
|
||||
#define IrradianceBrickPacked uint
|
||||
|
||||
static inline IrradianceBrickPacked irradiance_brick_pack(IrradianceBrick brick)
|
||||
{
|
||||
uint2 data = (uint2(brick.atlas_coord) & 0xFFFFu) << uint2(0u, 16u);
|
||||
IrradianceBrickPacked brick_packed = data.x | data.y;
|
||||
return brick_packed;
|
||||
}
|
||||
|
||||
static inline IrradianceBrick irradiance_brick_unpack(IrradianceBrickPacked brick_packed)
|
||||
{
|
||||
IrradianceBrick brick;
|
||||
brick.atlas_coord = (uint2(brick_packed) >> uint2(0u, 16u)) & uint2(0xFFFFu);
|
||||
return brick;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -1002,6 +1033,8 @@ using DepthOfFieldScatterListBuf = draw::StorageArrayBuffer<ScatterRect, 16, tru
|
|||
using DrawIndirectBuf = draw::StorageBuffer<DrawCommand, true>;
|
||||
using FilmDataBuf = draw::UniformBuffer<FilmData>;
|
||||
using HiZDataBuf = draw::UniformBuffer<HiZData>;
|
||||
using IrradianceGridDataBuf = draw::UniformArrayBuffer<IrradianceGridData, IRRADIANCE_GRID_MAX>;
|
||||
using IrradianceBrickBuf = draw::StorageVectorBuffer<IrradianceBrickPacked, 16>;
|
||||
using LightCullingDataBuf = draw::StorageBuffer<LightCullingData>;
|
||||
using LightCullingKeyBuf = draw::StorageArrayBuffer<uint, LIGHT_CHUNK, true>;
|
||||
using LightCullingTileBuf = draw::StorageArrayBuffer<uint, LIGHT_CHUNK, true>;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_light_eval_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_lightprobe_eval_lib.glsl)
|
||||
|
||||
void main()
|
||||
{
|
||||
|
@ -52,6 +53,8 @@ void main()
|
|||
vec3 diffuse_light = vec3(0.0);
|
||||
vec3 reflection_light = vec3(0.0);
|
||||
|
||||
lightprobe_eval(diffuse_data, reflection_data, P, Ng, V, diffuse_light, reflection_light);
|
||||
|
||||
light_eval(
|
||||
diffuse_data, reflection_data, P, Ng, V, vP_z, thickness, diffuse_light, reflection_light);
|
||||
|
||||
|
|
|
@ -22,6 +22,6 @@ void main()
|
|||
vec3 N = normal_view_to_world(vN);
|
||||
vec3 lN = transform_direction(world_to_grid, N);
|
||||
|
||||
vec4 irradiance = spherical_harmonics_evaluate_lambert(lN, sh);
|
||||
out_color = vec4(irradiance.rgb, 0.0);
|
||||
vec3 irradiance = spherical_harmonics_evaluate_lambert(lN, sh);
|
||||
out_color = vec4(irradiance, 0.0);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
|
||||
/**
|
||||
* The resources expected to be defined are:
|
||||
* - grids_infos_buf
|
||||
* - bricks_infos_buf
|
||||
* - irradiance_atlas_tx
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(eevee_lightprobe_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
|
||||
|
||||
/**
|
||||
* Return sample coordinates of the first SH coef in unormalized texture space.
|
||||
*/
|
||||
vec3 lightprobe_irradiance_grid_atlas_coord(IrradianceGridData grid_data, vec3 lP)
|
||||
{
|
||||
ivec3 brick_coord = ivec3((lP - 0.5) / float(IRRADIANCE_GRID_BRICK_SIZE - 1));
|
||||
/* Avoid sampling adjacent bricks. */
|
||||
brick_coord = max(brick_coord, ivec3(0));
|
||||
/* Avoid sampling adjacent bricks. */
|
||||
lP = max(lP, vec3(0.5));
|
||||
/* Local position inside the brick (still in grid sample spacing unit). */
|
||||
vec3 brick_lP = lP - vec3(brick_coord) * float(IRRADIANCE_GRID_BRICK_SIZE - 1);
|
||||
|
||||
int brick_index = lightprobe_irradiance_grid_brick_index_get(grid_data, brick_coord);
|
||||
|
||||
IrradianceBrick brick = irradiance_brick_unpack(bricks_infos_buf[brick_index]);
|
||||
vec3 output_coord = vec3(brick.atlas_coord, 0.0) + brick_lP;
|
||||
|
||||
return output_coord;
|
||||
}
|
||||
|
||||
vec4 textureUnormalizedCoord(sampler3D tx, vec3 co)
|
||||
{
|
||||
return texture(tx, co / vec3(textureSize(tx, 0)));
|
||||
}
|
||||
|
||||
SphericalHarmonicL1 lightprobe_irradiance_sample(sampler3D atlas_tx, vec3 P)
|
||||
{
|
||||
vec3 lP;
|
||||
int grid_index;
|
||||
for (grid_index = 0; grid_index < IRRADIANCE_GRID_MAX; grid_index++) {
|
||||
/* Last grid is tagged as invalid to stop the iteration. */
|
||||
if (grids_infos_buf[grid_index].grid_size.x == -1) {
|
||||
/* Sample the last grid instead. */
|
||||
/* TODO: Avoid this by using a default world grid. */
|
||||
grid_index -= 1;
|
||||
break;
|
||||
}
|
||||
/* If. */
|
||||
if (lightprobe_irradiance_grid_local_coord(grids_infos_buf[grid_index], P, lP)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
vec3 atlas_coord = lightprobe_irradiance_grid_atlas_coord(grids_infos_buf[grid_index], lP);
|
||||
|
||||
SphericalHarmonicL1 sh;
|
||||
sh.L0.M0 = textureUnormalizedCoord(atlas_tx, atlas_coord);
|
||||
atlas_coord.z += float(IRRADIANCE_GRID_BRICK_SIZE);
|
||||
sh.L1.Mn1 = textureUnormalizedCoord(atlas_tx, atlas_coord);
|
||||
atlas_coord.z += float(IRRADIANCE_GRID_BRICK_SIZE);
|
||||
sh.L1.M0 = textureUnormalizedCoord(atlas_tx, atlas_coord);
|
||||
atlas_coord.z += float(IRRADIANCE_GRID_BRICK_SIZE);
|
||||
sh.L1.Mp1 = textureUnormalizedCoord(atlas_tx, atlas_coord);
|
||||
return sh;
|
||||
}
|
||||
|
||||
void lightprobe_eval(ClosureDiffuse diffuse,
|
||||
ClosureReflection reflection,
|
||||
vec3 P,
|
||||
vec3 Ng,
|
||||
vec3 V,
|
||||
inout vec3 out_diffuse,
|
||||
inout vec3 out_specular)
|
||||
{
|
||||
SphericalHarmonicL1 irradiance = lightprobe_irradiance_sample(irradiance_atlas_tx, P);
|
||||
|
||||
out_diffuse += spherical_harmonics_evaluate_lambert(diffuse.N, irradiance);
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
|
||||
/**
|
||||
* Load an input lightgrid cache texture into the atlas.
|
||||
*
|
||||
* Each thread group will load a brick worth of data and add the needed padding texels.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_base_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_matrix_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_lightprobe_lib.glsl)
|
||||
|
||||
void atlas_store(vec4 sh_coefficient, ivec2 atlas_coord, int layer)
|
||||
{
|
||||
imageStore(irradiance_atlas_img,
|
||||
ivec3(atlas_coord, layer * IRRADIANCE_GRID_BRICK_SIZE) + ivec3(gl_LocalInvocationID),
|
||||
sh_coefficient);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
int brick_index = lightprobe_irradiance_grid_brick_index_get(grids_infos_buf[grid_index],
|
||||
ivec3(gl_WorkGroupID));
|
||||
/* Brick coordinate in the source grid. */
|
||||
ivec3 brick_coord = ivec3(gl_WorkGroupID);
|
||||
/* Add padding border to allow bilinear filtering. */
|
||||
ivec3 texel_coord = brick_coord * (IRRADIANCE_GRID_BRICK_SIZE - 1) + ivec3(gl_LocalInvocationID);
|
||||
ivec3 input_coord = min(texel_coord, textureSize(irradiance_a_tx, 0) - 1);
|
||||
|
||||
/* Brick coordinate in the destination atlas. */
|
||||
IrradianceBrick brick = irradiance_brick_unpack(bricks_infos_buf[brick_index]);
|
||||
ivec2 output_coord = ivec2(brick.atlas_coord);
|
||||
|
||||
SphericalHarmonicL1 sh;
|
||||
sh.L0.M0 = texelFetch(irradiance_a_tx, input_coord, 0);
|
||||
sh.L1.Mn1 = texelFetch(irradiance_b_tx, input_coord, 0);
|
||||
sh.L1.M0 = texelFetch(irradiance_c_tx, input_coord, 0);
|
||||
sh.L1.Mp1 = texelFetch(irradiance_d_tx, input_coord, 0);
|
||||
|
||||
/* Rotate Spherical Harmonic into world space. */
|
||||
mat3 world_to_grid_transposed = mat3(grids_infos_buf[grid_index].world_to_grid_transposed);
|
||||
mat3 rotation = normalize(world_to_grid_transposed);
|
||||
spherical_harmonics_L1_rotate(rotation, sh.L1);
|
||||
|
||||
atlas_store(sh.L0.M0, output_coord, 0);
|
||||
atlas_store(sh.L1.Mn1, output_coord, 1);
|
||||
atlas_store(sh.L1.M0, output_coord, 2);
|
||||
atlas_store(sh.L1.Mp1, output_coord, 3);
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl)
|
||||
|
||||
vec3 lightprobe_irradiance_grid_sample_position(mat4 grid_local_to_world,
|
||||
ivec3 grid_resolution,
|
||||
ivec3 cell_coord)
|
||||
|
@ -8,3 +10,28 @@ vec3 lightprobe_irradiance_grid_sample_position(mat4 grid_local_to_world,
|
|||
vec3 ws_cell_pos = (grid_local_to_world * vec4(ls_cell_pos, 1.0)).xyz;
|
||||
return ws_cell_pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if sample position is valid.
|
||||
* \a r_lP is the local position in grid units [0..grid_size).
|
||||
*/
|
||||
bool lightprobe_irradiance_grid_local_coord(IrradianceGridData grid_data, vec3 P, out vec3 r_lP)
|
||||
{
|
||||
/* Position in cell units. */
|
||||
/* NOTE: The vector-matrix multiplication swapped on purpose to cancel the matrix transpose. */
|
||||
vec3 lP = (vec4(P, 1.0) * grid_data.world_to_grid_transposed).xyz;
|
||||
r_lP = clamp(lP, vec3(0.0), vec3(grid_data.grid_size) - 1e-5);
|
||||
/* Sample is valid if position wasn't clamped. */
|
||||
return all(equal(lP, r_lP));
|
||||
}
|
||||
|
||||
int lightprobe_irradiance_grid_brick_index_get(IrradianceGridData grid_data, ivec3 brick_coord)
|
||||
{
|
||||
int3 grid_size_in_bricks = divide_ceil(grid_data.grid_size,
|
||||
int3(IRRADIANCE_GRID_BRICK_SIZE - 1));
|
||||
int brick_index = grid_data.brick_offset;
|
||||
brick_index += brick_coord.x;
|
||||
brick_index += brick_coord.y * grid_size_in_bricks.x;
|
||||
brick_index += brick_coord.z * grid_size_in_bricks.x * grid_size_in_bricks.y;
|
||||
return brick_index;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/* Directive for resetting the line numbering so the failing tests lines can be printed.
|
||||
* This conflict with the shader compiler error logging scheme.
|
||||
* Comment out for correct compilation error line. */
|
||||
#line 5
|
||||
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_matrix_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_test_lib.glsl)
|
||||
|
||||
#define TEST(a, b) if (true)
|
||||
|
||||
void main()
|
||||
{
|
||||
TEST(eevee_lightprobe, IrradianceBrickIndex)
|
||||
{
|
||||
float near = 0.5, far = 1.0;
|
||||
mat4 pers_mat = projection_perspective(-near, near, -near, near, near, far);
|
||||
mat4 normal_mat = invert(transpose(pers_mat));
|
||||
|
||||
LightData light;
|
||||
light.clip_near = floatBitsToInt(near);
|
||||
light.clip_far = floatBitsToInt(far);
|
||||
light.influence_radius_max = far;
|
||||
light.type = LIGHT_SPOT;
|
||||
light.normal_mat_packed.x = normal_mat[3][2];
|
||||
light.normal_mat_packed.y = normal_mat[3][3];
|
||||
|
||||
vec2 atlas_size = vec2(SHADOW_TILEMAP_RES);
|
||||
{
|
||||
/* Simulate a "2D" plane crossing the frustum diagonaly. */
|
||||
vec3 lP0 = vec3(-1.0, 0.0, -1.0);
|
||||
vec3 lP1 = vec3(0.5, 0.0, -0.5);
|
||||
vec3 lTg = normalize(lP1 - lP0);
|
||||
vec3 lNg = vec3(-lTg.z, 0.0, lTg.x);
|
||||
|
||||
float expect = 1.0 / (SHADOW_TILEMAP_RES * SHADOW_PAGE_RES);
|
||||
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 0), expect, 1e-4);
|
||||
EXPECT_NEAR(
|
||||
shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 1), expect * 2.0, 1e-4);
|
||||
EXPECT_NEAR(
|
||||
shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 2), expect * 4.0, 1e-4);
|
||||
}
|
||||
{
|
||||
/* Simulate a "2D" plane crossing the near plane at the center diagonaly. */
|
||||
vec3 lP0 = vec3(-1.0, 0.0, -1.0);
|
||||
vec3 lP1 = vec3(0.0, 0.0, -0.5);
|
||||
vec3 lTg = normalize(lP1 - lP0);
|
||||
vec3 lNg = vec3(-lTg.z, 0.0, lTg.x);
|
||||
|
||||
float expect = 2.0 / (SHADOW_TILEMAP_RES * SHADOW_PAGE_RES);
|
||||
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 0), expect, 1e-4);
|
||||
EXPECT_NEAR(
|
||||
shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 1), expect * 2.0, 1e-4);
|
||||
EXPECT_NEAR(
|
||||
shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 2), expect * 4.0, 1e-4);
|
||||
}
|
||||
{
|
||||
/* Simulate a "2D" plane parallel to near clip plane. */
|
||||
vec3 lP0 = vec3(-1.0, 0.0, -0.75);
|
||||
vec3 lP1 = vec3(0.0, 0.0, -0.75);
|
||||
vec3 lTg = normalize(lP1 - lP0);
|
||||
vec3 lNg = vec3(-lTg.z, 0.0, lTg.x);
|
||||
|
||||
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 0), 0.0, 1e-4);
|
||||
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 1), 0.0, 1e-4);
|
||||
EXPECT_NEAR(shadow_slope_bias_get(atlas_size, light, lNg, lP0, vec2(0.0), 2), 0.0, 1e-4);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -187,6 +187,40 @@ vec4 spherical_harmonics_L2_evaluate(vec3 direction, SphericalHarmonicBandL2 L2)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Rotation
|
||||
* \{ */
|
||||
|
||||
void spherical_harmonics_L0_rotate(mat3x3 rotation, inout SphericalHarmonicBandL0 L0)
|
||||
{
|
||||
/* L0 band being a constant function (i.e: there is no directionallity) there is nothing to
|
||||
* rotate. This is a no-op. */
|
||||
}
|
||||
|
||||
void spherical_harmonics_L1_rotate(mat3x3 rotation, inout SphericalHarmonicBandL1 L1)
|
||||
{
|
||||
/* Convert L1 coefficients to per channel column.
|
||||
* Note the component shuffle to match blender coordinate system. */
|
||||
mat4x3 per_channel = transpose(mat3x4(L1.Mp1, L1.Mn1, -L1.M0));
|
||||
/* Rotate each channel. */
|
||||
per_channel[0] = rotation * per_channel[0];
|
||||
per_channel[1] = rotation * per_channel[1];
|
||||
per_channel[2] = rotation * per_channel[2];
|
||||
/* Convert back to L1 coefficients to per channel column.
|
||||
* Note the component shuffle to match blender coordinate system. */
|
||||
mat3x4 per_coef = transpose(per_channel);
|
||||
L1.Mn1 = per_coef[1];
|
||||
L1.M0 = -per_coef[2];
|
||||
L1.Mp1 = per_coef[0];
|
||||
}
|
||||
|
||||
void spherical_harmonics_L2_rotate(mat3x3 rotation, inout SphericalHarmonicBandL2 L2)
|
||||
{
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Evaluation
|
||||
* \{ */
|
||||
|
@ -197,20 +231,23 @@ vec4 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/
|
||||
*/
|
||||
vec4 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL0 sh)
|
||||
vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL0 sh)
|
||||
{
|
||||
return spherical_harmonics_L0_evaluate(N, sh.L0);
|
||||
vec3 radiance = spherical_harmonics_L0_evaluate(N, sh.L0).rgb;
|
||||
return radiance;
|
||||
}
|
||||
vec4 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL1 sh)
|
||||
vec3 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 radiance = spherical_harmonics_L0_evaluate(N, sh.L0).rgb +
|
||||
spherical_harmonics_L1_evaluate(N, sh.L1).rgb * (2.0 / 3.0);
|
||||
return radiance;
|
||||
}
|
||||
vec4 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL2 sh)
|
||||
vec3 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) +
|
||||
spherical_harmonics_L2_evaluate(N, sh.L2) * (1.0 / 4.0);
|
||||
vec3 radiance = spherical_harmonics_L0_evaluate(N, sh.L0).rgb +
|
||||
spherical_harmonics_L1_evaluate(N, sh.L1).rgb * (2.0 / 3.0) +
|
||||
spherical_harmonics_L2_evaluate(N, sh.L2).rgb * (1.0 / 4.0);
|
||||
return radiance;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -37,6 +37,7 @@ GPU_SHADER_CREATE_INFO(eevee_deferred_light)
|
|||
.additional_info("eevee_shared",
|
||||
"eevee_utility_texture",
|
||||
"eevee_light_data",
|
||||
"eevee_lightprobe_data",
|
||||
"eevee_shadow_data",
|
||||
"eevee_deferred_base",
|
||||
"eevee_hiz_data",
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_hiz_data)
|
||||
.sampler(15, ImageType::FLOAT_2D, "hiz_tx")
|
||||
.uniform_buf(5, "HiZData", "hiz_buf");
|
||||
.sampler(HIZ_TEX_SLOT, ImageType::FLOAT_2D, "hiz_tx")
|
||||
.uniform_buf(HIZ_BUF_SLOT, "HiZData", "hiz_buf");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_hiz_update)
|
||||
.do_static_compilation(true)
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
#include "eevee_defines.hh"
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Display
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_INTERFACE_INFO(eevee_debug_surfel_iface, "")
|
||||
.smooth(Type::VEC3, "P")
|
||||
.flat(Type::INT, "surfel_index");
|
||||
|
@ -18,6 +22,32 @@ GPU_SHADER_CREATE_INFO(eevee_debug_surfels)
|
|||
.push_constant(Type::INT, "debug_mode")
|
||||
.do_static_compilation(true);
|
||||
|
||||
GPU_SHADER_INTERFACE_INFO(eevee_display_probe_grid_iface, "")
|
||||
.smooth(Type::VEC2, "lP")
|
||||
.flat(Type::IVEC3, "cell");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_display_probe_grid)
|
||||
.additional_info("eevee_shared", "draw_view")
|
||||
.vertex_source("eevee_display_probe_grid_vert.glsl")
|
||||
.vertex_out(eevee_display_probe_grid_iface)
|
||||
.fragment_source("eevee_display_probe_grid_frag.glsl")
|
||||
.fragment_out(0, Type::VEC4, "out_color")
|
||||
.push_constant(Type::FLOAT, "sphere_radius")
|
||||
.push_constant(Type::IVEC3, "grid_resolution")
|
||||
.push_constant(Type::MAT4, "grid_to_world")
|
||||
.push_constant(Type::MAT4, "world_to_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);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Baking
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_surfel_common)
|
||||
.storage_buf(SURFEL_BUF_SLOT, Qualifier::READ_WRITE, "Surfel", "surfel_buf[]")
|
||||
.storage_buf(CAPTURE_BUF_SLOT, Qualifier::READ, "CaptureInfoData", "capture_info_buf");
|
||||
|
@ -61,26 +91,6 @@ GPU_SHADER_CREATE_INFO(eevee_surfel_ray)
|
|||
.compute_source("eevee_surfel_ray_comp.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
||||
GPU_SHADER_INTERFACE_INFO(eevee_display_probe_grid_iface, "")
|
||||
.smooth(Type::VEC2, "lP")
|
||||
.flat(Type::IVEC3, "cell");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_display_probe_grid)
|
||||
.additional_info("eevee_shared", "draw_view")
|
||||
.vertex_source("eevee_display_probe_grid_vert.glsl")
|
||||
.vertex_out(eevee_display_probe_grid_iface)
|
||||
.fragment_source("eevee_display_probe_grid_frag.glsl")
|
||||
.fragment_out(0, Type::VEC4, "out_color")
|
||||
.push_constant(Type::FLOAT, "sphere_radius")
|
||||
.push_constant(Type::IVEC3, "grid_resolution")
|
||||
.push_constant(Type::MAT4, "grid_to_world")
|
||||
.push_constant(Type::MAT4, "world_to_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)
|
||||
.local_group_size(IRRADIANCE_GRID_GROUP_SIZE,
|
||||
IRRADIANCE_GRID_GROUP_SIZE,
|
||||
|
@ -94,3 +104,35 @@ GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_ray)
|
|||
.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);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Runtime
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_load)
|
||||
.local_group_size(IRRADIANCE_GRID_BRICK_SIZE,
|
||||
IRRADIANCE_GRID_BRICK_SIZE,
|
||||
IRRADIANCE_GRID_BRICK_SIZE)
|
||||
.additional_info("eevee_shared")
|
||||
.push_constant(Type::INT, "grid_index")
|
||||
.uniform_buf(0, "IrradianceGridData", "grids_infos_buf[IRRADIANCE_GRID_MAX]")
|
||||
.storage_buf(0, Qualifier::READ, "uint", "bricks_infos_buf[]")
|
||||
.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")
|
||||
.image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "irradiance_atlas_img")
|
||||
.compute_source("eevee_lightprobe_irradiance_load_comp.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_lightprobe_data)
|
||||
.uniform_buf(IRRADIANCE_GRID_BUF_SLOT,
|
||||
"IrradianceGridData",
|
||||
"grids_infos_buf[IRRADIANCE_GRID_MAX]")
|
||||
/* NOTE: Use uint instead of IrradianceBrickPacked because Metal need to know the exact type.*/
|
||||
.storage_buf(IRRADIANCE_BRICK_BUF_SLOT, Qualifier::READ, "uint", "bricks_infos_buf[]")
|
||||
.sampler(IRRADIANCE_ATLAS_TEX_SLOT, ImageType::FLOAT_3D, "irradiance_atlas_tx");
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -176,8 +176,11 @@ class UniformCommon : public DataBuffer<T, len, false>, NonMovable, NonCopyable
|
|||
#endif
|
||||
|
||||
public:
|
||||
UniformCommon()
|
||||
UniformCommon(const char *name = nullptr)
|
||||
{
|
||||
if (name) {
|
||||
name_ = name;
|
||||
}
|
||||
ubo_ = GPU_uniformbuf_create_ex(sizeof(T) * len, nullptr, name_);
|
||||
}
|
||||
|
||||
|
@ -276,7 +279,7 @@ template<
|
|||
/* bool device_only = false */>
|
||||
class UniformArrayBuffer : public detail::UniformCommon<T, len, false> {
|
||||
public:
|
||||
UniformArrayBuffer()
|
||||
UniformArrayBuffer(const char *name = nullptr) : detail::UniformCommon<T, len, false>(name)
|
||||
{
|
||||
/* TODO(@fclem): We should map memory instead. */
|
||||
this->data_ = (T *)MEM_mallocN_aligned(len * sizeof(T), 16, this->name_);
|
||||
|
@ -295,7 +298,7 @@ template<
|
|||
/* bool device_only = false */>
|
||||
class UniformBuffer : public T, public detail::UniformCommon<T, 1, false> {
|
||||
public:
|
||||
UniformBuffer()
|
||||
UniformBuffer(const char *name = nullptr) : detail::UniformCommon<T, 1, false>(name)
|
||||
{
|
||||
/* TODO(@fclem): How could we map this? */
|
||||
this->data_ = static_cast<T *>(this);
|
||||
|
@ -367,6 +370,11 @@ class StorageArrayBuffer : public detail::StorageCommon<T, len, device_only> {
|
|||
return this->len_;
|
||||
}
|
||||
|
||||
MutableSpan<T> as_span() const
|
||||
{
|
||||
return {this->data_, this->len_};
|
||||
}
|
||||
|
||||
static void swap(StorageArrayBuffer &a, StorageArrayBuffer &b)
|
||||
{
|
||||
SWAP(T *, a.data_, b.data_);
|
||||
|
@ -422,6 +430,14 @@ class StorageVectorBuffer : public StorageArrayBuffer<T, len, false> {
|
|||
new (ptr) T(std::forward<ForwardT>(value)...);
|
||||
}
|
||||
|
||||
void extend(const Span<T> &values)
|
||||
{
|
||||
/* TODO(fclem): Optimize to a single memcpy. */
|
||||
for (auto v : values) {
|
||||
this->append(v);
|
||||
}
|
||||
}
|
||||
|
||||
int64_t size() const
|
||||
{
|
||||
return item_len_;
|
||||
|
@ -1032,8 +1048,7 @@ class TextureRef : public Texture {
|
|||
* Dummy type to bind texture as image.
|
||||
* It is just a GPUTexture in disguise.
|
||||
*/
|
||||
class Image {
|
||||
};
|
||||
class Image {};
|
||||
|
||||
static inline Image *as_image(GPUTexture *tex)
|
||||
{
|
||||
|
|
|
@ -396,3 +396,12 @@ void drw_print_value(mat4 value)
|
|||
drw_print(" ", value[3]);
|
||||
drw_print(")");
|
||||
}
|
||||
|
||||
void drw_print_value(mat3 value)
|
||||
{
|
||||
drw_print("mat3x3(");
|
||||
drw_print(" ", value[0]);
|
||||
drw_print(" ", value[1]);
|
||||
drw_print(" ", value[2]);
|
||||
drw_print(")");
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
# ifndef GPU_METAL
|
||||
# define float2 vec2
|
||||
# define float3 vec3
|
||||
# define float3x4 mat3x4
|
||||
# define float4 vec4
|
||||
# define float4x4 mat4
|
||||
# define int2 ivec2
|
||||
|
@ -72,6 +73,7 @@
|
|||
# include "BLI_math_vector_types.hh"
|
||||
using blender::float2;
|
||||
using blender::float3;
|
||||
using blender::float3x4;
|
||||
using blender::float4;
|
||||
using blender::float4x4;
|
||||
using blender::int2;
|
||||
|
@ -91,6 +93,7 @@ typedef float float2[2];
|
|||
typedef float float3[3];
|
||||
typedef float float4[4];
|
||||
typedef float float4x4[4][4];
|
||||
typedef float float3x4[3][4];
|
||||
typedef int int2[2];
|
||||
typedef int int3[2];
|
||||
typedef int int4[4];
|
||||
|
|
Loading…
Reference in New Issue