Blender V4.3
volume_shader.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/* Volume shader evaluation and sampling. */
6
7#pragma once
8
10#include "kernel/closure/bsdf.h"
13
14#ifdef __SVM__
15# include "kernel/svm/svm.h"
16#endif
17#ifdef __OSL__
18# include "kernel/osl/osl.h"
19#endif
20
22
23#ifdef __VOLUME__
24
25/* Merging */
26
27ccl_device_inline void volume_shader_merge_closures(ccl_private ShaderData *sd)
28{
29 /* Merge identical closures to save closure space with stacked volumes. */
30 for (int i = 0; i < sd->num_closure; i++) {
31 ccl_private ShaderClosure *sci = &sd->closure[i];
32
33 if (!CLOSURE_IS_VOLUME_SCATTER(sci->type)) {
34 continue;
35 }
36
37 for (int j = i + 1; j < sd->num_closure; j++) {
38 ccl_private ShaderClosure *scj = &sd->closure[j];
39 if (!volume_phase_equal(sci, scj)) {
40 continue;
41 }
42
43 sci->weight += scj->weight;
44 sci->sample_weight += scj->sample_weight;
45
46 int size = sd->num_closure - (j + 1);
47 if (size > 0) {
48 for (int k = 0; k < size; k++) {
49 scj[k] = scj[k + 1];
50 }
51 }
52
53 sd->num_closure--;
54 kernel_assert(sd->num_closure >= 0);
55 j--;
56 }
57 }
58}
59
61 phases,
63{
64 phases->num_closure = 0;
65
66 for (int i = 0; i < sd->num_closure; i++) {
67 ccl_private const ShaderClosure *from_sc = &sd->closure[i];
68 if (CLOSURE_IS_VOLUME_SCATTER(from_sc->type)) {
69 /* ShaderVolumeClosure is a subset of ShaderClosure, so this is fine for all volume scatter
70 * closures. */
71 phases->closure[phases->num_closure++] = *((ccl_private const ShaderVolumeClosure *)from_sc);
72 if (phases->num_closure >= MAX_VOLUME_CLOSURE) {
73 break;
74 }
75 }
76 }
77}
78
79/* Guiding */
80
81# ifdef __PATH_GUIDING__
82ccl_device_inline void volume_shader_prepare_guiding(KernelGlobals kg,
85 float rand_phase_guiding,
86 const float3 P,
87 const float3 D,
89{
90 /* Have any phase functions to guide? */
91 const int num_phases = phases->num_closure;
92 if (!kernel_data.integrator.use_volume_guiding || num_phases == 0) {
93 state->guiding.use_volume_guiding = false;
94 return;
95 }
96
97 const float volume_guiding_probability = kernel_data.integrator.volume_guiding_probability;
98
99 /* If we have more than one phase function we select one random based on its
100 * sample weight to calculate the product distribution for guiding. */
101 int phase_id = 0;
102 float phase_weight = 1.0f;
103
104 if (num_phases > 1) {
105 /* Pick a phase closure based on sample weights. */
106 float sum = 0.0f;
107
108 for (phase_id = 0; phase_id < num_phases; phase_id++) {
109 ccl_private const ShaderVolumeClosure *svc = &phases->closure[phase_id];
110 sum += svc->sample_weight;
111 }
112
113 float r = rand_phase_guiding * sum;
114 float partial_sum = 0.0f;
115
116 for (phase_id = 0; phase_id < num_phases; phase_id++) {
117 ccl_private const ShaderVolumeClosure *svc = &phases->closure[phase_id];
118 float next_sum = partial_sum + svc->sample_weight;
119
120 if (r <= next_sum) {
121 /* Rescale to reuse. */
122 rand_phase_guiding = (r - partial_sum) / svc->sample_weight;
123 phase_weight = svc->sample_weight / sum;
124 break;
125 }
126
127 partial_sum = next_sum;
128 }
129
130 /* Adjust the sample weight of the component used for guiding. */
131 phases->closure[phase_id].sample_weight *= volume_guiding_probability;
132 }
133
134 /* Init guiding for selected phase function. */
135 ccl_private const ShaderVolumeClosure *svc = &phases->closure[phase_id];
136 const float phase_g = volume_phase_get_g(svc);
137 if (!guiding_phase_init(kg, state, P, D, phase_g, rand_phase_guiding)) {
138 state->guiding.use_volume_guiding = false;
139 return;
140 }
141
142 state->guiding.use_volume_guiding = true;
143 state->guiding.sample_volume_guiding_rand = rand_phase_guiding;
144 state->guiding.volume_guiding_sampling_prob = volume_guiding_probability * phase_weight;
145
146 kernel_assert(state->guiding.volume_guiding_sampling_prob > 0.0f &&
147 state->guiding.volume_guiding_sampling_prob <= 1.0f);
148}
149# endif
150
151/* Phase Evaluation & Sampling */
152
153/* Randomly sample a volume phase function proportional to ShaderClosure.sample_weight. */
154/* TODO: this isn't quite correct, we don't weight anisotropy properly depending on color channels,
155 * even if this is perhaps not a common case */
156ccl_device_inline ccl_private const ShaderVolumeClosure *volume_shader_phase_pick(
157 ccl_private const ShaderVolumePhases *phases, ccl_private float2 *rand_phase)
158{
159 int sampled = 0;
160
161 if (phases->num_closure > 1) {
162 /* Pick a phase closure based on sample weights. */
163 /* For reservoir sampling, always accept the first in the stream. */
164 float sum = phases->closure[0].sample_weight;
165
166 for (int i = 1; i < phases->num_closure; i++) {
167 const float sample_weight = phases->closure[i].sample_weight;
168 sum += sample_weight;
169 const float thresh = sample_weight / sum;
170
171 /* Rescale random number to reuse for volume phase direction sample. */
172 if (rand_phase->x < thresh) {
173 sampled = i;
174 rand_phase->x /= thresh;
175 }
176 else {
177 rand_phase->x = (rand_phase->x - thresh) / (1.0f - thresh);
178 }
179 }
180 }
181
182 return &phases->closure[sampled];
183}
184
185ccl_device_inline float _volume_shader_phase_eval_mis(ccl_private const ShaderData *sd,
186 ccl_private const ShaderVolumePhases *phases,
187 const float3 wo,
188 int skip_phase,
189 ccl_private BsdfEval *result_eval,
190 float sum_pdf,
191 float sum_sample_weight)
192{
193 for (int i = 0; i < phases->num_closure; i++) {
194 if (i == skip_phase)
195 continue;
196
197 ccl_private const ShaderVolumeClosure *svc = &phases->closure[i];
198 float phase_pdf = 0.0f;
199 Spectrum eval = volume_phase_eval(sd, svc, wo, &phase_pdf);
200
201 if (phase_pdf != 0.0f) {
202 bsdf_eval_accum(result_eval, eval * svc->sample_weight);
203 sum_pdf += phase_pdf * svc->sample_weight;
204 }
205
206 sum_sample_weight += svc->sample_weight;
207 }
208
209 bsdf_eval_mul(result_eval, 1.0f / sum_sample_weight);
210 return (sum_sample_weight > 0.0f) ? sum_pdf / sum_sample_weight : 0.0f;
211}
212
213ccl_device float volume_shader_phase_eval(KernelGlobals kg,
214 ccl_private const ShaderData *sd,
216 const float3 wo,
217 ccl_private BsdfEval *phase_eval)
218{
219 float phase_pdf = 0.0f;
220 Spectrum eval = volume_phase_eval(sd, svc, wo, &phase_pdf);
221
222 if (phase_pdf != 0.0f) {
223 bsdf_eval_accum(phase_eval, eval);
224 }
225
226 return phase_pdf;
227}
228
229ccl_device float volume_shader_phase_eval(KernelGlobals kg,
231 ccl_private const ShaderData *sd,
232 ccl_private const ShaderVolumePhases *phases,
233 const float3 wo,
234 ccl_private BsdfEval *phase_eval,
235 const uint light_shader_flags)
236{
237 bsdf_eval_init(phase_eval, zero_spectrum());
238
239 float pdf = _volume_shader_phase_eval_mis(sd, phases, wo, -1, phase_eval, 0.0f, 0.0f);
240
241# if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 4
242 if (state->guiding.use_volume_guiding) {
243 const float guiding_sampling_prob = state->guiding.volume_guiding_sampling_prob;
244 const float guide_pdf = guiding_phase_pdf(kg, state, wo);
245 pdf = (guiding_sampling_prob * guide_pdf) + (1.0f - guiding_sampling_prob) * pdf;
246 }
247# endif
248
249 /* If the light does not use MIS, then it is only sampled via NEE, so the probability of hitting
250 * the light using BSDF sampling is zero. */
251 if (!(light_shader_flags & SHADER_USE_MIS)) {
252 pdf = 0.0f;
253 }
254
255 return pdf;
256}
257
258# ifdef __PATH_GUIDING__
259ccl_device int volume_shader_phase_guided_sample(KernelGlobals kg,
261 ccl_private const ShaderData *sd,
263 const float2 rand_phase,
264 ccl_private BsdfEval *phase_eval,
266 ccl_private float *phase_pdf,
267 ccl_private float *unguided_phase_pdf,
268 ccl_private float *sampled_roughness)
269{
270 const bool use_volume_guiding = state->guiding.use_volume_guiding;
271 const float guiding_sampling_prob = state->guiding.volume_guiding_sampling_prob;
272
273 /* Decide between sampling guiding distribution and phase. */
274 float rand_phase_guiding = state->guiding.sample_volume_guiding_rand;
275 bool sample_guiding = false;
276 if (use_volume_guiding && rand_phase_guiding < guiding_sampling_prob) {
277 sample_guiding = true;
278 rand_phase_guiding /= guiding_sampling_prob;
279 }
280 else {
281 rand_phase_guiding -= guiding_sampling_prob;
282 rand_phase_guiding /= (1.0f - guiding_sampling_prob);
283 }
284
285 /* Initialize to zero. */
286 int label = LABEL_NONE;
287 Spectrum eval = zero_spectrum();
288
289 *unguided_phase_pdf = 0.0f;
290 float guide_pdf = 0.0f;
291 *sampled_roughness = 1.0f - fabsf(volume_phase_get_g(svc));
292
293 bsdf_eval_init(phase_eval, zero_spectrum());
294
295 if (sample_guiding) {
296 /* Sample guiding distribution. */
297 guide_pdf = guiding_phase_sample(kg, state, rand_phase, wo);
298 *phase_pdf = 0.0f;
299
300 if (guide_pdf != 0.0f) {
301 *unguided_phase_pdf = volume_shader_phase_eval(kg, sd, svc, *wo, phase_eval);
302 *phase_pdf = (guiding_sampling_prob * guide_pdf) +
303 ((1.0f - guiding_sampling_prob) * (*unguided_phase_pdf));
305 }
306 }
307 else {
308 /* Sample phase. */
309 *phase_pdf = 0.0f;
310 label = volume_phase_sample(sd, svc, rand_phase, &eval, wo, unguided_phase_pdf);
311
312 if (*unguided_phase_pdf != 0.0f) {
313 bsdf_eval_init(phase_eval, eval);
314
315 *phase_pdf = *unguided_phase_pdf;
316 if (use_volume_guiding) {
317 guide_pdf = guiding_phase_pdf(kg, state, *wo);
318 *phase_pdf *= 1.0f - guiding_sampling_prob;
319 *phase_pdf += guiding_sampling_prob * guide_pdf;
320 }
321
322 kernel_assert(reduce_min(bsdf_eval_sum(phase_eval)) >= 0.0f);
323 }
324 else {
325 bsdf_eval_init(phase_eval, zero_spectrum());
326 }
327
328 kernel_assert(reduce_min(bsdf_eval_sum(phase_eval)) >= 0.0f);
329 }
330
331 return label;
332}
333# endif
334
335ccl_device int volume_shader_phase_sample(KernelGlobals kg,
336 ccl_private const ShaderData *sd,
337 ccl_private const ShaderVolumePhases *phases,
339 float2 rand_phase,
340 ccl_private BsdfEval *phase_eval,
342 ccl_private float *pdf,
343 ccl_private float *sampled_roughness)
344{
345 *sampled_roughness = 1.0f - fabsf(volume_phase_get_g(svc));
346 Spectrum eval = zero_spectrum();
347
348 *pdf = 0.0f;
349 int label = volume_phase_sample(sd, svc, rand_phase, &eval, wo, pdf);
350
351 if (*pdf != 0.0f) {
352 bsdf_eval_init(phase_eval, eval);
353 }
354
355 return label;
356}
357
358/* Motion Blur */
359
360# ifdef __OBJECT_MOTION__
361ccl_device_inline void volume_shader_motion_blur(KernelGlobals kg,
363{
364 if ((sd->object_flag & SD_OBJECT_HAS_VOLUME_MOTION) == 0) {
365 return;
366 }
367
370
371 const float3 P = sd->P;
372 const float velocity_scale = kernel_data_fetch(objects, sd->object).velocity_scale;
373 const float time_offset = kernel_data.cam.motion_position == MOTION_POSITION_CENTER ? 0.5f :
374 0.0f;
375 const float time = kernel_data.cam.motion_position == MOTION_POSITION_END ?
376 (1.0f - kernel_data.cam.shuttertime) + sd->time :
377 sd->time;
378
379 /* Use a 1st order semi-lagrangian advection scheme to estimate what volume quantity
380 * existed, or will exist, at the given time:
381 *
382 * `phi(x, T) = phi(x - (T - t) * u(x, T), t)`
383 *
384 * where
385 *
386 * x : position
387 * T : super-sampled time (or ray time)
388 * t : current time of the simulation (in rendering we assume this is center frame with
389 * relative time = 0)
390 * phi : the volume quantity
391 * u : the velocity field
392 *
393 * But first we need to determine the velocity field `u(x, T)`, which we can estimate also
394 * using semi-lagrangian advection.
395 *
396 * `u(x, T) = u(x - (T - t) * u(x, T), t)`
397 *
398 * This is the typical way to model self-advection in fluid dynamics, however, we do not
399 * account for other forces affecting the velocity during simulation (pressure, buoyancy,
400 * etc.): this gives a linear interpolation when fluid are mostly "curvy". For better
401 * results, a higher order interpolation scheme can be used (at the cost of more lookups),
402 * or an interpolation of the velocity fields for the previous and next frames could also
403 * be used to estimate `u(x, T)` (which will cost more memory and lookups).
404 *
405 * References:
406 * "Eulerian Motion Blur", Kim and Ko, 2007
407 * "Production Volume Rendering", Wreninge et al., 2012
408 */
409
410 /* Find velocity. */
411 float3 velocity = primitive_volume_attribute_float3(kg, sd, v_desc);
412 object_dir_transform(kg, sd, &velocity);
413
414 /* Find advected P. */
415 sd->P = P - (time - time_offset) * velocity_scale * velocity;
416
417 /* Find advected velocity. */
418 velocity = primitive_volume_attribute_float3(kg, sd, v_desc);
419 object_dir_transform(kg, sd, &velocity);
420
421 /* Find advected P. */
422 sd->P = P - (time - time_offset) * velocity_scale * velocity;
423}
424# endif
425
426/* Volume Evaluation */
427
428template<const bool shadow, typename StackReadOp, typename ConstIntegratorGenericState>
429ccl_device_inline void volume_shader_eval(KernelGlobals kg,
430 ConstIntegratorGenericState state,
432 const uint32_t path_flag,
433 StackReadOp stack_read)
434{
435 /* If path is being terminated, we are tracing a shadow ray or evaluating
436 * emission, then we don't need to store closures. The emission and shadow
437 * shader data also do not have a closure array to save GPU memory. */
438 int max_closures;
440 max_closures = 0;
441 }
442 else {
443 max_closures = kernel_data.max_closures;
444 }
445
446 /* reset closures once at the start, we will be accumulating the closures
447 * for all volumes in the stack into a single array of closures */
448 sd->num_closure = 0;
449 sd->num_closure_left = max_closures;
450 sd->flag = SD_IS_VOLUME_SHADER_EVAL;
451 sd->object_flag = 0;
452
453 for (int i = 0;; i++) {
454 const VolumeStack entry = stack_read(i);
455 if (entry.shader == SHADER_NONE) {
456 break;
457 }
458
459 /* Setup shader-data from stack. it's mostly setup already in
460 * shader_setup_from_volume, this switching should be quick. */
461 sd->object = entry.object;
462 sd->lamp = LAMP_NONE;
463 sd->shader = entry.shader;
464
465 sd->flag &= ~SD_SHADER_FLAGS;
466 sd->flag |= kernel_data_fetch(shaders, (sd->shader & SHADER_MASK)).flags;
467 sd->object_flag &= ~SD_OBJECT_FLAGS;
468
469 if (sd->object != OBJECT_NONE) {
470 sd->object_flag |= kernel_data_fetch(object_flag, sd->object);
471
472 if (shadow && !(kernel_data_fetch(objects, sd->object).visibility &
473 (path_flag & PATH_RAY_ALL_VISIBILITY)))
474 {
475 /* If volume is invisible to shadow ray, the hit is not registered, but the volume is still
476 * in the stack. Skip the volume in such cases. */
477 /* NOTE: `SHADOW_CATCHER_PATH_VISIBILITY()` is omitted because `path_flag` is just
478 * `PATH_RAY_SHADOW` when evaluating shadows. */
479 continue;
480 }
481
482# ifdef __OBJECT_MOTION__
483 /* todo: this is inefficient for motion blur, we should be
484 * caching matrices instead of recomputing them each step */
485 shader_setup_object_transforms(kg, sd, sd->time);
486
487 volume_shader_motion_blur(kg, sd);
488# endif
489 }
490
491 /* evaluate shader */
492# ifdef __OSL__
493 if (kernel_data.kernel_features & KERNEL_FEATURE_OSL) {
494 osl_eval_nodes<SHADER_TYPE_VOLUME>(kg, state, sd, path_flag);
495 }
496 else
497# endif
498 {
499# ifdef __SVM__
501 kg, state, sd, NULL, path_flag);
502# endif
503 }
504
505 /* Merge closures to avoid exceeding number of closures limit. */
506 if (!shadow) {
507 if (i > 0) {
508 volume_shader_merge_closures(sd);
509 }
510 }
511 }
512}
513
514#endif /* __VOLUME__ */
515
unsigned int uint
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
static T sum(const btAlignedObjectArray< T > &items)
void osl_eval_nodes< SHADER_TYPE_VOLUME >(const KernelGlobalsCPU *kg, const void *state, ShaderData *sd, uint32_t path_flag)
Definition closures.cpp:210
const char * label
#define kernel_assert(cond)
#define kernel_data
const KernelGlobalsCPU *ccl_restrict KernelGlobals
#define kernel_data_fetch(name, index)
#define ccl_restrict
#define ccl_device
#define ccl_private
#define ccl_device_inline
#define CCL_NAMESPACE_END
#define NULL
#define fabsf(x)
ccl_device bool volume_phase_equal(ccl_private const ShaderClosure *c1, ccl_private const ShaderClosure *c2)
ccl_device int volume_phase_sample(ccl_private const ShaderData *sd, ccl_private const ShaderVolumeClosure *svc, float2 rand, ccl_private Spectrum *eval, ccl_private float3 *wo, ccl_private float *pdf)
ccl_device Spectrum volume_phase_eval(ccl_private const ShaderData *sd, ccl_private const ShaderVolumeClosure *svc, float3 wo, ccl_private float *pdf)
ccl_device float volume_phase_get_g(ccl_private const ShaderVolumeClosure *svc)
ccl_device_inline void object_dir_transform(KernelGlobals kg, ccl_private const ShaderData *sd, ccl_private float3 *D)
ccl_device_forceinline float guiding_phase_sample(KernelGlobals kg, IntegratorState state, const float2 rand_phase, ccl_private float3 *wo)
ccl_device_forceinline float guiding_phase_pdf(KernelGlobals kg, IntegratorState state, const float3 wo)
ccl_device_forceinline bool guiding_phase_init(KernelGlobals kg, IntegratorState state, const float3 P, const float3 D, const float g, ccl_private float &rand)
ccl_device void svm_eval_nodes(KernelGlobals kg, ConstIntegratorGenericState state, ccl_private ShaderData *sd, ccl_global float *render_buffer, uint32_t path_flag)
#define CLOSURE_IS_VOLUME_SCATTER(type)
@ SD_IS_VOLUME_SHADER_EVAL
#define MAX_VOLUME_CLOSURE
@ ATTR_STD_NOT_FOUND
@ ATTR_STD_VOLUME_VELOCITY
#define SHADER_NONE
#define KERNEL_FEATURE_OSL
@ PATH_RAY_SHADOW
@ PATH_RAY_TERMINATE
@ PATH_RAY_EMISSION
@ PATH_RAY_ALL_VISIBILITY
#define OBJECT_NONE
ShaderData
@ SHADER_USE_MIS
@ SHADER_MASK
@ MOTION_POSITION_END
@ MOTION_POSITION_CENTER
@ SD_OBJECT_HAS_VOLUME_MOTION
@ LABEL_VOLUME_SCATTER
@ LABEL_NONE
ShaderClosure
#define LAMP_NONE
ccl_device_inline Spectrum bsdf_eval_sum(ccl_private const BsdfEval *eval)
ccl_device_inline void bsdf_eval_mul(ccl_private BsdfEval *eval, float value)
CCL_NAMESPACE_BEGIN ccl_device_inline void bsdf_eval_init(ccl_private BsdfEval *eval, ccl_private const ShaderClosure *sc, const float3 wo, Spectrum value)
ccl_device_inline void bsdf_eval_accum(ccl_private BsdfEval *eval, ccl_private const ShaderClosure *sc, const float3 wo, Spectrum value)
ccl_device_inline float reduce_min(const float2 a)
static ulong state[N]
CCL_NAMESPACE_BEGIN ccl_device void shader_setup_object_transforms(KernelGlobals kg, ccl_private ShaderData *ccl_restrict sd, float time)
Definition shader_data.h:17
IntegratorStateCPU *ccl_restrict IntegratorState
Definition state.h:228
unsigned int uint32_t
Definition stdint.h:80
static bool find_attribute(const std::string &attributes, const char *search_attribute)
#define zero_spectrum
SPECTRUM_DATA_TYPE Spectrum