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