Blender V5.0
shade_volume.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
8
11
18
19#include "kernel/light/light.h"
20#include "kernel/light/sample.h"
21
23
24#include "kernel/sample/lcg.h"
25
27
28#ifdef __VOLUME__
29
30/* Events for probabilistic scattering. */
31
32enum VolumeIntegrateEvent {
33 VOLUME_PATH_SCATTERED = 0,
34 VOLUME_PATH_ATTENUATED = 1,
35 VOLUME_PATH_MISSED = 2
36};
37
38struct VolumeIntegrateResult {
39 /* Throughput and offset for direct light scattering. */
40 bool direct_scatter;
41 Spectrum direct_throughput;
42 float direct_t;
43 ShaderVolumePhases direct_phases;
44# if defined(__PATH_GUIDING__)
45 VolumeSampleMethod direct_sample_method;
46# endif
47
48 /* Throughput and offset for indirect light scattering. */
49 bool indirect_scatter;
50 Spectrum indirect_throughput;
51 float indirect_t;
52 ShaderVolumePhases indirect_phases;
53};
54
55/* We use both volume octree and volume stack, sometimes they disagree on whether a point is inside
56 * a volume or not. We accept small numerical precision issues, above this threshold the volume
57 * stack shall prevail. */
58/* TODO(weizhen): tweak this value. */
59# define OVERLAP_EXP 5e-4f
60/* Restrict the number of steps in case of numerical problems */
61# define VOLUME_MAX_STEPS 1024
62/* Number of mantissa bits of floating-point numbers. */
63# define MANTISSA_BITS 23
64
65/* Volume shader properties
66 *
67 * extinction coefficient = absorption coefficient + scattering coefficient
68 * sigma_t = sigma_a + sigma_s */
69
70struct VolumeShaderCoefficients {
71 Spectrum sigma_t;
72 Spectrum sigma_s;
73 Spectrum emission;
74};
75
76struct EquiangularCoefficients {
77 float3 P;
78 Interval<float> t_range;
79};
80
81/* Evaluate extinction coefficient at `sd->P`. */
82template<const bool shadow, typename IntegratorGenericState>
83ccl_device_inline Spectrum volume_shader_eval_extinction(KernelGlobals kg,
84 const IntegratorGenericState state,
85 ccl_private ShaderData *ccl_restrict sd,
86 uint32_t path_flag)
87{
88 /* Use emission flag to avoid storing phase function. */
89 /* TODO(weizhen): we could add another flag to skip evaluating the emission, but we've run out of
90 * bits for the path flag.*/
91 path_flag |= PATH_RAY_EMISSION;
92
93 volume_shader_eval<shadow>(kg, state, sd, path_flag);
94
95 return (sd->flag & SD_EXTINCTION) ? sd->closure_transparent_extinction : zero_spectrum();
96}
97
98/* Evaluate shader to get absorption, scattering and emission at P. */
99ccl_device_inline bool volume_shader_sample(KernelGlobals kg,
101 ccl_private ShaderData *ccl_restrict sd,
102 ccl_private VolumeShaderCoefficients *coeff)
103{
104 const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
105 volume_shader_eval<false>(kg, state, sd, path_flag);
106
107 if (!(sd->flag & (SD_EXTINCTION | SD_SCATTER | SD_EMISSION))) {
108 return false;
109 }
110
111 coeff->sigma_s = zero_spectrum();
112 coeff->sigma_t = (sd->flag & SD_EXTINCTION) ? sd->closure_transparent_extinction :
114 coeff->emission = (sd->flag & SD_EMISSION) ? sd->closure_emission_background : zero_spectrum();
115
116 if (sd->flag & SD_SCATTER) {
117 for (int i = 0; i < sd->num_closure; i++) {
118 const ccl_private ShaderClosure *sc = &sd->closure[i];
119
120 if (CLOSURE_IS_VOLUME(sc->type)) {
121 coeff->sigma_s += sc->weight;
122 }
123 }
124 }
125
126 return true;
127}
128
129/* -------------------------------------------------------------------- */
145
146struct OctreeTracing {
147 /* Current active leaf node. */
148 ccl_global const KernelOctreeNode *node = nullptr;
149
150 /* Current active ray segment, typically spans from the front face to the back face of the
151 * current leaf node. */
152 Interval<float> t;
153
154 /* Ray origin in octree coordinate space. */
155 packed_float3 ray_P;
156
157 /* Ray direction in octree coordinate space. */
158 packed_float3 ray_D;
159
160 /* Current active position in octree coordinate space. */
161 uint3 current_P;
162
163 /* Object and shader which the octree represents. */
164 VolumeStack entry = {OBJECT_NONE, SHADER_NONE};
165
166 /* Scale of the current active leaf node, relative to the smallest possible size representable by
167 * float. Initialize to the number of float mantissa bits. */
168 uint8_t scale = MANTISSA_BITS;
169 uint8_t next_scale;
170 /* Mark the dimension (x,y,z) to negate the ray so that we find the correct octant. */
171 uint8_t octant_mask;
172
173 /* Whether multiple volumes overlap in the ray segment. */
174 bool no_overlap = false;
175
176 /* Maximum and minimum of the densities in the current segment. */
177 Extrema<float> sigma = 0.0f;
178
179 ccl_device_inline_method OctreeTracing(const float tmin)
180 {
181 /* Initialize t.max to FLT_MAX so that any intersection with the node face is smaller. */
182 t = {tmin, FLT_MAX};
183 }
184
185 enum Dimension { DIM_X = 1U << 0U, DIM_Y = 1U << 1U, DIM_Z = 1U << 2U };
186
187 /* Given ray origin `P` and direction `D` in object space, convert them into octree space
188 * [1.0, 2.0).
189 * Returns false if ray is leaving the octree or octree has degenerate shape. */
190 ccl_device_inline_method bool to_octree_space(ccl_private const float3 &P,
191 ccl_private const float3 &D,
192 const float3 scale,
193 const float3 translation)
194 {
195 if (!isfinite_safe(scale)) {
196 /* Octree with a degenerate shape. */
197 return false;
198 }
199
200 /* Starting point of octree tracing. */
201 float3 local_P = (P + D * t.min) * scale + translation;
202 ray_D = D * scale;
203
204 /* Select octant mask to mirror the coordinate system so that ray direction is negative along
205 * each axis, and adjust `local_P` accordingly. */
206 const auto positive = ray_D > 0.0f;
207 octant_mask = (!!positive.x * DIM_X) | (!!positive.y * DIM_Y) | (!!positive.z * DIM_Z);
208 local_P = select(positive, 3.0f - local_P, local_P);
209
210 /* Clamp to the largest floating-point number smaller than 2.0f, for numerical stability. */
211 local_P = min(local_P, make_float3(1.9999999f));
212 current_P = float3_as_uint3(local_P);
213
214 ray_D = -fabs(ray_D);
215
216 /* Ray origin. */
217 ray_P = local_P - ray_D * t.min;
218
219 /* Returns false if point lies outside of the octree and the ray is leaving the octree. */
220 return all(local_P > 1.0f);
221 }
222
223 /* Find the bounding box min of the node that `current_P` lies in within the current scale. */
224 ccl_device_inline_method float3 floor_pos() const
225 {
226 /* Erase bits lower than scale. */
227 const uint mask = ~0u << scale;
228 return make_float3(__uint_as_float(current_P.x & mask),
229 __uint_as_float(current_P.y & mask),
230 __uint_as_float(current_P.z & mask));
231 }
232
233 /* Find arbitrary position inside the next node.
234 * We use the end of the current segment offsetted by half of the minimal node size in the normal
235 * direction of the last face intersection. */
236 ccl_device_inline_method void find_next_pos(const float3 bbox_min,
237 const float3 t,
238 const float tmax)
239 {
240 constexpr float half_size = 1.0f / (2 << VOLUME_OCTREE_MAX_DEPTH);
241 const uint3 next_P = float3_as_uint3(
242 select(t == tmax, bbox_min - half_size, ray_D * tmax + ray_P));
243
244 /* Find the nearest common ancestor of two positions by checking the shared higher bits. */
245 const uint diff = (current_P.x ^ next_P.x) | (current_P.y ^ next_P.y) |
246 (current_P.z ^ next_P.z);
247
248 current_P = next_P;
249 next_scale = 32u - count_leading_zeros(diff);
250 }
251
252 /* See `ray_aabb_intersect()`. We only need to intersect the 3 back sides because the ray
253 * direction is all negative. */
254 ccl_device_inline_method float ray_voxel_intersect(const float ray_tmax)
255 {
256 const float3 bbox_min = floor_pos();
257
258 /* Distances to the three surfaces. */
259 float3 intersect_t = (bbox_min - ray_P) / ray_D;
260
261 /* Select the smallest element that is larger than `t.min`, to avoid self intersection. */
262 intersect_t = select(intersect_t > t.min, intersect_t, make_float3(FLT_MAX));
263
264 /* The first intersection is given by the smallest t. */
265 const float tmax = reduce_min(intersect_t);
266
267 find_next_pos(bbox_min, intersect_t, tmax);
268
269 return fminf(tmax, ray_tmax);
270 }
271
272 /* Returns the octant of `current_P` in the node at given scale. */
273 ccl_device_inline_method int get_octant() const
274 {
275 const uint8_t x = (current_P.x >> scale) & 1u;
276 const uint8_t y = ((current_P.y >> scale) & 1u) << 1u;
277 const uint8_t z = ((current_P.z >> scale) & 1u) << 2u;
278 return (x | y | z) ^ octant_mask;
279 }
280};
281
282/* Check if an octree node is leaf node. */
283ccl_device_inline bool volume_node_is_leaf(const ccl_global KernelOctreeNode *knode)
284{
285 return knode->first_child == -1;
286}
287
288/* Find the leaf node of the current position, and replace `octree.node` with that node. */
289ccl_device void volume_voxel_get(KernelGlobals kg, ccl_private OctreeTracing &octree)
290{
291 while (!volume_node_is_leaf(octree.node)) {
292 octree.scale -= 1;
293 const int child_index = octree.node->first_child + octree.get_octant();
294 octree.node = &kernel_data_fetch(volume_tree_nodes, child_index);
295 }
296}
297
298/* If there exists a Light Path Node, it could affect the density evaluation at runtime.
299 * Randomly sample a few points on the ray to estimate the extrema. */
300template<const bool shadow, typename IntegratorGenericState>
301ccl_device_noinline Extrema<float> volume_estimate_extrema(KernelGlobals kg,
302 const ccl_private Ray *ccl_restrict ray,
303 ccl_private ShaderData *ccl_restrict sd,
304 const IntegratorGenericState state,
305 const ccl_private RNGState *rng_state,
306 const uint32_t path_flag,
307/* Work around apparent HIP compiler bug. */
308# ifdef __KERNEL_HIP__
309 const ccl_private OctreeTracing &octree
310# else
311 const Interval<float> t,
312 const VolumeStack entry
313# endif
314)
315{
316# ifdef __KERNEL_HIP__
317 const ccl_private Interval<float> &t = octree.t;
318 const ccl_private VolumeStack &entry = octree.entry;
319# endif
320 const bool homogeneous = volume_is_homogeneous(kg, entry);
321 const int samples = homogeneous ? 1 : 4;
322 const float shade_offset = homogeneous ?
323 0.5f :
325 const float step_size = t.length() / float(samples);
326
327 /* Do not allocate closures. */
328 sd->num_closure_left = 0;
329
330 Extrema<float> extrema = {FLT_MAX, -FLT_MAX};
331 for (int i = 0; i < samples; i++) {
332 const float shade_t = t.min + (shade_offset + i) * step_size;
333 sd->P = ray->P + ray->D * shade_t;
334
335 sd->closure_transparent_extinction = zero_float3();
336 sd->closure_emission_background = zero_float3();
337
338 volume_shader_eval_entry<shadow, KERNEL_FEATURE_NODE_MASK_VOLUME>(
339 kg, state, sd, entry, path_flag);
340
341 const float sigma = reduce_max(sd->closure_transparent_extinction);
342 const float emission = reduce_max(sd->closure_emission_background);
343
344 extrema = merge(extrema, fmaxf(sigma, emission));
345 }
346
347 if (!homogeneous) {
348 /* Slightly increase the majorant in case the estimation is not accurate. */
349 extrema.max = fmaxf(0.5f, extrema.max * 1.5f);
350 }
351
352 return extrema;
353}
354
355/* Given an octree node, compute it's extrema.
356 * In most common cases, the extrema are already stored in the node, but if the shader contains
357 * a light path node, we need to evaluate the densities on the fly. */
358template<const bool shadow, typename IntegratorGenericState>
359ccl_device_inline Extrema<float> volume_object_get_extrema(KernelGlobals kg,
360 const ccl_private Ray *ccl_restrict ray,
361 ccl_private ShaderData *ccl_restrict sd,
362 const IntegratorGenericState state,
363 const ccl_private OctreeTracing &octree,
364 const ccl_private RNGState *rng_state,
365 const uint32_t path_flag)
366{
367 const int shader_flag = kernel_data_fetch(shaders, (octree.entry.shader & SHADER_MASK)).flags;
368 if ((path_flag & PATH_RAY_CAMERA) || !(shader_flag & SD_HAS_LIGHT_PATH_NODE)) {
369 /* Use the baked volume density extrema. */
370 return octree.node->sigma * object_volume_density(kg, octree.entry.object);
371 }
372
373# ifdef __KERNEL_HIP__
374 return volume_estimate_extrema<shadow>(kg, ray, sd, state, rng_state, path_flag, octree);
375# else
376 return volume_estimate_extrema<shadow>(
377 kg, ray, sd, state, rng_state, path_flag, octree.t, octree.entry);
378# endif
379}
380
381/* Find the octree root node in the kernel array that corresponds to the volume stack entry. */
382ccl_device_inline const ccl_global KernelOctreeRoot *volume_find_octree_root(
383 KernelGlobals kg, const VolumeStack entry)
384{
385 int root = kernel_data_fetch(volume_tree_root_ids, entry.object);
386 const ccl_global KernelOctreeRoot *kroot = &kernel_data_fetch(volume_tree_roots, root);
387 while ((entry.shader & SHADER_MASK) != kroot->shader) {
388 /* If one object has multiple shaders, we store the index of the last shader, and search
389 * backwards for the octree with the corresponding shader. */
390 kroot = &kernel_data_fetch(volume_tree_roots, --root);
391 }
392 return kroot;
393}
394
395/* Find the current active ray segment.
396 * We might have multiple overlapping octrees, so find the smallest `tmax` of all and store the
397 * information of that octree in `OctreeTracing`.
398 * Meanwhile, accumulate the density of all the leaf nodes that overlap with the active segment. */
399template<const bool shadow, typename IntegratorGenericState>
400ccl_device bool volume_octree_setup(KernelGlobals kg,
401 const ccl_private Ray *ccl_restrict ray,
402 ccl_private ShaderData *ccl_restrict sd,
403 const IntegratorGenericState state,
404 const ccl_private RNGState *rng_state,
405 const uint32_t path_flag,
406 ccl_private OctreeTracing &global)
407{
408 if (global.no_overlap) {
409 /* If the current active octree is already set up. */
410 return !global.t.is_empty();
411 }
412
413 const VolumeStack skip = global.entry;
414
415 int i = 0;
416 for (;; i++) {
417 /* Loop through all the object in the volume stack and find their octrees. */
418 const VolumeStack entry = volume_stack_read<shadow>(state, i);
419
420 if (entry.shader == SHADER_NONE) {
421 break;
422 }
423
424 if (entry.object == skip.object && entry.shader == skip.shader) {
425 continue;
426 }
427
428 const ccl_global KernelOctreeRoot *kroot = volume_find_octree_root(kg, entry);
429
430 OctreeTracing local(global.t.min);
431 local.node = &kernel_data_fetch(volume_tree_nodes, kroot->id);
432 local.entry = entry;
433
434 /* Convert to object space. */
435 float3 local_P = ray->P, local_D = ray->D;
436 if (!(kernel_data_fetch(object_flag, entry.object) & SD_OBJECT_TRANSFORM_APPLIED)) {
438 local_P = transform_point(&itfm, ray->P);
439 local_D = transform_direction(&itfm, ray->D);
440 }
441
442 /* Convert to octree space. */
443 if (local.to_octree_space(local_P, local_D, kroot->scale, kroot->translation)) {
444 volume_voxel_get(kg, local);
445 local.t.max = local.ray_voxel_intersect(ray->tmax);
446 }
447 else {
448 /* Current ray segment lies outside of the octree, usually happens with implicit volume, i.e.
449 * everything behind a surface is considered as volume. */
450 local.t.max = ray->tmax;
451 }
452
453 global.sigma += volume_object_get_extrema<shadow>(
454 kg, ray, sd, state, local, rng_state, path_flag);
455 if (local.t.max <= global.t.max) {
456 /* Replace the current active octree with the one that has the smallest `tmax`. */
457 local.sigma = global.sigma;
458 global = local;
459 }
460 }
461
462 if (i == 1) {
463 global.no_overlap = true;
464 }
465
466 return global.node && !global.t.is_empty();
467}
468
469/* Advance to the next adjacent leaf node and update the active interval. */
470template<const bool shadow, typename IntegratorGenericState>
471ccl_device_inline bool volume_octree_advance(KernelGlobals kg,
472 const ccl_private Ray *ccl_restrict ray,
473 ccl_private ShaderData *ccl_restrict sd,
474 const IntegratorGenericState state,
475 const ccl_private RNGState *rng_state,
476 const uint32_t path_flag,
477 ccl_private OctreeTracing &octree)
478{
479 if (octree.t.max >= ray->tmax) {
480 /* Reached the last segment. */
481 return false;
482 }
483
484 if (octree.next_scale > MANTISSA_BITS) {
485 if (fabsf(octree.t.max - ray->tmax) <= OVERLAP_EXP) {
486 /* This could happen due to numerical issues, when the bounding box overlaps with a
487 * primitive, but different intersections are registered for octree and ray intersection. */
488 return false;
489 }
490
491 /* Outside of the root node, continue tracing using the extrema of the root node. */
492 octree.t = {octree.t.max, ray->tmax};
493 octree.node = &kernel_data_fetch(volume_tree_nodes,
494 volume_find_octree_root(kg, octree.entry)->id);
495 }
496 else {
497 kernel_assert(octree.next_scale > octree.scale);
498
499 /* Fetch the common ancestor of the current and the next leaf nodes. */
500 for (; octree.scale < octree.next_scale; octree.scale++) {
501 kernel_assert(octree.node->parent != -1);
502 octree.node = &kernel_data_fetch(volume_tree_nodes, octree.node->parent);
503 }
504
505 /* Find the current active leaf node. */
506 volume_voxel_get(kg, octree);
507
508 /* Advance to the next segment. */
509 octree.t.min = octree.t.max;
510 octree.t.max = octree.ray_voxel_intersect(ray->tmax);
511 }
512
513 octree.sigma = volume_object_get_extrema<shadow>(
514 kg, ray, sd, state, octree, rng_state, path_flag);
515 return volume_octree_setup<shadow>(kg, ray, sd, state, rng_state, path_flag, octree);
516}
517
519
520/* Volume Shadows
521 *
522 * These functions are used to attenuate shadow rays to lights. Both absorption
523 * and scattering will block light, represented by the extinction coefficient. */
524
525/* Advance until the majorant optical depth is at least one, or we have reached the end of the
526 * volume. Because telescoping has to take at least one sample per segment, having a larger
527 * segment helps to take less samples. */
528ccl_device_inline bool volume_octree_advance_shadow(KernelGlobals kg,
529 const ccl_private Ray *ccl_restrict ray,
530 ccl_private ShaderData *ccl_restrict sd,
532 ccl_private RNGState *rng_state,
533 const uint32_t path_flag,
534 ccl_private OctreeTracing &octree)
535{
536 /* Advance random number offset. */
537 rng_state->rng_offset += PRNG_BOUNCE_NUM;
538
539 Extrema<float> sigma = octree.t.is_empty() ? Extrema<float>(FLT_MAX, -FLT_MAX) : octree.sigma;
540 const float tmin = octree.t.min;
541
542 while (octree.t.is_empty() || sigma.range() * octree.t.length() < 1.0f) {
543 if (!volume_octree_advance<true>(kg, ray, sd, state, rng_state, path_flag, octree)) {
544 return !octree.t.is_empty();
545 }
546
547 octree.sigma = sigma = merge(sigma, octree.sigma);
548 octree.t.min = tmin;
549 }
550
551 return true;
552}
553
554/* Compute transmittance along the ray using
555 * "Unbiased and consistent rendering using biased estimators" by Misso et. al,
556 * https://cs.dartmouth.edu/~wjarosz/publications/misso22unbiased.html
557 *
558 * The telescoping sum is
559 * T = T_k + \sum_{j=k}^\infty(T_{j+1} - T_{j})
560 * where T_k is a biased estimation of the transmittance T by taking k samples,
561 * and (T_{j+1} - T_{j}) is the debiasing term.
562 * We decide the order k based on the optical thickness, and randomly pick a debiasing term of
563 * order j to evaluate.
564 * In the practice we take the powers of 2 to reuse samples for all orders.
565 *
566 * \param sigma_c: the difference between the density majorant and minorant
567 * \param t: the ray segment between which we compute the transmittance
568 */
569template<const bool shadow, typename IntegratorGenericState>
570ccl_device Spectrum volume_transmittance(KernelGlobals kg,
571 IntegratorGenericState state,
572 const ccl_private Ray *ray,
573 ccl_private ShaderData *ccl_restrict sd,
574 const float sigma_c,
575 const Interval<float> t,
576 const ccl_private RNGState *rng_state,
577 const uint32_t path_flag)
578{
579 constexpr float r = 0.9f;
580 const float ray_length = t.length();
581
582 /* Expected number of steps with residual ratio tracking. */
583 const float expected_steps = sigma_c * ray_length;
584 /* Number of samples for the biased estimator. */
585 const int k = clamp(int(roundf(expected_steps)), 1, VOLUME_MAX_STEPS);
586
587 /* Sample the evaluation order of the debiasing term. */
588 /* Use the same random number for all pixels to sync the workload on GPU. */
589 /* TODO(weizhen): need to check if such correlation introduces artefacts. */
590 const float rand = path_rng_1D(
591 kg, 0, rng_state->sample, rng_state->rng_offset + PRNG_VOLUME_EXPANSION_ORDER);
592 /* A hard cut-off to prevent taking too many samples on the GPU. The probability of going beyond
593 * this order is 1e-5f. */
594 constexpr int cut_off = 4;
595 float pmf;
596 /* Number of independent estimators of T_k. */
597 const int N = (sigma_c == 0.0f) ?
598 1 :
599 power_of_2(sample_geometric_distribution(rand, r, pmf, cut_off));
600
601 /* Total number of density evaluations. */
602 const int samples = N * k;
603
604 const float shade_offset = path_state_rng_1D(kg, rng_state, PRNG_VOLUME_SHADE_OFFSET);
605 const float step_size = ray_length / float(samples);
606
607 if (N == 1) {
608 /* Only compute the biased estimator. */
609 Spectrum tau_k = zero_spectrum();
610 for (int i = 0; i < k; i++) {
611 const float shade_t = min(t.max, t.min + (shade_offset + i) * step_size);
612 sd->P = ray->P + ray->D * shade_t;
613 tau_k += volume_shader_eval_extinction<shadow>(kg, state, sd, path_flag);
614 }
615 /* OneAPI has some problem with exp(-0 * FLT_MAX). */
616 return is_zero(tau_k) ? one_spectrum() : exp(-tau_k * step_size);
617 }
618
619 /* Estimations of optical thickness. */
620 Spectrum tau_j[2];
621 tau_j[0] = zero_spectrum();
622 tau_j[1] = zero_spectrum();
623 Spectrum tau_j_1 = zero_spectrum();
624
625 Spectrum T_k = zero_spectrum();
626 for (int n = 0; n < N; n++) {
627 Spectrum tau_k = zero_spectrum();
628 for (int i = 0; i < k; i++) {
629 const int step = i * N + n;
630 const float shade_t = min(t.max, t.min + (shade_offset + step) * step_size);
631 sd->P = ray->P + ray->D * shade_t;
632
633 const Spectrum tau = step_size *
634 volume_shader_eval_extinction<shadow>(kg, state, sd, path_flag);
635
636 tau_k += tau * N;
637 tau_j[step % 2] += tau * 2.0f;
638 tau_j_1 += tau;
639 }
640 T_k += exp(-tau_k);
641 }
642
643 const Spectrum T_j_1 = exp(-tau_j_1);
644
645 /* Eq (16). This is the secondary estimator which averages a few independent estimations. */
646 T_k /= float(N);
647 const Spectrum T_j = 0.5f * (exp(-tau_j[0]) + exp(-tau_j[1]));
648
649 /* Eq (14), single-term primary estimator. */
650 return T_k + (T_j_1 - T_j) / pmf;
651}
652
653/* Compute the volumetric transmittance of the segment [ray->tmin, ray->tmax],
654 * used for the shadow ray throughput. */
655ccl_device void volume_shadow_null_scattering(KernelGlobals kg,
658 ccl_private ShaderData *ccl_restrict sd,
660{
661 /* Load random number state. */
662 RNGState rng_state;
664
665 /* For stochastic texture sampling. */
666 sd->lcg_state = lcg_state_init(
667 rng_state.rng_pixel, rng_state.rng_offset, rng_state.sample, 0xd9111870);
668
669 path_state_rng_scramble(&rng_state, 0x8647ace4);
670
671 OctreeTracing octree(ray->tmin);
672 const uint32_t path_flag = PATH_RAY_SHADOW;
673 if (!volume_octree_setup<true>(kg, ray, sd, state, &rng_state, path_flag, octree)) {
674 return;
675 }
676
677 while (volume_octree_advance_shadow(kg, ray, sd, state, &rng_state, path_flag, octree)) {
678 const float sigma = octree.sigma.range();
679 *throughput *= volume_transmittance<true>(
680 kg, state, ray, sd, sigma, octree.t, &rng_state, path_flag);
681
682 if (reduce_max(fabs(*throughput)) < VOLUME_THROUGHPUT_EPSILON) {
683 return;
684 }
685 octree.t.min = octree.t.max;
686 }
687}
688
689/* Equi-angular sampling as in:
690 * "Importance Sampling Techniques for Path Tracing in Participating Media" */
691
692/* Below this pdf we ignore samples, as they tend to lead to very long distances.
693 * This can cause performance issues with BVH traversal in OptiX, leading it to
694 * traverse many nodes. Since these contribute very little to the image, just ignore
695 * those samples. */
696# define VOLUME_SAMPLE_PDF_CUTOFF 1e-8f
697
698ccl_device float volume_equiangular_sample(const ccl_private Ray *ccl_restrict ray,
699 const ccl_private EquiangularCoefficients &coeffs,
700 const float xi,
701 ccl_private float *pdf)
702{
703 const float delta = dot((coeffs.P - ray->P), ray->D);
704 const float D = len(coeffs.P - ray->P - ray->D * delta);
705 if (UNLIKELY(D == 0.0f)) {
706 *pdf = 0.0f;
707 return 0.0f;
708 }
709 const float tmin = coeffs.t_range.min;
710 const float tmax = coeffs.t_range.max;
711
712 const float theta_a = atan2f(tmin - delta, D);
713 const float theta_b = atan2f(tmax - delta, D);
714 const float theta_d = theta_b - theta_a;
715 if (UNLIKELY(theta_d < 1e-6f)) {
716 /* Use uniform sampling when `theta_d` is too small. */
717 *pdf = safe_divide(1.0f, tmax - tmin);
718 return mix(tmin, tmax, xi);
719 }
720
721 const float t_ = D * tanf((xi * theta_b) + (1 - xi) * theta_a);
722 *pdf = D / (theta_d * (D * D + t_ * t_));
723
724 return clamp(delta + t_, tmin, tmax); /* clamp is only for float precision errors */
725}
726
727ccl_device float volume_equiangular_pdf(const ccl_private Ray *ccl_restrict ray,
728 const ccl_private EquiangularCoefficients &coeffs,
729 const float sample_t)
730{
731 const float delta = dot((coeffs.P - ray->P), ray->D);
732 const float D = len(coeffs.P - ray->P - ray->D * delta);
733 if (UNLIKELY(D == 0.0f)) {
734 return 0.0f;
735 }
736
737 const float tmin = coeffs.t_range.min;
738 const float tmax = coeffs.t_range.max;
739
740 const float theta_a = atan2f(tmin - delta, D);
741 const float theta_b = atan2f(tmax - delta, D);
742 const float theta_d = theta_b - theta_a;
743 if (UNLIKELY(theta_d < 1e-6f)) {
744 return safe_divide(1.0f, tmax - tmin);
745 }
746
747 const float t_ = sample_t - delta;
748 const float pdf = D / (theta_d * (D * D + t_ * t_));
749
750 return pdf;
751}
752
753/* Compute ray segment directly visible to the sampled light. */
754ccl_device_inline bool volume_valid_direct_ray_segment(KernelGlobals kg,
755 const float3 ray_P,
756 const float3 ray_D,
758 const ccl_private LightSample *ls)
759{
760 if (ls->type == LIGHT_SPOT) {
761 const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->prim);
762 return spot_light_valid_ray_segment(kg, klight, ray_P, ray_D, t_range);
763 }
764 if (ls->type == LIGHT_AREA) {
765 const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->prim);
766 return area_light_valid_ray_segment(&klight->area, ray_P - klight->co, ray_D, t_range);
767 }
768 if (ls->type == LIGHT_TRIANGLE) {
769 return triangle_light_valid_ray_segment(kg, ray_P - ls->P, ray_D, t_range, ls);
770 }
771
772 /* Point light or distant light, the whole range of the ray is visible. */
773 kernel_assert(ls->type == LIGHT_POINT || ls->t == FLT_MAX);
774 return !t_range->is_empty();
775}
776
777/* Emission */
778
779ccl_device Spectrum volume_emission_integrate(ccl_private VolumeShaderCoefficients *coeff,
780 const int closure_flag,
781 const float t)
782{
783 /* integral E * exp(-sigma_t * t) from 0 to t = E * (1 - exp(-sigma_t * t))/sigma_t
784 * this goes to E * t as sigma_t goes to zero. */
785 Spectrum emission = coeff->emission;
786
787 if (closure_flag & SD_EXTINCTION) {
788 const Spectrum optical_depth = coeff->sigma_t * t;
789 emission *= select(optical_depth > 1e-5f,
790 (1.0f - exp(-optical_depth)) / coeff->sigma_t,
791 /* Second order Taylor expansion to avoid precision issue. */
792 t * (1.0f - 0.5f * optical_depth));
793 }
794 else {
795 emission *= t;
796 }
797
798 return emission;
799}
800
801/* Volume Integration */
802
803struct VolumeIntegrateState {
804 /* Random number. */
805 float rscatter;
806
807 /* Method used for sampling direct scatter position. */
808 VolumeSampleMethod direct_sample_method;
809 /* Probability of sampling the scatter position using null scattering. */
810 float distance_pdf;
811 /* Probability of sampling the scatter position using equiangular sampling. */
812 float equiangular_pdf;
813 /* Majorant density at the equiangular scatter position. Used to compute the pdf. */
814 float sigma_max;
815
816 /* Ratio tracking estimator of the volume transmittance, with MIS applied. */
817 float transmittance;
818 /* Current sample position. */
819 float t;
820 /* Majorant optical depth until now. */
821 float optical_depth;
822 /* Steps taken while tracking. Should not exceed `VOLUME_MAX_STEPS`. */
824 /* Multiple importance sampling. */
825 bool use_mis;
826
827 /* Volume scattering probability guiding. */
828 bool vspg;
829 /* The guided probability that the ray is scattered in the volume. `P_vol` in the paper. */
830 float scatter_prob;
831 /* Minimal scale of majorant for achieving the desired scatter probability. */
832 float majorant_scale;
833 /* Scale to apply after direct throughput due to Russian Roulette. */
834 float direct_rr_scale;
835
836 /* Extra fields for path guiding and denoising. */
837 PackedSpectrum emission;
838# ifdef __DENOISING_FEATURES__
839 PackedSpectrum albedo;
840# endif
841
842 /* The distance between the current and the last sample position. */
843 float dt;
844 /* `dt` at equiangular scatter position. Used to compute the pdf. */
845 float sample_dt;
846};
847
848/* Accumulate transmittance for equiangular distance sampling without MIS. Using telescoping to
849 * reduce noise. */
850ccl_device_inline void volume_equiangular_transmittance(
851 KernelGlobals kg,
853 const ccl_private Ray *ccl_restrict ray,
854 const ccl_private Extrema<float> &sigma,
855 const ccl_private Interval<float> &interval,
856 ccl_private ShaderData *ccl_restrict sd,
857 const ccl_private RNGState *rng_state,
858 const ccl_private VolumeIntegrateState &ccl_restrict vstate,
859 ccl_private VolumeIntegrateResult &ccl_restrict result)
860{
861 if (vstate.direct_sample_method != VOLUME_SAMPLE_EQUIANGULAR || vstate.use_mis ||
862 result.direct_scatter)
863 {
864 return;
865 }
866
868 if (interval.contains(result.direct_t)) {
869 /* Compute transmittance until the direct scatter position. */
870 t = {interval.min, result.direct_t};
871 result.direct_scatter = true;
872 }
873 else {
874 /* Compute transmittance of the whole segment. */
875 t = interval;
876 }
877
878 const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
879 result.direct_throughput *= volume_transmittance<false>(
880 kg, state, ray, sd, sigma.range(), t, rng_state, path_flag);
881}
882
883/* Sample the next candidate indirect scatter position following exponential distribution,
884 * and compute the direct throughput for equiangular sampling if using MIS.
885 * Returns true if should continue advancing. */
886ccl_device_inline bool volume_indirect_scatter_advance(const ccl_private OctreeTracing &octree,
887 const bool equiangular,
888 ccl_private float &residual_optical_depth,
889 ccl_private VolumeIntegrateState &vstate,
890 ccl_private VolumeIntegrateResult &result)
891{
892 const float sigma_max = octree.sigma.max * vstate.majorant_scale;
893 residual_optical_depth = (octree.t.max - vstate.t) * sigma_max;
894 if (sigma_max == 0.0f) {
895 return true;
896 }
897
898 vstate.dt = sample_exponential_distribution(vstate.rscatter, sigma_max);
899 vstate.t += vstate.dt;
900
901 const bool segment_has_equiangular = equiangular && octree.t.contains(result.direct_t);
902 if (segment_has_equiangular && vstate.t > result.direct_t && !result.direct_scatter) {
903 /* Stepped beyond the equiangular scatter position, compute direct throughput. */
904 result.direct_scatter = true;
905 result.direct_throughput = result.indirect_throughput * vstate.transmittance *
906 vstate.direct_rr_scale;
907 vstate.sample_dt = result.direct_t - vstate.t + vstate.dt;
908 vstate.distance_pdf = vstate.transmittance * sigma_max;
909 vstate.sigma_max = sigma_max;
910 }
911
912 /* Sampled a position outside the current voxel. */
913 return vstate.t > octree.t.max;
914}
915
916/* Adavance to the next candidate indirect scatter position, and compute the direct throughput. */
917ccl_device_inline bool volume_integrate_advance(KernelGlobals kg,
918 const ccl_private Ray *ccl_restrict ray,
919 ccl_private ShaderData *ccl_restrict sd,
921 ccl_private RNGState *rng_state,
922 const uint32_t path_flag,
923 ccl_private OctreeTracing &octree,
924 ccl_private VolumeIntegrateState &vstate,
925 ccl_private VolumeIntegrateResult &result)
926{
927 if (vstate.step++ > VOLUME_MAX_STEPS) {
928 /* Exceeds maximal steps. */
929 return false;
930 }
931
932 float residual_optical_depth;
933 vstate.rscatter = path_state_rng_1D(kg, rng_state, PRNG_VOLUME_SCATTER_DISTANCE);
934 const bool equiangular = (vstate.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR) &&
935 vstate.use_mis;
936
937 while (
938 volume_indirect_scatter_advance(octree, equiangular, residual_optical_depth, vstate, result))
939 {
940 /* Advance to the next voxel if the sampled distance is beyond the current voxel. */
941 if (!volume_octree_advance<false>(kg, ray, sd, state, rng_state, path_flag, octree)) {
942 return false;
943 }
944
945 vstate.optical_depth += octree.sigma.max * octree.t.length();
946 vstate.t = octree.t.min;
947 volume_equiangular_transmittance(
948 kg, state, ray, octree.sigma, octree.t, sd, rng_state, vstate, result);
949
950 /* Scale the random number by the residual depth for reusing. */
951 vstate.rscatter = saturatef(1.0f - (1.0f - vstate.rscatter) * expf(residual_optical_depth));
952 }
953
954 /* Advance random number offset. */
955 rng_state->rng_offset += PRNG_BOUNCE_NUM;
956
957 return true;
958}
959
960/* -------------------------------------------------------------------- */
971
972/* Candidate scatter position for VSPG. */
973struct VolumeSampleCandidate {
974 PackedSpectrum emission;
975 float t;
976 PackedSpectrum throughput;
977 float distance_pdf;
978# ifdef __DENOISING_FEATURES__
979 PackedSpectrum albedo;
980# endif
981 /* Remember the random number so that we sample the sample point for stochastic evaluation. */
982 uint lcg_state;
983};
984
985/* Sample reservoir for VSPG. */
986struct VolumeSampleReservoir {
987 float total_weight = 0.0f;
988 float rand;
989 VolumeSampleCandidate candidate;
990
991 ccl_device_inline_method VolumeSampleReservoir(const float rand_) : rand(rand_) {}
992
993 /* Stream the candidate samples through the reservoir. */
994 ccl_device_inline_method void add_sample(const float weight,
995 const VolumeSampleCandidate new_candidate)
996 {
997 if (!(weight > 0.0f)) {
998 return;
999 }
1000
1001 total_weight += weight;
1002 const float thresh = weight / total_weight;
1003
1004 if ((rand <= thresh) || (total_weight == weight)) {
1005 /* Explicitly select the first candidate in case of numerical issues. */
1006 candidate = new_candidate;
1007 rand /= thresh;
1008 }
1009 else {
1010 rand = (rand - thresh) / (1.0f - thresh);
1011 }
1012
1013 /* Ensure the `rand` is always within 0..1 range, which could be violated above when
1014 * `-ffast-math` is used. */
1015 rand = saturatef(rand);
1016 }
1017
1018 ccl_device_inline_method bool is_empty() const
1019 {
1020 return total_weight == 0.0f;
1021 }
1022};
1023
1024/* Estimate volume majorant optical depth `\sum\sigma_{max}t` along the ray, by accumulating the
1025 * result from previous samples in a render buffer. */
1026ccl_device_inline float volume_majorant_optical_depth(KernelGlobals kg,
1027 const ccl_global float *buffer)
1028{
1029 kernel_assert(kernel_data.film.pass_volume_majorant != PASS_UNUSED);
1030 kernel_assert(kernel_data.film.pass_volume_majorant_sample_count != PASS_UNUSED);
1031
1032 const ccl_global float *accumulated_optical_depth = buffer +
1033 kernel_data.film.pass_volume_majorant;
1034 const ccl_global float *count = buffer + kernel_data.film.pass_volume_majorant_sample_count;
1035
1036 /* Assume `FLT_MAX` when we have no information of the optical depth. */
1037 return (*count == 0.0f) ? FLT_MAX : *accumulated_optical_depth / *count;
1038}
1039
1040/* Compute guided volume scatter probability and the majorant scale needed for achieving the
1041 * scatter probability, for heterogeneous volume. */
1042ccl_device_inline void volume_scatter_probability_heterogeneous(
1043 KernelGlobals kg,
1044 const IntegratorState state,
1046 ccl_private VolumeIntegrateState &vstate)
1047{
1048 if (!vstate.vspg) {
1049 return;
1050 }
1051
1052 const ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer);
1053
1054 kernel_assert(kernel_data.film.pass_volume_scatter_denoised != PASS_UNUSED);
1055 kernel_assert(kernel_data.film.pass_volume_transmit_denoised != PASS_UNUSED);
1056
1057 /* Contribution based criterion, see Eq. (15). */
1058 const float L_scattered = reduce_add(
1059 kernel_read_pass_rgbe(buffer + kernel_data.film.pass_volume_scatter_denoised));
1060 const float L_transmitted = reduce_add(
1061 kernel_read_pass_rgbe(buffer + kernel_data.film.pass_volume_transmit_denoised));
1062 const float L_volume = L_transmitted + L_scattered;
1063
1064 /* Compute guided scattering probability. */
1065 if (L_volume == 0.0f) {
1066 /* Equal probability if no information gathered yet. */
1067 vstate.scatter_prob = 0.5f;
1068 }
1069 else {
1070 /* Exponential distribution has non-zero probability beyond the boundary, so the scatter
1071 * probability can never reach 1. Clamp to avoid scaling the majorant to infinity. */
1072 vstate.scatter_prob = fminf(L_scattered / L_volume, 0.9999f);
1073 }
1074
1075 const float optical_depth = volume_majorant_optical_depth(kg, buffer);
1076
1077 /* There is a non-zero probability of sampling no scatter events in the volume segment. In order
1078 * to reach the desired scattering probability, we might need to upscale the majorant and/or the
1079 * guiding scattering probability. See Eq (25,26). */
1080 vstate.majorant_scale = (optical_depth == 0.0f) ?
1081 1.0f :
1082 -fast_logf(1.0f - vstate.scatter_prob) / optical_depth;
1083 if (vstate.majorant_scale < 1.0f) {
1084 vstate.majorant_scale = 1.0f;
1085 vstate.scatter_prob = safe_divide(vstate.scatter_prob, 1.0f - fast_expf(-optical_depth));
1086 }
1087 else {
1088 vstate.scatter_prob = 1.0f;
1089 }
1090}
1091
1092/* Final guiding decision on sampling scatter or transmit event. */
1093ccl_device_inline void volume_distance_sampling_finalize(
1094 KernelGlobals kg,
1095 const IntegratorState state,
1096 const ccl_private Ray *ccl_restrict ray,
1097 ccl_private ShaderData *ccl_restrict sd,
1098 ccl_private VolumeIntegrateState &ccl_restrict vstate,
1099 ccl_private VolumeIntegrateResult &ccl_restrict result,
1100 ccl_private VolumeSampleReservoir &reservoir)
1101{
1102 if (reservoir.is_empty()) {
1103 return;
1104 }
1105
1106 const bool sample_distance = !(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_TERMINATE) &&
1107 (vstate.direct_sample_method == VOLUME_SAMPLE_DISTANCE);
1108
1109 if (!vstate.vspg) {
1110 result.indirect_throughput = reservoir.candidate.throughput;
1111 vstate.emission = reservoir.candidate.emission;
1112# ifdef __DENOISING_FEATURES__
1113 vstate.albedo = reservoir.candidate.albedo;
1114# endif
1115 result.indirect_t = reservoir.candidate.t;
1116
1117 if (sample_distance) {
1118 /* If using distance sampling for direct light, just copy parameters of indirect light
1119 * since we scatter at the same point. */
1120 result.direct_scatter = true;
1121 result.direct_t = result.indirect_t;
1122 result.direct_throughput = result.indirect_throughput;
1123 if (vstate.use_mis) {
1124 vstate.distance_pdf = reservoir.candidate.distance_pdf;
1125 }
1126 }
1127 return;
1128 }
1129
1130 const uint lcg_state = reservoir.candidate.lcg_state;
1131
1132 if (sample_distance) {
1133 /* Always sample direct scatter, regardless of indirect scatter guiding decision. */
1134 result.direct_throughput = reservoir.candidate.throughput * reservoir.total_weight;
1135 vstate.distance_pdf = reservoir.candidate.distance_pdf;
1136 }
1137
1138 /* We only guide scatter decisions, no need to apply on emission and albedo. */
1139 vstate.emission = mix(vstate.emission, reservoir.candidate.emission, reservoir.total_weight);
1140# ifdef __DENOISING_FEATURES__
1141 vstate.albedo = mix(vstate.albedo, reservoir.candidate.albedo, reservoir.total_weight);
1142# endif
1143
1144 const float unguided_scatter_prob = reservoir.total_weight;
1145 float guided_scatter_prob;
1146 if (is_zero(result.indirect_throughput)) {
1147 /* Always sample scatter event if the contribution of transmitted event is zero. */
1148 guided_scatter_prob = 1.0f;
1149 }
1150 else {
1151 /* Defensive resampling. */
1152 const float alpha = 0.75f;
1153 reservoir.total_weight = mix(reservoir.total_weight, vstate.scatter_prob, alpha);
1154 guided_scatter_prob = reservoir.total_weight;
1155
1156 /* Add transmitted candidate. */
1157 reservoir.add_sample(
1158 1.0f - guided_scatter_prob,
1160 { vstate.emission, reservoir.candidate.t, result.indirect_throughput, 0.0f, vstate.albedo }
1161# else
1162 {vstate.emission, reservoir.candidate.t, result.indirect_throughput, 0.0f}
1163# endif
1164 );
1165 }
1166
1167 const bool scatter = (reservoir.candidate.distance_pdf > 0.0f);
1168 const float scale = scatter ? unguided_scatter_prob / guided_scatter_prob :
1169 (1.0f - unguided_scatter_prob) / (1.0f - guided_scatter_prob);
1170 result.indirect_throughput = reservoir.candidate.throughput * scale;
1171
1172 if (!scatter && !sample_distance) {
1173 /* No scatter event sampled. */
1174 return;
1175 }
1176
1177 /* Recover the volume coefficients at the scatter position. */
1178 sd->P = ray->P + ray->D * reservoir.candidate.t;
1179 sd->lcg_state = lcg_state;
1180 VolumeShaderCoefficients coeff ccl_optional_struct_init;
1181 if (!volume_shader_sample(kg, state, sd, &coeff)) {
1182 kernel_assert(false);
1183 return;
1184 }
1185
1186 kernel_assert(sd->flag & SD_SCATTER);
1187 if (sample_distance) {
1188 /* Direct scatter. */
1189 result.direct_scatter = true;
1190 result.direct_t = reservoir.candidate.t;
1191 volume_shader_copy_phases(&result.direct_phases, sd);
1192 }
1193
1194 if (scatter) {
1195 /* Indirect scatter. */
1196 result.indirect_scatter = true;
1197 result.indirect_t = reservoir.candidate.t;
1198 volume_shader_copy_phases(&result.indirect_phases, sd);
1199 }
1200}
1201
1203
1204ccl_device bool volume_integrate_should_stop(const ccl_private VolumeIntegrateResult &result)
1205{
1206 if (is_zero(result.indirect_throughput) && is_zero(result.direct_throughput)) {
1207 /* Stopped during Russian Roulette. */
1208 return true;
1209 }
1210
1211 /* If we have scattering data for both direct and indirect, we're done. */
1212 return (result.direct_scatter && result.indirect_scatter);
1213}
1214
1215/* Perform Russian Roulette termination to avoid drawing too many samples for indirect scatter, but
1216 * only if both direct and indirect scatter positions are available, or if no scattering is needed.
1217 */
1218ccl_device_inline bool volume_russian_roulette_termination(
1219 const IntegratorState state,
1220 ccl_private VolumeSampleReservoir &reservoir,
1221 ccl_private VolumeIntegrateResult &ccl_restrict result,
1222 ccl_private VolumeIntegrateState &ccl_restrict vstate)
1223{
1224 if (result.direct_scatter && result.indirect_scatter) {
1225 return true;
1226 }
1227
1228 const float thresh = reduce_max(fabs(result.indirect_throughput));
1229 if (thresh > 0.05f) {
1230 /* Only stop if contribution is low enough. */
1231 return false;
1232 }
1233
1234 /* Whether equiangular estimator of the direct throughput depends on the indirect throughput. */
1235 const bool equiangular = (vstate.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR) &&
1236 vstate.use_mis && !result.direct_scatter;
1237 /* Whether both indirect and direct scatter are possible. */
1238 const bool has_scatter_samples = !reservoir.is_empty() && !equiangular;
1239 /* The path is to be terminated, no scatter position is needed along the ray. */
1240 const bool absorption_only = INTEGRATOR_STATE(state, path, flag) & PATH_RAY_TERMINATE;
1241
1242 /* Randomly stop indirect scatter. */
1243 if (absorption_only || has_scatter_samples) {
1244 if (reservoir.rand > thresh) {
1245 result.indirect_throughput = zero_spectrum();
1246 if (equiangular || (vstate.direct_sample_method == VOLUME_SAMPLE_DISTANCE)) {
1247 /* Direct throughput depends on the indirect throughput, set to 0 for early termination. */
1248 result.direct_throughput = zero_spectrum();
1249 }
1250 return true;
1251 }
1252
1253 reservoir.rand = saturatef(reservoir.rand / thresh);
1254 result.indirect_throughput /= thresh;
1255 }
1256
1257 /* Randomly stop direct scatter. */
1258 if (equiangular) {
1259 if (reservoir.rand > thresh) {
1260 result.direct_scatter = true;
1261 result.direct_throughput = zero_spectrum();
1262 reservoir.rand = (reservoir.rand - thresh) / (1.0f - thresh);
1263 }
1264 else {
1265 reservoir.rand /= thresh;
1266 vstate.direct_rr_scale /= thresh;
1267 }
1268 reservoir.rand = saturatef(reservoir.rand);
1269 }
1270
1271 return false;
1272}
1273
1274/* -------------------------------------------------------------------- */
1277
1278/* In a null-scattering framework, we fill the volume with fictitious particles, so that the
1279 * density is `majorant` everywhere. The null-scattering coefficients `sigma_n` is then defined by
1280 * the density of such particles. */
1281ccl_device_inline Spectrum volume_null_event_coefficients(const Spectrum sigma_t,
1282 const float sigma_max,
1283 ccl_private float &majorant)
1284{
1285 majorant = fmaxf(reduce_max(sigma_t), sigma_max);
1286 return make_spectrum(majorant) - sigma_t;
1287}
1288
1289/* The probability of sampling real scattering event at each candidate point of delta tracking. */
1290ccl_device_inline float volume_scatter_probability(
1291 const ccl_private VolumeShaderCoefficients &coeff,
1292 const Spectrum sigma_n,
1293 const Spectrum throughput)
1294{
1295 /* We use `sigma_s` instead of `sigma_t` to skip sampling the absorption event, because it
1296 * always returns zero and has high variance. */
1297 const Spectrum sigma_c = coeff.sigma_s + sigma_n;
1298
1299 /* Set `albedo` to 1 for the channel where extinction coefficient `sigma_t` is zero, to make
1300 * sure that we sample a distance outside the current segment when that channel is picked,
1301 * meaning light passes through without attenuation. */
1302 const Spectrum albedo = safe_divide_color(coeff.sigma_s, coeff.sigma_t, 1.0f);
1303
1304 /* Assign weights per channel to pick scattering event based on throughput and single
1305 * scattering albedo. */
1306 /* TODO(weizhen): currently the sample distance is the same for each color channel, revisit
1307 * the MIS weight when we use Spectral Majorant. */
1308 const Spectrum channel_pdf = volume_sample_channel_pdf(albedo, throughput);
1309
1310 return dot(coeff.sigma_s / sigma_c, channel_pdf);
1311}
1312
1313/* Decide between real and null scatter events at the current position. */
1314ccl_device_inline void volume_sample_indirect_scatter(
1315 const float sigma_max,
1316 const float prob_s,
1317 const Spectrum sigma_s,
1318 ccl_private ShaderData *ccl_restrict sd,
1319 ccl_private VolumeIntegrateState &ccl_restrict vstate,
1320 ccl_private VolumeIntegrateResult &ccl_restrict result,
1321 const uint lcg_state,
1322 ccl_private VolumeSampleReservoir &reservoir)
1323{
1324 const float weight = vstate.transmittance * prob_s;
1325 const Spectrum throughput = result.indirect_throughput * sigma_s / prob_s;
1326
1327 if (vstate.vspg) {
1328 /* If we guide the scatter probability, simply put the candidate in the reservoir. */
1329 reservoir.add_sample(
1331 weight,
1332 {vstate.emission, vstate.t, throughput, weight * sigma_max, vstate.albedo, lcg_state}
1333# else
1334 weight, {vstate.emission, vstate.t, throughput, weight * sigma_max, lcg_state}
1335# endif
1336 );
1337 }
1338 else if (!result.indirect_scatter) {
1339 /* If no guiding and indirect scatter position has not been found, decide between real and null
1340 * scatter events. */
1341 if (reservoir.rand <= prob_s) {
1342 /* Rescale random number for reusing. */
1343 reservoir.rand /= prob_s;
1344
1345 /* Sampled scatter event. */
1346 result.indirect_scatter = true;
1347 volume_shader_copy_phases(&result.indirect_phases, sd);
1348 reservoir.add_sample(
1350 weight,
1351 {vstate.emission, vstate.t, throughput, weight * sigma_max, vstate.albedo, lcg_state}
1352# else
1353 weight, {vstate.emission, vstate.t, throughput, weight * sigma_max, lcg_state}
1354# endif
1355 );
1356
1357 if (vstate.direct_sample_method == VOLUME_SAMPLE_DISTANCE) {
1358 result.direct_scatter = true;
1359 volume_shader_copy_phases(&result.direct_phases, sd);
1360 }
1361 }
1362 else {
1363 /* Rescale random number for reusing. */
1364 reservoir.rand = (reservoir.rand - prob_s) / (1.0f - prob_s);
1365 }
1366 reservoir.rand = saturatef(reservoir.rand);
1367 }
1368}
1369
1386ccl_device void volume_integrate_step_scattering(
1387 KernelGlobals kg,
1388 const IntegratorState state,
1389 const ccl_private Ray *ccl_restrict ray,
1390 const float sigma_max,
1391 ccl_private ShaderData *ccl_restrict sd,
1392 ccl_private VolumeIntegrateState &ccl_restrict vstate,
1393 ccl_private VolumeIntegrateResult &ccl_restrict result,
1394 ccl_private VolumeSampleReservoir &reservoir)
1395{
1396 if (volume_russian_roulette_termination(state, reservoir, result, vstate)) {
1397 return;
1398 }
1399
1400 sd->P = ray->P + ray->D * vstate.t;
1401 VolumeShaderCoefficients coeff ccl_optional_struct_init;
1402 const uint lcg_state = sd->lcg_state;
1403 if (!volume_shader_sample(kg, state, sd, &coeff)) {
1404 return;
1405 }
1406
1407 kernel_assert(sigma_max != 0.0f);
1408
1409 /* Null scattering coefficients. */
1410 float majorant;
1411 const Spectrum sigma_n = volume_null_event_coefficients(coeff.sigma_t, sigma_max, majorant);
1412 if (majorant != sigma_max) {
1413 /* Standard null scattering uses the majorant as the rate parameter for distance sampling, thus
1414 * the MC estimator is
1415 * <L> = T(t) / p(t) * (L_e + sigma_s * L_s + sigma_n * L)
1416 * = 1 / majorant * (L_e + sigma_s * L_s + sigma_n * L).
1417 * If we use another rate parameter sigma for distance sampling, the equation becomes
1418 * <L> = T(t) / p(t) * (L_e + sigma_s * L_s + sigma_n * L)
1419 * = exp(-majorant * t) / sigma * exp(-sigma * t ) * (L_e + sigma_s * L_s + sigma_n *L),
1420 * there is a scaling of majorant / sigma * exp(-(majorant - sigma) * t).
1421 * NOTE: this is not really unbiased, because the scaling is only applied when we sample an
1422 * event inside the segment, but in practice, if the majorant is reasonable, this doesn't
1423 * happen too often and shouldn't affect the result much. */
1424 result.indirect_throughput *= expf((sigma_max - majorant) * vstate.dt) / sigma_max;
1425 }
1426 else {
1427 result.indirect_throughput /= majorant;
1428 }
1429
1430 /* Emission. */
1431 if (sd->flag & SD_EMISSION) {
1432 /* Emission = inv_sigma * (L_e + sigma_n * (inv_sigma * (L_e + sigma_n * ···))). */
1433 vstate.emission += result.indirect_throughput * coeff.emission;
1434 if (!result.indirect_scatter) {
1435 /* Record emission until scatter position. */
1436 guiding_record_volume_emission(kg, state, coeff.emission);
1437 }
1438 }
1439
1440 if (reduce_add(coeff.sigma_s) == 0.0f) {
1441 /* Absorption only. Deterministically choose null scattering and estimate the transmittance
1442 * of the current ray segment. */
1443 result.indirect_throughput *= sigma_n;
1444 return;
1445 }
1446
1447# ifdef __DENOISING_FEATURES__
1449 /* Albedo = inv_sigma * (sigma_s + sigma_n * (inv_sigma * (sigma_s + sigma_n * ···))). */
1450 vstate.albedo += result.indirect_throughput * coeff.sigma_s;
1451 }
1452# endif
1453
1454 /* Indirect scatter. */
1455 const float prob_s = volume_scatter_probability(coeff, sigma_n, result.indirect_throughput);
1456 volume_sample_indirect_scatter(
1457 sigma_max, prob_s, coeff.sigma_s, sd, vstate, result, lcg_state, reservoir);
1458
1459 /* Null scattering. Accumulate weight and continue. */
1460 const float prob_n = 1.0f - prob_s;
1461 result.indirect_throughput *= safe_divide(sigma_n, prob_n);
1462 vstate.transmittance *= prob_n;
1463}
1464
1465/* Evaluate coefficients at the equiangular scatter position, and update the direct throughput. */
1466ccl_device_inline void volume_equiangular_direct_scatter(
1467 KernelGlobals kg,
1468 const IntegratorState state,
1469 const ccl_private Ray *ccl_restrict ray,
1470 ccl_private ShaderData *ccl_restrict sd,
1471 ccl_private VolumeIntegrateState &vstate,
1472 ccl_private VolumeIntegrateResult &ccl_restrict result)
1473{
1474 if (vstate.direct_sample_method != VOLUME_SAMPLE_EQUIANGULAR || !result.direct_scatter) {
1475 return;
1476 }
1477
1478 sd->P = ray->P + ray->D * result.direct_t;
1479 VolumeShaderCoefficients coeff ccl_optional_struct_init;
1480 if (volume_shader_sample(kg, state, sd, &coeff) && (sd->flag & SD_SCATTER)) {
1481 volume_shader_copy_phases(&result.direct_phases, sd);
1482
1483 if (vstate.use_mis) {
1484 /* Compute distance pdf for multiple importance sampling. */
1485 float majorant;
1486 const Spectrum sigma_n = volume_null_event_coefficients(
1487 coeff.sigma_t, vstate.sigma_max, majorant);
1488 if ((vstate.sample_dt != FLT_MAX) && (majorant != vstate.sigma_max)) {
1489 result.direct_throughput *= expf((vstate.sigma_max - majorant) * vstate.sample_dt);
1490 }
1491 vstate.distance_pdf *= volume_scatter_probability(coeff, sigma_n, result.direct_throughput);
1492 }
1493
1494 result.direct_throughput *= coeff.sigma_s / vstate.equiangular_pdf;
1495 }
1496 else {
1497 /* Scattering coefficient is zero at the sampled position. */
1498 result.direct_scatter = false;
1499 }
1500}
1501
1502/* Multiple Importance Sampling between equiangular sampling and distance sampling.
1503 *
1504 * According to [A null-scattering path integral formulation of light transport]
1505 * (https://cs.dartmouth.edu/~wjarosz/publications/miller19null.html), the pdf of sampling a
1506 * scattering event at point P using distance sampling is the probability of sampling a series of
1507 * null events, and then a scatter event at P, i.e.
1508 *
1509 * distance_pdf = (∏p_dist * p_null) * p_dist * p_scatter,
1510 *
1511 * where `p_dist = sigma_max * exp(-sigma_max * dt)` is the probability of sampling an incremental
1512 * distance `dt` following exponential distribution, and `p_null = sigma_n / sigma_c` is the
1513 * probability of sampling a null event at a certain point, `p_scatter = sigma_s / sigma_c` the
1514 * probability of sampling a scatter event.
1515 *
1516 * The pdf of sampling a scattering event at point P using equiangular sampling is the probability
1517 * of sampling a series of null events deterministically, and then a scatter event at the point of
1518 * equiangular sampling, i.e.
1519 *
1520 * equiangular_pdf = (∏p_dist * 1) * T * p_equi,
1521 *
1522 * where `T = exp(-sigma_max * dt)` is the probability of sampling a distance beyond `dt` following
1523 * exponential distribution, `p_equi` is the equiangular pdf. Since the null events are sampled
1524 * deterministically, the pdf is 1 instead of `p_null`.
1525 *
1526 * When performing MIS between distance and equiangular sampling, since we use single-channel
1527 * majorant, `p_dist` is shared in both pdfs, therefore we can write
1528 *
1529 * distance_pdf / equiangular_pdf = (∏p_null) * sigma_max * p_scatter / p_equi.
1530 *
1531 * If we want to use multi-channel majorants in the future, the components do not cancel, but we
1532 * can divide by the `p_dist` of the hero channel to alleviate numerical issues.
1533 */
1534ccl_device_inline void volume_direct_scatter_mis(
1535 const ccl_private Ray *ccl_restrict ray,
1536 const ccl_private VolumeIntegrateState &ccl_restrict vstate,
1537 const ccl_private EquiangularCoefficients &equiangular_coeffs,
1538 ccl_private VolumeIntegrateResult &ccl_restrict result)
1539{
1540 if (!vstate.use_mis || !result.direct_scatter) {
1541 return;
1542 }
1543
1544 float mis_weight;
1545 if (vstate.direct_sample_method == VOLUME_SAMPLE_DISTANCE) {
1546 const float equiangular_pdf = volume_equiangular_pdf(ray, equiangular_coeffs, result.direct_t);
1547 mis_weight = power_heuristic(vstate.distance_pdf, equiangular_pdf);
1548 }
1549 else {
1550 kernel_assert(vstate.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR);
1551 mis_weight = power_heuristic(vstate.equiangular_pdf, vstate.distance_pdf);
1552 }
1553
1554 result.direct_throughput *= 2.0f * mis_weight;
1555}
1556
1558
1559ccl_device_inline void volume_integrate_state_init(KernelGlobals kg,
1560 const IntegratorState state,
1561 const VolumeSampleMethod direct_sample_method,
1562 const ccl_private RNGState *rng_state,
1563 const float tmin,
1564 ccl_private VolumeIntegrateState &vstate)
1565{
1566 vstate.rscatter = path_state_rng_1D(kg, rng_state, PRNG_VOLUME_SCATTER_DISTANCE);
1567
1568 /* Multiple importance sampling: pick between equiangular and distance sampling strategy. */
1569 vstate.direct_sample_method = direct_sample_method;
1570 vstate.use_mis = (direct_sample_method == VOLUME_SAMPLE_MIS);
1571 if (vstate.use_mis) {
1572 if (vstate.rscatter < 0.5f) {
1573 vstate.direct_sample_method = VOLUME_SAMPLE_DISTANCE;
1574 vstate.rscatter *= 2.0f;
1575 }
1576 else {
1577 /* Rescale for equiangular distance sampling. */
1578 vstate.rscatter = (vstate.rscatter - 0.5f) * 2.0f;
1579 vstate.direct_sample_method = VOLUME_SAMPLE_EQUIANGULAR;
1580 }
1581 }
1582
1583 vstate.distance_pdf = 0.0f;
1584 vstate.equiangular_pdf = 0.0f;
1585 vstate.sigma_max = 0.0f;
1586 vstate.transmittance = 1.0f;
1587 vstate.t = tmin;
1588 vstate.optical_depth = 0.0f;
1589 vstate.step = 0;
1590 /* Only guide primary rays. */
1591 vstate.vspg = (INTEGRATOR_STATE(state, path, bounce) == 0);
1592 vstate.scatter_prob = 1.0f;
1593 vstate.majorant_scale = 1.0f;
1594 vstate.direct_rr_scale = 1.0f;
1595 vstate.emission = zero_spectrum();
1596# ifdef __DENOISING_FEATURES__
1597 vstate.albedo = zero_spectrum();
1598# endif
1599}
1600
1601ccl_device_inline void volume_integrate_result_init(
1603 const ccl_private Ray *ccl_restrict ray,
1604 ccl_private VolumeIntegrateState &vstate,
1605 const ccl_private EquiangularCoefficients &equiangular_coeffs,
1606 ccl_private VolumeIntegrateResult &result)
1607{
1608 const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
1609 result.direct_throughput = (vstate.direct_sample_method == VOLUME_SAMPLE_NONE) ?
1610 zero_spectrum() :
1611 throughput;
1612 result.indirect_throughput = throughput;
1613
1614 /* Equiangular sampling: compute distance and PDF in advance. */
1615 if (vstate.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR) {
1616 result.direct_t = volume_equiangular_sample(
1617 ray, equiangular_coeffs, vstate.rscatter, &vstate.equiangular_pdf);
1618 }
1619
1620# if defined(__PATH_GUIDING__)
1621 result.direct_sample_method = vstate.direct_sample_method;
1622# endif
1623}
1624
1625/* Compute guided volume scatter probability and the majorant scale needed for achieving the
1626 * scatter probability, for homogeneous volume. */
1628volume_scatter_probability_homogeneous(KernelGlobals kg,
1629 const IntegratorState state,
1631 const float ray_length,
1632 const ccl_private VolumeShaderCoefficients &coeff,
1633 ccl_private VolumeIntegrateState &vstate)
1634{
1635 const bool attenuation_only = (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_TERMINATE) ||
1636 is_zero(coeff.sigma_s);
1637 if (attenuation_only) {
1638 return zero_spectrum();
1639 }
1640
1641 const Spectrum attenuation = 1.0f - volume_color_transmittance(coeff.sigma_t, ray_length);
1642 if (!vstate.vspg) {
1643 return attenuation;
1644 }
1645
1646 const ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer);
1647
1648 kernel_assert(kernel_data.film.pass_volume_scatter_denoised != PASS_UNUSED);
1649 kernel_assert(kernel_data.film.pass_volume_transmit_denoised != PASS_UNUSED);
1650
1651 /* Contribution based criterion, see Eq. (15). */
1652 const Spectrum L_scattered = kernel_read_pass_rgbe(
1653 buffer + kernel_data.film.pass_volume_scatter_denoised);
1654 const Spectrum L_transmitted = kernel_read_pass_rgbe(
1655 buffer + kernel_data.film.pass_volume_transmit_denoised);
1656 const Spectrum L_volume = L_transmitted + L_scattered;
1657
1658 Spectrum guided_scatter_prob;
1659 if (is_zero(L_volume)) {
1660 /* Equal probability if no information gathered yet. */
1661 guided_scatter_prob = select(coeff.sigma_t > 0.0f, make_spectrum(0.5f), zero_spectrum());
1662 }
1663 else {
1664 /* VSPG guide the scattering probability along the primary ray, but not necessarily in the
1665 * current segment. Scale the probability based on the relative majorant transmittance. */
1666 /* TODO(weizhen): spectrum optical depth. */
1667 const float optical_depth = volume_majorant_optical_depth(kg, buffer);
1668 const float scale = reduce_max(attenuation) / (1.0f - expf(-optical_depth));
1669
1670 guided_scatter_prob = clamp(
1671 safe_divide(L_scattered, L_volume) * scale, zero_spectrum(), one_spectrum());
1672 }
1673
1674 /* Defensive sampling. */
1675 return mix(attenuation, guided_scatter_prob, 0.75f);
1676}
1677
1678/* Homogeneous volume distance sampling, using analytic solution to avoid drawing multiple samples
1679 * with the reservoir.
1680 * Decide the indirect scatter probability, and sample an indirect scatter position inside the
1681 * volume or transmit through the volume.
1682 * Direct scatter is always sampled, if possible. */
1683ccl_device_forceinline void volume_integrate_homogeneous(KernelGlobals kg,
1684 const IntegratorState state,
1685 const ccl_private Ray *ccl_restrict ray,
1686 ccl_private ShaderData *ccl_restrict sd,
1687 const ccl_private RNGState *rng_state,
1690 ccl_private VolumeIntegrateState &vstate,
1691 const Interval<float> interval,
1692 ccl_private VolumeIntegrateResult &result)
1693{
1694 sd->P = ray->P + ray->D * ray->tmin;
1695 VolumeShaderCoefficients coeff ccl_optional_struct_init;
1696 if (!volume_shader_sample(kg, state, sd, &coeff)) {
1697 return;
1698 }
1699
1700 const float ray_length = ray->tmax - ray->tmin;
1701 vstate.optical_depth = reduce_max(coeff.sigma_t) * ray_length;
1702
1703 /* Emission. */
1704 const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
1705 if (sd->flag & SD_EMISSION) {
1706 const Spectrum emission = volume_emission_integrate(&coeff, sd->flag, ray_length);
1707 vstate.emission = throughput * emission;
1709 }
1710
1711 /* Transmittance of the complete ray segment. */
1712 const Spectrum transmittance = volume_color_transmittance(coeff.sigma_t, ray_length);
1713 if ((INTEGRATOR_STATE(state, path, flag) & PATH_RAY_TERMINATE) || is_zero(coeff.sigma_s)) {
1714 /* Attenuation only. */
1715 result.indirect_throughput *= transmittance;
1716 return;
1717 }
1718
1719 float rchannel = path_state_rng_1D(kg, rng_state, PRNG_VOLUME_RESERVOIR);
1720 /* Single scattering albedo. */
1721 const Spectrum albedo = safe_divide_color(coeff.sigma_s, coeff.sigma_t);
1722 /* Multiple scattering albedo. */
1723 vstate.albedo = albedo * (1.0f - transmittance) * throughput;
1724
1725 /* Indirect scatter. */
1726 {
1727 /* Consider the contribution of both scattering and transmission when sampling indirect
1728 * scatter. */
1729 Spectrum channel_pdf;
1730 const int channel = volume_sample_channel(
1731 vstate.albedo + transmittance, throughput, &rchannel, &channel_pdf);
1732
1733 const Spectrum scatter_prob = volume_scatter_probability_homogeneous(
1734 kg, state, render_buffer, ray_length, coeff, vstate);
1735 const float scatter_pdf_channel = volume_channel_get(scatter_prob, channel);
1736
1737 if (vstate.rscatter < scatter_pdf_channel) {
1738 /* Sampled scatter event. */
1739 vstate.rscatter /= scatter_pdf_channel;
1740 const Interval<float> t_range = {0.0f, ray_length};
1741 result.indirect_scatter = !t_range.is_empty();
1742 const float sigma = volume_channel_get(coeff.sigma_t, channel);
1743 const float dt = sample_exponential_distribution(vstate.rscatter, sigma, t_range);
1744 result.indirect_t = ray->tmin + dt;
1745 const Spectrum distance_pdf = pdf_exponential_distribution(dt, coeff.sigma_t, t_range);
1746 const float indirect_distance_pdf = dot(distance_pdf * scatter_prob, channel_pdf);
1747 const Spectrum transmittance = volume_color_transmittance(coeff.sigma_t, dt);
1748 result.indirect_throughput *= coeff.sigma_s * transmittance / indirect_distance_pdf;
1749 volume_shader_copy_phases(&result.indirect_phases, sd);
1750 }
1751 else {
1752 /* Sampled transmit event. */
1753 const float indirect_distance_pdf = dot((1.0f - scatter_prob), channel_pdf);
1754 result.indirect_throughput *= transmittance / indirect_distance_pdf;
1755 vstate.rscatter = (vstate.rscatter - scatter_pdf_channel) / (1.0f - scatter_pdf_channel);
1756 }
1757 }
1758
1759 /* Direct scatter. */
1760 if (vstate.direct_sample_method == VOLUME_SAMPLE_NONE) {
1761 return;
1762 }
1763
1764 /* Sample inside the valid ray segment. */
1765 const Interval<float> t_range = {interval.min - ray->tmin, interval.max - ray->tmin};
1766 result.direct_scatter = !t_range.is_empty();
1767 volume_shader_copy_phases(&result.direct_phases, sd);
1768
1769 Spectrum channel_pdf;
1770 const int channel = volume_sample_channel(vstate.albedo, throughput, &rchannel, &channel_pdf);
1771
1772 if (vstate.direct_sample_method == VOLUME_SAMPLE_DISTANCE) {
1773 const float sigma = volume_channel_get(coeff.sigma_t, channel);
1774 const float dt = sample_exponential_distribution(vstate.rscatter, sigma, t_range);
1775 result.direct_t = ray->tmin + dt;
1776 const Spectrum distance_pdf = pdf_exponential_distribution(dt, coeff.sigma_t, t_range);
1777 vstate.distance_pdf = dot(distance_pdf, channel_pdf);
1778 const Spectrum transmittance = volume_color_transmittance(coeff.sigma_t, dt);
1779 result.direct_throughput *= coeff.sigma_s * transmittance / vstate.distance_pdf;
1780 }
1781 else {
1782 kernel_assert(vstate.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR);
1783 const float dt = result.direct_t - ray->tmin;
1784 const Spectrum transmittance = volume_color_transmittance(coeff.sigma_t, dt);
1785 result.direct_throughput *= coeff.sigma_s * transmittance / vstate.equiangular_pdf;
1786 if (vstate.use_mis) {
1787 vstate.distance_pdf = dot(pdf_exponential_distribution(dt, coeff.sigma_t, t_range),
1788 channel_pdf);
1789 }
1790 }
1791}
1792
1793/* heterogeneous volume distance sampling: integrate stepping through the
1794 * volume until we reach the end, get absorbed entirely, or run out of
1795 * iterations. this does probabilistically scatter or get transmitted through
1796 * for path tracing where we don't want to branch. */
1797ccl_device_forceinline void volume_integrate_heterogeneous(
1798 KernelGlobals kg,
1799 const IntegratorState state,
1800 const ccl_private Ray *ccl_restrict ray,
1801 ccl_private ShaderData *ccl_restrict sd,
1802 RNGState rng_state,
1804 ccl_private VolumeIntegrateState &vstate,
1805 ccl_private VolumeIntegrateResult &result)
1806{
1807 OctreeTracing octree(ray->tmin);
1808 const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
1809 if (!volume_octree_setup<false>(kg, ray, sd, state, &rng_state, path_flag, octree)) {
1810 return;
1811 }
1812
1813 /* Prepare struct for guiding. */
1814 vstate.optical_depth = octree.sigma.max * octree.t.length();
1815 volume_scatter_probability_heterogeneous(kg, state, render_buffer, vstate);
1816
1817 /* Initialize reservoir for sampling scatter position. */
1818 VolumeSampleReservoir reservoir = path_state_rng_1D(kg, &rng_state, PRNG_VOLUME_RESERVOIR);
1819
1820 /* Scramble for stepping through volume. */
1821 path_state_rng_scramble(&rng_state, 0xe35fad82);
1822
1823 volume_equiangular_transmittance(
1824 kg, state, ray, octree.sigma, octree.t, sd, &rng_state, vstate, result);
1825
1826 while (
1827 volume_integrate_advance(kg, ray, sd, state, &rng_state, path_flag, octree, vstate, result))
1828 {
1829 const float sigma_max = octree.sigma.max * vstate.majorant_scale;
1830 volume_integrate_step_scattering(kg, state, ray, sigma_max, sd, vstate, result, reservoir);
1831
1832 if (volume_integrate_should_stop(result)) {
1833 break;
1834 }
1835 }
1836
1837 volume_distance_sampling_finalize(kg, state, ray, sd, vstate, result, reservoir);
1838 volume_equiangular_direct_scatter(kg, state, ray, sd, vstate, result);
1839}
1840
1841/* Path tracing: sample point on light using equiangular sampling. */
1842ccl_device_forceinline bool integrate_volume_sample_direct_light(
1843 KernelGlobals kg,
1844 const IntegratorState state,
1845 const ccl_private Ray *ccl_restrict ray,
1846 const ccl_private ShaderData *ccl_restrict sd,
1847 const ccl_private RNGState *ccl_restrict rng_state,
1848 ccl_private EquiangularCoefficients *ccl_restrict equiangular_coeffs,
1850{
1851 /* Test if there is a light or BSDF that needs direct light. */
1852 if (!kernel_data.integrator.use_direct_light) {
1853 return false;
1854 }
1855
1856 /* Sample position on a light. */
1857 const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
1858 const uint bounce = INTEGRATOR_STATE(state, path, bounce);
1859 const float3 rand_light = path_state_rng_3D(kg, rng_state, PRNG_LIGHT);
1860
1862 rand_light,
1863 sd->time,
1864 sd->P,
1865 ray->D,
1866 ray->tmax - ray->tmin,
1868 bounce,
1869 path_flag,
1870 ls))
1871 {
1872 ls->emitter_id = EMITTER_NONE;
1873 return false;
1874 }
1875
1876 if (ls->shader & SHADER_EXCLUDE_SCATTER) {
1877 ls->emitter_id = EMITTER_NONE;
1878 return false;
1879 }
1880
1881 equiangular_coeffs->P = ls->P;
1882
1883 return volume_valid_direct_ray_segment(kg, ray->P, ray->D, &equiangular_coeffs->t_range, ls);
1884}
1885
1886/* Determine the method to sample direct light, based on the volume property and settings. */
1887ccl_device_forceinline VolumeSampleMethod
1888volume_direct_sample_method(KernelGlobals kg,
1889 const IntegratorState state,
1890 const ccl_private Ray *ccl_restrict ray,
1891 const ccl_private ShaderData *ccl_restrict sd,
1892 const ccl_private RNGState *ccl_restrict rng_state,
1893 ccl_private EquiangularCoefficients *coeffs,
1895{
1897 return VOLUME_SAMPLE_NONE;
1898 }
1899
1900 if (!integrate_volume_sample_direct_light(kg, state, ray, sd, rng_state, coeffs, ls)) {
1901 return VOLUME_SAMPLE_NONE;
1902 }
1903
1904 /* Sample the scatter position with distance sampling for distant/background light. */
1905 const bool has_equiangular_sample = (ls->t != FLT_MAX);
1906 return has_equiangular_sample ? volume_stack_sample_method(kg, state) : VOLUME_SAMPLE_DISTANCE;
1907}
1908
1909/* Shared function of integrating homogeneous and heterogeneous volume. */
1910ccl_device void volume_integrate_null_scattering(KernelGlobals kg,
1911 const IntegratorState state,
1912 const ccl_private Ray *ccl_restrict ray,
1913 ccl_private ShaderData *ccl_restrict sd,
1914 const ccl_private RNGState *rng_state,
1917 ccl_private VolumeIntegrateResult &result)
1918{
1920
1921 EquiangularCoefficients equiangular_coeffs = {zero_float3(), {ray->tmin, ray->tmax}};
1922 const VolumeSampleMethod direct_sample_method = volume_direct_sample_method(
1923 kg, state, ray, sd, rng_state, &equiangular_coeffs, ls);
1924
1925 /* Initialize volume integration state. */
1926 VolumeIntegrateState vstate ccl_optional_struct_init;
1927 volume_integrate_state_init(kg, state, direct_sample_method, rng_state, ray->tmin, vstate);
1928
1929 /* Initialize volume integration result. */
1930 volume_integrate_result_init(state, ray, vstate, equiangular_coeffs, result);
1931
1932 if (volume_is_homogeneous<false>(kg, state)) {
1933 volume_integrate_homogeneous(
1934 kg, state, ray, sd, rng_state, render_buffer, vstate, equiangular_coeffs.t_range, result);
1935 }
1936 else {
1937 volume_integrate_heterogeneous(kg, state, ray, sd, *rng_state, render_buffer, vstate, result);
1938 }
1939
1940 volume_direct_scatter_mis(ray, vstate, equiangular_coeffs, result);
1941
1942 /* Write accumulated emission. */
1943 if (!is_zero(vstate.emission)) {
1944 if (light_link_object_match(kg, light_link_receiver_forward(kg, state), sd->object)) {
1946 kg, state, vstate.emission, render_buffer, object_lightgroup(kg, sd->object));
1947 }
1948 }
1949
1950# ifdef __DENOISING_FEATURES__
1951 /* Write denoising features. */
1953 film_write_denoising_features_volume(
1954 kg, state, vstate.albedo, result.indirect_scatter, render_buffer);
1955 }
1956# endif /* __DENOISING_FEATURES__ */
1957
1958 if (INTEGRATOR_STATE(state, path, bounce) == 0) {
1959 INTEGRATOR_STATE_WRITE(state, path, optical_depth) += vstate.optical_depth;
1960 }
1961}
1962
1963/* -------------------------------------------------------------------- */
1966
1967/* Determines the next shading position. */
1968struct VolumeStep {
1969 /* Shift starting point of all segments by a random amount to avoid banding artifacts due to
1970 * biased ray marching with insufficient step size. */
1971 float offset;
1972
1973 /* Step size taken at each marching step. */
1974 float size;
1975
1976 /* Perform shading at this offset within a step, to integrate over the entire step segment. */
1977 float shade_offset;
1978
1979 /* Maximal steps allowed between `ray->tmin` and `ray->tmax`. */
1980 int max_steps;
1981
1982 /* Current active segment. */
1983 Interval<float> t;
1984};
1985
1986template<const bool shadow>
1987ccl_device_forceinline void volume_step_init(KernelGlobals kg,
1988 const ccl_private RNGState *rng_state,
1989 const float object_step_size,
1990 const float tmin,
1991 const float tmax,
1992 ccl_private VolumeStep *vstep)
1993{
1994 vstep->t.min = vstep->t.max = tmin;
1995
1996 if (object_step_size == FLT_MAX) {
1997 /* Homogeneous volume. */
1998 vstep->size = tmax - tmin;
1999 vstep->shade_offset = 0.0f;
2000 vstep->offset = 1.0f;
2001 vstep->max_steps = 1;
2002 }
2003 else {
2004 /* Heterogeneous volume. */
2005 vstep->max_steps = kernel_data.integrator.volume_max_steps;
2006 const float t = tmax - tmin;
2007 float step_size = min(object_step_size, t);
2008
2009 if (t > vstep->max_steps * step_size) {
2010 /* Increase step size to cover the whole ray segment. */
2011 step_size = t / (float)vstep->max_steps;
2012 }
2013
2014 vstep->size = step_size;
2015 vstep->shade_offset = path_state_rng_1D(kg, rng_state, PRNG_VOLUME_SHADE_OFFSET);
2016
2017 if (shadow) {
2018 /* For shadows we do not offset all segments, since the starting point is already a random
2019 * distance inside the volume. It also appears to create banding artifacts for unknown
2020 * reasons. */
2021 vstep->offset = 1.0f;
2022 }
2023 else {
2024 vstep->offset = path_state_rng_1D(kg, rng_state, PRNG_VOLUME_OFFSET);
2025 }
2026 }
2027}
2028
2029ccl_device_inline bool volume_ray_marching_advance(const int step,
2030 const ccl_private Ray *ccl_restrict ray,
2031 ccl_private float3 *shade_P,
2032 ccl_private VolumeStep &vstep)
2033{
2034 if (vstep.t.max == ray->tmax) {
2035 /* Reached the last segment. */
2036 return false;
2037 }
2038
2039 /* Advance to new position. */
2040 vstep.t.min = vstep.t.max;
2041 vstep.t.max = min(ray->tmax, ray->tmin + (step + vstep.offset) * vstep.size);
2042 const float shade_t = mix(vstep.t.min, vstep.t.max, vstep.shade_offset);
2043 *shade_P = ray->P + ray->D * shade_t;
2044
2045 return step < vstep.max_steps;
2046}
2047
2048ccl_device void volume_shadow_ray_marching(KernelGlobals kg,
2051 ccl_private ShaderData *ccl_restrict sd,
2052 ccl_private Spectrum *ccl_restrict throughput,
2053 const float object_step_size)
2054{
2055 /* Load random number state. */
2056 RNGState rng_state;
2057 shadow_path_state_rng_load(state, &rng_state);
2058
2059 /* For stochastic texture sampling. */
2060 sd->lcg_state = lcg_state_init(
2061 rng_state.rng_pixel, rng_state.rng_offset, rng_state.sample, 0xd9111870);
2062
2063 Spectrum tp = *throughput;
2064
2065 /* Prepare for stepping. */
2066 VolumeStep vstep;
2067 volume_step_init<true>(kg, &rng_state, object_step_size, ray->tmin, ray->tmax, &vstep);
2068
2069 /* compute extinction at the start */
2071 for (int step = 0; volume_ray_marching_advance(step, ray, &sd->P, vstep); step++) {
2072 /* compute attenuation over segment */
2073 const Spectrum sigma_t = volume_shader_eval_extinction<true>(kg, state, sd, PATH_RAY_SHADOW);
2074 /* Compute `expf()` only for every Nth step, to save some calculations
2075 * because `exp(a)*exp(b) = exp(a+b)`, also do a quick #VOLUME_THROUGHPUT_EPSILON
2076 * check then. */
2077 sum += (-sigma_t * vstep.t.length());
2078 if ((step & 0x07) == 0) { /* TODO: Other interval? */
2079 tp = *throughput * exp(sum);
2080
2081 /* stop if nearly all light is blocked */
2083 break;
2084 }
2085 }
2086 }
2087
2088 if (vstep.t.max == ray->tmax) {
2089 /* Update throughput in case we haven't done it above. */
2090 tp = *throughput * exp(sum);
2091 }
2092
2093 *throughput = tp;
2094}
2095
2096struct VolumeRayMarchingState {
2097 /* Random numbers for scattering. */
2098 float rscatter;
2099 float rchannel;
2100
2101 /* Multiple importance sampling. */
2102 VolumeSampleMethod direct_sample_method;
2103 bool use_mis;
2104 float distance_pdf;
2105 float equiangular_pdf;
2106};
2107
2108ccl_device_inline void volume_ray_marching_state_init(
2109 KernelGlobals kg,
2110 const ccl_private RNGState *rng_state,
2111 const VolumeSampleMethod direct_sample_method,
2112 ccl_private VolumeRayMarchingState &vstate)
2113{
2114 vstate.rscatter = path_state_rng_1D(kg, rng_state, PRNG_VOLUME_SCATTER_DISTANCE);
2115 vstate.rchannel = path_state_rng_1D(kg, rng_state, PRNG_VOLUME_COLOR_CHANNEL);
2116
2117 /* Multiple importance sampling: pick between equiangular and distance sampling strategy. */
2118 vstate.direct_sample_method = direct_sample_method;
2119 vstate.use_mis = (direct_sample_method == VOLUME_SAMPLE_MIS);
2120 if (vstate.use_mis) {
2121 if (vstate.rscatter < 0.5f) {
2122 vstate.rscatter *= 2.0f;
2123 vstate.direct_sample_method = VOLUME_SAMPLE_DISTANCE;
2124 }
2125 else {
2126 vstate.rscatter = (vstate.rscatter - 0.5f) * 2.0f;
2127 vstate.direct_sample_method = VOLUME_SAMPLE_EQUIANGULAR;
2128 }
2129 }
2130 vstate.equiangular_pdf = 0.0f;
2131 vstate.distance_pdf = 1.0f;
2132}
2133
2134/* Returns true if we found the indirect scatter position within the current active ray segment. */
2135ccl_device bool volume_sample_indirect_scatter_ray_marching(
2136 const Spectrum transmittance,
2137 const Spectrum channel_pdf,
2138 const int channel,
2139 const ccl_private ShaderData *ccl_restrict sd,
2140 const ccl_private VolumeShaderCoefficients &ccl_restrict coeff,
2142 ccl_private VolumeRayMarchingState &ccl_restrict vstate,
2143 ccl_private VolumeIntegrateResult &ccl_restrict result)
2144{
2145 if (result.indirect_scatter) {
2146 /* Already sampled indirect scatter position. */
2147 return false;
2148 }
2149
2150 /* If sampled distance does not go beyond the current segment, we have found the scatter
2151 * position. Otherwise continue searching and accumulate the transmittance along the ray. */
2152 const float sample_transmittance = volume_channel_get(transmittance, channel);
2153 if (1.0f - vstate.rscatter >= sample_transmittance) {
2154 /* Pick `sigma_t` from a random channel. */
2155 const float sample_sigma_t = volume_channel_get(coeff.sigma_t, channel);
2156
2157 /* Generate the next distance using random walk, following exponential distribution
2158 * p(dt) = sigma_t * exp(-sigma_t * dt). */
2159 const float new_dt = -logf(1.0f - vstate.rscatter) / sample_sigma_t;
2160 const float new_t = t.min + new_dt;
2161
2162 const Spectrum new_transmittance = volume_color_transmittance(coeff.sigma_t, new_dt);
2163 /* PDF for density-based distance sampling is handled implicitly via
2164 * transmittance / pdf = exp(-sigma_t * dt) / (sigma_t * exp(-sigma_t * dt)) = 1 / sigma_t. */
2165 const float distance_pdf = dot(channel_pdf, coeff.sigma_t * new_transmittance);
2166
2167 if (vstate.distance_pdf * distance_pdf > VOLUME_SAMPLE_PDF_CUTOFF) {
2168 /* Update throughput. */
2169 result.indirect_scatter = true;
2170 result.indirect_t = new_t;
2171 result.indirect_throughput *= coeff.sigma_s * new_transmittance / distance_pdf;
2172 if (vstate.direct_sample_method == VOLUME_SAMPLE_DISTANCE) {
2173 vstate.distance_pdf *= distance_pdf;
2174 }
2175
2176 volume_shader_copy_phases(&result.indirect_phases, sd);
2177
2178 return true;
2179 }
2180 }
2181 else {
2182 /* Update throughput. */
2183 const float distance_pdf = dot(channel_pdf, transmittance);
2184 result.indirect_throughput *= transmittance / distance_pdf;
2185 if (vstate.direct_sample_method == VOLUME_SAMPLE_DISTANCE) {
2186 vstate.distance_pdf *= distance_pdf;
2187 }
2188
2189 /* Remap rscatter so we can reuse it and keep thing stratified. */
2190 vstate.rscatter = 1.0f - (1.0f - vstate.rscatter) / sample_transmittance;
2191 }
2192
2193 return false;
2194}
2195
2196/* Find direct and indirect scatter positions. */
2197ccl_device_forceinline void volume_ray_marching_step_scattering(
2198 const ccl_private ShaderData *sd,
2199 const ccl_private Ray *ray,
2200 const ccl_private EquiangularCoefficients &equiangular_coeffs,
2201 const ccl_private VolumeShaderCoefficients &ccl_restrict coeff,
2202 const Spectrum transmittance,
2204 ccl_private VolumeRayMarchingState &ccl_restrict vstate,
2205 ccl_private VolumeIntegrateResult &ccl_restrict result)
2206{
2207 /* Pick random color channel for sampling the scatter distance. We use the Veach one-sample model
2208 * with balance heuristic for the channels.
2209 * Set `albedo` to 1 for the channel where extinction coefficient `sigma_t` is zero, to make sure
2210 * that we sample a distance outside the current segment when that channel is picked, meaning
2211 * light passes through without attenuation. */
2212 const Spectrum albedo = safe_divide_color(coeff.sigma_s, coeff.sigma_t, 1.0f);
2213 Spectrum channel_pdf;
2214 const int channel = volume_sample_channel(
2215 albedo, result.indirect_throughput, &vstate.rchannel, &channel_pdf);
2216
2217 /* Equiangular sampling for direct lighting. */
2218 if (vstate.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR && !result.direct_scatter) {
2219 if (t.contains(result.direct_t) && vstate.equiangular_pdf > VOLUME_SAMPLE_PDF_CUTOFF) {
2220 const float new_dt = result.direct_t - t.min;
2221 const Spectrum new_transmittance = volume_color_transmittance(coeff.sigma_t, new_dt);
2222
2223 result.direct_scatter = true;
2224 result.direct_throughput *= coeff.sigma_s * new_transmittance / vstate.equiangular_pdf;
2225 volume_shader_copy_phases(&result.direct_phases, sd);
2226
2227 /* Multiple importance sampling. */
2228 if (vstate.use_mis) {
2229 const float distance_pdf = vstate.distance_pdf *
2230 dot(channel_pdf, coeff.sigma_t * new_transmittance);
2231 const float mis_weight = 2.0f * power_heuristic(vstate.equiangular_pdf, distance_pdf);
2232 result.direct_throughput *= mis_weight;
2233 }
2234 }
2235 else {
2236 result.direct_throughput *= transmittance;
2237 vstate.distance_pdf *= dot(channel_pdf, transmittance);
2238 }
2239 }
2240
2241 /* Distance sampling for indirect and optional direct lighting. */
2242 if (volume_sample_indirect_scatter_ray_marching(
2243 transmittance, channel_pdf, channel, sd, coeff, t, vstate, result))
2244 {
2245 if (vstate.direct_sample_method == VOLUME_SAMPLE_DISTANCE) {
2246 /* If using distance sampling for direct light, just copy parameters of indirect light
2247 * since we scatter at the same point. */
2248 result.direct_scatter = true;
2249 result.direct_t = result.indirect_t;
2250 result.direct_throughput = result.indirect_throughput;
2251 volume_shader_copy_phases(&result.direct_phases, sd);
2252
2253 /* Multiple importance sampling. */
2254 if (vstate.use_mis) {
2255 const float equiangular_pdf = volume_equiangular_pdf(
2256 ray, equiangular_coeffs, result.indirect_t);
2257 const float mis_weight = power_heuristic(vstate.distance_pdf, equiangular_pdf);
2258 result.direct_throughput *= 2.0f * mis_weight;
2259 }
2260 }
2261 }
2262}
2263
2264/* heterogeneous volume distance sampling: integrate stepping through the
2265 * volume until we reach the end, get absorbed entirely, or run out of
2266 * iterations. this does probabilistically scatter or get transmitted through
2267 * for path tracing where we don't want to branch. */
2268ccl_device_forceinline void volume_integrate_ray_marching(
2269 KernelGlobals kg,
2270 const IntegratorState state,
2271 const ccl_private Ray *ccl_restrict ray,
2272 ccl_private ShaderData *ccl_restrict sd,
2273 const ccl_private RNGState *ccl_restrict rng_state,
2275 const float object_step_size,
2277 ccl_private VolumeIntegrateResult &result)
2278{
2280
2281 EquiangularCoefficients equiangular_coeffs = {zero_float3(), {ray->tmin, ray->tmax}};
2282 const VolumeSampleMethod direct_sample_method = volume_direct_sample_method(
2283 kg, state, ray, sd, rng_state, &equiangular_coeffs, ls);
2284
2285 /* Prepare for stepping. */
2286 VolumeStep vstep;
2287 volume_step_init<false>(kg, rng_state, object_step_size, ray->tmin, ray->tmax, &vstep);
2288
2289 /* Initialize volume integration state. */
2290 VolumeRayMarchingState vstate ccl_optional_struct_init;
2291 volume_ray_marching_state_init(kg, rng_state, direct_sample_method, vstate);
2292
2293 /* Initialize volume integration result. */
2294 const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
2295 result.direct_throughput = (vstate.direct_sample_method == VOLUME_SAMPLE_NONE) ?
2296 zero_spectrum() :
2297 throughput;
2298 result.indirect_throughput = throughput;
2299
2300 /* Equiangular sampling: compute distance and PDF in advance. */
2301 if (vstate.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR) {
2302 result.direct_t = volume_equiangular_sample(
2303 ray, equiangular_coeffs, vstate.rscatter, &vstate.equiangular_pdf);
2304 }
2305# if defined(__PATH_GUIDING__)
2306 result.direct_sample_method = vstate.direct_sample_method;
2307# endif
2308
2309# ifdef __DENOISING_FEATURES__
2310 const bool write_denoising_features = (INTEGRATOR_STATE(state, path, flag) &
2312 Spectrum accum_albedo = zero_spectrum();
2313# endif
2314 Spectrum accum_emission = zero_spectrum();
2315
2316 for (int step = 0; volume_ray_marching_advance(step, ray, &sd->P, vstep); step++) {
2317 /* compute segment */
2318 VolumeShaderCoefficients coeff ccl_optional_struct_init;
2319 if (volume_shader_sample(kg, state, sd, &coeff)) {
2320 const int closure_flag = sd->flag;
2321
2322 /* Evaluate transmittance over segment. */
2323 const float dt = vstep.t.length();
2324 const Spectrum transmittance = (closure_flag & SD_EXTINCTION) ?
2325 volume_color_transmittance(coeff.sigma_t, dt) :
2326 one_spectrum();
2327
2328 /* Emission. */
2329 if (closure_flag & SD_EMISSION) {
2330 /* Only write emission before indirect light scatter position, since we terminate
2331 * stepping at that point if we have already found a direct light scatter position. */
2332 if (!result.indirect_scatter) {
2333 const Spectrum emission = volume_emission_integrate(&coeff, closure_flag, dt);
2334 accum_emission += result.indirect_throughput * emission;
2336 }
2337 }
2338
2339 if (closure_flag & SD_SCATTER) {
2340# ifdef __DENOISING_FEATURES__
2341 /* Accumulate albedo for denoising features. */
2342 if (write_denoising_features && (closure_flag & SD_SCATTER)) {
2343 const Spectrum albedo = safe_divide_color(coeff.sigma_s, coeff.sigma_t);
2344 accum_albedo += result.indirect_throughput * albedo * (one_spectrum() - transmittance);
2345 }
2346# endif
2347
2348 /* Scattering and absorption. */
2349 volume_ray_marching_step_scattering(
2350 sd, ray, equiangular_coeffs, coeff, transmittance, vstep.t, vstate, result);
2351 }
2352 else if (closure_flag & SD_EXTINCTION) {
2353 /* Absorption only. */
2354 result.indirect_throughput *= transmittance;
2355 result.direct_throughput *= transmittance;
2356 }
2357
2358 if (volume_integrate_should_stop(result)) {
2359 break;
2360 }
2361 }
2362 }
2363
2364 /* Write accumulated emission. */
2365 if (!is_zero(accum_emission)) {
2366 if (light_link_object_match(kg, light_link_receiver_forward(kg, state), sd->object)) {
2368 kg, state, accum_emission, render_buffer, object_lightgroup(kg, sd->object));
2369 }
2370 }
2371
2372# ifdef __DENOISING_FEATURES__
2373 /* Write denoising features. */
2374 if (write_denoising_features) {
2375 film_write_denoising_features_volume(
2376 kg, state, accum_albedo, result.indirect_scatter, render_buffer);
2377 }
2378# endif /* __DENOISING_FEATURES__ */
2379}
2380
2382
2383/* Path tracing: sample point on light and evaluate light shader, then
2384 * queue shadow ray to be traced. */
2385ccl_device_forceinline void integrate_volume_direct_light(
2386 KernelGlobals kg,
2388 const ccl_private ShaderData *ccl_restrict sd,
2389 const ccl_private RNGState *ccl_restrict rng_state,
2390 const float3 P,
2392# if defined(__PATH_GUIDING__)
2393 const ccl_private Spectrum unlit_throughput,
2394# endif
2395 const ccl_private Spectrum throughput,
2397{
2399
2400 if (!kernel_data.integrator.use_direct_light || ls.emitter_id == EMITTER_NONE) {
2401 return;
2402 }
2403
2404 /* Sample position on the same light again, now from the shading point where we scattered. */
2405 {
2406 const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
2407 const uint bounce = INTEGRATOR_STATE(state, path, bounce);
2408 const float3 rand_light = path_state_rng_3D(kg, rng_state, PRNG_LIGHT);
2409 const float3 N = zero_float3();
2410 const int object_receiver = light_link_receiver_nee(kg, sd);
2411 const int shader_flags = SD_BSDF_HAS_TRANSMISSION;
2412
2414 kg, rand_light, sd->time, P, N, object_receiver, shader_flags, bounce, path_flag, &ls))
2415 {
2416 return;
2417 }
2418 }
2419
2420 if (ls.shader & SHADER_EXCLUDE_SCATTER) {
2421 return;
2422 }
2423
2424 /* Evaluate light shader.
2425 *
2426 * TODO: can we reuse sd memory? In theory we can move this after
2427 * integrate_surface_bounce, evaluate the BSDF, and only then evaluate
2428 * the light shader. This could also move to its own kernel, for
2429 * non-constant light sources. */
2430 ShaderDataTinyStorage emission_sd_storage;
2431 ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage);
2432 const Spectrum light_eval = light_sample_shader_eval(kg, state, emission_sd, &ls, sd->time);
2433 if (is_zero(light_eval)) {
2434 return;
2435 }
2436
2437 /* Evaluate BSDF. */
2439 const float phase_pdf = volume_shader_phase_eval(
2440 kg, state, sd, phases, ls.D, &phase_eval, ls.shader);
2441 const float mis_weight = light_sample_mis_weight_nee(kg, ls.pdf, phase_pdf);
2442 bsdf_eval_mul(&phase_eval, light_eval / ls.pdf * mis_weight);
2443
2444 /* Path termination. */
2445 const float terminate = path_state_rng_light_termination(kg, rng_state);
2446 if (light_sample_terminate(kg, &phase_eval, terminate)) {
2447 return;
2448 }
2449
2450 /* Create shadow ray. */
2452 light_sample_to_volume_shadow_ray(sd, &ls, P, &ray);
2453
2454 /* Branch off shadow kernel. */
2457
2458 /* Write shadow ray and associated state to global memory. */
2459 integrator_state_write_shadow_ray(shadow_state, &ray);
2460 integrator_state_write_shadow_ray_self(shadow_state, &ray);
2461
2462 /* Copy state from main path to shadow path. */
2463 const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce);
2464 const uint16_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce);
2465 uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag);
2466 const Spectrum throughput_phase = throughput * bsdf_eval_sum(&phase_eval);
2467
2468 if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
2469 PackedSpectrum pass_diffuse_weight;
2470 PackedSpectrum pass_glossy_weight;
2471
2472 if (shadow_flag & PATH_RAY_ANY_PASS) {
2473 /* Indirect bounce, use weights from earlier surface or volume bounce. */
2474 pass_diffuse_weight = INTEGRATOR_STATE(state, path, pass_diffuse_weight);
2475 pass_glossy_weight = INTEGRATOR_STATE(state, path, pass_glossy_weight);
2476 }
2477 else {
2478 /* Direct light, no diffuse/glossy distinction needed for volumes. */
2479 shadow_flag |= PATH_RAY_VOLUME_PASS;
2480 pass_diffuse_weight = one_spectrum();
2481 pass_glossy_weight = zero_spectrum();
2482 }
2483
2484 INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_diffuse_weight) = pass_diffuse_weight;
2485 INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_glossy_weight) = pass_glossy_weight;
2486 }
2487
2488 if (bounce == 0) {
2489 shadow_flag |= PATH_RAY_VOLUME_SCATTER;
2490 shadow_flag &= ~PATH_RAY_VOLUME_PRIMARY_TRANSMIT;
2491 }
2492
2493 INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, render_pixel_index) = INTEGRATOR_STATE(
2494 state, path, render_pixel_index);
2495 INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_offset) = INTEGRATOR_STATE(
2496 state, path, rng_offset);
2497 INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_pixel) = INTEGRATOR_STATE(
2498 state, path, rng_pixel);
2499 INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, sample) = INTEGRATOR_STATE(
2500 state, path, sample);
2501 INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, flag) = shadow_flag;
2502 INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, bounce) = bounce;
2503 INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, transparent_bounce) = transparent_bounce;
2504 INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, diffuse_bounce) = INTEGRATOR_STATE(
2505 state, path, diffuse_bounce);
2506 INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, glossy_bounce) = INTEGRATOR_STATE(
2507 state, path, glossy_bounce);
2508 INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, transmission_bounce) = INTEGRATOR_STATE(
2509 state, path, transmission_bounce);
2510 INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, volume_bounds_bounce) = INTEGRATOR_STATE(
2511 state, path, volume_bounds_bounce);
2512 INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, throughput) = throughput_phase;
2513
2514 /* Write Light-group, +1 as light-group is int but we need to encode into a uint8_t. */
2515 INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, lightgroup) = ls.group + 1;
2516
2517# if defined(__PATH_GUIDING__)
2518 if ((kernel_data.kernel_features & KERNEL_FEATURE_PATH_GUIDING)) {
2519 INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unlit_throughput) = unlit_throughput;
2520 INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, path_segment) = INTEGRATOR_STATE(
2521 state, guiding, path_segment);
2522 INTEGRATOR_STATE(shadow_state, shadow_path, guiding_mis_weight) = 0.0f;
2523 }
2524# endif
2525
2526 integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state);
2527}
2528
2529/* Path tracing: scatter in new direction using phase function */
2530ccl_device_forceinline bool integrate_volume_phase_scatter(
2531 KernelGlobals kg,
2533 ccl_private ShaderData *sd,
2534 const ccl_private Ray *ray,
2535 const ccl_private RNGState *rng_state,
2536 const ccl_private ShaderVolumePhases *phases)
2537{
2539
2540 float2 rand_phase = path_state_rng_2D(kg, rng_state, PRNG_VOLUME_PHASE);
2541
2542 const ccl_private ShaderVolumeClosure *svc = volume_shader_phase_pick(phases, &rand_phase);
2543
2544 /* Phase closure, sample direction. */
2545 float phase_pdf = 0.0f;
2546 float unguided_phase_pdf = 0.0f;
2549 float sampled_roughness = 1.0f;
2550 int label;
2551
2552# if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
2553 if (kernel_data.integrator.use_guiding &&
2554 (kernel_data.kernel_features & KERNEL_FEATURE_PATH_GUIDING))
2555 {
2556 label = volume_shader_phase_guided_sample(kg,
2557 state,
2558 sd,
2559 svc,
2560 rand_phase,
2561 &phase_eval,
2562 &phase_wo,
2563 &phase_pdf,
2564 &unguided_phase_pdf,
2565 &sampled_roughness);
2566
2567 if (phase_pdf == 0.0f || bsdf_eval_is_zero(&phase_eval)) {
2568 return false;
2569 }
2570
2571 INTEGRATOR_STATE_WRITE(state, path, unguided_throughput) *= phase_pdf / unguided_phase_pdf;
2572 }
2573 else
2574# endif
2575 {
2576 label = volume_shader_phase_sample(
2577 sd, svc, rand_phase, &phase_eval, &phase_wo, &phase_pdf, &sampled_roughness);
2578
2579 if (phase_pdf == 0.0f || bsdf_eval_is_zero(&phase_eval)) {
2580 return false;
2581 }
2582
2583 unguided_phase_pdf = phase_pdf;
2584 }
2585
2586 /* Setup ray. */
2587 INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P;
2588 INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(phase_wo);
2589 INTEGRATOR_STATE_WRITE(state, ray, tmin) = 0.0f;
2590# ifdef __LIGHT_TREE__
2591 if (kernel_data.integrator.use_light_tree) {
2592 INTEGRATOR_STATE_WRITE(state, ray, previous_dt) = ray->tmax - ray->tmin;
2593 }
2594# endif
2595 INTEGRATOR_STATE_WRITE(state, ray, tmax) = FLT_MAX;
2596# ifdef __RAY_DIFFERENTIALS__
2598# endif
2599 // Save memory by storing last hit prim and object in isect
2600 INTEGRATOR_STATE_WRITE(state, isect, prim) = sd->prim;
2601 INTEGRATOR_STATE_WRITE(state, isect, object) = sd->object;
2602
2603 const Spectrum phase_weight = bsdf_eval_sum(&phase_eval) / phase_pdf;
2604
2605 /* Add phase function sampling data to the path segment. */
2607 kg, state, phase_weight, phase_pdf, normalize(phase_wo), sampled_roughness);
2608
2609 /* Update throughput. */
2610 const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
2611 const Spectrum throughput_phase = throughput * phase_weight;
2612 INTEGRATOR_STATE_WRITE(state, path, throughput) = throughput_phase;
2613
2614 if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
2615 if (INTEGRATOR_STATE(state, path, bounce) == 0) {
2616 INTEGRATOR_STATE_WRITE(state, path, pass_diffuse_weight) = one_spectrum();
2617 INTEGRATOR_STATE_WRITE(state, path, pass_glossy_weight) = zero_spectrum();
2618 }
2619 }
2620
2621 /* Update path state */
2622 INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = phase_pdf;
2623 const float3 previous_P = ray->P + ray->D * ray->tmin;
2624 INTEGRATOR_STATE_WRITE(state, path, mis_origin_n) = sd->P - previous_P;
2625 INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf(
2626 unguided_phase_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf));
2627
2628# ifdef __LIGHT_LINKING__
2629 if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_LINKING) {
2630 INTEGRATOR_STATE_WRITE(state, path, mis_ray_object) = sd->object;
2631 }
2632# endif
2633
2634 path_state_next(kg, state, label, sd->flag);
2635 return true;
2636}
2637
2638ccl_device_inline VolumeIntegrateEvent
2639volume_integrate_event(KernelGlobals kg,
2641 const ccl_private Ray *ccl_restrict ray,
2642 ccl_private ShaderData *sd,
2643 const ccl_private RNGState *rng_state,
2645 ccl_private VolumeIntegrateResult &result)
2646{
2647# if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
2648 /* The current path throughput which is used later to calculate per-segment throughput. */
2649 const float3 initial_throughput = INTEGRATOR_STATE(state, path, throughput);
2650 /* The path throughput used to calculate the throughput for direct light. */
2651 float3 unlit_throughput = initial_throughput;
2652 /* If a new path segment is generated at the direct scatter position. */
2653 bool guiding_generated_new_segment = false;
2654 float rand_phase_guiding = 0.5f;
2655# endif
2656
2657 /* Perform path termination. The intersect_closest will have already marked this path
2658 * to be terminated. That will shading evaluating to leave out any scattering closures,
2659 * but emission and absorption are still handled for multiple importance sampling. */
2660 const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
2661 const float continuation_probability = (path_flag & PATH_RAY_TERMINATE_IN_NEXT_VOLUME) ?
2662 0.0f :
2664 state, path, continuation_probability);
2665 if (continuation_probability == 0.0f) {
2666 return VOLUME_PATH_MISSED;
2667 }
2668
2669 /* Direct light. */
2670 if (result.direct_scatter) {
2671 const float3 direct_P = ray->P + result.direct_t * ray->D;
2672
2673# if defined(__PATH_GUIDING__)
2674 if (kernel_data.integrator.use_guiding) {
2675# if PATH_GUIDING_LEVEL >= 1
2676 if (result.direct_sample_method == VOLUME_SAMPLE_DISTANCE) {
2677 /* If the direct scatter event is generated using VOLUME_SAMPLE_DISTANCE the direct event
2678 * will happen at the same position as the indirect event and the direct light contribution
2679 * will contribute to the position of the next path segment. */
2680 const float3 transmittance_weight = spectrum_to_rgb(
2681 safe_divide_color(result.indirect_throughput, initial_throughput));
2682 guiding_record_volume_transmission(kg, state, transmittance_weight);
2683 guiding_record_volume_segment(kg, state, direct_P, sd->wi);
2684 guiding_generated_new_segment = true;
2685 unlit_throughput = result.indirect_throughput / continuation_probability;
2686 rand_phase_guiding = path_state_rng_1D(kg, rng_state, PRNG_VOLUME_PHASE_GUIDING_DISTANCE);
2687 }
2688 else if (result.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR) {
2689 /* If the direct scatter event is generated using VOLUME_SAMPLE_EQUIANGULAR the direct
2690 * event will happen at a separate position as the indirect event and the direct light
2691 * contribution will contribute to the position of the current/previous path segment. The
2692 * unlit_throughput has to be adjusted to include the scattering at the previous segment.
2693 */
2694 float3 scatterEval = one_float3();
2695 if (INTEGRATOR_STATE(state, guiding, path_segment)) {
2696 const pgl_vec3f scatteringWeight =
2697 INTEGRATOR_STATE(state, guiding, path_segment)->scatteringWeight;
2698 scatterEval = make_float3(scatteringWeight.x, scatteringWeight.y, scatteringWeight.z);
2699 }
2700 unlit_throughput /= scatterEval;
2701 unlit_throughput *= continuation_probability;
2702 rand_phase_guiding = path_state_rng_1D(
2703 kg, rng_state, PRNG_VOLUME_PHASE_GUIDING_EQUIANGULAR);
2704 }
2705# endif
2706# if PATH_GUIDING_LEVEL >= 4
2707 if ((kernel_data.kernel_features & KERNEL_FEATURE_PATH_GUIDING)) {
2708 volume_shader_prepare_guiding(
2709 kg, state, rand_phase_guiding, direct_P, ray->D, &result.direct_phases);
2710 }
2711# endif
2712 }
2713# endif
2714
2715 result.direct_throughput /= continuation_probability;
2716 integrate_volume_direct_light(kg,
2717 state,
2718 sd,
2719 rng_state,
2720 direct_P,
2721 &result.direct_phases,
2722# if defined(__PATH_GUIDING__)
2723 unlit_throughput,
2724# endif
2725 result.direct_throughput,
2726 ls);
2727 }
2728
2729 /* Indirect light.
2730 *
2731 * Only divide throughput by continuation_probability if we scatter. For the attenuation
2732 * case the next surface will already do this division. */
2733 if (result.indirect_scatter) {
2734# if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
2735 if (!guiding_generated_new_segment) {
2736 const float3 transmittance_weight = spectrum_to_rgb(
2737 safe_divide_color(result.indirect_throughput, initial_throughput));
2738 guiding_record_volume_transmission(kg, state, transmittance_weight);
2739 }
2740# endif
2741 result.indirect_throughput /= continuation_probability;
2742 }
2743 INTEGRATOR_STATE_WRITE(state, path, throughput) = result.indirect_throughput;
2744
2745 if (result.indirect_scatter) {
2746 sd->P = ray->P + result.indirect_t * ray->D;
2747
2748# if defined(__PATH_GUIDING__)
2749 if ((kernel_data.kernel_features & KERNEL_FEATURE_PATH_GUIDING)) {
2750# if PATH_GUIDING_LEVEL >= 1
2751 if (!guiding_generated_new_segment) {
2752 guiding_record_volume_segment(kg, state, sd->P, sd->wi);
2753 }
2754# endif
2755# if PATH_GUIDING_LEVEL >= 4
2756 /* If the direct scatter event was generated using VOLUME_SAMPLE_EQUIANGULAR we need to
2757 * initialize the guiding distribution at the indirect scatter position. */
2758 if (result.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR) {
2759 rand_phase_guiding = path_state_rng_1D(kg, rng_state, PRNG_VOLUME_PHASE_GUIDING_DISTANCE);
2760 volume_shader_prepare_guiding(
2761 kg, state, rand_phase_guiding, sd->P, ray->D, &result.indirect_phases);
2762 }
2763# endif
2764 }
2765# endif
2766
2767 if (integrate_volume_phase_scatter(kg, state, sd, ray, rng_state, &result.indirect_phases)) {
2768 return VOLUME_PATH_SCATTERED;
2769 }
2770 return VOLUME_PATH_MISSED;
2771 }
2772# if defined(__PATH_GUIDING__)
2773 /* No guiding if we don't scatter. */
2774 if ((kernel_data.kernel_features & KERNEL_FEATURE_PATH_GUIDING)) {
2775 INTEGRATOR_STATE_WRITE(state, guiding, use_volume_guiding) = false;
2776 }
2777# endif
2778 return VOLUME_PATH_ATTENUATED;
2779}
2780
2781/* get the volume attenuation and emission over line segment defined by
2782 * ray, with the assumption that there are no surfaces blocking light
2783 * between the endpoints. distance sampling is used to decide if we will
2784 * scatter or not. */
2785ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
2789{
2790 kernel_assert(!kernel_data.integrator.volume_ray_marching);
2791
2792 if (integrator_state_volume_stack_is_empty(kg, state)) {
2793 return VOLUME_PATH_ATTENUATED;
2794 }
2795
2796 ShaderData sd;
2797 /* FIXME: `object` is used for light linking. We read the bottom of the stack for simplicity, but
2798 * this does not work for overlapping volumes. */
2799 shader_setup_from_volume(&sd, ray, INTEGRATOR_STATE_ARRAY(state, volume_stack, 0, object));
2800
2801 /* Load random number state. */
2802 RNGState rng_state;
2803 path_state_rng_load(state, &rng_state);
2804
2805 /* For stochastic texture sampling. */
2806 sd.lcg_state = lcg_state_init(
2807 rng_state.rng_pixel, rng_state.rng_offset, rng_state.sample, 0x15b4f88d);
2808
2810
2811 /* TODO: expensive to zero closures? */
2812 VolumeIntegrateResult result = {};
2813 volume_integrate_null_scattering(kg, state, ray, &sd, &rng_state, render_buffer, &ls, result);
2814
2815 return volume_integrate_event(kg, state, ray, &sd, &rng_state, ls, result);
2816}
2817
2818ccl_device VolumeIntegrateEvent
2819volume_integrate_ray_marching(KernelGlobals kg,
2823{
2824 kernel_assert(kernel_data.integrator.volume_ray_marching);
2825
2826 if (integrator_state_volume_stack_is_empty(kg, state)) {
2827 return VOLUME_PATH_ATTENUATED;
2828 }
2829
2830 ShaderData sd;
2831 /* FIXME: `object` is used for light linking. We read the bottom of the stack for simplicity, but
2832 * this does not work for overlapping volumes. */
2833 shader_setup_from_volume(&sd, ray, INTEGRATOR_STATE_ARRAY(state, volume_stack, 0, object));
2834
2835 /* Load random number state. */
2836 RNGState rng_state;
2837 path_state_rng_load(state, &rng_state);
2838
2839 /* For stochastic texture sampling. */
2840 sd.lcg_state = lcg_state_init(
2841 rng_state.rng_pixel, rng_state.rng_offset, rng_state.sample, 0x15b4f88d);
2842
2844
2845 /* TODO: expensive to zero closures? */
2846 VolumeIntegrateResult result = {};
2847
2848 const float step_size = volume_stack_step_size<false>(kg, state);
2849 volume_integrate_ray_marching(
2850 kg, state, ray, &sd, &rng_state, render_buffer, step_size, &ls, result);
2851
2852 return volume_integrate_event(kg, state, ray, &sd, &rng_state, ls, result);
2853}
2854
2855#endif
2856
2857#ifdef __VOLUME__
2858ccl_device_inline void integrator_shade_volume_setup(KernelGlobals kg,
2862{
2864
2865 /* Setup shader data. */
2868
2869 /* Set ray length to current segment. */
2870 ray->tmax = (isect->prim != PRIM_NONE) ? isect->t : FLT_MAX;
2871
2872 /* Clean volume stack for background rays. */
2873 if (isect->prim == PRIM_NONE) {
2874 volume_stack_clean(kg, state);
2875 }
2876
2877 /* Assign flag to transmitted volume rays for scattering probability guiding. */
2878 if (INTEGRATOR_STATE(state, path, bounce) == 0) {
2880 }
2881}
2882#endif
2883
2884template<DeviceKernel volume_kernel>
2886 KernelGlobals kg,
2887 const IntegratorState state,
2890 const VolumeIntegrateEvent event)
2891{
2892 if (event == VOLUME_PATH_MISSED) {
2893 /* End path. */
2894 integrator_path_terminate(kg, state, render_buffer, volume_kernel);
2895 return;
2896 }
2897
2898 if (event == VOLUME_PATH_ATTENUATED) {
2899 /* Continue to background, light or surface. */
2901 return;
2902 }
2903
2904#ifdef __SHADOW_LINKING__
2905 if (shadow_linking_schedule_intersection_kernel<volume_kernel>(kg, state)) {
2906 return;
2907 }
2908#endif /* __SHADOW_LINKING__ */
2909
2910 kernel_assert(event == VOLUME_PATH_SCATTERED);
2911
2912 /* Queue intersect_closest kernel. */
2914}
2915
2919{
2920#ifdef __VOLUME__
2923 integrator_shade_volume_setup(kg, state, &ray, &isect);
2924
2925 const VolumeIntegrateEvent event = volume_integrate(kg, state, &ray, render_buffer);
2927 kg, state, render_buffer, &isect, event);
2928
2929#endif /* __VOLUME__ */
2930}
2931
2935{
2936#ifdef __VOLUME__
2939 integrator_shade_volume_setup(kg, state, &ray, &isect);
2940
2941 const VolumeIntegrateEvent event = volume_integrate_ray_marching(kg, state, &ray, render_buffer);
2943 kg, state, render_buffer, &isect, event);
2944
2945#endif /* __VOLUME__ */
2946}
#define D
MINLINE float power_of_2(float f)
MINLINE float safe_divide(float a, float b)
unsigned int uint
#define UNLIKELY(x)
blender::float3 packed_float3
ccl_device_inline bool area_light_valid_ray_segment(const ccl_global KernelAreaLight *light, float3 P, float3 D, ccl_private Interval< float > *t_range)
Definition area.h:458
#define U
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
static T sum(const btAlignedObjectArray< T > &items)
nullptr float
ccl_device_inline Spectrum safe_divide_color(Spectrum a, Spectrum b, const float fallback=0.0f)
Definition color.h:388
reduce_max(value.rgb)") DEFINE_VALUE("REDUCE(lhs
dot(value.rgb, luminance_coefficients)") DEFINE_VALUE("REDUCE(lhs
ccl_device_inline bool light_sample_terminate(KernelGlobals kg, ccl_private BsdfEval *ccl_restrict eval, const float rand_terminate)
CCL_NAMESPACE_BEGIN ccl_device_noinline_cpu Spectrum light_sample_shader_eval(KernelGlobals kg, IntegratorState state, ccl_private ShaderData *ccl_restrict emission_sd, ccl_private LightSample *ccl_restrict ls, const float time)
ccl_device_inline void light_sample_to_volume_shadow_ray(const ccl_private ShaderData *ccl_restrict sd, const ccl_private LightSample *ccl_restrict ls, const float3 P, ccl_private Ray *ray)
ccl_device_inline float light_sample_mis_weight_nee(KernelGlobals kg, const float nee_pdf, const float forward_pdf)
ccl_device_inline bool light_sample_from_volume_segment(KernelGlobals kg, const float3 rand, const float time, const float3 P, const float3 D, const float t, const int object_receiver, const int bounce, const uint32_t path_flag, ccl_private LightSample *ls)
#define kernel_assert(cond)
#define CLOSURE_IS_VOLUME(type)
#define kernel_data
#define PASS_UNUSED
#define ccl_restrict
#define ccl_device_forceinline
#define KERNEL_FEATURE_PATH_GUIDING
#define ccl_optional_struct_init
#define kernel_data_fetch(name, index)
#define AS_SHADER_DATA(shader_data_tiny_storage)
#define SHADER_NONE
#define one_spectrum
#define PRIM_NONE
#define zero_spectrum
#define OBJECT_NONE
#define make_spectrum(f)
#define ccl_private
const ThreadKernelGlobalsCPU * KernelGlobals
#define ccl_device_inline
#define EMITTER_NONE
#define KERNEL_FEATURE_LIGHT_PASSES
#define __DENOISING_FEATURES__
#define KERNEL_FEATURE_LIGHT_LINKING
#define ccl_global
#define VOLUME_OCTREE_MAX_DEPTH
#define ccl_device_noinline
#define ccl_device_inline_method
#define logf(x)
#define expf(x)
#define CCL_NAMESPACE_END
#define __KERNEL_HIP__
#define saturatef(x)
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
#define roundf(x)
#define __uint_as_float(x)
ccl_device_forceinline float differential_make_compact(const float dD)
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
#define exp
VecBase< float, D > normalize(VecOp< float, D >) RET
#define select(A, B, C)
bool all(VecOp< bool, D >) RET
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
constexpr T clamp(T, U, U) RET
VecBase< float, 3 > float3
VecBase< uint, 3 > uint3
int count
ccl_device_forceinline void integrator_intersect_next_kernel_after_volume(KernelGlobals kg, IntegratorState state, const ccl_private Intersection *ccl_restrict isect, ccl_global float *ccl_restrict render_buffer)
#define VOLUME_THROUGHPUT_EPSILON
ccl_device Spectrum volume_color_transmittance(Spectrum sigma, const float t)
ccl_device int volume_sample_channel(Spectrum albedo, Spectrum throughput, ccl_private float *rand, ccl_private Spectrum *pdf)
ccl_device_inline Spectrum volume_sample_channel_pdf(Spectrum albedo, Spectrum throughput)
ccl_device float volume_channel_get(Spectrum value, const int channel)
ccl_gpu_kernel_postfix ccl_global KernelWorkTile const int ccl_global float * render_buffer
@ OBJECT_INVERSE_TRANSFORM
ccl_device_inline float object_volume_density(KernelGlobals kg, const int object)
ccl_device_inline int object_lightgroup(KernelGlobals kg, const int object)
ccl_device_inline Transform object_fetch_transform(KernelGlobals kg, const int object, enum ObjectTransform type)
ccl_device_forceinline void guiding_record_volume_segment(KernelGlobals kg, IntegratorState state, const float3 P, const float3 I)
ccl_device_forceinline void guiding_record_volume_emission(KernelGlobals kg, IntegratorState state, const Spectrum Le)
ccl_device_forceinline void guiding_record_volume_bounce(KernelGlobals kg, IntegratorState state, const Spectrum weight, const float pdf, const float3 wo, const float roughness)
ccl_device_inline bool light_link_object_match(KernelGlobals kg, const int object_receiver, const int object_emitter)
ccl_device_inline bool light_sample(KernelGlobals kg, const int lamp, const float2 rand, const float3 P, const float3 N, const int shader_flags, const uint32_t path_flag, ccl_private LightSample *ls)
ccl_device_inline int light_link_receiver_nee(KernelGlobals kg, const ccl_private ShaderData *sd)
ccl_device_inline int light_link_receiver_forward(KernelGlobals kg, IntegratorState state)
@ SD_HAS_LIGHT_PATH_NODE
@ SD_EXTINCTION
@ SD_BSDF_HAS_TRANSMISSION
@ SD_SCATTER
@ SD_EMISSION
ShaderData ShaderDataTinyStorage
@ PRNG_BOUNCE_NUM
@ PRNG_VOLUME_PHASE
@ PRNG_LIGHT
@ PRNG_VOLUME_OFFSET
@ PRNG_VOLUME_EXPANSION_ORDER
@ PRNG_VOLUME_SHADE_OFFSET
@ PRNG_VOLUME_RESERVOIR
@ PRNG_VOLUME_PHASE_GUIDING_DISTANCE
@ PRNG_VOLUME_COLOR_CHANNEL
@ PRNG_VOLUME_SCATTER_DISTANCE
@ PATH_RAY_SHADOW
@ PATH_RAY_VOLUME_PASS
@ PATH_RAY_TERMINATE
@ PATH_RAY_EMISSION
@ PATH_RAY_VOLUME_SCATTER
@ PATH_RAY_TERMINATE_IN_NEXT_VOLUME
@ PATH_RAY_DENOISING_FEATURES
@ PATH_RAY_CAMERA
@ PATH_RAY_ANY_PASS
@ PATH_RAY_VOLUME_PRIMARY_TRANSMIT
@ SHADER_EXCLUDE_SCATTER
@ SHADER_MASK
@ SD_OBJECT_TRANSFORM_APPLIED
@ LIGHT_AREA
@ LIGHT_SPOT
@ LIGHT_TRIANGLE
@ LIGHT_POINT
@ DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW
@ DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST
ccl_device_inline uint lcg_state_init(const uint rng_hash, const uint rng_offset, const uint sample, const uint scramble)
Definition lcg.h:45
ccl_device_inline bool triangle_light_valid_ray_segment(KernelGlobals kg, const float3 P, const float3 D, ccl_private Interval< float > *t_range, const ccl_private LightSample *ls)
ccl_device_inline bool bsdf_eval_is_zero(ccl_private BsdfEval *eval)
ccl_device_inline void film_write_volume_emission(KernelGlobals kg, ConstIntegratorState state, const Spectrum L, ccl_global float *ccl_restrict render_buffer, const int lightgroup=LIGHTGROUP_NONE)
ccl_device_inline void bsdf_eval_mul(ccl_private BsdfEval *eval, const float value)
ccl_device_inline Spectrum bsdf_eval_sum(const ccl_private BsdfEval *eval)
OrientationBounds merge(const OrientationBounds &cone_a, const OrientationBounds &cone_b)
ccl_device_inline uint count_leading_zeros(const uint x)
Definition math_base.h:707
ccl_device_inline bool isfinite_safe(const float f)
Definition math_base.h:348
ccl_device_inline dual1 reduce_add(const dual< T > a)
Definition math_dual.h:49
ccl_device_inline float fast_logf(const float x)
Definition math_fast.h:379
ccl_device_inline bool is_zero(const float2 a)
ccl_device_inline float reduce_min(const float2 a)
ccl_device_inline float2 fabs(const float2 a)
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
CCL_NAMESPACE_BEGIN ccl_device_inline float3 zero_float3()
Definition math_float3.h:17
ccl_device_inline uint3 float3_as_uint3(const float3 f)
static ulong state[N]
#define N
#define PROFILING_INIT(kg, event)
ccl_device float power_heuristic(const float a, const float b)
Definition mis.h:26
MatBase< T, NumCol, NumRow > scale(const MatBase< T, NumCol, NumRow > &mat, const VectorT &scale)
closure color scatter(string phase, float Anisotropy, float IOR, float Backscatter, float Alpha, float Diameter)
ccl_device_inline float path_state_rng_1D(KernelGlobals kg, const ccl_private RNGState *rng_state, const int dimension)
Definition path_state.h:353
ccl_device_inline void shadow_path_state_rng_load(ConstIntegratorShadowState state, ccl_private RNGState *rng_state)
Definition path_state.h:337
ccl_device_inline float path_state_rng_light_termination(KernelGlobals kg, const ccl_private RNGState *state)
Definition path_state.h:416
ccl_device_inline void path_state_rng_load(ConstIntegratorState state, ccl_private RNGState *rng_state)
Definition path_state.h:329
ccl_device_inline void path_state_next(KernelGlobals kg, IntegratorState state, const int label, const int shader_flag)
Definition path_state.h:110
ccl_device_inline void path_state_rng_scramble(ccl_private RNGState *rng_state, const int seed)
Definition path_state.h:345
ccl_device_inline float2 path_state_rng_2D(KernelGlobals kg, const ccl_private RNGState *rng_state, const int dimension)
Definition path_state.h:361
ccl_device_inline float3 path_state_rng_3D(KernelGlobals kg, const ccl_private RNGState *rng_state, const int dimension)
Definition path_state.h:369
ccl_device_forceinline float path_rng_1D(KernelGlobals kg, const uint rng_pixel, const uint sample, const int dimension)
Definition pattern.h:66
@ PROFILING_SHADE_VOLUME_INTEGRATE
Definition profiling.h:34
@ PROFILING_SHADE_VOLUME_SETUP
Definition profiling.h:33
@ PROFILING_SHADE_VOLUME_DIRECT_LIGHT
Definition profiling.h:35
@ PROFILING_SHADE_VOLUME_INDIRECT_LIGHT
Definition profiling.h:36
#define mix
#define fabsf
#define ccl_device
#define fmaxf
#define tanf
#define fminf
#define atan2f
ccl_device_inline float sample_exponential_distribution(const float rand, const float lambda)
ccl_device_inline int sample_geometric_distribution(const float rand, const float r, ccl_private float &pmf, const int cut_off=INT_MAX)
ccl_device_inline Spectrum pdf_exponential_distribution(const float x, const Spectrum lambda, const Interval< float > t)
ccl_device void integrator_shade_volume_ray_marching(KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer)
ccl_device void integrator_shade_volume(KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer)
CCL_NAMESPACE_BEGIN ccl_device_inline void integrator_next_kernel_after_shade_volume(KernelGlobals kg, const IntegratorState state, ccl_global float *ccl_restrict render_buffer, const ccl_private Intersection *ccl_restrict isect, const VolumeIntegrateEvent event)
#define min(a, b)
Definition sort.cc:36
ccl_device_inline bool spot_light_valid_ray_segment(KernelGlobals kg, const ccl_global KernelLight *klight, const float3 P, const float3 D, ccl_private Interval< float > *t_range)
Definition spot.h:277
IntegratorShadowStateCPU * IntegratorShadowState
Definition state.h:230
#define INTEGRATOR_STATE_WRITE(state, nested_struct, member)
Definition state.h:236
#define INTEGRATOR_STATE(state, nested_struct, member)
Definition state.h:235
#define INTEGRATOR_STATE_ARRAY(state, nested_struct, array_index, member)
Definition state.h:238
IntegratorStateCPU * IntegratorState
Definition state.h:228
const IntegratorStateCPU * ConstIntegratorState
Definition state.h:229
ccl_device_forceinline void integrator_path_next(IntegratorState state, const DeviceKernel current_kernel, const DeviceKernel next_kernel)
Definition state_flow.h:195
ccl_device_forceinline void integrator_path_terminate(KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer, const DeviceKernel current_kernel)
Definition state_flow.h:203
ccl_device_forceinline IntegratorShadowState integrator_shadow_path_init(KernelGlobals kg, IntegratorState state, const DeviceKernel next_kernel, const bool is_ao)
Definition state_flow.h:225
ccl_device_forceinline void integrator_state_write_shadow_ray_self(IntegratorShadowState state, const ccl_private Ray *ccl_restrict ray)
Definition state_util.h:98
ccl_device_forceinline void integrator_state_read_ray(ConstIntegratorState state, ccl_private Ray *ccl_restrict ray)
Definition state_util.h:55
ccl_device_forceinline void integrator_state_write_shadow_ray(IntegratorShadowState state, const ccl_private Ray *ccl_restrict ray)
Definition state_util.h:75
ccl_device_forceinline void integrator_state_read_isect(ConstIntegratorState state, ccl_private Intersection *ccl_restrict isect)
Definition state_util.h:179
#define FLT_MAX
Definition stdcycles.h:14
ccl_device_inline_method T range() const
Definition math_base.h:915
ccl_device_inline_method T length() const
Definition math_base.h:885
ccl_device_inline_method bool is_empty() const
Definition math_base.h:874
uint rng_pixel
Definition path_state.h:324
uint rng_offset
Definition path_state.h:325
float y
uint y
Definition types_uint3.h:13
uint z
Definition types_uint3.h:13
uint x
Definition types_uint3.h:13
i
Definition text_draw.cc:230
ccl_device_inline float3 transform_direction(const ccl_private Transform *t, const float3 a)
Definition transform.h:127
ccl_device_inline float3 transform_point(const ccl_private Transform *t, const float3 a)
Definition transform.h:56
float3 Spectrum
packed_float3 PackedSpectrum
uint len
uint8_t flag
Definition wm_window.cc:145
CCL_NAMESPACE_BEGIN ccl_device_forceinline ccl_global float * film_pass_pixel_render_buffer(KernelGlobals kg, ConstIntegratorState state, ccl_global float *ccl_restrict render_buffer)
Definition write.h:24
ccl_device_inline float3 kernel_read_pass_rgbe(const ccl_global float *ccl_restrict buffer)
Definition write.h:153