Blender V5.0
volume_stack.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
9#ifdef __VOLUME__
10
11/* Volume Stack
12 *
13 * This is an array of object/shared ID's that the current segment of the path
14 * is inside of. */
15
16template<const bool shadow, typename IntegratorGenericState>
17ccl_device_forceinline VolumeStack volume_stack_read(const IntegratorGenericState state,
18 const int i)
19{
20 if constexpr (shadow) {
21 return integrator_state_read_shadow_volume_stack(state, i);
22 }
23 else {
24 return integrator_state_read_volume_stack(state, i);
25 }
26
27# ifdef __KERNEL_GPU__
28 /* Silence false positive warning with some GPU compilers. */
29 VolumeStack stack = {};
30 return stack;
31# endif
32}
33
34template<const bool shadow, typename IntegratorGenericState>
35ccl_device_forceinline void volume_stack_write(IntegratorGenericState state,
36 const int i,
37 const VolumeStack entry)
38{
39 if constexpr (shadow) {
40 integrator_state_write_shadow_volume_stack(state, i, entry);
41 }
42 else {
43 integrator_state_write_volume_stack(state, i, entry);
44 }
45}
46
47template<const bool shadow, typename IntegratorGenericState>
48ccl_device void volume_stack_enter_exit(KernelGlobals kg,
49 IntegratorGenericState state,
50 const ccl_private ShaderData *sd)
51{
52# ifdef __KERNEL_USE_DATA_CONSTANTS__
53 /* If we're using data constants, this fetch disappears.
54 * On Apple GPUs, scenes without volumetric features can render 1 or 2% faster by dead-stripping
55 * this function. */
56 if (!(kernel_data.kernel_features & KERNEL_FEATURE_VOLUME)) {
57 return;
58 }
59# endif
60
61 /* todo: we should have some way for objects to indicate if they want the
62 * world shader to work inside them. excluding it by default is problematic
63 * because non-volume objects can't be assumed to be closed manifolds */
64 if (!(sd->flag & SD_HAS_VOLUME)) {
65 return;
66 }
67
68 if (sd->flag & SD_BACKFACING) {
69 /* Exit volume object: remove from stack. */
70 for (int i = 0;; i++) {
71 VolumeStack entry = volume_stack_read<shadow>(state, i);
72 if (entry.shader == SHADER_NONE) {
73 break;
74 }
75
76 if (entry.object == sd->object && entry.shader == sd->shader) {
77 /* Shift back next stack entries. */
78 do {
79 entry = volume_stack_read<shadow>(state, i + 1);
80 volume_stack_write<shadow>(state, i, entry);
81 i++;
82 } while (entry.shader != SHADER_NONE);
83
84 return;
85 }
86 }
87 }
88 else {
89 /* Enter volume object: add to stack. */
90 uint i;
91 for (i = 0;; i++) {
92 const VolumeStack entry = volume_stack_read<shadow>(state, i);
93 if (entry.shader == SHADER_NONE) {
94 break;
95 }
96
97 /* Already in the stack? then we have nothing to do. */
98 if (entry.object == sd->object && entry.shader == sd->shader) {
99 return;
100 }
101 }
102
103 /* If we exceed the stack limit, ignore. */
104 if (i >= kernel_data.volume_stack_size - 1) {
105 return;
106 }
107
108 /* Add to the end of the stack. */
109 const VolumeStack new_entry = {sd->object, sd->shader};
110 const VolumeStack empty_entry = {OBJECT_NONE, SHADER_NONE};
111 volume_stack_write<shadow>(state, i, new_entry);
112 volume_stack_write<shadow>(state, i + 1, empty_entry);
113 }
114}
115
116/* Clean stack after the last bounce.
117 *
118 * It is expected that all volumes are closed manifolds, so at the time when ray
119 * hits nothing (for example, it is a last bounce which goes to environment) the
120 * only expected volume in the stack is the world's one. All the rest volume
121 * entries should have been exited already.
122 *
123 * This isn't always true because of ray intersection precision issues, which
124 * could lead us to an infinite non-world volume in the stack, causing render
125 * artifacts.
126 *
127 * Use this function after the last bounce to get rid of all volumes apart from
128 * the world's one after the last bounce to avoid render artifacts.
129 */
130ccl_device_inline void volume_stack_clean(KernelGlobals kg, IntegratorState state)
131{
132 if (kernel_data.background.volume_shader != SHADER_NONE) {
133 /* Keep the world's volume in stack. */
134 INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, 1, shader) = SHADER_NONE;
135 }
136 else {
137 INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, 0, shader) = SHADER_NONE;
138 }
139}
140
141/* Check if the volume is homogeneous by checking if the shader flag is set or if volume attributes
142 * are needed. */
143ccl_device_inline bool volume_is_homogeneous(KernelGlobals kg,
144 const ccl_private VolumeStack &entry)
145{
146 const int shader_flag = kernel_data_fetch(shaders, (entry.shader & SHADER_MASK)).flags;
147
148 if (shader_flag & SD_HETEROGENEOUS_VOLUME) {
149 return false;
150 }
151
152 if (shader_flag & SD_NEED_VOLUME_ATTRIBUTES) {
153 const int object = entry.object;
154 if (object == kernel_data.background.object_index) {
155 /* Volume attributes for world is not supported. */
156 return true;
157 }
158
159 const int object_flag = kernel_data_fetch(object_flag, object);
160 if (object_flag & SD_OBJECT_HAS_VOLUME_ATTRIBUTES) {
161 /* If both the shader and the object needs volume attributes, the volume is heterogeneous. */
162 return false;
163 }
164 }
165
166 return true;
167}
168
169template<const bool shadow, typename IntegratorGenericState>
170ccl_device_inline bool volume_is_homogeneous(KernelGlobals kg, const IntegratorGenericState state)
171{
172 for (int i = 0;; i++) {
173 const VolumeStack entry = volume_stack_read<shadow>(state, i);
174
175 if (entry.shader == SHADER_NONE) {
176 return true;
177 }
178
179 if (!volume_is_homogeneous(kg, entry)) {
180 return false;
181 }
182 }
183
184 kernel_assert(false);
185 return false;
186}
187
188template<const bool shadow, typename IntegratorGenericState>
189ccl_device float volume_stack_step_size(KernelGlobals kg, const IntegratorGenericState state)
190{
191 kernel_assert(kernel_data.integrator.volume_ray_marching);
192
193 float step_size = FLT_MAX;
194
195 for (int i = 0;; i++) {
196 const VolumeStack entry = volume_stack_read<shadow>(state, i);
197 if (entry.shader == SHADER_NONE) {
198 break;
199 }
200
201 if (!volume_is_homogeneous(kg, entry)) {
202 const float object_step_size = kernel_data_fetch(volume_step_size, entry.object);
203 step_size = fminf(object_step_size, step_size);
204 }
205 }
206
207 return step_size;
208}
209
210enum VolumeSampleMethod {
211 VOLUME_SAMPLE_NONE = 0,
212 VOLUME_SAMPLE_DISTANCE = (1 << 0),
213 VOLUME_SAMPLE_EQUIANGULAR = (1 << 1),
214 VOLUME_SAMPLE_MIS = (VOLUME_SAMPLE_DISTANCE | VOLUME_SAMPLE_EQUIANGULAR),
215};
216
217ccl_device VolumeSampleMethod volume_stack_sample_method(KernelGlobals kg, IntegratorState state)
218{
219 VolumeSampleMethod method = VOLUME_SAMPLE_NONE;
220
221 for (int i = 0;; i++) {
222 VolumeStack entry = integrator_state_read_volume_stack(state, i);
223 if (entry.shader == SHADER_NONE) {
224 break;
225 }
226
227 int shader_flag = kernel_data_fetch(shaders, (entry.shader & SHADER_MASK)).flags;
228
229 if (shader_flag & SD_VOLUME_MIS) {
230 /* Multiple importance sampling. */
231 return VOLUME_SAMPLE_MIS;
232 }
233 else if (shader_flag & SD_VOLUME_EQUIANGULAR) {
234 /* Distance + equiangular sampling -> multiple importance sampling. */
235 if (method == VOLUME_SAMPLE_DISTANCE) {
236 return VOLUME_SAMPLE_MIS;
237 }
238
239 /* Only equiangular sampling. */
240 method = VOLUME_SAMPLE_EQUIANGULAR;
241 }
242 else {
243 /* Distance + equiangular sampling -> multiple importance sampling. */
244 if (method == VOLUME_SAMPLE_EQUIANGULAR) {
245 return VOLUME_SAMPLE_MIS;
246 }
247
248 /* Distance sampling only. */
249 method = VOLUME_SAMPLE_DISTANCE;
250 }
251 }
252
253 return method;
254}
255
256#endif /* __VOLUME__*/
257
unsigned int uint
#define kernel_assert(cond)
#define KERNEL_FEATURE_VOLUME
#define kernel_data
#define ccl_device_forceinline
#define kernel_data_fetch(name, index)
#define SHADER_NONE
#define OBJECT_NONE
#define ccl_private
const ThreadKernelGlobalsCPU * KernelGlobals
#define ccl_device_inline
#define CCL_NAMESPACE_END
@ SD_VOLUME_MIS
@ SD_VOLUME_EQUIANGULAR
@ SD_BACKFACING
@ SD_HAS_VOLUME
@ SD_NEED_VOLUME_ATTRIBUTES
@ SD_HETEROGENEOUS_VOLUME
@ SHADER_MASK
@ SD_OBJECT_HAS_VOLUME_ATTRIBUTES
static ulong state[N]
#define ccl_device
#define fminf
#define INTEGRATOR_STATE_ARRAY_WRITE(state, nested_struct, array_index, member)
Definition state.h:240
IntegratorStateCPU * IntegratorState
Definition state.h:228
#define FLT_MAX
Definition stdcycles.h:14
i
Definition text_draw.cc:230