Blender V5.0
eevee_lightprobe.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
12
14
15#include "eevee_instance.hh"
16#include "eevee_lightprobe.hh"
17
18#include <iostream>
19
20namespace blender::eevee {
21
22/* -------------------------------------------------------------------- */
25
27{
28 /* Initialize the world probe. */
29 world_sphere_.clipping_distances = float2(1.0f, 10.0f);
30 world_sphere_.world_to_probe_transposed = float3x4::identity();
31 world_sphere_.influence_shape = SHAPE_ELIPSOID;
32 world_sphere_.parallax_shape = SHAPE_ELIPSOID;
33 /* Full influence. */
34 world_sphere_.influence_scale = 0.0f;
35 world_sphere_.influence_bias = 1.0f;
36 world_sphere_.parallax_distance = 1e10f;
37 /* In any case, the world must always be up to valid and used for render. */
38 world_sphere_.use_for_render = true;
39}
40
42{
43 switch (resolution) {
44 case 128:
46 case 256:
48 case 512:
50 case 1024:
52 case 2048:
54 case 4096:
56 }
59}
60
62{
63 const SceneEEVEE &sce_eevee = inst_.scene->eevee;
64 sphere_object_resolution_ = resolution_to_probe_resolution_enum(sce_eevee.gi_cubemap_resolution);
65}
66
68{
69 auto_bake_enabled_ = inst_.is_viewport() &&
70 (inst_.scene->eevee.flag & SCE_EEVEE_GI_AUTOBAKE) != 0;
71}
72
73void LightProbeModule::sync_volume(const Object *ob, ObjectHandle &handle)
74{
75 VolumeProbe &grid = volume_map_.lookup_or_add_default(handle.object_key);
76 grid.used = true;
77 if (handle.recalc != 0 || grid.initialized == false) {
78 const ::LightProbe &lightprobe = DRW_object_get_data_for_drawing<const ::LightProbe>(*ob);
79
80 grid.initialized = true;
81 grid.updated = true;
82 grid.surfel_density = lightprobe.grid_surfel_density;
83 grid.object_to_world = ob->object_to_world();
84 grid.cache = ob->lightprobe_cache;
85
88
89 grid.normal_bias = lightprobe.grid_normal_bias;
90 grid.view_bias = lightprobe.grid_view_bias;
91 grid.facing_bias = lightprobe.grid_facing_bias;
92
93 grid.validity_threshold = lightprobe.grid_validity_threshold;
94 grid.dilation_threshold = lightprobe.grid_dilation_threshold;
95 grid.dilation_radius = lightprobe.grid_dilation_radius;
96 grid.intensity = lightprobe.intensity;
97
98 const bool has_valid_cache = grid.cache && grid.cache->grid_static_cache;
99 grid.viewport_display = has_valid_cache && (lightprobe.flag & LIGHTPROBE_FLAG_SHOW_DATA);
100 if (grid.viewport_display) {
101 int3 cache_size = grid.cache->grid_static_cache->size;
102 float3 scale = math::transform_direction(ob->object_to_world(),
103 1.0f / float3(cache_size + 1));
104 grid.viewport_display_size = math::reduce_min(scale) * lightprobe.data_display_size;
105 }
106
107 /* Force reupload. */
108 inst_.volume_probes.bricks_free(grid.bricks);
109 }
110}
111
112void LightProbeModule::sync_sphere(const Object *ob, ObjectHandle &handle)
113{
114 SphereProbe &cube = sphere_map_.lookup_or_add_default(handle.object_key);
115 cube.used = true;
116 if (handle.recalc != 0 || cube.initialized == false) {
117 const ::LightProbe &light_probe = DRW_object_get_data_for_drawing<::LightProbe>(*ob);
118
119 cube.initialized = true;
120 cube.updated = true;
121 cube.do_render = true;
122
123 SphereProbeModule &probe_module = inst_.sphere_probes;
124 eLightProbeResolution probe_resolution = sphere_object_resolution_;
125 int subdivision_lvl = probe_module.subdivision_level_get(probe_resolution);
126
127 if (cube.atlas_coord.subdivision_lvl != subdivision_lvl) {
128 cube.atlas_coord.free();
129 cube.atlas_coord = find_empty_atlas_region(subdivision_lvl);
130 SphereProbeData &cube_data = *static_cast<SphereProbeData *>(&cube);
131 /* Update gpu data sampling coordinates. */
132 cube_data.atlas_coord = cube.atlas_coord.as_sampling_coord();
133 /* Coordinates have changed. Area might contain random data. Do not use for rendering. */
134 cube.use_for_render = false;
135 }
136
137 bool use_custom_parallax = (light_probe.flag & LIGHTPROBE_FLAG_CUSTOM_PARALLAX) != 0;
138 float influence_distance = light_probe.distinf;
139 float influence_falloff = light_probe.falloff;
140 float parallax_distance = light_probe.distpar;
141 parallax_distance = use_custom_parallax ? max_ff(parallax_distance, influence_distance) :
142 influence_distance;
143
144 auto to_eevee_shape = [](int bl_shape_type) {
145 return (bl_shape_type == LIGHTPROBE_SHAPE_BOX) ? SHAPE_CUBOID : SHAPE_ELIPSOID;
146 };
147 cube.influence_shape = to_eevee_shape(light_probe.attenuation_type);
148 cube.parallax_shape = to_eevee_shape(use_custom_parallax ? light_probe.parallax_type :
149 light_probe.attenuation_type);
150
151 float4x4 object_to_world = math::scale(ob->object_to_world(), float3(influence_distance));
152 cube.location = object_to_world.location();
153 cube.volume = math::abs(math::determinant(object_to_world));
154 cube.world_to_probe_transposed = float3x4(math::transpose(math::invert(object_to_world)));
155 cube.influence_scale = 1.0 / max_ff(1e-8f, influence_falloff);
156 cube.influence_bias = cube.influence_scale;
157 cube.parallax_distance = parallax_distance / influence_distance;
158 cube.clipping_distances = float2(light_probe.clipsta, light_probe.clipend);
159
160 float3 scale = influence_distance * math::to_scale(ob->object_to_world());
161 cube.viewport_display = light_probe.flag & LIGHTPROBE_FLAG_SHOW_DATA;
162 cube.viewport_display_size = light_probe.data_display_size * math::reduce_add(scale / 3.0f);
163 }
164}
165
166void LightProbeModule::sync_planar(const Object *ob, ObjectHandle &handle)
167{
168 PlanarProbe &plane = planar_map_.lookup_or_add_default(handle.object_key);
169 plane.used = true;
170 if (handle.recalc != 0 || plane.initialized == false) {
171 const ::LightProbe &light_probe = DRW_object_get_data_for_drawing<::LightProbe>(*ob);
172
173 plane.initialized = true;
174 plane.updated = true;
175 plane.plane_to_world = ob->object_to_world();
176 plane.plane_to_world.z_axis() = math::normalize(plane.plane_to_world.z_axis()) *
177 light_probe.distinf;
178 plane.world_to_plane = math::invert(plane.plane_to_world);
179 plane.clipping_offset = light_probe.clipsta;
180 plane.viewport_display = (light_probe.flag & LIGHTPROBE_FLAG_SHOW_DATA) != 0;
181 }
182}
183
185{
186 const ::LightProbe &lightprobe = DRW_object_get_data_for_drawing<const ::LightProbe>(*ob);
187 switch (lightprobe.type) {
189 sync_sphere(ob, handle);
190 return;
192 sync_planar(ob, handle);
193 return;
195 sync_volume(ob, handle);
196 return;
197 }
199}
200
201void LightProbeModule::sync_world(const ::World *world, bool has_update)
202{
203 const eLightProbeResolution probe_resolution = static_cast<eLightProbeResolution>(
204 world->probe_resolution);
205
206 SphereProbeModule &sph_module = inst_.sphere_probes;
207 int subdivision_lvl = sph_module.subdivision_level_get(probe_resolution);
208
209 if (subdivision_lvl != world_sphere_.atlas_coord.subdivision_lvl) {
210 world_sphere_.atlas_coord.free();
211 world_sphere_.atlas_coord = find_empty_atlas_region(subdivision_lvl);
212 SphereProbeData &world_data = *static_cast<SphereProbeData *>(&world_sphere_);
213 world_data.atlas_coord = world_sphere_.atlas_coord.as_sampling_coord();
214 has_update = true;
215 }
216
217 if (has_update) {
218 world_sphere_.do_render = true;
219 }
220}
221
223{
224 /* Check for deleted or updated grid. */
225 volume_update_ = false;
226 volume_map_.remove_if([&](const Map<ObjectKey, VolumeProbe>::MutableItem &item) {
227 VolumeProbe &grid = item.value;
228 bool remove_grid = !grid.used;
229 if (grid.updated || remove_grid) {
230 volume_update_ = true;
231 }
232 grid.updated = false;
233 grid.used = false;
234 return remove_grid;
235 });
236
237 /* Check for deleted or updated cube. */
238 sphere_update_ = false;
239 sphere_map_.remove_if([&](const Map<ObjectKey, SphereProbe>::MutableItem &item) {
240 SphereProbe &cube = item.value;
241 bool remove_cube = !cube.used;
242 if (cube.updated || remove_cube) {
243 sphere_update_ = true;
244 }
245 cube.updated = false;
246 cube.used = false;
247 return remove_cube;
248 });
249
250 /* Check for deleted or updated plane. */
251 planar_update_ = false;
252 planar_map_.remove_if([&](const Map<ObjectKey, PlanarProbe>::MutableItem &item) {
253 PlanarProbe &plane = item.value;
254 bool remove_plane = !plane.used;
255 if (plane.updated || remove_plane) {
256 planar_update_ = true;
257 }
258 plane.updated = false;
259 plane.used = false;
260 return remove_plane;
261 });
262}
263
264SphereProbeAtlasCoord LightProbeModule::find_empty_atlas_region(int subdivision_level) const
265{
266 int layer_count = sphere_layer_count();
267 SphereProbeAtlasCoord::LocationFinder location_finder(layer_count, subdivision_level);
268
269 location_finder.mark_space_used(world_sphere_.atlas_coord);
270 for (const SphereProbe &probe : sphere_map_.values()) {
271 location_finder.mark_space_used(probe.atlas_coord);
272 }
273 return location_finder.first_free_spot();
274}
275
276int LightProbeModule::sphere_layer_count() const
277{
278 int max_layer = world_sphere_.atlas_coord.atlas_layer;
279 for (const SphereProbe &probe : sphere_map_.values()) {
280 max_layer = max_ii(max_layer, probe.atlas_coord.atlas_layer);
281 }
282 int layer_count = max_layer + 1;
283 return layer_count;
284}
285
287
288/* -------------------------------------------------------------------- */
291
293 int subdivision_level)
294{
295 subdivision_level_ = subdivision_level;
296 areas_per_dimension_ = 1 << subdivision_level_;
297 areas_per_layer_ = square_i(areas_per_dimension_);
298 /* Always add an additional layer to make sure that there is always a free area.
299 * If this area is chosen the atlas will grow. */
300 int area_len = (allocated_layer_count + 1) * areas_per_layer_;
301 areas_occupancy_.resize(area_len, false);
302}
303
305{
306 if (coord.atlas_layer == -1) {
307 /* Coordinate not allocated yet. */
308 return;
309 }
310 /* The input probe data can be stored in a different subdivision level and should tag all areas
311 * of the target subdivision level. Shift right if subdivision is higher, left if lower. */
312 const int shift_right = max_ii(coord.subdivision_lvl - subdivision_level_, 0);
313 const int shift_left = max_ii(subdivision_level_ - coord.subdivision_lvl, 0);
314 const int2 pos_in_location_finder = (coord.area_location() >> shift_right) << shift_left;
315 /* Tag all areas this probe overlaps. */
316 const int layer_offset = coord.atlas_layer * areas_per_layer_;
317 const int areas_overlapped_per_dim = 1 << shift_left;
318 for (const int y : IndexRange(areas_overlapped_per_dim)) {
319 for (const int x : IndexRange(areas_overlapped_per_dim)) {
320 const int2 pos = pos_in_location_finder + int2(x, y);
321 const int area_index = pos.x + pos.y * areas_per_dimension_;
322 areas_occupancy_[area_index + layer_offset].set();
323 }
324 }
325}
326
328{
330 result.subdivision_lvl = subdivision_level_;
331 for (int index : areas_occupancy_.index_range()) {
332 if (!areas_occupancy_[index]) {
333 result.atlas_layer = index / areas_per_layer_;
334 result.area_index = index % areas_per_layer_;
335 return result;
336 }
337 }
338 /* There should always be a free area. See constructor. */
340 return result;
341}
342
344{
345 std::ostream &os = std::cout;
346 int layer = 0, row = 0, column = 0;
347 os << "subdivision " << subdivision_level_ << "\n";
348 for (bool spot_taken : areas_occupancy_) {
349 if (row == 0 && column == 0) {
350 os << "layer " << layer << "\n";
351 }
352 os << (spot_taken ? 'X' : '-');
353 column++;
354 if (column == areas_per_dimension_) {
355 os << "\n";
356 column = 0;
357 row++;
358 }
359 if (row == areas_per_dimension_) {
360 row = 0;
361 layer++;
362 }
363 }
364}
365
367
368} // namespace blender::eevee
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
MINLINE float max_ff(float a, float b)
MINLINE int square_i(int a)
MINLINE int max_ii(int a, int b)
@ LIGHTPROBE_FLAG_SHOW_DATA
@ LIGHTPROBE_FLAG_CUSTOM_PARALLAX
@ LIGHTPROBE_SHAPE_BOX
@ LIGHTPROBE_TYPE_PLANE
@ LIGHTPROBE_TYPE_VOLUME
@ LIGHTPROBE_TYPE_SPHERE
struct Object Object
@ SCE_EEVEE_GI_AUTOBAKE
eLightProbeResolution
@ LIGHT_PROBE_RESOLUTION_128
@ LIGHT_PROBE_RESOLUTION_512
@ LIGHT_PROBE_RESOLUTION_4096
@ LIGHT_PROBE_RESOLUTION_256
@ LIGHT_PROBE_RESOLUTION_2048
@ LIGHT_PROBE_RESOLUTION_1024
T & DRW_object_get_data_for_drawing(const Object &object)
MutableMapItem< Key, Value > MutableItem
Definition BLI_map.hh:133
A running instance of the engine.
VolumeProbeModule volume_probes
void sync_world(const ::World *world, bool has_update)
void sync_probe(const Object *ob, ObjectHandle &handle)
void mark_space_used(const SphereProbeAtlasCoord &coord)
LocationFinder(int allocated_layer_count, int subdivision_level)
void bricks_free(Vector< IrradianceBrickPacked > &bricks)
uint pos
static eLightProbeResolution resolution_to_probe_resolution_enum(int resolution)
MatBase< T, NumCol, NumRow > transpose(const MatBase< T, NumRow, NumCol > &mat)
MatBase< T, NumCol, NumRow > scale(const MatBase< T, NumCol, NumRow > &mat, const VectorT &scale)
T reduce_min(const VecBase< T, Size > &a)
CartesianBasis invert(const CartesianBasis &basis)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
VecBase< T, 3 > to_scale(const MatBase< T, NumCol, NumRow > &mat)
T cube(const T &a)
T abs(const T &a)
VecBase< T, 3 > transform_direction(const MatBase< T, 3, 3 > &mat, const VecBase< T, 3 > &direction)
T reduce_add(const VecBase< T, Size > &a)
T determinant(const MatBase< T, Size, Size > &mat)
MatBase< float, 4, 4 > float4x4
MatBase< float, 3, 4 > float3x4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< int32_t, 3 > int3
MatBase< float, 3, 3 > float3x3
VecBase< float, 3 > float3
struct LightProbeGridCacheFrame * grid_static_cache
struct LightProbeObjectCache * lightprobe_cache
SphereProbeAtlasCoord atlas_coord
Vector< IrradianceBrickPacked > bricks
const LightProbeObjectCache * cache