EEVEE-Next: Sunlight Extraction #121455
|
@ -702,14 +702,7 @@ class RENDER_PT_eevee_next_clamping(RenderButtonsPanel, Panel):
|
|||
return (context.engine in cls.COMPAT_ENGINES)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
scene = context.scene
|
||||
props = scene.eevee
|
||||
|
||||
col = layout.column()
|
||||
col.prop(props, "clamp_world", text="World")
|
||||
pass
|
||||
|
||||
|
||||
class RENDER_PT_eevee_next_clamping_surface(RenderButtonsPanel, Panel):
|
||||
|
|
|
@ -151,9 +151,8 @@ class EEVEE_WORLD_PT_volume(WorldButtonsPanel, Panel):
|
|||
layout.label(text="No output node")
|
||||
|
||||
|
||||
class EEVEE_WORLD_PT_probe(WorldButtonsPanel, Panel):
|
||||
bl_label = "Light Probe"
|
||||
bl_translation_context = i18n_contexts.id_id
|
||||
class EEVEE_WORLD_PT_settings(WorldButtonsPanel, Panel):
|
||||
bl_label = "Settings"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'}
|
||||
|
||||
|
@ -163,13 +162,56 @@ class EEVEE_WORLD_PT_probe(WorldButtonsPanel, Panel):
|
|||
world = context.world
|
||||
return world and (engine in cls.COMPAT_ENGINES)
|
||||
|
||||
def draw(self, context):
|
||||
pass
|
||||
|
||||
|
||||
class EEVEE_WORLD_PT_lightprobe(WorldButtonsPanel, Panel):
|
||||
bl_label = "Light Probe"
|
||||
bl_parent_id = "EEVEE_WORLD_PT_settings"
|
||||
COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
world = context.world
|
||||
|
||||
layout.use_property_split = True
|
||||
layout.prop(world, "probe_resolution")
|
||||
layout.prop(world, "probe_resolution", text="Resolution")
|
||||
|
||||
|
||||
class EEVEE_WORLD_PT_sun(WorldButtonsPanel, Panel):
|
||||
bl_label = "Sun"
|
||||
bl_parent_id = "EEVEE_WORLD_PT_settings"
|
||||
COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
world = context.world
|
||||
|
||||
layout.use_property_split = True
|
||||
layout.prop(world, "sun_threshold", text="Threshold")
|
||||
layout.prop(world, "sun_angle", text="Angle")
|
||||
|
||||
|
||||
class EEVEE_WORLD_PT_sun_shadow(WorldButtonsPanel, Panel):
|
||||
bl_label = "Shadow"
|
||||
bl_parent_id = "EEVEE_WORLD_PT_sun"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'}
|
||||
|
||||
def draw_header(self, context):
|
||||
world = context.world
|
||||
self.layout.prop(world, "use_sun_shadow", text="")
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
world = context.world
|
||||
|
||||
layout.use_property_split = True
|
||||
layout.prop(world, "sun_shadow_maximum_resolution", text="Resolution Limit")
|
||||
|
||||
|
||||
class WORLD_PT_viewport_display(WorldButtonsPanel, Panel):
|
||||
|
@ -193,7 +235,10 @@ classes = (
|
|||
EEVEE_WORLD_PT_surface,
|
||||
EEVEE_WORLD_PT_volume,
|
||||
EEVEE_WORLD_PT_mist,
|
||||
EEVEE_WORLD_PT_probe,
|
||||
EEVEE_WORLD_PT_settings,
|
||||
EEVEE_WORLD_PT_lightprobe,
|
||||
EEVEE_WORLD_PT_sun,
|
||||
EEVEE_WORLD_PT_sun_shadow,
|
||||
WORLD_PT_viewport_display,
|
||||
WORLD_PT_custom_props,
|
||||
)
|
||||
|
|
|
@ -29,7 +29,7 @@ extern "C" {
|
|||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 36
|
||||
#define BLENDER_FILE_SUBVERSION 37
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and cancel loading the file, showing a warning to
|
||||
|
|
|
@ -3383,7 +3383,6 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
|
|||
SceneEEVEE default_scene_eevee = *DNA_struct_default_get(SceneEEVEE);
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
scene->eevee.shadow_resolution_scale = default_scene_eevee.shadow_resolution_scale;
|
||||
scene->eevee.clamp_world = 10.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3548,6 +3547,16 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
|
|||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 37)) {
|
||||
const World *default_world = DNA_struct_default_get(World);
|
||||
LISTBASE_FOREACH (World *, world, &bmain->worlds) {
|
||||
world->sun_threshold = default_world->sun_threshold;
|
||||
world->sun_angle = default_world->sun_angle;
|
||||
world->sun_shadow_maximum_resolution = default_world->sun_shadow_maximum_resolution;
|
||||
world->flag |= WO_USE_SUN_SHADOW;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Always bump subversion in BKE_blender_version.h when adding versioning
|
||||
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.
|
||||
|
|
|
@ -521,6 +521,7 @@ set(GLSL_SRC
|
|||
engines/eevee_next/shaders/eevee_light_culling_sort_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_light_culling_tile_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_light_culling_zbin_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_light_shadow_setup_comp.glsl
|
||||
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
|
||||
|
@ -564,6 +565,7 @@ set(GLSL_SRC
|
|||
engines/eevee_next/shaders/eevee_reflection_probe_mapping_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_remap_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_select_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_sunlight_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_renderpass_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_sampling_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_debug_frag.glsl
|
||||
|
|
|
@ -175,6 +175,9 @@ void Instance::view_update()
|
|||
|
||||
void Instance::begin_sync()
|
||||
{
|
||||
/* Needs to be first for sun light parameters. */
|
||||
world.sync();
|
||||
|
||||
materials.begin_sync();
|
||||
velocity.begin_sync(); /* NOTE: Also syncs camera. */
|
||||
lights.begin_sync();
|
||||
|
@ -192,7 +195,6 @@ void Instance::begin_sync()
|
|||
motion_blur.sync();
|
||||
hiz_buffer.sync();
|
||||
main_view.sync();
|
||||
world.sync();
|
||||
film.sync();
|
||||
render_buffers.sync();
|
||||
ambient_occlusion.sync();
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "eevee_light.hh"
|
||||
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "DNA_defaults.h"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
|
@ -45,12 +46,14 @@ static eLightType to_light_type(short blender_light_type,
|
|||
/** \name Light Object
|
||||
* \{ */
|
||||
|
||||
void Light::sync(ShadowModule &shadows, const Object *ob, float threshold)
|
||||
void Light::sync(ShadowModule &shadows,
|
||||
float4x4 object_to_world,
|
||||
char visibility_flag,
|
||||
const ::Light *la,
|
||||
float threshold)
|
||||
{
|
||||
using namespace blender::math;
|
||||
|
||||
const ::Light *la = (const ::Light *)ob->data;
|
||||
|
||||
eLightType new_type = to_light_type(la->type, la->area_shape, la->mode & LA_USE_SOFT_FALLOFF);
|
||||
if (assign_if_different(this->type, new_type)) {
|
||||
shadow_discard_safe(shadows);
|
||||
|
@ -59,7 +62,6 @@ void Light::sync(ShadowModule &shadows, const Object *ob, float threshold)
|
|||
this->color = float3(&la->r) * la->energy;
|
||||
|
||||
float3 scale;
|
||||
float4x4 object_to_world = ob->object_to_world();
|
||||
object_to_world.view<3, 3>() = normalize_and_get_size(object_to_world.view<3, 3>(), scale);
|
||||
|
||||
/* Make sure we have consistent handedness (in case of negatively scaled Z axis). */
|
||||
|
@ -72,10 +74,10 @@ void Light::sync(ShadowModule &shadows, const Object *ob, float threshold)
|
|||
|
||||
shape_parameters_set(la, scale, threshold);
|
||||
|
||||
const bool diffuse_visibility = (ob->visibility_flag & OB_HIDE_DIFFUSE) == 0;
|
||||
const bool glossy_visibility = (ob->visibility_flag & OB_HIDE_GLOSSY) == 0;
|
||||
const bool transmission_visibility = (ob->visibility_flag & OB_HIDE_TRANSMISSION) == 0;
|
||||
const bool volume_visibility = (ob->visibility_flag & OB_HIDE_VOLUME_SCATTER) == 0;
|
||||
const bool diffuse_visibility = (visibility_flag & OB_HIDE_DIFFUSE) == 0;
|
||||
const bool glossy_visibility = (visibility_flag & OB_HIDE_GLOSSY) == 0;
|
||||
const bool transmission_visibility = (visibility_flag & OB_HIDE_TRANSMISSION) == 0;
|
||||
const bool volume_visibility = (visibility_flag & OB_HIDE_VOLUME_SCATTER) == 0;
|
||||
|
||||
float shape_power = shape_radiance_get();
|
||||
float point_power = point_radiance_get();
|
||||
|
@ -325,16 +327,37 @@ void LightModule::begin_sync()
|
|||
|
||||
sun_lights_len_ = 0;
|
||||
local_lights_len_ = 0;
|
||||
|
||||
if (use_sun_lights_ && inst_.world.sun_threshold() > 0.0) {
|
||||
/* Create a placeholder light to be fed by the GPU after sunlight extraction.
|
||||
* Sunlight is disabled if power is zero. */
|
||||
::Light la = blender::dna::shallow_copy(
|
||||
*(const ::Light *)DNA_default_table[SDNA_TYPE_FROM_STRUCT(Light)]);
|
||||
la.type = LA_SUN;
|
||||
/* Set on the GPU. */
|
||||
la.r = la.g = la.b = -1.0f; /* Tag as world sun light. */
|
||||
la.energy = 1.0f;
|
||||
la.sun_angle = inst_.world.sun_angle();
|
||||
la.shadow_maximum_resolution = inst_.world.sun_shadow_max_resolution();
|
||||
SET_FLAG_FROM_TEST(la.mode, inst_.world.use_sun_shadow(), LA_SHADOW);
|
||||
|
||||
Light &light = light_map_.lookup_or_add_default(world_sunlight_key);
|
||||
light.used = true;
|
||||
light.sync(inst_.shadows, float4x4::identity(), 0, &la, light_threshold_);
|
||||
|
||||
sun_lights_len_ += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void LightModule::sync_light(const Object *ob, ObjectHandle &handle)
|
||||
{
|
||||
const ::Light *la = static_cast<const ::Light *>(ob->data);
|
||||
if (use_scene_lights_ == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (use_sun_lights_ == false) {
|
||||
if (static_cast<const ::Light *>(ob->data)->type == LA_SUN) {
|
||||
if (la->type == LA_SUN) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -343,7 +366,7 @@ void LightModule::sync_light(const Object *ob, ObjectHandle &handle)
|
|||
light.used = true;
|
||||
if (handle.recalc != 0 || !light.initialized) {
|
||||
light.initialized = true;
|
||||
light.sync(inst_.shadows, ob, light_threshold_);
|
||||
light.sync(inst_.shadows, ob->object_to_world(), ob->visibility_flag, la, light_threshold_);
|
||||
}
|
||||
sun_lights_len_ += int(is_sun_light(light.type));
|
||||
local_lights_len_ += int(!is_sun_light(light.type));
|
||||
|
@ -428,6 +451,7 @@ void LightModule::end_sync()
|
|||
culling_tile_buf_.resize(total_word_count_);
|
||||
|
||||
culling_pass_sync();
|
||||
update_pass_sync();
|
||||
debug_pass_sync();
|
||||
}
|
||||
|
||||
|
@ -444,6 +468,7 @@ void LightModule::culling_pass_sync()
|
|||
{
|
||||
auto &sub = culling_ps_.sub("Select");
|
||||
sub.shader_set(inst_.shaders.static_shader_get(LIGHT_CULLING_SELECT));
|
||||
sub.bind_ubo("sunlight_buf", &inst_.world.sunlight);
|
||||
sub.bind_ssbo("light_cull_buf", &culling_data_buf_);
|
||||
sub.bind_ssbo("in_light_buf", light_buf_);
|
||||
sub.bind_ssbo("out_light_buf", culling_light_buf_);
|
||||
|
@ -483,6 +508,20 @@ void LightModule::culling_pass_sync()
|
|||
}
|
||||
}
|
||||
|
||||
void LightModule::update_pass_sync()
|
||||
{
|
||||
auto &pass = update_ps_;
|
||||
pass.init();
|
||||
pass.shader_set(inst_.shaders.static_shader_get(LIGHT_SHADOW_SETUP));
|
||||
pass.bind_ssbo("light_buf", &culling_light_buf_);
|
||||
pass.bind_ssbo("light_cull_buf", &culling_data_buf_);
|
||||
pass.bind_ssbo("tilemaps_buf", &inst_.shadows.tilemap_pool.tilemaps_data);
|
||||
pass.bind_resources(inst_.uniform_data);
|
||||
/* TODO(fclem): Dispatch for all light. */
|
||||
pass.dispatch(int3(1, 1, 1));
|
||||
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
}
|
||||
|
||||
void LightModule::debug_pass_sync()
|
||||
{
|
||||
if (inst_.debug_mode == eDebugMode::DEBUG_LIGHT_CULLING) {
|
||||
|
@ -512,6 +551,7 @@ void LightModule::set_view(View &view, const int2 extent)
|
|||
culling_data_buf_.push_update();
|
||||
|
||||
inst_.manager->submit(culling_ps_, view);
|
||||
inst_.manager->submit(update_ps_);
|
||||
}
|
||||
|
||||
void LightModule::debug_draw(View &view, GPUFrameBuffer *view_fb)
|
||||
|
|
|
@ -76,7 +76,11 @@ struct Light : public LightData, NonCopyable {
|
|||
}
|
||||
#endif
|
||||
|
||||
void sync(ShadowModule &shadows, const Object *ob, float threshold);
|
||||
void sync(ShadowModule &shadows,
|
||||
float4x4 object_to_world,
|
||||
char visibility_flag,
|
||||
const ::Light *la,
|
||||
float threshold);
|
||||
|
||||
void shadow_ensure(ShadowModule &shadows);
|
||||
void shadow_discard_safe(ShadowModule &shadows);
|
||||
|
@ -113,6 +117,7 @@ class LightModule {
|
|||
|
||||
/** Map of light objects data. Converted to flat array each frame. */
|
||||
Map<ObjectKey, Light> light_map_;
|
||||
ObjectKey world_sunlight_key;
|
||||
/** Flat array sent to GPU, populated from light_map_. Source buffer for light culling. */
|
||||
LightDataBuf light_buf_ = {"Lights_no_cull"};
|
||||
/** Luminous intensity to consider the light boundary at. Used for culling. */
|
||||
|
@ -148,6 +153,9 @@ class LightModule {
|
|||
/** Total number of words the tile buffer needs to contain for the render resolution. */
|
||||
uint total_word_count_ = 0;
|
||||
|
||||
/** Update light on the GPU after culling. Ran for each sample. */
|
||||
PassSimple update_ps_ = {"LightUpdate"};
|
||||
|
||||
/** Debug Culling visualization. */
|
||||
PassSimple debug_draw_ps_ = {"LightCulling.Debug"};
|
||||
|
||||
|
@ -176,6 +184,7 @@ class LightModule {
|
|||
|
||||
private:
|
||||
void culling_pass_sync();
|
||||
void update_pass_sync();
|
||||
void debug_pass_sync();
|
||||
};
|
||||
|
||||
|
|
|
@ -68,15 +68,20 @@ class LookdevWorld {
|
|||
return &world;
|
||||
}
|
||||
|
||||
float background_opacity_get()
|
||||
float background_opacity_get() const
|
||||
{
|
||||
return parameters_.background_opacity;
|
||||
}
|
||||
|
||||
float background_blur_get()
|
||||
float background_blur_get() const
|
||||
{
|
||||
return parameters_.blur;
|
||||
}
|
||||
|
||||
float intensity_get() const
|
||||
{
|
||||
return parameters_.intensity;
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -39,11 +39,13 @@ void SphereProbeModule::begin_sync()
|
|||
PassSimple &pass = remap_ps_;
|
||||
pass.init();
|
||||
pass.specialize_constant(shader, "extract_sh", &extract_sh_);
|
||||
pass.specialize_constant(shader, "extract_sun", &extract_sh_);
|
||||
pass.shader_set(shader);
|
||||
pass.bind_texture("cubemap_tx", &cubemap_tx_);
|
||||
pass.bind_texture("atlas_tx", &probes_tx_);
|
||||
pass.bind_image("atlas_img", &probes_tx_);
|
||||
pass.bind_ssbo("out_sh", &tmp_spherical_harmonics_);
|
||||
pass.bind_ssbo("out_sun", &tmp_sunlight_);
|
||||
pass.push_constant("probe_coord_packed", reinterpret_cast<int4 *>(&probe_sampling_coord_));
|
||||
pass.push_constant("write_coord_packed", reinterpret_cast<int4 *>(&probe_write_coord_));
|
||||
pass.push_constant("world_coord_packed", reinterpret_cast<int4 *>(&world_data.atlas_coord));
|
||||
|
@ -74,6 +76,17 @@ void SphereProbeModule::begin_sync()
|
|||
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
pass.dispatch(1);
|
||||
}
|
||||
{
|
||||
PassSimple &pass = sum_sun_ps_;
|
||||
pass.init();
|
||||
pass.shader_set(instance_.shaders.static_shader_get(SPHERE_PROBE_SUNLIGHT));
|
||||
pass.push_constant("probe_remap_dispatch_size", &dispatch_probe_pack_);
|
||||
pass.bind_ssbo("in_sun", &tmp_sunlight_);
|
||||
pass.bind_ssbo("sunlight_buf", &instance_.world.sunlight);
|
||||
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
pass.dispatch(1);
|
||||
pass.barrier(GPU_BARRIER_UNIFORM);
|
||||
}
|
||||
{
|
||||
PassSimple &pass = select_ps_;
|
||||
pass.init();
|
||||
|
@ -227,6 +240,7 @@ void SphereProbeModule::remap_to_octahedral_projection(const SphereProbeAtlasCoo
|
|||
|
||||
if (extract_spherical_harmonics) {
|
||||
instance_.manager->submit(sum_sh_ps_);
|
||||
instance_.manager->submit(sum_sun_ps_);
|
||||
/* All volume probe that needs to composite the world probe need to be updated. */
|
||||
instance_.volume_probes.update_world_irradiance();
|
||||
}
|
||||
|
|
|
@ -46,6 +46,8 @@ class SphereProbeModule {
|
|||
PassSimple remap_ps_ = {"Probe.CubemapToOctahedral"};
|
||||
/** Sum irradiance information optionally extracted during `remap_ps_`. */
|
||||
PassSimple sum_sh_ps_ = {"Probe.SumSphericalHarmonics"};
|
||||
/** Sum sunlight information optionally extracted during `remap_ps_`. */
|
||||
PassSimple sum_sun_ps_ = {"Probe.SumSunlight"};
|
||||
/** Copy volume probe irradiance for the center of sphere probes. */
|
||||
PassSimple select_ps_ = {"Probe.Select"};
|
||||
/** Convolve the octahedral map to fill the Mip-map levels. */
|
||||
|
@ -86,6 +88,10 @@ class SphereProbeModule {
|
|||
/** Final buffer containing the spherical harmonics for the world. */
|
||||
StorageBuffer<SphereProbeHarmonic, true> spherical_harmonics_ = {"spherical_harmonics_"};
|
||||
|
||||
/** Intermediate buffer to store sun light. */
|
||||
StorageArrayBuffer<SphereProbeSunLight, SPHERE_PROBE_MAX_HARMONIC, true> tmp_sunlight_ = {
|
||||
"tmp_sunlight_"};
|
||||
|
||||
/**
|
||||
* True if the next redraw will trigger a light-probe sphere update.
|
||||
* As syncing the draw passes for rendering has a significant overhead,
|
||||
|
|
|
@ -63,7 +63,7 @@ void Sampling::init(const Scene *scene)
|
|||
|
||||
auto clamp_value_load = [](float value) { return (value > 0.0) ? value : 1e20; };
|
||||
|
||||
clamp_data_.world = clamp_value_load(scene->eevee.clamp_world);
|
||||
clamp_data_.sun_threshold = clamp_value_load(inst_.world.sun_threshold());
|
||||
clamp_data_.surface_direct = clamp_value_load(scene->eevee.clamp_surface_direct);
|
||||
clamp_data_.surface_indirect = clamp_value_load(scene->eevee.clamp_surface_indirect);
|
||||
clamp_data_.volume_direct = clamp_value_load(scene->eevee.clamp_volume_direct);
|
||||
|
|
|
@ -189,6 +189,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
|
|||
return "eevee_light_culling_tile";
|
||||
case LIGHT_CULLING_ZBIN:
|
||||
return "eevee_light_culling_zbin";
|
||||
case LIGHT_SHADOW_SETUP:
|
||||
return "eevee_light_shadow_setup";
|
||||
case RAY_DENOISE_SPATIAL:
|
||||
return "eevee_ray_denoise_spatial";
|
||||
case RAY_DENOISE_TEMPORAL:
|
||||
|
@ -225,6 +227,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
|
|||
return "eevee_reflection_probe_irradiance";
|
||||
case SPHERE_PROBE_SELECT:
|
||||
return "eevee_reflection_probe_select";
|
||||
case SPHERE_PROBE_SUNLIGHT:
|
||||
return "eevee_reflection_probe_sunlight";
|
||||
case SHADOW_CLIPMAP_CLEAR:
|
||||
return "eevee_shadow_clipmap_clear";
|
||||
case SHADOW_DEBUG:
|
||||
|
|
|
@ -81,6 +81,7 @@ enum eShaderType {
|
|||
LIGHT_CULLING_SORT,
|
||||
LIGHT_CULLING_TILE,
|
||||
LIGHT_CULLING_ZBIN,
|
||||
LIGHT_SHADOW_SETUP,
|
||||
|
||||
LIGHTPROBE_IRRADIANCE_BOUNDS,
|
||||
LIGHTPROBE_IRRADIANCE_OFFSET,
|
||||
|
@ -106,9 +107,10 @@ enum eShaderType {
|
|||
RAY_TRACE_SCREEN,
|
||||
|
||||
SPHERE_PROBE_CONVOLVE,
|
||||
SPHERE_PROBE_IRRADIANCE,
|
||||
SPHERE_PROBE_REMAP,
|
||||
SPHERE_PROBE_SELECT,
|
||||
SPHERE_PROBE_IRRADIANCE,
|
||||
SPHERE_PROBE_SUNLIGHT,
|
||||
|
||||
SHADOW_CLIPMAP_CLEAR,
|
||||
SHADOW_DEBUG,
|
||||
|
|
|
@ -1560,6 +1560,13 @@ struct SphereProbeHarmonic {
|
|||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(SphereProbeHarmonic, 16)
|
||||
|
||||
struct SphereProbeSunLight {
|
||||
float4 direction;
|
||||
packed_float3 radiance;
|
||||
float _pad0;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(SphereProbeSunLight, 16)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -1725,7 +1732,7 @@ BLI_STATIC_ASSERT_ALIGN(HiZData, 16)
|
|||
* \{ */
|
||||
|
||||
struct ClampData {
|
||||
float world;
|
||||
float sun_threshold;
|
||||
float surface_direct;
|
||||
float surface_indirect;
|
||||
float volume_direct;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_world.h"
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
#include "NOD_shader.h"
|
||||
|
||||
|
@ -76,6 +77,21 @@ World::~World()
|
|||
return default_world_;
|
||||
}
|
||||
|
||||
::World *World::scene_world_get()
|
||||
{
|
||||
return (inst_.scene->world != nullptr) ? inst_.scene->world : default_world_get();
|
||||
}
|
||||
|
||||
float World::sun_threshold()
|
||||
{
|
||||
float sun_threshold = scene_world_get()->sun_threshold;
|
||||
if (inst_.use_studio_light()) {
|
||||
/* Do not call `lookdev_world_.intensity_get()` as it might not be initialized yet. */
|
||||
sun_threshold *= inst_.v3d->shading.studiolight_intensity;
|
||||
}
|
||||
return sun_threshold;
|
||||
}
|
||||
|
||||
void World::sync()
|
||||
{
|
||||
bool has_update = false;
|
||||
|
@ -100,11 +116,8 @@ void World::sync()
|
|||
else if (has_volume_absorption_) {
|
||||
bl_world = default_world_get();
|
||||
}
|
||||
else if (inst_.scene->world != nullptr) {
|
||||
bl_world = inst_.scene->world;
|
||||
}
|
||||
else {
|
||||
bl_world = default_world_get();
|
||||
bl_world = scene_world_get();
|
||||
}
|
||||
|
||||
bNodeTree *ntree = (bl_world->nodetree && bl_world->use_nodes) ?
|
||||
|
|
|
@ -47,6 +47,12 @@ class DefaultWorldNodeTree {
|
|||
* \{ */
|
||||
|
||||
class World {
|
||||
public:
|
||||
/**
|
||||
* Buffer containing the sun light for the world.
|
||||
* Filled by #LightProbeModule and read by #LightModule. */
|
||||
UniformBuffer<LightData> sunlight = {"sunlight"};
|
||||
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
|
@ -88,12 +94,32 @@ class World {
|
|||
return has_volume_scatter_;
|
||||
}
|
||||
|
||||
float sun_threshold();
|
||||
|
||||
float sun_angle()
|
||||
{
|
||||
return scene_world_get()->sun_angle;
|
||||
}
|
||||
|
||||
float sun_shadow_max_resolution()
|
||||
{
|
||||
return scene_world_get()->sun_shadow_maximum_resolution;
|
||||
}
|
||||
|
||||
bool use_sun_shadow()
|
||||
{
|
||||
return scene_world_get()->flag & WO_USE_SUN_SHADOW;
|
||||
}
|
||||
|
||||
private:
|
||||
void sync_volume();
|
||||
|
||||
/* Returns a dummy black world for when a valid world isn't present or when we want to suppress
|
||||
* any light coming from the world. */
|
||||
::World *default_world_get();
|
||||
|
||||
/* Returns either the scene world or the default world if scene has no world. */
|
||||
::World *scene_world_get();
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -21,6 +21,22 @@ void main()
|
|||
|
||||
/* Sun lights are packed at the end of the array. Perform early copy. */
|
||||
if (is_sun_light(light.type)) {
|
||||
/* First sun-light is reserved for world light. Perform copy from dedicated buffer. */
|
||||
bool is_world_sun_light = light.color.r < 0.0;
|
||||
if (is_world_sun_light) {
|
||||
light.color = sunlight_buf.color;
|
||||
light.object_to_world = sunlight_buf.object_to_world;
|
||||
/* NOTE: Use the radius from UI instead of auto sun size for now. */
|
||||
// light.power = sunlight_buf.power;
|
||||
#if USE_LIGHT_UNION
|
||||
// light.sun.radius = sunlight_buf.sun.radius;
|
||||
// light.sun.shadow_angle = sunlight_buf.sun.shadow_angle;
|
||||
#else
|
||||
// light.do_not_access_directly.radius_squared =
|
||||
// sunlight_buf.do_not_access_directly.radius_squared;
|
||||
// light.do_not_access_directly._pad1 = sunlight_buf.do_not_access_directly._pad1;
|
||||
#endif
|
||||
}
|
||||
/* NOTE: We know the index because sun lights are packed at the start of the input buffer. */
|
||||
out_light_buf[light_cull_buf.local_lights_len + l_idx] = light;
|
||||
return;
|
||||
|
|
|
@ -0,0 +1,273 @@
|
|||
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* Setup tilemap positionning for each shadow casting light.
|
||||
* Dispatched one thread per light.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_matrix_lib.glsl)
|
||||
|
||||
int shadow_directional_coverage_get(int level)
|
||||
{
|
||||
return 1 << level;
|
||||
}
|
||||
|
||||
void orthographic_sync(int tilemap_id,
|
||||
Transform light_tx,
|
||||
int2 origin_offset,
|
||||
int clipmap_level,
|
||||
eShadowProjectionType projection_type)
|
||||
{
|
||||
if (all(equal(tilemaps_buf[tilemap_id].grid_shift, int2(0)))) {
|
||||
/* Only replace shift if it is not already dirty. */
|
||||
tilemaps_buf[tilemap_id].grid_shift = tilemaps_buf[tilemap_id].grid_offset - origin_offset;
|
||||
}
|
||||
tilemaps_buf[tilemap_id].grid_offset = origin_offset;
|
||||
|
||||
mat3x3 object_to_world_transposed = mat3x3(tilemaps_buf[tilemap_id].viewmat);
|
||||
|
||||
if (!all(equal(object_to_world_transposed[0], light_tx.x.xyz)) ||
|
||||
!all(equal(object_to_world_transposed[1], light_tx.y.xyz)) ||
|
||||
!all(equal(object_to_world_transposed[2], light_tx.z.xyz)))
|
||||
{
|
||||
tilemaps_buf[tilemap_id].grid_shift = int2(SHADOW_TILEMAP_RES);
|
||||
}
|
||||
|
||||
float level_size = shadow_directional_coverage_get(clipmap_level);
|
||||
float half_size = level_size / 2.0;
|
||||
float tile_size = level_size / float(SHADOW_TILEMAP_RES);
|
||||
vec2 center_offset = vec2(origin_offset) * tile_size;
|
||||
|
||||
/* object_mat is a rotation matrix. Reduce imprecision by taking the transpose which is also the
|
||||
* inverse in this particular case. */
|
||||
tilemaps_buf[tilemap_id].viewmat[0] = vec4(light_tx.x.xyz, 0.0);
|
||||
tilemaps_buf[tilemap_id].viewmat[1] = vec4(light_tx.y.xyz, 0.0);
|
||||
tilemaps_buf[tilemap_id].viewmat[2] = vec4(light_tx.z.xyz, 0.0);
|
||||
tilemaps_buf[tilemap_id].viewmat[3] = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
tilemaps_buf[tilemap_id].projection_type = projection_type;
|
||||
tilemaps_buf[tilemap_id].half_size = half_size;
|
||||
tilemaps_buf[tilemap_id].center_offset = center_offset;
|
||||
tilemaps_buf[tilemap_id].winmat = projection_orthographic(
|
||||
-half_size + center_offset.x,
|
||||
half_size + center_offset.x,
|
||||
-half_size + center_offset.y,
|
||||
half_size + center_offset.y,
|
||||
/* Near/far is computed on GPU using casters bounds. */
|
||||
-1.0,
|
||||
1.0);
|
||||
}
|
||||
|
||||
void cascade_sync(inout LightData light)
|
||||
{
|
||||
int level_min = light_sun_data_get(light).clipmap_lod_min;
|
||||
int level_max = light_sun_data_get(light).clipmap_lod_max;
|
||||
int level_range = level_max - level_min;
|
||||
int level_len = level_range + 1;
|
||||
|
||||
vec3 ws_camera_position = uniform_buf.camera.viewinv[3].xyz;
|
||||
vec3 ws_camera_forward = uniform_buf.camera.viewinv[2].xyz;
|
||||
float camera_clip_near = uniform_buf.camera.clip_near;
|
||||
float camera_clip_far = uniform_buf.camera.clip_far;
|
||||
|
||||
/* All tile-maps use the first level size. */
|
||||
float level_size = shadow_directional_coverage_get(level_min);
|
||||
float half_size = level_size / 2.0;
|
||||
float tile_size = level_size / float(SHADOW_TILEMAP_RES);
|
||||
|
||||
/* Ideally we should only take the intersection with the scene bounds. */
|
||||
vec3 ws_far_point = ws_camera_position - ws_camera_forward * camera_clip_far;
|
||||
vec3 ws_near_point = ws_camera_position - ws_camera_forward * camera_clip_near;
|
||||
|
||||
vec3 ls_far_point = transform_direction_transposed(light.object_to_world, ws_far_point);
|
||||
vec3 ls_near_point = transform_direction_transposed(light.object_to_world, ws_near_point);
|
||||
|
||||
float2 local_view_direction = normalize(ls_far_point.xy - ls_near_point.xy);
|
||||
float2 farthest_tilemap_center = local_view_direction * half_size * level_range;
|
||||
|
||||
/* Offset for smooth level transitions. */
|
||||
light.object_to_world.x.w = ls_near_point.x;
|
||||
light.object_to_world.y.w = ls_near_point.y;
|
||||
light.object_to_world.z.w = ls_near_point.z;
|
||||
|
||||
/* Offset in tiles from the scene origin to the center of the first tile-maps. */
|
||||
int2 origin_offset = int2(round(ls_near_point.xy / tile_size));
|
||||
/* Offset in tiles between the first and the last tile-maps. */
|
||||
int2 offset_vector = int2(round(farthest_tilemap_center / tile_size));
|
||||
|
||||
int2 base_offset_pos = (offset_vector * (1 << 16)) / max(level_range, 1);
|
||||
|
||||
/* \note cascade_level_range starts the range at the unique LOD to apply to all tile-maps. */
|
||||
for (int i = 0; i < level_len; i++) {
|
||||
/* Equal spacing between cascades layers since we want uniform shadow density. */
|
||||
int2 level_offset = origin_offset + shadow_cascade_grid_offset(base_offset_pos, i);
|
||||
|
||||
orthographic_sync(light.tilemap_index + i,
|
||||
light.object_to_world,
|
||||
level_offset,
|
||||
level_min,
|
||||
SHADOW_PROJECTION_CASCADE);
|
||||
}
|
||||
|
||||
vec2 clipmap_origin = vec2(origin_offset) * tile_size;
|
||||
|
||||
#if USE_LIGHT_UNION
|
||||
/* Used as origin for the clipmap_base_offset trick. */
|
||||
light.sun.clipmap_origin = clipmap_origin;
|
||||
/* Number of levels is limited to 32 by `clipmap_level_range()` for this reason. */
|
||||
light.sun.clipmap_base_offset_pos = base_offset_pos;
|
||||
light.sun.clipmap_base_offset_neg = ivec2(0);
|
||||
#else
|
||||
/* Used as origin for the clipmap_base_offset trick. */
|
||||
light.do_not_access_directly._pad3 = clipmap_origin;
|
||||
/* Number of levels is limited to 32 by `clipmap_level_range()` for this reason. */
|
||||
light.do_not_access_directly._pad0_reserved = intBitsToFloat(base_offset_pos.x);
|
||||
light.do_not_access_directly._pad1_reserved = intBitsToFloat(base_offset_pos.y);
|
||||
light.do_not_access_directly._pad7 = intBitsToFloat(0);
|
||||
light.do_not_access_directly.shadow_projection_shift = intBitsToFloat(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void clipmap_sync(inout LightData light)
|
||||
{
|
||||
vec3 ws_camera_position = uniform_buf.camera.viewinv[3].xyz;
|
||||
vec3 ls_camera_position = transform_direction_transposed(light.object_to_world,
|
||||
ws_camera_position);
|
||||
|
||||
int level_min = light_sun_data_get(light).clipmap_lod_min;
|
||||
int level_max = light_sun_data_get(light).clipmap_lod_max;
|
||||
int level_len = level_max - level_min + 1;
|
||||
|
||||
vec2 clipmap_origin;
|
||||
for (int lod = 0; lod < level_len; lod++) {
|
||||
int level = level_min + lod;
|
||||
/* Compute full offset from world origin to the smallest clipmap tile centered around the
|
||||
* camera position. The offset is computed in smallest tile unit. */
|
||||
float tile_size = float(1 << level) / float(SHADOW_TILEMAP_RES);
|
||||
int2 level_offset = int2(round(ls_camera_position.xy / tile_size));
|
||||
|
||||
orthographic_sync(light.tilemap_index + lod,
|
||||
light.object_to_world,
|
||||
level_offset,
|
||||
level,
|
||||
SHADOW_PROJECTION_CLIPMAP);
|
||||
|
||||
clipmap_origin = vec2(level_offset) * tile_size;
|
||||
}
|
||||
|
||||
int2 pos_offset = int2(0);
|
||||
int2 neg_offset = int2(0);
|
||||
for (int lod = 0; lod < level_len - 1; lod++) {
|
||||
/* Since offset can only differ by one tile from the higher level, we can compress that as a
|
||||
* single integer where one bit contains offset between 2 levels. Then a single bit shift in
|
||||
* the shader gives the number of tile to offset in the given tile-map space. However we need
|
||||
* also the sign of the offset for each level offset. To this end, we split the negative
|
||||
* offsets to a separate int. */
|
||||
int2 lvl_offset_next = tilemaps_buf[light.tilemap_index + lod + 1].grid_offset;
|
||||
int2 lvl_offset = tilemaps_buf[light.tilemap_index + lod].grid_offset;
|
||||
int2 lvl_delta = lvl_offset - (lvl_offset_next << 1);
|
||||
pos_offset |= max(lvl_delta, int2(0)) << lod;
|
||||
neg_offset |= max(-lvl_delta, int2(0)) << lod;
|
||||
}
|
||||
|
||||
/* Used for selecting the clipmap level. */
|
||||
light.object_to_world.x.w = ls_camera_position.x;
|
||||
light.object_to_world.y.w = ls_camera_position.y;
|
||||
light.object_to_world.z.w = ls_camera_position.z;
|
||||
#if USE_LIGHT_UNION
|
||||
/* Used as origin for the clipmap_base_offset trick. */
|
||||
light.sun.clipmap_origin = clipmap_origin;
|
||||
/* Number of levels is limited to 32 by `clipmap_level_range()` for this reason. */
|
||||
light.sun.clipmap_base_offset_pos = pos_offset;
|
||||
light.sun.clipmap_base_offset_neg = neg_offset;
|
||||
#else
|
||||
/* Used as origin for the clipmap_base_offset trick. */
|
||||
light.do_not_access_directly._pad3 = clipmap_origin;
|
||||
/* Number of levels is limited to 32 by `clipmap_level_range()` for this reason. */
|
||||
light.do_not_access_directly._pad0_reserved = intBitsToFloat(pos_offset.x);
|
||||
light.do_not_access_directly._pad1_reserved = intBitsToFloat(pos_offset.y);
|
||||
light.do_not_access_directly._pad7 = intBitsToFloat(neg_offset.x);
|
||||
light.do_not_access_directly.shadow_projection_shift = intBitsToFloat(neg_offset.y);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
void cubeface_sync(int tilemap_id, vec3 jitter_offset)
|
||||
{
|
||||
/* Update corners. */
|
||||
viewmat = shadow_face_mat[cubeface] * from_location<float4x4>(float3(0.0, 0.0, -shift)) *
|
||||
invert(object_mat);
|
||||
|
||||
/* Update corners. */
|
||||
corners[0] += jitter_offset;
|
||||
corners[1] += jitter_offset;
|
||||
corners[2] += jitter_offset;
|
||||
corners[3] += jitter_offset;
|
||||
|
||||
/* Set dirty. */
|
||||
grid_shift = int2(SHADOW_TILEMAP_RES);
|
||||
}
|
||||
#endif
|
||||
|
||||
void main()
|
||||
{
|
||||
uint l_idx = gl_GlobalInvocationID.x;
|
||||
if (l_idx >= light_cull_buf.items_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
LightData light = light_buf[l_idx];
|
||||
|
||||
if (light.tilemap_index == LIGHT_NO_SHADOW) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_sun_light(light.type)) {
|
||||
/* Distant lights. */
|
||||
|
||||
#if 0 /* Jittered shadows. */
|
||||
vec3 position_on_light = random_position_on_light(light);
|
||||
vec3 light_direction = normalize(position_on_light);
|
||||
float3x3 object_to_world_transposed = transpose(from_up_axis(light_direction));
|
||||
|
||||
light.object_to_world.x.xyz = object_to_world_transposed[0];
|
||||
light.object_to_world.y.xyz = object_to_world_transposed[1];
|
||||
light.object_to_world.z.xyz = object_to_world_transposed[2];
|
||||
#endif
|
||||
|
||||
if (light.type == LIGHT_SUN_ORTHO) {
|
||||
cascade_sync(light);
|
||||
}
|
||||
else {
|
||||
clipmap_sync(light);
|
||||
}
|
||||
}
|
||||
#if 0 /* Jittered shadows. */
|
||||
else {
|
||||
/* Local lights. */
|
||||
# if 0 /* Jittered shadows. */
|
||||
vec3 position_on_light = random_position_on_light(light);
|
||||
light_buf[l_idx].shadow_position = position_on_light;
|
||||
|
||||
int tilemap_count = 0;
|
||||
if (is_area_light(light.type)) {
|
||||
tilemap_count = 5;
|
||||
}
|
||||
else if (is_spot_light(light.type)) {
|
||||
tilemap_count = (spot_angle > M_PI * 0.25) ? 5 : 1;
|
||||
}
|
||||
else {
|
||||
tilemap_count = 6;
|
||||
}
|
||||
|
||||
for (int i = 0; i < tilemap_count; i++) {
|
||||
cubeface_sync(light.tilemap_id + i, position_on_light);
|
||||
}
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
|
||||
light_buf[l_idx] = light;
|
||||
}
|
|
@ -12,24 +12,6 @@
|
|||
|
||||
shared vec4 local_sh_coefs[gl_WorkGroupSize.x][4];
|
||||
|
||||
void spherical_harmonic_lds_store(uint index, SphericalHarmonicL1 sh)
|
||||
{
|
||||
local_sh_coefs[index][0] = sh.L0.M0;
|
||||
local_sh_coefs[index][1] = sh.L1.Mn1;
|
||||
local_sh_coefs[index][2] = sh.L1.M0;
|
||||
local_sh_coefs[index][3] = sh.L1.Mp1;
|
||||
}
|
||||
|
||||
SphericalHarmonicL1 spherical_harmonic_lds_load(uint index)
|
||||
{
|
||||
SphericalHarmonicL1 sh;
|
||||
sh.L0.M0 = local_sh_coefs[index][0];
|
||||
sh.L1.Mn1 = local_sh_coefs[index][1];
|
||||
sh.L1.M0 = local_sh_coefs[index][2];
|
||||
sh.L1.Mp1 = local_sh_coefs[index][3];
|
||||
return sh;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
SphericalHarmonicL1 sh;
|
||||
|
|
|
@ -84,6 +84,10 @@ float octahedral_texel_solid_angle(ivec2 local_texel,
|
|||
|
||||
void main()
|
||||
{
|
||||
uint work_group_index = gl_NumWorkGroups.x * gl_WorkGroupID.y + gl_WorkGroupID.x;
|
||||
const uint local_index = gl_LocalInvocationIndex;
|
||||
const uint group_size = gl_WorkGroupSize.x * gl_WorkGroupSize.y;
|
||||
|
||||
SphereProbeUvArea world_coord = reinterpret_as_atlas_coord(world_coord_packed);
|
||||
SphereProbeUvArea sample_coord = reinterpret_as_atlas_coord(probe_coord_packed);
|
||||
SphereProbePixelArea write_coord = reinterpret_as_write_coord(write_coord_packed);
|
||||
|
@ -107,8 +111,10 @@ void main()
|
|||
radiance.rgb = mix(world_radiance.rgb, radiance.rgb, opacity);
|
||||
}
|
||||
|
||||
float clamp_world = uniform_buf.clamp.world;
|
||||
radiance = colorspace_brightness_clamp_max(radiance, clamp_world);
|
||||
float sun_threshold = uniform_buf.clamp.sun_threshold;
|
||||
vec3 radiance_clamped = colorspace_brightness_clamp_max(radiance, sun_threshold);
|
||||
vec3 radiance_sun = radiance - radiance_clamped;
|
||||
radiance = radiance_clamped;
|
||||
|
||||
if (!any(greaterThanEqual(local_texel, ivec2(write_coord.extent)))) {
|
||||
float clamp_indirect = uniform_buf.clamp.surface_indirect;
|
||||
|
@ -118,12 +124,42 @@ void main()
|
|||
imageStore(atlas_img, texel, vec4(out_radiance, 1.0));
|
||||
}
|
||||
|
||||
float sample_weight = octahedral_texel_solid_angle(local_texel, write_coord, sample_coord);
|
||||
|
||||
if (extract_sun) {
|
||||
/* Parallel sum. Result is stored inside local_radiance[0]. */
|
||||
local_radiance[local_index] = radiance_sun.xyzz * sample_weight;
|
||||
for (uint stride = group_size / 2; stride > 0; stride /= 2) {
|
||||
barrier();
|
||||
if (local_index < stride) {
|
||||
local_radiance[local_index] += local_radiance[local_index + stride];
|
||||
}
|
||||
}
|
||||
barrier();
|
||||
|
||||
if (gl_LocalInvocationIndex == 0u) {
|
||||
out_sun[work_group_index].radiance = local_radiance[0].xyz;
|
||||
}
|
||||
barrier();
|
||||
|
||||
/* Reusing local_radiance for directions. */
|
||||
local_radiance[local_index] = vec4(normalize(direction), 1.0) * sample_weight *
|
||||
length(radiance_sun.xyz);
|
||||
for (uint stride = group_size / 2; stride > 0; stride /= 2) {
|
||||
barrier();
|
||||
if (local_index < stride) {
|
||||
local_radiance[local_index] += local_radiance[local_index + stride];
|
||||
}
|
||||
}
|
||||
barrier();
|
||||
|
||||
if (gl_LocalInvocationIndex == 0u) {
|
||||
out_sun[work_group_index].direction = local_radiance[0];
|
||||
}
|
||||
barrier();
|
||||
}
|
||||
|
||||
if (extract_sh) {
|
||||
float sample_weight = octahedral_texel_solid_angle(local_texel, write_coord, sample_coord);
|
||||
|
||||
const uint local_index = gl_LocalInvocationIndex;
|
||||
const uint group_size = gl_WorkGroupSize.x * gl_WorkGroupSize.y;
|
||||
|
||||
/* Parallel sum. Result is stored inside local_radiance[0]. */
|
||||
local_radiance[local_index] = radiance.xyzz * sample_weight;
|
||||
uint stride = group_size / 2;
|
||||
|
@ -158,7 +194,6 @@ void main()
|
|||
* instead of adding to it? */
|
||||
spherical_harmonics_encode_signal_sample(L, local_radiance[0], sh);
|
||||
/* Outputs one SH for each thread-group. */
|
||||
uint work_group_index = gl_NumWorkGroups.x * gl_WorkGroupID.y + gl_WorkGroupID.x;
|
||||
out_sh[work_group_index].L0_M0 = sh.L0.M0;
|
||||
out_sh[work_group_index].L1_Mn1 = sh.L1.Mn1;
|
||||
out_sh[work_group_index].L1_M0 = sh.L1.M0;
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/* Sum all Suns extracting during remapping to octahedral map.
|
||||
* Dispatch only one thread-group that sums. */
|
||||
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_matrix_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_reflection_probe_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_reflection_probe_mapping_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
|
||||
|
||||
shared vec3 local_radiance[gl_WorkGroupSize.x];
|
||||
shared vec4 local_direction[gl_WorkGroupSize.x];
|
||||
|
||||
void main()
|
||||
{
|
||||
SphereProbeSunLight sun;
|
||||
sun.radiance = vec3(0.0);
|
||||
sun.direction = vec4(0.0);
|
||||
|
||||
/* First sum onto the local memory. */
|
||||
uint valid_data_len = probe_remap_dispatch_size.x * probe_remap_dispatch_size.y;
|
||||
const uint iter_count = uint(SPHERE_PROBE_MAX_HARMONIC) / gl_WorkGroupSize.x;
|
||||
for (uint i = 0; i < iter_count; i++) {
|
||||
uint index = gl_WorkGroupSize.x * i + gl_LocalInvocationIndex;
|
||||
if (index >= valid_data_len) {
|
||||
break;
|
||||
}
|
||||
sun.radiance += in_sun[index].radiance;
|
||||
sun.direction += in_sun[index].direction;
|
||||
}
|
||||
|
||||
/* Then sum across invocations. */
|
||||
const uint local_index = gl_LocalInvocationIndex;
|
||||
local_radiance[local_index] = sun.radiance;
|
||||
local_direction[local_index] = sun.direction;
|
||||
|
||||
/* Parallel sum. */
|
||||
const uint group_size = gl_WorkGroupSize.x * gl_WorkGroupSize.y;
|
||||
for (uint stride = group_size / 2; stride > 0; stride /= 2) {
|
||||
barrier();
|
||||
if (local_index < stride) {
|
||||
local_radiance[local_index] += local_radiance[local_index + stride];
|
||||
local_direction[local_index] += local_direction[local_index + stride];
|
||||
}
|
||||
}
|
||||
|
||||
barrier();
|
||||
if (gl_LocalInvocationIndex == 0u) {
|
||||
sunlight_buf.color = local_radiance[0];
|
||||
|
||||
/* Normalize the sum to get the mean direction. The length of the vector gives us the size of
|
||||
* the sun light. */
|
||||
float len;
|
||||
vec3 direction = normalize_and_get_length(local_direction[0].xyz / local_direction[0].w, len);
|
||||
|
||||
mat3x3 tx = transpose(from_up_axis(direction));
|
||||
/* Convert to transform. */
|
||||
sunlight_buf.object_to_world.x = vec4(tx[0], 0.0);
|
||||
sunlight_buf.object_to_world.y = vec4(tx[1], 0.0);
|
||||
sunlight_buf.object_to_world.z = vec4(tx[2], 0.0);
|
||||
|
||||
/* Auto sun angle. */
|
||||
float sun_angle_cos = 2.0 * len - 1.0;
|
||||
float sun_angle = acos(sun_angle_cos);
|
||||
/* Compute tangent from cosine. */
|
||||
float sun_angle_tan = sqrt(-1.0 + 1.0 / square(sun_angle_cos));
|
||||
/* Clamp value to avoid float imprecision artifacts. */
|
||||
float sun_radius = clamp(sun_angle_tan, 0.001, 20.0);
|
||||
|
||||
/* Convert irradiance to radiance. */
|
||||
float shape_power = M_1_PI * (1.0 + 1.0 / square(sun_radius));
|
||||
float point_power = 1.0;
|
||||
|
||||
sunlight_buf.power[LIGHT_DIFFUSE] = shape_power;
|
||||
sunlight_buf.power[LIGHT_SPECULAR] = shape_power;
|
||||
sunlight_buf.power[LIGHT_TRANSMISSION] = shape_power;
|
||||
sunlight_buf.power[LIGHT_VOLUME] = point_power;
|
||||
|
||||
#if USE_LIGHT_UNION
|
||||
sunlight_buf.sun.radius = sun_radius;
|
||||
sunlight_buf.sun.shadow_angle = sun_angle;
|
||||
#else
|
||||
sunlight_buf.do_not_access_directly.radius_squared = sun_radius;
|
||||
sunlight_buf.do_not_access_directly._pad1 = sun_angle;
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -30,6 +30,7 @@ GPU_SHADER_CREATE_INFO(eevee_light_culling_select)
|
|||
.storage_buf(2, Qualifier::WRITE, "LightData", "out_light_buf[]")
|
||||
.storage_buf(3, Qualifier::WRITE, "float", "out_zdist_buf[]")
|
||||
.storage_buf(4, Qualifier::WRITE, "uint", "out_key_buf[]")
|
||||
.uniform_buf(0, "LightData", "sunlight_buf")
|
||||
.compute_source("eevee_light_culling_select_comp.glsl");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_light_culling_sort)
|
||||
|
@ -61,6 +62,15 @@ GPU_SHADER_CREATE_INFO(eevee_light_culling_tile)
|
|||
.storage_buf(2, Qualifier::WRITE, "uint", "out_light_tile_buf[]")
|
||||
.compute_source("eevee_light_culling_tile_comp.glsl");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_light_shadow_setup)
|
||||
.do_static_compilation(true)
|
||||
.additional_info("eevee_shared", "draw_view", "draw_view_culling", "eevee_global_ubo")
|
||||
.local_group_size(CULLING_SELECT_GROUP_SIZE)
|
||||
.storage_buf(0, Qualifier::READ, "LightCullingData", "light_cull_buf")
|
||||
.storage_buf(1, Qualifier::READ_WRITE, "LightData", "light_buf[]")
|
||||
.storage_buf(2, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
|
||||
.compute_source("eevee_light_shadow_setup_comp.glsl");
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -20,12 +20,14 @@ GPU_SHADER_CREATE_INFO(eevee_reflection_probe_data)
|
|||
GPU_SHADER_CREATE_INFO(eevee_reflection_probe_remap)
|
||||
.local_group_size(SPHERE_PROBE_REMAP_GROUP_SIZE, SPHERE_PROBE_REMAP_GROUP_SIZE)
|
||||
.specialization_constant(Type::BOOL, "extract_sh", true)
|
||||
.specialization_constant(Type::BOOL, "extract_sun", true)
|
||||
.push_constant(Type::IVEC4, "probe_coord_packed")
|
||||
.push_constant(Type::IVEC4, "write_coord_packed")
|
||||
.push_constant(Type::IVEC4, "world_coord_packed")
|
||||
.sampler(0, ImageType::FLOAT_CUBE, "cubemap_tx")
|
||||
.sampler(1, ImageType::FLOAT_2D_ARRAY, "atlas_tx")
|
||||
.storage_buf(0, Qualifier::WRITE, "SphereProbeHarmonic", "out_sh[SPHERE_PROBE_MAX_HARMONIC]")
|
||||
.storage_buf(1, Qualifier::WRITE, "SphereProbeSunLight", "out_sun[SPHERE_PROBE_MAX_HARMONIC]")
|
||||
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "atlas_img")
|
||||
.compute_source("eevee_reflection_probe_remap_comp.glsl")
|
||||
.additional_info("eevee_shared", "eevee_global_ubo")
|
||||
|
@ -40,6 +42,15 @@ GPU_SHADER_CREATE_INFO(eevee_reflection_probe_irradiance)
|
|||
.do_static_compilation(true)
|
||||
.compute_source("eevee_reflection_probe_irradiance_comp.glsl");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_reflection_probe_sunlight)
|
||||
.local_group_size(SPHERE_PROBE_SH_GROUP_SIZE)
|
||||
.push_constant(Type::IVEC3, "probe_remap_dispatch_size")
|
||||
.storage_buf(0, Qualifier::READ, "SphereProbeSunLight", "in_sun[SPHERE_PROBE_MAX_HARMONIC]")
|
||||
.storage_buf(1, Qualifier::WRITE, "LightData", "sunlight_buf")
|
||||
.additional_info("eevee_shared")
|
||||
.do_static_compilation(true)
|
||||
.compute_source("eevee_reflection_probe_sunlight_comp.glsl");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_reflection_probe_select)
|
||||
.local_group_size(SPHERE_PROBE_SELECT_GROUP_SIZE)
|
||||
.storage_buf(0,
|
||||
|
|
|
@ -230,7 +230,6 @@
|
|||
.motion_blur_max = 32, \
|
||||
.motion_blur_steps = 1, \
|
||||
\
|
||||
.clamp_world = 10.0f, \
|
||||
.clamp_surface_indirect = 10.0f, \
|
||||
\
|
||||
.shadow_cube_size = 512, \
|
||||
|
|
|
@ -1891,12 +1891,10 @@ typedef struct SceneEEVEE {
|
|||
int shadow_step_count;
|
||||
float shadow_resolution_scale;
|
||||
|
||||
float clamp_world;
|
||||
float clamp_surface_direct;
|
||||
float clamp_surface_indirect;
|
||||
float clamp_volume_direct;
|
||||
float clamp_volume_indirect;
|
||||
char _pad[4];
|
||||
|
||||
int ray_tracing_method;
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#define _DNA_DEFAULT_World \
|
||||
{ \
|
||||
.flag = WO_USE_SUN_SHADOW, \
|
||||
\
|
||||
.horr = 0.05f, \
|
||||
.horg = 0.05f, \
|
||||
.horb = 0.05f, \
|
||||
|
@ -28,6 +30,9 @@
|
|||
.mistdist = 25.0f, \
|
||||
\
|
||||
.probe_resolution = LIGHT_PROBE_RESOLUTION_1024, \
|
||||
.sun_threshold = 10.0f, \
|
||||
.sun_angle = DEG2RADF(0.526f), \
|
||||
.sun_shadow_maximum_resolution = 0.001f, \
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -67,6 +67,13 @@ typedef struct World {
|
|||
* Resolution of the world probe when baked to a texture. Contains `eLightProbeResolution`.
|
||||
*/
|
||||
int probe_resolution;
|
||||
/** Threshold for sun extraction. */
|
||||
float sun_threshold;
|
||||
/** Angle for sun extraction. */
|
||||
float sun_angle;
|
||||
/** Maximum resolution for extracted sun shadow. */
|
||||
float sun_shadow_maximum_resolution;
|
||||
char _pad4[4];
|
||||
|
||||
/** Old animation system, deprecated for 2.5. */
|
||||
struct Ipo *ipo DNA_DEPRECATED;
|
||||
|
@ -120,6 +127,10 @@ enum {
|
|||
* converted manually. (Ref: #119734).
|
||||
*/
|
||||
WO_USE_EEVEE_FINITE_VOLUME = 1 << 3,
|
||||
/**
|
||||
* Use shadowing from the extracted sun light.
|
||||
*/
|
||||
WO_USE_SUN_SHADOW = 1 << 4,
|
||||
};
|
||||
|
||||
/** #World::probe_resolution. */
|
||||
|
|
|
@ -1942,11 +1942,6 @@ static void rna_SceneEEVEE_gi_cubemap_resolution_update(Main * /*main*/,
|
|||
FOREACH_SCENE_OBJECT_END;
|
||||
}
|
||||
|
||||
static void rna_SceneEEVEE_clamp_world_update(Main * /*main*/, Scene *scene, PointerRNA * /*ptr*/)
|
||||
{
|
||||
DEG_id_tag_update(&scene->world->id, ID_RECALC_SHADING);
|
||||
}
|
||||
|
||||
static void rna_SceneEEVEE_clamp_surface_indirect_update(Main * /*main*/,
|
||||
Scene *scene,
|
||||
PointerRNA * /*ptr*/)
|
||||
|
@ -8073,17 +8068,6 @@ static void rna_def_scene_eevee(BlenderRNA *brna)
|
|||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr);
|
||||
|
||||
/* Clamping */
|
||||
prop = RNA_def_property(srna, "clamp_world", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Clamp World",
|
||||
"If non-zero, the maximum value for world contribution to the scene lighting. "
|
||||
"Higher values will be scaled down to avoid too "
|
||||
"much light bleeding at the cost of accuracy");
|
||||
RNA_def_property_range(prop, 0.0f, FLT_MAX);
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_SceneEEVEE_clamp_world_update");
|
||||
|
||||
prop = RNA_def_property(srna, "clamp_surface_direct", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Clamp Surface Direct",
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "RNA_define.hh"
|
||||
|
||||
#include "BLI_math_rotation.h"
|
||||
|
||||
#include "rna_internal.hh"
|
||||
|
||||
#include "DNA_lightprobe_types.h"
|
||||
|
@ -282,6 +284,37 @@ void RNA_def_world(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(prop, "Resolution", "Resolution when baked to a texture");
|
||||
RNA_def_property_update(prop, 0, "rna_World_draw_update");
|
||||
|
||||
prop = RNA_def_property(srna, "sun_threshold", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Sun Threshold",
|
||||
"If non-zero, the maximum value for world contribution that will be "
|
||||
"recorded inside the world light probe. The excess contribution is "
|
||||
"converted to a sun light. This reduces the light bleeding caused by "
|
||||
"very bright light sources");
|
||||
RNA_def_property_range(prop, 0.0f, FLT_MAX);
|
||||
RNA_def_property_update(prop, 0, "rna_World_draw_update");
|
||||
|
||||
prop = RNA_def_property(srna, "sun_angle", PROP_FLOAT, PROP_ANGLE);
|
||||
RNA_def_property_range(prop, DEG2RADF(0.0f), DEG2RADF(180.0f));
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Sun Angle", "Angular diameter of the Sun as seen from the Earth");
|
||||
RNA_def_property_update(prop, 0, "rna_World_draw_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_sun_shadow", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", WO_USE_SUN_SHADOW);
|
||||
RNA_def_property_ui_text(prop, "Use Shadow", "Enable sun shadow casting");
|
||||
RNA_def_property_update(prop, 0, "rna_World_draw_update");
|
||||
|
||||
prop = RNA_def_property(srna, "sun_shadow_maximum_resolution", PROP_FLOAT, PROP_DISTANCE);
|
||||
RNA_def_property_range(prop, 0.0f, FLT_MAX);
|
||||
RNA_def_property_ui_range(prop, 0.0001f, 0.020f, 0.05f, 4);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Shadows Resolution Limit",
|
||||
"Maximum size of a shadow map pixel. Higher values use less memory at "
|
||||
"the cost of shadow quality");
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
|
||||
RNA_def_property_update(prop, 0, "rna_World_draw_update");
|
||||
|
||||
rna_def_lighting(brna);
|
||||
rna_def_world_mist(brna);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue