Blender V4.3
intersect_dedicated_light.h
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2023 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#pragma once
6
7#include "kernel/bvh/bvh.h"
11#include "kernel/light/light.h"
12#include "kernel/sample/lcg.h"
13
15
16#ifdef __SHADOW_LINKING__
17
18# define SHADOW_LINK_MAX_INTERSECTION_COUNT 1024
19
20/* Intersect mesh objects.
21 *
22 * Returns the total number of emissive surfaces hit, and the intersection contains a random
23 * intersected emitter to which the dedicated shadow ray is to eb traced.
24 *
25 * NOTE: Sets the ray tmax to the maximum intersection distance (past which no lights are to be
26 * considered for shadow linking). */
27ccl_device int shadow_linking_pick_mesh_intersection(KernelGlobals kg,
30 const int object_receiver,
32 linked_isect,
33 ccl_private uint *lcg_state,
34 int num_hits)
35{
36 /* The tmin will be offset, so store its current value and restore later on, allowing a separate
37 * light intersection loop starting from the actual ray origin. */
38 const float old_tmin = ray->tmin;
39
40 const uint visibility = path_state_ray_visibility(state);
41
42 int transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce);
43 int volume_bounce = INTEGRATOR_STATE(state, path, volume_bounce);
44
45 /* TODO: Replace the look with sequential calls to the kernel, similar to the transparent shadow
46 * intersection kernel. */
47 for (int i = 0; i < SHADOW_LINK_MAX_INTERSECTION_COUNT; i++) {
49 current_isect.object = OBJECT_NONE;
50 current_isect.prim = PRIM_NONE;
51
52 const bool hit = scene_intersect(kg, ray, visibility, &current_isect);
53 if (!hit) {
54 break;
55 }
56
57 /* Only record primitives that potentially have emission.
58 * TODO: optimize with a dedicated ray visibility flag, which could then also be
59 * used once lights are in the BVH as geometry? */
60 const int shader = intersection_get_shader(kg, &current_isect);
61 const int shader_flags = kernel_data_fetch(shaders, shader).flags;
62 if (light_link_object_match(kg, object_receiver, current_isect.object) &&
63 (shader_flags & SD_HAS_EMISSION))
64 {
65 const uint64_t set_membership =
66 kernel_data_fetch(objects, current_isect.object).shadow_set_membership;
67 if (set_membership != LIGHT_LINK_MASK_ALL) {
68 ++num_hits;
69
70 if ((linked_isect->prim == PRIM_NONE) || (lcg_step_float(lcg_state) < 1.0f / num_hits)) {
71 *linked_isect = current_isect;
72 }
73 }
74 }
75
76 /* Contribution from the lights past the default opaque blocker is accumulated
77 * using the main path. */
78 if (!(shader_flags & (SD_HAS_ONLY_VOLUME | SD_HAS_TRANSPARENT_SHADOW))) {
79 const uint blocker_set = kernel_data_fetch(objects, current_isect.object).blocker_shadow_set;
80 if (blocker_set == 0) {
81 ray->tmax = current_isect.t;
82 break;
83 }
84 }
85 else {
86 /* Lights past the maximum allowed transparency bounce do not contribute any light, so
87 * consider them as fully blocked and only consider lights prior to this intersection. */
88 if (shader_flags & SD_HAS_TRANSPARENT_SHADOW) {
89 ++transparent_bounce;
90 if (transparent_bounce >= kernel_data.integrator.transparent_max_bounce) {
91 ray->tmax = current_isect.t;
92 break;
93 }
94 }
95 else {
96 kernel_assert(shader_flags & SD_HAS_ONLY_VOLUME);
97 ++volume_bounce;
98 if (volume_bounce >= kernel_data.integrator.max_volume_bounce) {
99 ray->tmax = current_isect.t;
100 break;
101 }
102 }
103 }
104
105 /* Move the ray forward. */
106 ray->tmin = intersection_t_offset(current_isect.t);
107 }
108
109 ray->tmin = old_tmin;
110
111 return num_hits;
112}
113
114/* Pick a light for tracing a shadow ray for the shadow linking.
115 * Picks a random light which is intersected by the given ray, and stores the intersection result.
116 * If no lights were hit false is returned.
117 *
118 * NOTE: Sets the ray tmax to the maximum intersection distance (past which no lights are to be
119 * considered for shadow linking). */
120ccl_device bool shadow_linking_pick_light_intersection(KernelGlobals kg,
124 linked_isect)
125{
126 const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
127
128 const int last_type = INTEGRATOR_STATE(state, isect, type);
129
130 const int object_receiver = light_link_receiver_forward(kg, state);
131
132 uint lcg_state = lcg_state_init(INTEGRATOR_STATE(state, path, rng_pixel),
133 INTEGRATOR_STATE(state, path, rng_offset),
135 0x68bc21eb);
136
137 /* Indicate that no intersection has been picked yet. */
138 linked_isect->prim = PRIM_NONE;
139
140 int num_hits = 0;
141
142 // TODO: Only if there are emissive meshes in the scene?
143
144 // TODO: Only if the ray hits any light? As in, check that there is a light first, before
145 // tracing potentially expensive ray.
146
147 num_hits = shadow_linking_pick_mesh_intersection(
148 kg, state, ray, object_receiver, linked_isect, &lcg_state, num_hits);
149
150 num_hits = lights_intersect_shadow_linked(kg,
151 ray,
152 linked_isect,
153 ray->self.prim,
154 ray->self.object,
155 last_type,
156 path_flag,
157 object_receiver,
158 &lcg_state,
159 num_hits);
160
161 if (num_hits == 0) {
162 return false;
163 }
164
165 INTEGRATOR_STATE_WRITE(state, shadow_link, dedicated_light_weight) = num_hits;
166
167 return true;
168}
169
170/* Check whether a special shadow ray is needed to calculate direct light contribution which comes
171 * from emitters which are behind objects which are blocking light for the main path, but are
172 * excluded from blocking light via shadow linking.
173 *
174 * If a special ray is needed a blocked light kernel is scheduled and true is returned, otherwise
175 * false is returned. */
176ccl_device bool shadow_linking_intersect(KernelGlobals kg, IntegratorState state)
177{
178 /* Verify that the kernel is only scheduled if it is actually needed. */
179 kernel_assert(shadow_linking_scene_need_shadow_ray(kg));
180
181 /* Read ray from integrator state into local memory. */
184
185 ray.self.prim = INTEGRATOR_STATE(state, isect, prim);
186 ray.self.object = INTEGRATOR_STATE(state, isect, object);
187 ray.self.light_object = OBJECT_NONE;
188 ray.self.light_prim = PRIM_NONE;
189 ray.self.light = LAMP_NONE;
190
192 if (!shadow_linking_pick_light_intersection(kg, state, &ray, &isect)) {
193 /* No light is hit, no need in the extra shadow ray for the direct light. */
194 return false;
195 }
196
197 /* Make a copy of primitives needed by the main path self-intersection check before writing the
198 * new intersection. Those primitives will be restored before the main path is returned to the
199 * intersect_closest state. */
200 shadow_linking_store_last_primitives(state);
201
202 /* Write intersection result into global integrator state memory, so that the
203 * shade_dedicated_light kernel can use it for calculation of the light sample. */
205
207 state,
210
211 return true;
212}
213
214#endif /* __SHADOW_LINKING__ */
215
217{
219
220#ifdef __SHADOW_LINKING__
221 if (shadow_linking_intersect(kg, state)) {
222 return;
223 }
224#else
225 kernel_assert(!"integrator_intersect_dedicated_light is not supposed to be scheduled");
226#endif
227
229 state);
230}
231
unsigned int uint
ccl_device_forceinline float intersection_t_offset(const float t)
ccl_device_forceinline int intersection_get_shader(KernelGlobals kg, ccl_private const Intersection *ccl_restrict isect)
#define kernel_assert(cond)
#define kernel_data
const KernelGlobalsCPU *ccl_restrict KernelGlobals
#define kernel_data_fetch(name, index)
#define ccl_restrict
#define ccl_optional_struct_init
#define ccl_device
#define ccl_private
#define CCL_NAMESPACE_END
CCL_NAMESPACE_BEGIN ccl_device void integrator_intersect_dedicated_light(KernelGlobals kg, IntegratorState state)
ccl_device_intersect bool scene_intersect(KernelGlobals kg, ccl_private const Ray *ray, const uint visibility, ccl_private Intersection *isect)
ccl_device int lights_intersect_shadow_linked(KernelGlobals kg, ccl_private const Ray *ccl_restrict ray, ccl_private Intersection *ccl_restrict isect, const int last_prim, const int last_object, const int last_type, const uint32_t path_flag, const int receiver_forward, ccl_private uint *lcg_state, const int num_hits)
ccl_device_inline bool light_link_object_match(KernelGlobals kg, const int object_receiver, const int object_emitter)
ccl_device_inline int light_link_receiver_forward(KernelGlobals kg, IntegratorState state)
@ SD_HAS_TRANSPARENT_SHADOW
@ SD_HAS_EMISSION
@ SD_HAS_ONLY_VOLUME
#define PRIM_NONE
#define OBJECT_NONE
#define LIGHT_LINK_MASK_ALL
#define LAMP_NONE
@ DEVICE_KERNEL_INTEGRATOR_SHADE_DEDICATED_LIGHT
@ DEVICE_KERNEL_INTEGRATOR_INTERSECT_DEDICATED_LIGHT
#define PROFILING_INIT(kg, event)
ccl_device_inline uint lcg_state_init(const uint rng_hash, const uint rng_offset, const uint sample, const uint scramble)
Definition lcg.h:36
ccl_device float lcg_step_float(T rng)
Definition lcg.h:22
static ulong state[N]
ccl_device_inline uint path_state_ray_visibility(ConstIntegratorState state)
Definition path_state.h:238
ccl_device_forceinline void integrator_shade_surface_next_kernel(KernelGlobals kg, IntegratorState state)
IntegratorStateCPU *ccl_restrict IntegratorState
Definition state.h:228
#define INTEGRATOR_STATE_WRITE(state, nested_struct, member)
Definition state.h:236
#define INTEGRATOR_STATE(state, nested_struct, member)
Definition state.h:235
ccl_device_forceinline void integrator_path_next(KernelGlobals kg, IntegratorState state, const DeviceKernel current_kernel, const DeviceKernel next_kernel)
Definition state_flow.h:169
ccl_device_forceinline void integrator_state_read_ray(ConstIntegratorState state, ccl_private Ray *ccl_restrict ray)
Definition state_util.h:53
ccl_device_forceinline void integrator_state_write_isect(IntegratorState state, ccl_private const Intersection *ccl_restrict isect)
Definition state_util.h:128
unsigned int uint32_t
Definition stdint.h:80
unsigned __int64 uint64_t
Definition stdint.h:90
@ PROFILING_INTERSECT_DEDICATED_LIGHT
uint8_t flag
Definition wm_window.cc:138