Blender V5.0
spot.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
9
10#include "util/math_fast.h"
11#include "util/math_intersect.h"
12
14
15/* Transform vector to spot light's local coordinate system. */
17 const ccl_global KernelLight *klight,
18 const float3 ray)
19{
20 const Transform itfm = lamp_get_inverse_transform(kg, klight);
21 float3 transformed_ray = safe_normalize(transform_direction(&itfm, ray));
22 transformed_ray.z = -transformed_ray.z;
23
24 return transformed_ray;
25}
26
27/* Compute spot light attenuation of a ray given in local coordinate system. */
29{
30 return smoothstepf((ray.z - spot->cos_half_spot_angle) * spot->spot_smooth);
31}
32
34 const float half_cot_half_spot_angle,
35 ccl_private float *u,
36 ccl_private float *v)
37{
38 /* Ensures that the spot light projects the full image regardless of the spot angle. */
39 const float factor = half_cot_half_spot_angle / ray.z;
40
41 /* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */
42 *u = ray.y * factor + 0.5f;
43 *v = -(ray.x + ray.y) * factor;
44}
45
46template<bool in_volume_segment>
48 const ccl_global KernelLight *klight,
49 const float2 rand,
50 const float3 P,
51 const float3 N,
52 const int shader_flags,
54{
55 const float r_sq = sqr(klight->spot.radius);
56
57 float3 lightN = P - klight->co;
58 const float d_sq = len_squared(lightN);
59 const float d = sqrtf(d_sq);
60 lightN /= d;
61
62 ls->eval_fac = klight->spot.eval_fac;
63
64 if (klight->spot.is_sphere) {
65 /* Spherical light geometry. */
66 float cos_theta;
67 ls->t = FLT_MAX;
68 if (d_sq > r_sq) {
69 /* Outside sphere. */
70 const float one_minus_cos_half_spot_spread = 1.0f - klight->spot.cos_half_larger_spread;
71 const float one_minus_cos_half_angle = sin_sqr_to_one_minus_cos(r_sq / d_sq);
72
73 if (in_volume_segment || one_minus_cos_half_angle < one_minus_cos_half_spot_spread) {
74 /* Sample visible part of the sphere. */
75 ls->D = sample_uniform_cone(-lightN, one_minus_cos_half_angle, rand, &cos_theta, &ls->pdf);
76 }
77 else {
78 /* Sample spread cone. */
79 ls->D = sample_uniform_cone(
80 -klight->spot.dir, one_minus_cos_half_spot_spread, rand, &cos_theta, &ls->pdf);
81
83 P, ls->D, 0.0f, FLT_MAX, klight->co, klight->spot.radius, &ls->P, &ls->t))
84 {
85 /* Sampled direction does not intersect with the light. */
86 return false;
87 }
88 }
89 }
90 else {
91 /* Inside sphere. */
92 const bool has_transmission = (shader_flags & SD_BSDF_HAS_TRANSMISSION);
93 if (has_transmission) {
94 ls->D = sample_uniform_sphere(rand);
95 ls->pdf = M_1_2PI_F * 0.5f;
96 }
97 else {
98 sample_cos_hemisphere(N, rand, &ls->D, &ls->pdf);
99 }
100 cos_theta = -dot(ls->D, lightN);
101 }
102
103 /* Attenuation. */
104 const float3 local_ray = spot_light_to_local(kg, klight, -ls->D);
105 if (d_sq > r_sq) {
106 ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
107 }
108 if (!in_volume_segment && ls->eval_fac == 0.0f) {
109 return false;
110 }
111
112 if (ls->t == FLT_MAX) {
113 /* Law of cosines. */
114 ls->t = d * cos_theta -
115 copysignf(safe_sqrtf(r_sq - d_sq + d_sq * sqr(cos_theta)), d_sq - r_sq);
116 ls->P = P + ls->D * ls->t;
117 }
118 else {
119 /* Already computed when sampling the spread cone. */
120 }
121
122 /* Remap sampled point onto the sphere to prevent precision issues with small radius. */
123 ls->Ng = normalize(ls->P - klight->co);
124 ls->P = ls->Ng * klight->spot.radius + klight->co;
125
126 /* Texture coordinates. */
127 spot_light_uv(local_ray, klight->spot.half_cot_half_spot_angle, &ls->u, &ls->v);
128 }
129 else {
130 /* Point light with ad-hoc radius based on oriented disk. */
131 ls->P = klight->co;
132 if (r_sq > 0.0f) {
133 ls->P += disk_light_sample(lightN, rand) * klight->spot.radius;
134 }
135
136 ls->D = safe_normalize_len(ls->P - P, &ls->t);
137 ls->Ng = -ls->D;
138
139 /* Attenuation. */
140 const float3 local_ray = spot_light_to_local(kg, klight, -ls->D);
141 ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
142 if (!in_volume_segment && ls->eval_fac == 0.0f) {
143 return false;
144 }
145
146 /* PDF. */
147 const float invarea = (r_sq > 0.0f) ? 1.0f / (r_sq * M_PI_F) : 1.0f;
148 ls->pdf = invarea * light_pdf_area_to_solid_angle(lightN, -ls->D, ls->t);
149
150 /* Texture coordinates. */
151 spot_light_uv(local_ray, klight->spot.half_cot_half_spot_angle, &ls->u, &ls->v);
152 }
153
154 return true;
155}
156
158 const float d_sq,
159 const float r_sq,
160 const float3 N,
161 const float3 D,
162 const uint32_t path_flag)
163{
164 if (d_sq > r_sq) {
165 return M_1_2PI_F /
166 min(sin_sqr_to_one_minus_cos(r_sq / d_sq), 1.0f - spot->cos_half_larger_spread);
167 }
168
169 const bool has_transmission = (path_flag & PATH_RAY_MIS_HAD_TRANSMISSION);
170 return has_transmission ? M_1_2PI_F * 0.5f : pdf_cos_hemisphere(N, D);
171}
172
174 const ccl_global KernelLight *klight,
176 const float3 P,
177 const float3 N,
178 const uint32_t path_flag)
179{
180 ls->D = safe_normalize_len(ls->P - P, &ls->t);
181
182 ls->eval_fac = klight->spot.eval_fac;
183
184 const float radius = klight->spot.radius;
185 bool use_attenuation = true;
186
187 if (klight->spot.is_sphere) {
188 const float d_sq = len_squared(P - klight->co);
189 const float r_sq = sqr(radius);
190 const float t_sq = sqr(ls->t);
191
192 /* NOTE : preserve pdf in area measure. */
193 const float jacobian_solid_angle_to_area = 0.5f * fabsf(d_sq - r_sq - t_sq) /
194 (radius * ls->t * t_sq);
195 ls->pdf = spot_light_pdf(&klight->spot, d_sq, r_sq, N, ls->D, path_flag) *
196 jacobian_solid_angle_to_area;
197
198 ls->Ng = normalize(ls->P - klight->co);
199
200 use_attenuation = (d_sq > r_sq);
201 }
202 else {
203 /* NOTE : preserve pdf in area measure. */
204 ls->pdf = ls->eval_fac * 4.0f * M_PI_F;
205
206 ls->Ng = -ls->D;
207 }
208
209 /* Attenuation. */
210 const float3 local_ray = spot_light_to_local(kg, klight, -ls->D);
211 if (use_attenuation) {
212 ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
213 }
214
215 /* Texture coordinates. */
216 spot_light_uv(local_ray, klight->spot.half_cot_half_spot_angle, &ls->u, &ls->v);
217}
218
220 const ccl_private Ray *ccl_restrict ray,
221 ccl_private float *t)
222{
223 /* One sided. */
224 if (dot(ray->D, ray->P - klight->co) >= 0.0f) {
225 return false;
226 }
227
228 return point_light_intersect(klight, ray, t);
229}
230
232 const ccl_global KernelLight *klight,
233 const float3 ray_P,
234 const float3 ray_D,
235 const float3 N,
236 const uint32_t path_flag,
238 ls)
239{
240 const float r_sq = sqr(klight->spot.radius);
241 const float d_sq = len_squared(ray_P - klight->co);
242
243 ls->eval_fac = klight->spot.eval_fac;
244
245 if (klight->spot.is_sphere) {
246 ls->pdf = spot_light_pdf(&klight->spot, d_sq, r_sq, N, ray_D, path_flag);
247 ls->Ng = normalize(ls->P - klight->co);
248 }
249 else {
250 if (ls->t != FLT_MAX) {
251 const float3 lightN = normalize(ray_P - klight->co);
252 const float invarea = (r_sq > 0.0f) ? 1.0f / (r_sq * M_PI_F) : 1.0f;
253 ls->pdf = invarea * light_pdf_area_to_solid_angle(lightN, -ray_D, ls->t);
254 }
255 else {
256 ls->pdf = 0.0f;
257 }
258 ls->Ng = -ray_D;
259 }
260
261 /* Attenuation. */
262 const float3 local_ray = spot_light_to_local(kg, klight, -ray_D);
263 if (!klight->spot.is_sphere || d_sq > r_sq) {
264 ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
265 }
266 if (ls->eval_fac == 0) {
267 return false;
268 }
269
270 /* Texture coordinates. */
271 spot_light_uv(local_ray, klight->spot.half_cot_half_spot_angle, &ls->u, &ls->v);
272
273 return true;
274}
275
276/* Find the ray segment lit by the spot light. */
278 const ccl_global KernelLight *klight,
279 const float3 P,
280 const float3 D,
282{
283 /* Convert to local space of the spot light. */
284 const Transform itfm = lamp_get_inverse_transform(kg, klight);
285 float3 local_P = P + klight->spot.dir * klight->spot.ray_segment_dp;
286 local_P = transform_point(&itfm, local_P);
287 const float3 local_D = transform_direction(&itfm, D);
288 const float3 axis = make_float3(0.0f, 0.0f, -1.0f);
289
290 /* Intersect the ray with the smallest enclosing cone of the light spread. */
291 return ray_cone_intersect(
292 axis, local_P, local_D, sqr(klight->spot.cos_half_spot_angle), t_range);
293}
294
295template<bool in_volume_segment>
297 const float3 centroid,
298 const float3 P,
299 const ccl_private KernelBoundingCone &bcone,
300 ccl_private float &cos_theta_u,
302 ccl_private float3 &point_to_centroid,
303 ccl_private float &energy)
304{
305 float min_distance;
306 point_to_centroid = safe_normalize_len(centroid - P, &min_distance);
307 distance = min_distance * one_float2();
308
309 const float radius = klight->spot.radius;
310
311 if (klight->spot.is_sphere) {
312 cos_theta_u = (min_distance > radius) ? cos_from_sin(radius / min_distance) : -1.0f;
313
314 if (in_volume_segment) {
315 return true;
316 }
317
318 distance = (min_distance > radius) ? min_distance * make_float2(1.0f / cos_theta_u, 1.0f) :
319 one_float2() * radius / M_SQRT2_F;
320 }
321 else {
322 const float hypotenus = sqrtf(sqr(radius) + sqr(min_distance));
323 cos_theta_u = min_distance / hypotenus;
324
325 if (in_volume_segment) {
326 return true;
327 }
328
329 distance.x = hypotenus;
330 }
331
332 /* Apply a similar scaling as in `spot_light_attenuation()` to account for spot blend. */
333 {
334 /* Minimum angle formed by the emitter axis and the direction to the shading point,
335 * cos(theta') in the paper. */
336 const float cos_min_outgoing_angle = cosf(
337 fmaxf(0.0f, fast_acosf(dot(bcone.axis, -point_to_centroid)) - fast_acosf(cos_theta_u)));
338 /* Use `cos(bcone.theta_e)` instead of `klight->spot.cos_half_spot_angle` to account for
339 * non-uniform scaling. */
340 energy *= smoothstepf((cos_min_outgoing_angle - cosf(bcone.theta_e)) *
341 klight->spot.spot_smooth);
342 }
343
344 return true;
345}
346
#define D
MINLINE float safe_sqrtf(float a)
ATTR_WARN_UNUSED_RESULT const BMVert * v
ccl_device_inline float cos_theta(const float3 w)
dot(value.rgb, luminance_coefficients)") DEFINE_VALUE("REDUCE(lhs
#define ccl_restrict
#define ccl_device_forceinline
#define M_SQRT2_F
#define ccl_private
const ThreadKernelGlobalsCPU * KernelGlobals
#define ccl_device_inline
#define M_1_2PI_F
#define ccl_global
#define CCL_NAMESPACE_END
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
#define copysignf(x, y)
VecBase< float, D > normalize(VecOp< float, D >) RET
float distance(VecOp< float, D >, VecOp< float, D >) RET
ccl_device_inline Transform lamp_get_inverse_transform(KernelGlobals kg, const ccl_global KernelLight *klight)
@ SD_BSDF_HAS_TRANSMISSION
@ PATH_RAY_MIS_HAD_TRANSMISSION
ccl_device float3 disk_light_sample(const float3 n, const float2 rand)
ccl_device float light_pdf_area_to_solid_angle(const float3 Ng, const float3 I, const float t)
ccl_device_inline bool point_light_intersect(const ccl_global KernelLight *klight, const ccl_private Ray *ccl_restrict ray, ccl_private float *t)
ccl_device_inline float smoothstepf(const float f)
Definition math_base.h:485
ccl_device_inline float sin_sqr_to_one_minus_cos(const float s_sq)
Definition math_base.h:619
ccl_device_inline float cos_from_sin(const float s)
Definition math_base.h:614
ccl_device float fast_acosf(const float x)
Definition math_fast.h:259
ccl_device_inline float2 one_float2()
Definition math_float2.h:18
ccl_device_inline float len_squared(const float2 a)
ccl_device_inline float2 safe_normalize(const float2 a)
ccl_device_inline float3 safe_normalize_len(const float3 a, ccl_private float *t)
ccl_device_inline bool ray_cone_intersect(const float3 axis, const float3 P, float3 D, const float cos_angle_sq, ccl_private Interval< float > *t_range)
CCL_NAMESPACE_BEGIN ccl_device bool ray_sphere_intersect(const float3 ray_P, const float3 ray_D, const float ray_tmin, const float ray_tmax, const float3 sphere_P, const float sphere_radius, ccl_private float3 *isect_P, ccl_private float *isect_t)
#define N
#define sqr
#define fabsf
#define sqrtf
#define ccl_device
#define fmaxf
#define make_float2
#define M_PI_F
#define cosf
ccl_device_inline void sample_cos_hemisphere(const float3 N, const float2 rand_in, ccl_private float3 *wo, ccl_private float *pdf)
ccl_device float3 sample_uniform_sphere(const float2 rand)
ccl_device_inline float3 sample_uniform_cone(const float3 N, const float one_minus_cos_angle, const float2 rand, ccl_private float *cos_theta, ccl_private float *pdf)
ccl_device_inline float pdf_cos_hemisphere(const float3 N, const float3 D)
#define min(a, b)
Definition sort.cc:36
ccl_device_forceinline void spot_light_mnee_sample_update(KernelGlobals kg, const ccl_global KernelLight *klight, ccl_private LightSample *ls, const float3 P, const float3 N, const uint32_t path_flag)
Definition spot.h:173
ccl_device void spot_light_uv(const float3 ray, const float half_cot_half_spot_angle, ccl_private float *u, ccl_private float *v)
Definition spot.h:33
ccl_device float spot_light_attenuation(const ccl_global KernelSpotLight *spot, const float3 ray)
Definition spot.h:28
ccl_device_inline bool spot_light_sample(KernelGlobals kg, const ccl_global KernelLight *klight, const float2 rand, const float3 P, const float3 N, const int shader_flags, ccl_private LightSample *ls)
Definition spot.h:47
CCL_NAMESPACE_BEGIN ccl_device float3 spot_light_to_local(KernelGlobals kg, const ccl_global KernelLight *klight, const float3 ray)
Definition spot.h:16
ccl_device_inline bool spot_light_intersect(const ccl_global KernelLight *klight, const ccl_private Ray *ccl_restrict ray, ccl_private float *t)
Definition spot.h:219
ccl_device_inline bool spot_light_sample_from_intersection(KernelGlobals kg, const ccl_global KernelLight *klight, const float3 ray_P, const float3 ray_D, const float3 N, const uint32_t path_flag, ccl_private LightSample *ccl_restrict ls)
Definition spot.h:231
ccl_device_forceinline bool spot_light_tree_parameters(const ccl_global KernelLight *klight, const float3 centroid, const float3 P, const ccl_private KernelBoundingCone &bcone, ccl_private float &cos_theta_u, ccl_private float2 &distance, ccl_private float3 &point_to_centroid, ccl_private float &energy)
Definition spot.h:296
ccl_device_forceinline float spot_light_pdf(const ccl_global KernelSpotLight *spot, const float d_sq, const float r_sq, const float3 N, const float3 D, const uint32_t path_flag)
Definition spot.h:157
ccl_device_inline bool spot_light_valid_ray_segment(KernelGlobals kg, const ccl_global KernelLight *klight, const float3 P, const float3 D, ccl_private Interval< float > *t_range)
Definition spot.h:277
#define FLT_MAX
Definition stdcycles.h:14
float z
Definition sky_math.h:136
float y
Definition sky_math.h:136
float x
Definition sky_math.h:136
ccl_device_inline float3 transform_direction(const ccl_private Transform *t, const float3 a)
Definition transform.h:127
ccl_device_inline float3 transform_point(const ccl_private Transform *t, const float3 a)
Definition transform.h:56