Blender  V3.3
volume_stack.h
Go to the documentation of this file.
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright 2011-2022 Blender Foundation */
3 
4 #pragma once
5 
7 
8 /* Volumetric read/write lambda functions - default implementations */
9 #ifndef VOLUME_READ_LAMBDA
10 # define VOLUME_READ_LAMBDA(function_call) \
11  auto volume_read_lambda_pass = [=](const int i) { return function_call; };
12 # define VOLUME_WRITE_LAMBDA(function_call) \
13  auto volume_write_lambda_pass = [=](const int i, VolumeStack entry) { function_call; };
14 #endif
15 
16 /* Volume Stack
17  *
18  * This is an array of object/shared ID's that the current segment of the path
19  * is inside of. */
20 
21 template<typename StackReadOp, typename StackWriteOp>
23  ccl_private const ShaderData *sd,
24  StackReadOp stack_read,
25  StackWriteOp stack_write)
26 {
27  /* todo: we should have some way for objects to indicate if they want the
28  * world shader to work inside them. excluding it by default is problematic
29  * because non-volume objects can't be assumed to be closed manifolds */
30  if (!(sd->flag & SD_HAS_VOLUME)) {
31  return;
32  }
33 
34  if (sd->flag & SD_BACKFACING) {
35  /* Exit volume object: remove from stack. */
36  for (int i = 0;; i++) {
37  VolumeStack entry = stack_read(i);
38  if (entry.shader == SHADER_NONE) {
39  break;
40  }
41 
42  if (entry.object == sd->object && entry.shader == sd->shader) {
43  /* Shift back next stack entries. */
44  do {
45  entry = stack_read(i + 1);
46  stack_write(i, entry);
47  i++;
48  } while (entry.shader != SHADER_NONE);
49 
50  return;
51  }
52  }
53  }
54  else {
55  /* Enter volume object: add to stack. */
56  int i;
57  for (i = 0;; i++) {
58  VolumeStack entry = stack_read(i);
59  if (entry.shader == SHADER_NONE) {
60  break;
61  }
62 
63  /* Already in the stack? then we have nothing to do. */
64  if (entry.object == sd->object && entry.shader == sd->shader) {
65  return;
66  }
67  }
68 
69  /* If we exceed the stack limit, ignore. */
70  if (i >= kernel_data.volume_stack_size - 1) {
71  return;
72  }
73 
74  /* Add to the end of the stack. */
75  const VolumeStack new_entry = {sd->object, sd->shader};
76  const VolumeStack empty_entry = {OBJECT_NONE, SHADER_NONE};
77  stack_write(i, new_entry);
78  stack_write(i + 1, empty_entry);
79  }
80 }
81 
84  ccl_private const ShaderData *sd)
85 {
88  volume_stack_enter_exit(kg, sd, volume_read_lambda_pass, volume_write_lambda_pass);
89 }
90 
93  ccl_private const ShaderData *sd)
94 {
97  volume_stack_enter_exit(kg, sd, volume_read_lambda_pass, volume_write_lambda_pass);
98 }
99 
100 /* Clean stack after the last bounce.
101  *
102  * It is expected that all volumes are closed manifolds, so at the time when ray
103  * hits nothing (for example, it is a last bounce which goes to environment) the
104  * only expected volume in the stack is the world's one. All the rest volume
105  * entries should have been exited already.
106  *
107  * This isn't always true because of ray intersection precision issues, which
108  * could lead us to an infinite non-world volume in the stack, causing render
109  * artifacts.
110  *
111  * Use this function after the last bounce to get rid of all volumes apart from
112  * the world's one after the last bounce to avoid render artifacts.
113  */
115 {
116  if (kernel_data.background.volume_shader != SHADER_NONE) {
117  /* Keep the world's volume in stack. */
118  INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, 1, shader) = SHADER_NONE;
119  }
120  else {
121  INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, 0, shader) = SHADER_NONE;
122  }
123 }
124 
125 template<typename StackReadOp>
126 ccl_device float volume_stack_step_size(KernelGlobals kg, StackReadOp stack_read)
127 {
128  float step_size = FLT_MAX;
129 
130  for (int i = 0;; i++) {
131  VolumeStack entry = stack_read(i);
132  if (entry.shader == SHADER_NONE) {
133  break;
134  }
135 
136  int shader_flag = kernel_data_fetch(shaders, (entry.shader & SHADER_MASK)).flags;
137 
138  bool heterogeneous = false;
139 
140  if (shader_flag & SD_HETEROGENEOUS_VOLUME) {
141  heterogeneous = true;
142  }
143  else if (shader_flag & SD_NEED_VOLUME_ATTRIBUTES) {
144  /* We want to render world or objects without any volume grids
145  * as homogeneous, but can only verify this at run-time since other
146  * heterogeneous volume objects may be using the same shader. */
147  int object = entry.object;
148  if (object != OBJECT_NONE) {
149  int object_flag = kernel_data_fetch(object_flag, object);
150  if (object_flag & SD_OBJECT_HAS_VOLUME_ATTRIBUTES) {
151  heterogeneous = true;
152  }
153  }
154  }
155 
156  if (heterogeneous) {
157  float object_step_size = object_volume_step_size(kg, entry.object);
158  object_step_size *= kernel_data.integrator.volume_step_rate;
159  step_size = fminf(object_step_size, step_size);
160  }
161  }
162 
163  return step_size;
164 }
165 
166 typedef enum VolumeSampleMethod {
172 
174 {
176 
177  for (int i = 0;; i++) {
179  if (entry.shader == SHADER_NONE) {
180  break;
181  }
182 
183  int shader_flag = kernel_data_fetch(shaders, (entry.shader & SHADER_MASK)).flags;
184 
185  if (shader_flag & SD_VOLUME_MIS) {
186  /* Multiple importance sampling. */
187  return VOLUME_SAMPLE_MIS;
188  }
189  else if (shader_flag & SD_VOLUME_EQUIANGULAR) {
190  /* Distance + equiangular sampling -> multiple importance sampling. */
191  if (method == VOLUME_SAMPLE_DISTANCE) {
192  return VOLUME_SAMPLE_MIS;
193  }
194 
195  /* Only equiangular sampling. */
196  method = VOLUME_SAMPLE_EQUIANGULAR;
197  }
198  else {
199  /* Distance + equiangular sampling -> multiple importance sampling. */
200  if (method == VOLUME_SAMPLE_EQUIANGULAR) {
201  return VOLUME_SAMPLE_MIS;
202  }
203 
204  /* Distance sampling only. */
205  method = VOLUME_SAMPLE_DISTANCE;
206  }
207  }
208 
209  return method;
210 }
211 
#define ccl_device
Definition: cuda/compat.h:32
#define ccl_private
Definition: cuda/compat.h:48
#define ccl_device_inline
Definition: cuda/compat.h:34
#define CCL_NAMESPACE_END
Definition: cuda/compat.h:9
#define kernel_data
const KernelGlobalsCPU *ccl_restrict KernelGlobals
#define kernel_data_fetch(name, index)
const int state
ccl_device_inline float object_volume_step_size(KernelGlobals kg, int object)
@ SD_VOLUME_MIS
Definition: kernel/types.h:778
@ SD_VOLUME_EQUIANGULAR
Definition: kernel/types.h:776
@ SD_BACKFACING
Definition: kernel/types.h:738
@ SD_HAS_VOLUME
Definition: kernel/types.h:768
@ SD_NEED_VOLUME_ATTRIBUTES
Definition: kernel/types.h:788
@ SD_HETEROGENEOUS_VOLUME
Definition: kernel/types.h:772
#define SHADER_NONE
Definition: kernel/types.h:39
#define OBJECT_NONE
Definition: kernel/types.h:40
ShaderData
Definition: kernel/types.h:925
@ SHADER_MASK
Definition: kernel/types.h:449
@ SD_OBJECT_HAS_VOLUME_ATTRIBUTES
Definition: kernel/types.h:820
#define fminf(x, y)
Definition: metal/compat.h:229
#define INTEGRATOR_STATE_ARRAY_WRITE(state, nested_struct, array_index, member)
Definition: state.h:159
IntegratorStateCPU *ccl_restrict IntegratorState
Definition: state.h:147
IntegratorShadowStateCPU *ccl_restrict IntegratorShadowState
Definition: state.h:149
ccl_device_forceinline VolumeStack integrator_state_read_volume_stack(ConstIntegratorState state, int i)
Definition: state_util.h:90
ccl_device_forceinline VolumeStack integrator_state_read_shadow_volume_stack(ConstIntegratorShadowState state, int i)
Definition: state_util.h:180
ccl_device_forceinline void integrator_state_write_shadow_volume_stack(IntegratorShadowState state, int i, VolumeStack entry)
Definition: state_util.h:195
ccl_device_forceinline void integrator_state_write_volume_stack(IntegratorState state, int i, VolumeStack entry)
Definition: state_util.h:98
#define VOLUME_WRITE_LAMBDA(function_call)
Definition: volume_stack.h:12
ccl_device VolumeSampleMethod volume_stack_sample_method(KernelGlobals kg, IntegratorState state)
Definition: volume_stack.h:173
#define VOLUME_READ_LAMBDA(function_call)
Definition: volume_stack.h:10
ccl_device_inline void volume_stack_clean(KernelGlobals kg, IntegratorState state)
Definition: volume_stack.h:114
VolumeSampleMethod
Definition: volume_stack.h:166
@ VOLUME_SAMPLE_DISTANCE
Definition: volume_stack.h:168
@ VOLUME_SAMPLE_MIS
Definition: volume_stack.h:170
@ VOLUME_SAMPLE_NONE
Definition: volume_stack.h:167
@ VOLUME_SAMPLE_EQUIANGULAR
Definition: volume_stack.h:169
ccl_device float volume_stack_step_size(KernelGlobals kg, StackReadOp stack_read)
Definition: volume_stack.h:126
ccl_device void volume_stack_enter_exit(KernelGlobals kg, ccl_private const ShaderData *sd, StackReadOp stack_read, StackWriteOp stack_write)
Definition: volume_stack.h:22
ccl_device void shadow_volume_stack_enter_exit(KernelGlobals kg, IntegratorShadowState state, ccl_private const ShaderData *sd)
Definition: volume_stack.h:91