EEVEE-Next: Sunlight Extraction #121455

Merged
Clément Foucault merged 26 commits from fclem/blender:eevee-next-sun-extraction into main 2024-05-14 16:36:24 +02:00
30 changed files with 705 additions and 81 deletions

View File

@ -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):

View File

@ -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,
)

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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();

View File

@ -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)

View File

@ -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();
};

View File

@ -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;
}
};
/** \} */

View File

@ -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();
}

View File

@ -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,

View File

@ -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);

View File

@ -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:

View File

@ -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,

View File

@ -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;

View File

@ -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) ?

View File

@ -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();
};
/** \} */

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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
}
}

View File

@ -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");
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -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,

View File

@ -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, \

View File

@ -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;

View File

@ -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, \
}
/** \} */

View File

@ -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. */

View File

@ -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",

View File

@ -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);
}