Blender V5.0
kernel/light/background.h
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#pragma once
6
7#include "kernel/globals.h"
8
10
11#include "kernel/light/area.h"
12#include "kernel/light/common.h"
13
14#include "util/math_intersect.h"
15
17
18/* Background Light */
19
21 const float2 rand,
22 ccl_private float *pdf)
23{
24 /* for the following, the CDF values are actually a pair of floats, with the
25 * function value as X and the actual CDF as Y. The last entry's function
26 * value is the CDF total. */
27 const int res_x = kernel_data.background.map_res_x;
28 const int res_y = kernel_data.background.map_res_y;
29 const int cdf_width = res_x + 1;
30
31 /* This is basically std::lower_bound as used by PBRT. */
32 int first = 0;
33 int count = res_y;
34
35 while (count > 0) {
36 const int step = count >> 1;
37 const int middle = first + step;
38
39 if (kernel_data_fetch(light_background_marginal_cdf, middle).y < rand.y) {
40 first = middle + 1;
41 count -= step + 1;
42 }
43 else {
44 count = step;
45 }
46 }
47
48 const int index_v = max(0, first - 1);
49 kernel_assert(index_v >= 0 && index_v < res_y);
50
51 const float2 cdf_v = kernel_data_fetch(light_background_marginal_cdf, index_v);
52 const float2 cdf_next_v = kernel_data_fetch(light_background_marginal_cdf, index_v + 1);
53 const float2 cdf_last_v = kernel_data_fetch(light_background_marginal_cdf, res_y);
54
55 /* importance-sampled V direction */
56 const float dv = inverse_lerp(cdf_v.y, cdf_next_v.y, rand.y);
57 const float v = (index_v + dv) / res_y;
58
59 /* This is basically std::lower_bound as used by PBRT. */
60 first = 0;
61 count = res_x;
62 while (count > 0) {
63 const int step = count >> 1;
64 const int middle = first + step;
65
66 if (kernel_data_fetch(light_background_conditional_cdf, index_v * cdf_width + middle).y <
67 rand.x)
68 {
69 first = middle + 1;
70 count -= step + 1;
71 }
72 else {
73 count = step;
74 }
75 }
76
77 const int index_u = max(0, first - 1);
78 kernel_assert(index_u >= 0 && index_u < res_x);
79
80 const float2 cdf_u = kernel_data_fetch(light_background_conditional_cdf,
81 index_v * cdf_width + index_u);
82 const float2 cdf_next_u = kernel_data_fetch(light_background_conditional_cdf,
83 index_v * cdf_width + index_u + 1);
84 const float2 cdf_last_u = kernel_data_fetch(light_background_conditional_cdf,
85 index_v * cdf_width + res_x);
86
87 /* importance-sampled U direction */
88 const float du = inverse_lerp(cdf_u.y, cdf_next_u.y, rand.x);
89 const float u = (index_u + du) / res_x;
90
91 /* compute pdf */
92 const float sin_theta = sinf(M_PI_F * v);
93 const float denom = (M_2PI_F * M_PI_F * sin_theta) * cdf_last_u.x * cdf_last_v.x;
94
95 if (sin_theta == 0.0f || denom == 0.0f) {
96 *pdf = 0.0f;
97 }
98 else {
99 *pdf = (cdf_u.x * cdf_v.x) / denom;
100 }
101
102 /* compute direction */
104}
105
106/* TODO(sergey): Same as above, after the release we should consider using
107 * `noinline` for all devices.
108 */
110{
111 const float2 uv = direction_to_equirectangular(direction);
112 const int res_x = kernel_data.background.map_res_x;
113 const int res_y = kernel_data.background.map_res_y;
114 const int cdf_width = res_x + 1;
115
116 const float sin_theta = sinf(uv.y * M_PI_F);
117
118 if (sin_theta == 0.0f) {
119 return 0.0f;
120 }
121
122 const int index_u = clamp(float_to_int(uv.x * res_x), 0, res_x - 1);
123 const int index_v = clamp(float_to_int(uv.y * res_y), 0, res_y - 1);
124
125 /* pdfs in V direction */
126 const float2 cdf_last_u = kernel_data_fetch(light_background_conditional_cdf,
127 index_v * cdf_width + res_x);
128 const float2 cdf_last_v = kernel_data_fetch(light_background_marginal_cdf, res_y);
129
130 const float denom = (M_2PI_F * M_PI_F * sin_theta) * cdf_last_u.x * cdf_last_v.x;
131
132 if (denom == 0.0f) {
133 return 0.0f;
134 }
135
136 /* pdfs in U direction */
137 const float2 cdf_u = kernel_data_fetch(light_background_conditional_cdf,
138 index_v * cdf_width + index_u);
139 const float2 cdf_v = kernel_data_fetch(light_background_marginal_cdf, index_v);
140
141 return (cdf_u.x * cdf_v.x) / denom;
142}
143
145 const float3 P,
146 const int index,
147 ccl_private float3 *lightpos,
148 ccl_private float3 *dir)
149{
150 const int portal = kernel_data.integrator.portal_offset + index;
151 const ccl_global KernelLight *klight = &kernel_data_fetch(lights, portal);
152
153 *lightpos = klight->co;
154 *dir = klight->area.dir;
155
156 /* Check whether portal is on the right side. */
157 if (dot(*dir, P - *lightpos) > 1e-4f) {
158 return true;
159 }
160
161 return false;
162}
163
165 const float3 P,
166 float3 direction,
167 const int ignore_portal,
168 ccl_private bool *is_possible)
169{
170 float portal_pdf = 0.0f;
171
172 int num_possible = 0;
173 for (int p = 0; p < kernel_data.integrator.num_portals; p++) {
174 if (p == ignore_portal) {
175 continue;
176 }
177
178 float3 lightpos;
179 float3 dir;
180 if (!background_portal_data_fetch_and_check_side(kg, P, p, &lightpos, &dir)) {
181 continue;
182 }
183
184 /* There's a portal that could be sampled from this position. */
185 if (is_possible) {
186 *is_possible = true;
187 }
188 num_possible++;
189
190 const int portal = kernel_data.integrator.portal_offset + p;
191 const ccl_global KernelLight *klight = &kernel_data_fetch(lights, portal);
192
193 const float3 axis_u = klight->area.axis_u;
194 const float len_u = klight->area.len_u;
195 const float3 axis_v = klight->area.axis_v;
196 const float len_v = klight->area.len_v;
197 const float3 inv_extent_u = axis_u / len_u;
198 const float3 inv_extent_v = axis_v / len_v;
199
200 const bool is_round = (klight->area.invarea < 0.0f);
201
203 direction,
204 1e-4f,
205 FLT_MAX,
206 lightpos,
207 inv_extent_u,
208 inv_extent_v,
209 dir,
210 nullptr,
211 nullptr,
212 nullptr,
213 nullptr,
214 is_round))
215 {
216 continue;
217 }
218
219 if (is_round) {
220 float t;
221 const float3 D = normalize_len(lightpos - P, &t);
222 portal_pdf += fabsf(klight->area.invarea) * light_pdf_area_to_solid_angle(dir, -D, t);
223 }
224 else {
225 portal_pdf += area_light_rect_sample(
226 P, &lightpos, axis_u, len_u, axis_v, len_v, zero_float2(), false);
227 }
228 }
229
230 if (ignore_portal >= 0) {
231 /* We have skipped a portal that could be sampled as well. */
232 num_possible++;
233 }
234
235 return (num_possible > 0) ? portal_pdf / num_possible : 0.0f;
236}
237
239{
240 int num_possible_portals = 0;
241 for (int p = 0; p < kernel_data.integrator.num_portals; p++) {
242 float3 lightpos;
243 float3 dir;
244 if (background_portal_data_fetch_and_check_side(kg, P, p, &lightpos, &dir)) {
245 num_possible_portals++;
246 }
247 }
248 return num_possible_portals;
249}
250
252 const float3 P,
253 float2 rand,
254 const int num_possible,
255 ccl_private int *sampled_portal,
256 ccl_private float *pdf)
257{
258 /* Pick a portal, then re-normalize rand.y. */
259 rand.y *= num_possible;
260 int portal = (int)rand.y;
261 rand.y -= portal;
262
263 /* TODO(sergey): Some smarter way of finding portal to sample
264 * is welcome.
265 */
266 for (int p = 0; p < kernel_data.integrator.num_portals; p++) {
267 /* Search for the sampled portal. */
268 float3 lightpos;
269 float3 dir;
270 if (!background_portal_data_fetch_and_check_side(kg, P, p, &lightpos, &dir)) {
271 continue;
272 }
273
274 if (portal == 0) {
275 /* p is the portal to be sampled. */
276 const int portal = kernel_data.integrator.portal_offset + p;
277 const ccl_global KernelLight *klight = &kernel_data_fetch(lights, portal);
278 const float3 axis_u = klight->area.axis_u;
279 const float3 axis_v = klight->area.axis_v;
280 const float len_u = klight->area.len_u;
281 const float len_v = klight->area.len_v;
282 const bool is_round = (klight->area.invarea < 0.0f);
283
284 float3 D;
285 if (is_round) {
286 lightpos += ellipse_sample(axis_u * len_u * 0.5f, axis_v * len_v * 0.5f, rand);
287 float t;
288 D = normalize_len(lightpos - P, &t);
289 *pdf = fabsf(klight->area.invarea) * light_pdf_area_to_solid_angle(dir, -D, t);
290 }
291 else {
292 *pdf = area_light_rect_sample(P, &lightpos, axis_u, len_u, axis_v, len_v, rand, true);
293 D = normalize(lightpos - P);
294 }
295
296 *pdf /= num_possible;
297 *sampled_portal = p;
298 return D;
299 }
300
301 portal--;
302 }
303
304 return zero_float3();
305}
306
308 const float2 rand,
309 ccl_private float *pdf)
310{
311 const float3 N = make_float3(kernel_data.background.sun);
312 const float angle = kernel_data.background.sun.w;
313 float unused;
314 return sample_uniform_cone(N, one_minus_cos(angle), rand, &unused, pdf);
315}
316
318{
319 const float3 N = make_float3(kernel_data.background.sun);
320 const float angle = kernel_data.background.sun.w;
321 return pdf_uniform_cone(N, D, angle);
322}
323
325 const float3 P,
326 float2 rand,
327 ccl_private float *pdf)
328{
329 float portal_method_pdf = kernel_data.background.portal_weight;
330 float sun_method_pdf = kernel_data.background.sun_weight;
331 float map_method_pdf = kernel_data.background.map_weight;
332
333 int num_portals = 0;
334 if (portal_method_pdf > 0.0f) {
335 /* Check if there are portals in the scene which we can sample. */
336 num_portals = background_num_possible_portals(kg, P);
337 if (num_portals == 0) {
338 portal_method_pdf = 0.0f;
339 }
340 }
341
342 float pdf_fac = (portal_method_pdf + sun_method_pdf + map_method_pdf);
343 if (pdf_fac == 0.0f) {
344 /* Use uniform as a fallback if we can't use any strategy. */
345 *pdf = 1.0f / M_4PI_F;
346 return sample_uniform_sphere(rand);
347 }
348
349 pdf_fac = 1.0f / pdf_fac;
350 portal_method_pdf *= pdf_fac;
351 sun_method_pdf *= pdf_fac;
352 map_method_pdf *= pdf_fac;
353
354 /* We have 100% in total and split it between the three categories.
355 * Therefore, we pick portals if rand.x is between 0 and portal_method_pdf,
356 * sun if rand.x is between portal_method_pdf and (portal_method_pdf + sun_method_pdf)
357 * and map if rand.x is between (portal_method_pdf + sun_method_pdf) and 1. */
358 const float sun_method_cdf = portal_method_pdf + sun_method_pdf;
359
360 int method = 0;
361 float3 D;
362 if (rand.x < portal_method_pdf) {
363 method = 0;
364 /* Rescale rand.x. */
365 if (portal_method_pdf != 1.0f) {
366 rand.x /= portal_method_pdf;
367 }
368
369 /* Sample a portal. */
370 int portal;
371 D = background_portal_sample(kg, P, rand, num_portals, &portal, pdf);
372 if (num_portals > 1) {
373 /* Ignore the chosen portal, its pdf is already included. */
374 *pdf += background_portal_pdf(kg, P, D, portal, nullptr);
375 }
376
377 /* Skip MIS if this is the only method. */
378 if (portal_method_pdf == 1.0f) {
379 return D;
380 }
381 *pdf *= portal_method_pdf;
382 }
383 else if (rand.x < sun_method_cdf) {
384 method = 1;
385 /* Rescale rand.x. */
386 if (sun_method_pdf != 1.0f) {
387 rand.x = (rand.x - portal_method_pdf) / sun_method_pdf;
388 }
389
390 D = background_sun_sample(kg, rand, pdf);
391
392 /* Skip MIS if this is the only method. */
393 if (sun_method_pdf == 1.0f) {
394 return D;
395 }
396 *pdf *= sun_method_pdf;
397 }
398 else {
399 method = 2;
400 /* Rescale rand.x. */
401 if (map_method_pdf != 1.0f) {
402 rand.x = (rand.x - sun_method_cdf) / map_method_pdf;
403 }
404
405 D = background_map_sample(kg, rand, pdf);
406
407 /* Skip MIS if this is the only method. */
408 if (map_method_pdf == 1.0f) {
409 return D;
410 }
411 *pdf *= map_method_pdf;
412 }
413
414 /* MIS weighting. */
415 if (method != 0 && portal_method_pdf != 0.0f) {
416 *pdf += portal_method_pdf * background_portal_pdf(kg, P, D, -1, nullptr);
417 }
418 if (method != 1 && sun_method_pdf != 0.0f) {
419 *pdf += sun_method_pdf * background_sun_pdf(kg, D);
420 }
421 if (method != 2 && map_method_pdf != 0.0f) {
422 *pdf += map_method_pdf * background_map_pdf(kg, D);
423 }
424 return D;
425}
426
428{
429 float portal_method_pdf = kernel_data.background.portal_weight;
430 float sun_method_pdf = kernel_data.background.sun_weight;
431 float map_method_pdf = kernel_data.background.map_weight;
432
433 float portal_pdf = 0.0f;
434 /* Portals are a special case here since we need to compute their pdf in order
435 * to find out if we can sample them. */
436 if (portal_method_pdf > 0.0f) {
437 /* Evaluate PDF of sampling this direction by portal sampling. */
438 bool is_possible = false;
439 portal_pdf = background_portal_pdf(kg, P, direction, -1, &is_possible);
440 if (!is_possible) {
441 /* Portal sampling is not possible here because all portals point to the wrong side.
442 * If other methods can be used instead, do so, otherwise uniform sampling is used as a
443 * fallback. */
444 portal_method_pdf = 0.0f;
445 }
446 }
447
448 float pdf_fac = (portal_method_pdf + sun_method_pdf + map_method_pdf);
449 if (pdf_fac == 0.0f) {
450 /* Use uniform as a fallback if we can't use any strategy. */
451 return 1.0f / M_4PI_F;
452 }
453
454 pdf_fac = 1.0f / pdf_fac;
455 portal_method_pdf *= pdf_fac;
456 sun_method_pdf *= pdf_fac;
457 map_method_pdf *= pdf_fac;
458
459 float pdf = portal_pdf * portal_method_pdf;
460 if (sun_method_pdf != 0.0f) {
461 pdf += background_sun_pdf(kg, direction) * sun_method_pdf;
462 }
463 if (map_method_pdf != 0.0f) {
464 pdf += background_map_pdf(kg, direction) * map_method_pdf;
465 }
466
467 return pdf;
468}
469
470template<bool in_volume_segment>
472 const float t,
473 ccl_private float &cos_theta_u,
475 ccl_private float3 &point_to_centroid,
476 ccl_private float &theta_d)
477{
478 if (in_volume_segment) {
479 if (t == FLT_MAX) {
480 /* In world volumes, distant lights can contribute to the lighting of the volume with
481 * specific configurations of procedurally generated volumes. Use a ray length of 1.0 in this
482 * case to give the distant light some weight, but one that isn't too high for a typical
483 * world volume use case. */
484 theta_d = 1.0f;
485 }
486 else {
487 theta_d = t;
488 }
489 }
490
491 /* Cover the whole sphere */
492 cos_theta_u = -1.0f;
493
494 distance = make_float2(1.0f, 1.0f);
495 point_to_centroid = -centroid;
496
497 return true;
498}
499
#define D
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
CCL_NAMESPACE_BEGIN ccl_device_inline float area_light_rect_sample(const float3 P, ccl_private float3 *light_p, const float3 axis_u, const float len_u, const float3 axis_v, const float len_v, const float2 rand, bool sample_coord)
Definition area.h:19
ATTR_WARN_UNUSED_RESULT const BMVert * v
ccl_device_inline float sin_theta(const float3 w)
dot(value.rgb, luminance_coefficients)") DEFINE_VALUE("REDUCE(lhs
ccl_device float2 direction_to_equirectangular(const float3 dir)
ccl_device float3 equirectangular_to_direction(const float u, const float v)
#define kernel_assert(cond)
#define kernel_data
#define ccl_device_forceinline
#define kernel_data_fetch(name, index)
#define ccl_private
const ThreadKernelGlobalsCPU * KernelGlobals
#define ccl_device_inline
#define M_4PI_F
#define ccl_global
#define CCL_NAMESPACE_END
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
VecBase< float, D > normalize(VecOp< float, D >) RET
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
constexpr T clamp(T, U, U) RET
float distance(VecOp< float, D >, VecOp< float, D >) RET
int count
CCL_NAMESPACE_BEGIN ccl_device float3 background_map_sample(KernelGlobals kg, const float2 rand, ccl_private float *pdf)
ccl_device_inline float3 background_sun_sample(KernelGlobals kg, const float2 rand, ccl_private float *pdf)
ccl_device_inline float background_sun_pdf(KernelGlobals kg, const float3 D)
ccl_device_forceinline bool background_light_tree_parameters(const float3 centroid, const float t, ccl_private float &cos_theta_u, ccl_private float2 &distance, ccl_private float3 &point_to_centroid, ccl_private float &theta_d)
ccl_device int background_num_possible_portals(KernelGlobals kg, const float3 P)
ccl_device_inline float background_portal_pdf(KernelGlobals kg, const float3 P, float3 direction, const int ignore_portal, ccl_private bool *is_possible)
ccl_device float background_light_pdf(KernelGlobals kg, const float3 P, float3 direction)
ccl_device_inline bool background_portal_data_fetch_and_check_side(KernelGlobals kg, const float3 P, const int index, ccl_private float3 *lightpos, ccl_private float3 *dir)
ccl_device float background_map_pdf(KernelGlobals kg, const float3 direction)
ccl_device float3 background_portal_sample(KernelGlobals kg, const float3 P, float2 rand, const int num_possible, ccl_private int *sampled_portal, ccl_private float *pdf)
ccl_device_inline float3 background_light_sample(KernelGlobals kg, const float3 P, float2 rand, ccl_private float *pdf)
ccl_device_inline float3 ellipse_sample(const float3 ru, const float3 rv, const float2 rand)
ccl_device float light_pdf_area_to_solid_angle(const float3 Ng, const float3 I, const float t)
ccl_device_inline float inverse_lerp(const float a, const float b, const float x)
Definition math_base.h:507
ccl_device_inline int float_to_int(const float f)
Definition math_base.h:407
ccl_device_inline float one_minus_cos(const float angle)
Definition math_base.h:625
CCL_NAMESPACE_BEGIN ccl_device_inline float2 zero_float2()
Definition math_float2.h:13
ccl_device_inline float2 normalize_len(const float2 a, ccl_private float *t)
CCL_NAMESPACE_BEGIN ccl_device_inline float3 zero_float3()
Definition math_float3.h:17
ccl_device bool ray_quad_intersect(const float3 ray_P, const float3 ray_D, const float ray_tmin, const float ray_tmax, const float3 quad_P, const float3 inv_quad_u, const float3 inv_quad_v, const float3 quad_n, ccl_private float3 *isect_P, ccl_private float *isect_t, ccl_private float *isect_u, ccl_private float *isect_v, bool ellipse)
#define N
#define fabsf
#define ccl_device
#define M_2PI_F
#define sinf
#define make_float2
#define M_PI_F
ccl_device_inline float pdf_uniform_cone(const float3 N, const float3 D, const float angle)
ccl_device float3 sample_uniform_sphere(const float2 rand)
ccl_device_inline float3 sample_uniform_cone(const float3 N, const float one_minus_cos_angle, const float2 rand, ccl_private float *cos_theta, ccl_private float *pdf)
#define FLT_MAX
Definition stdcycles.h:14
float x
float y
max
Definition text_draw.cc:251