Blender V5.0
math_intersect.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
7#include "util/math_float2.h"
8#include "util/math_float3.h"
9#include "util/math_float4.h"
10
12
13/* Ray Intersection */
14
16 const float3 ray_D,
17 const float ray_tmin,
18 const float ray_tmax,
19 const float3 sphere_P,
20 const float sphere_radius,
21 ccl_private float3 *isect_P,
22 ccl_private float *isect_t)
23{
24 const float3 d_vec = sphere_P - ray_P;
25 const float r_sq = sphere_radius * sphere_radius;
26 const float d_sq = dot(d_vec, d_vec);
27 const float d_cos_theta = dot(d_vec, ray_D);
28
29 if (d_sq > r_sq && d_cos_theta < 0.0f) {
30 /* Ray origin outside sphere and points away from sphere. */
31 return false;
32 }
33
34 const float d_sin_theta_sq = len_squared(d_vec - d_cos_theta * ray_D);
35
36 if (d_sin_theta_sq > r_sq) {
37 /* Closest point on ray outside sphere. */
38 return false;
39 }
40
41 /* Law of cosines. */
42 const float t = d_cos_theta - copysignf(sqrtf(r_sq - d_sin_theta_sq), d_sq - r_sq);
43
44 if (t > ray_tmin && t < ray_tmax) {
45 *isect_t = t;
46 *isect_P = ray_P + ray_D * t;
47 return true;
48 }
49
50 return false;
51}
52
54 const float3 ray_D,
55 const float ray_tmin,
56 const float ray_tmax,
57 const float3 disk_P,
58 const float disk_radius,
59 ccl_private float3 *isect_P,
60 ccl_private float *isect_t)
61{
62 /* Aligned disk normal. */
63 float disk_t;
64 const float3 disk_N = normalize_len(ray_P - disk_P, &disk_t);
65 const float div = dot(ray_D, disk_N);
66 if (UNLIKELY(div == 0.0f)) {
67 return false;
68 }
69 /* Compute t to intersection point. */
70 const float t = -disk_t / div;
71 if (!(t > ray_tmin && t < ray_tmax)) {
72 return false;
73 }
74 /* Test if within radius. */
75 const float3 P = ray_P + ray_D * t;
76 if (len_squared(P - disk_P) > disk_radius * disk_radius) {
77 return false;
78 }
79 *isect_P = P;
80 *isect_t = t;
81 return true;
82}
83
85 const float3 ray_D,
86 const float ray_tmin,
87 const float ray_tmax,
88 const float3 disk_P,
89 const float3 disk_N,
90 const float disk_radius,
91 ccl_private float3 *isect_P,
92 ccl_private float *isect_t)
93{
94 const float3 vp = ray_P - disk_P;
95 const float dp = dot(vp, disk_N);
96 const float cos_angle = dot(disk_N, -ray_D);
97 if (dp * cos_angle > 0.f) // front of light
98 {
99 const float t = dp / cos_angle;
100 if (t < 0.f) { /* Ray points away from the light. */
101 return false;
102 }
103 const float3 P = ray_P + t * ray_D;
104 const float3 T = P - disk_P;
105
106 if (dot(T, T) < sqr(disk_radius) && (t > ray_tmin && t < ray_tmax)) {
107 *isect_P = ray_P + t * ray_D;
108 *isect_t = t;
109 return true;
110 }
111 }
112 return false;
113}
114
115/* Custom rcp, cross and dot implementations that match Embree bit for bit. */
117{
118#ifdef __KERNEL_NEON__
119 /* Move scalar to vector register and do rcp. */
120 __m128 a = {0};
121 a = vsetq_lane_f32(x, a, 0);
122 float32x4_t rt_rcp = vrecpeq_f32(a);
123 rt_rcp = vmulq_f32(vrecpsq_f32(a, rt_rcp), rt_rcp);
124 rt_rcp = vmulq_f32(vrecpsq_f32(a, rt_rcp), rt_rcp);
125 return vgetq_lane_f32(rt_rcp, 0);
126#elif defined(__KERNEL_SSE__)
127 const __m128 a = _mm_set_ss(x);
128 const __m128 r = _mm_rcp_ss(a);
129
130# ifdef __KERNEL_AVX2_
131 return _mm_cvtss_f32(_mm_mul_ss(r, _mm_fnmadd_ss(r, a, _mm_set_ss(2.0f))));
132# else
133 return _mm_cvtss_f32(_mm_mul_ss(r, _mm_sub_ss(_mm_set_ss(2.0f), _mm_mul_ss(r, a))));
134# endif
135#else
136 return 1.0f / x;
137#endif
138}
139
141{
142#if defined(__KERNEL_SSE42__) && defined(__KERNEL_SSE__)
143 return madd(make_float4(a.x),
144 make_float4(b.x),
145 madd(make_float4(a.y), make_float4(b.y), make_float4(a.z) * make_float4(b.z)))[0];
146#else
147 return a.x * b.x + a.y * b.y + a.z * b.z;
148#endif
149}
150
152{
153#if defined(__KERNEL_SSE42__) && defined(__KERNEL_SSE__)
154 return make_float3(
157 msub(make_float4(a.x), make_float4(b.y), make_float4(a.y) * make_float4(b.x))[0]);
158#else
159 return make_float3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
160#endif
161}
162
164 const float3 ray_D,
165 const float ray_tmin,
166 const float ray_tmax,
167 const float3 tri_a,
168 const float3 tri_b,
169 const float3 tri_c,
170 ccl_private float *isect_u,
171 ccl_private float *isect_v,
172 ccl_private float *isect_t)
173{
174 /* This implementation matches the Plücker coordinates triangle intersection
175 * in Embree. */
176
177 /* Calculate vertices relative to ray origin. */
178 const float3 v0 = tri_a - ray_P;
179 const float3 v1 = tri_b - ray_P;
180 const float3 v2 = tri_c - ray_P;
181
182 /* Calculate triangle edges. */
183 const float3 e0 = v2 - v0;
184 const float3 e1 = v0 - v1;
185 const float3 e2 = v1 - v2;
186
187 /* Perform edge tests. */
188 const float U = ray_triangle_dot(ray_triangle_cross(e0, v2 + v0), ray_D);
189 const float V = ray_triangle_dot(ray_triangle_cross(e1, v0 + v1), ray_D);
190 const float W = ray_triangle_dot(ray_triangle_cross(e2, v1 + v2), ray_D);
191
192 const float UVW = U + V + W;
193 const float eps = FLT_EPSILON * fabsf(UVW);
194 const float minUVW = min(U, min(V, W));
195 const float maxUVW = max(U, max(V, W));
196
197 if (!(minUVW >= -eps || maxUVW <= eps)) {
198 return false;
199 }
200
201 /* Calculate geometry normal and denominator. */
202 const float3 Ng1 = ray_triangle_cross(e1, e0);
203 const float3 Ng = Ng1 + Ng1;
204 const float den = dot(Ng, ray_D);
205 /* Avoid division by 0. */
206 if (UNLIKELY(den == 0.0f)) {
207 return false;
208 }
209
210 /* Perform depth test. */
211 const float T = dot(v0, Ng);
212 const float t = T / den;
213 if (!(t >= ray_tmin && t <= ray_tmax)) {
214 return false;
215 }
216
217 const float rcp_uvw = (fabsf(UVW) < 1e-18f) ? 0.0f : ray_triangle_reciprocal(UVW);
218 *isect_u = min(U * rcp_uvw, 1.0f);
219 *isect_v = min(V * rcp_uvw, 1.0f);
220 *isect_t = t;
221 return true;
222}
223
225 const float3 ray_D,
226 const float3 verts[3])
227{
228 /* Matches logic in ray_triangle_intersect, self intersection test to validate
229 * if a ray is going to hit self or might incorrectly hit a neighboring triangle. */
230
231 /* Calculate vertices relative to ray origin. */
232 const float3 v0 = verts[0] - ray_P;
233 const float3 v1 = verts[1] - ray_P;
234 const float3 v2 = verts[2] - ray_P;
235
236 /* Calculate triangle edges. */
237 const float3 e0 = v2 - v0;
238 const float3 e1 = v0 - v1;
239 const float3 e2 = v1 - v2;
240
241 /* Perform edge tests. */
242 const float U = ray_triangle_dot(ray_triangle_cross(v2 + v0, e0), ray_D);
243 const float V = ray_triangle_dot(ray_triangle_cross(v0 + v1, e1), ray_D);
244 const float W = ray_triangle_dot(ray_triangle_cross(v1 + v2, e2), ray_D);
245
246 const float eps = FLT_EPSILON * fabsf(U + V + W);
247 const float minUVW = min(U, min(V, W));
248 const float maxUVW = max(U, max(V, W));
249
250 /* Note the extended epsilon compared to ray_triangle_intersect, to account
251 * for intersections with neighboring triangles that have an epsilon. */
252 return (minUVW >= eps || maxUVW <= -eps);
253}
254
255/* Tests for an intersection between a ray and a quad defined by
256 * its midpoint, normal and sides.
257 * If ellipse is true, hits outside the ellipse that's enclosed by the
258 * quad are rejected.
259 */
261 const float3 ray_D,
262 const float ray_tmin,
263 const float ray_tmax,
264 const float3 quad_P,
265 const float3 inv_quad_u,
266 const float3 inv_quad_v,
267 const float3 quad_n,
268 ccl_private float3 *isect_P,
269 ccl_private float *isect_t,
270 ccl_private float *isect_u,
271 ccl_private float *isect_v,
272 bool ellipse)
273{
274 /* Perform intersection test. */
275 const float t = -(dot(ray_P, quad_n) - dot(quad_P, quad_n)) / dot(ray_D, quad_n);
276 if (!(t > ray_tmin && t < ray_tmax)) {
277 return false;
278 }
279 const float3 hit = ray_P + t * ray_D;
280 const float3 inplane = hit - quad_P;
281 const float u = dot(inplane, inv_quad_u);
282 if (u < -0.5f || u > 0.5f) {
283 return false;
284 }
285 const float v = dot(inplane, inv_quad_v);
286 if (v < -0.5f || v > 0.5f) {
287 return false;
288 }
289 if (ellipse && (u * u + v * v > 0.25f)) {
290 return false;
291 }
292 /* Store the result. */
293 /* TODO(sergey): Check whether we can avoid some checks here. */
294 if (isect_P != nullptr) {
295 *isect_P = hit;
296 }
297 if (isect_t != nullptr) {
298 *isect_t = t;
299 }
300
301 /* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */
302 if (isect_u != nullptr) {
303 *isect_u = v + 0.5f;
304 }
305 if (isect_v != nullptr) {
306 *isect_v = -u - v;
307 }
308
309 return true;
310}
311
312/* Find the ray segment that lies in the same side as the normal `N` of the plane.
313 * `P` is the vector pointing from any point on the plane to the ray origin. */
315 const float3 P,
316 const float3 ray_D,
318{
319 const float DN = dot(ray_D, N);
320
321 /* Distance from P to the plane. */
322 const float t = -dot(P, N) / DN;
323
324 /* Limit the range to the positive side. */
325 if (DN > 0.0f) {
326 t_range->min = fmaxf(t_range->min, t);
327 }
328 else {
329 t_range->max = fminf(t_range->max, t);
330 }
331
332 return !t_range->is_empty();
333}
334
335/* Find the ray segment inside an axis-aligned bounding box. */
337 const float3 bbox_max,
338 const float3 ray_P,
339 const float3 ray_D,
341{
342 const float3 inv_ray_D = reciprocal(ray_D);
343
344 /* Absolute distances to lower and upper box coordinates; */
345 const float3 t_lower = (bbox_min - ray_P) * inv_ray_D;
346 const float3 t_upper = (bbox_max - ray_P) * inv_ray_D;
347
348 /* The four t-intervals (for x-/y-/z-slabs, and ray p(t)). */
349 const float4 tmins = make_float4(min(t_lower, t_upper), t_range->min);
350 const float4 tmaxes = make_float4(max(t_lower, t_upper), t_range->max);
351
352 /* Max of mins and min of maxes. */
353 const float tmin = reduce_max(tmins);
354 const float tmax = reduce_min(tmaxes);
355
356 *t_range = {tmin, tmax};
357
358 return !t_range->is_empty();
359}
360
361/* Find the segment of a ray defined by P + D * t that lies inside a cylinder defined by
362 * (x / len_u)^2 + (y / len_v)^2 = 1. */
364 const float3 D,
365 const float len_u,
366 const float len_v,
368{
369 /* Convert to a 2D problem. */
370 const float2 inv_len = 1.0f / make_float2(len_u, len_v);
371 float2 P_proj = make_float2(P) * inv_len;
372 const float2 D_proj = make_float2(D) * inv_len;
373
374 /* Solve quadratic equation a*t^2 + 2b*t + c = 0. */
375 const float a = dot(D_proj, D_proj);
376 float b = dot(P_proj, D_proj);
377
378 /* Move ray origin closer to the cylinder to prevent precision issue when the ray is far away. */
379 const float t_mid = -b / a;
380 P_proj += D_proj * t_mid;
381
382 /* Recompute b from the shifted origin. */
383 b = dot(P_proj, D_proj);
384 const float c = dot(P_proj, P_proj) - 1.0f;
385
386 float tmin;
387 float tmax;
388 const bool valid = solve_quadratic(a, 2.0f * b, c, tmin, tmax);
389
390 *t_range = intervals_intersection(*t_range, {tmin + t_mid, tmax + t_mid});
391
392 return valid && !t_range->is_empty();
393}
394
395/* *
396 * Find the ray segment inside a single-sided cone.
397 *
398 * \param axis: a unit-length direction around which the cone has a circular symmetry
399 * \param P: the vector pointing from the cone apex to the ray origin
400 * \param D: the direction of the ray, does not need to have unit-length
401 * \param cos_angle_sq: `sqr(cos(half_aperture_of_the_cone))`
402 * \param t_range: the ray segment that lies inside the cone
403 * \return whether the intersection exists and is in the provided range
404 *
405 * See https://www.geometrictools.com/Documentation/IntersectionLineCone.pdf for illustration
406 */
408 const float3 P,
409 float3 D,
410 const float cos_angle_sq,
412{
413 if (cos_angle_sq < 1e-4f) {
414 /* The cone is nearly a plane. */
415 return ray_plane_intersect(axis, P, D, t_range);
416 }
417
418 const float inv_len = inversesqrtf(len_squared(D));
419 D *= inv_len;
420
421 const float AD = dot(axis, D);
422 const float AP = dot(axis, P);
423
424 const float a = sqr(AD) - cos_angle_sq;
425 const float b = 2.0f * (AD * AP - cos_angle_sq * dot(D, P));
426 const float c = sqr(AP) - cos_angle_sq * dot(P, P);
427
428 float tmin = 0.0f;
429 float tmax = FLT_MAX;
430 bool valid = solve_quadratic(a, b, c, tmin, tmax);
431
432 /* Check if the intersections are in the same hemisphere as the cone. */
433 const bool tmin_valid = AP + tmin * AD > 0.0f;
434 const bool tmax_valid = AP + tmax * AD > 0.0f;
435
436 valid &= (tmin_valid || tmax_valid);
437
438 if (!tmax_valid) {
439 tmax = tmin;
440 tmin = 0.0f;
441 }
442 else if (!tmin_valid) {
443 tmin = tmax;
444 tmax = FLT_MAX;
445 }
446
447 *t_range = intervals_intersection(*t_range, {tmin * inv_len, tmax * inv_len});
448
449 return valid && !t_range->is_empty();
450}
451
#define D
#define UNLIKELY(x)
#define U
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert * v
reduce_max(value.rgb)") DEFINE_VALUE("REDUCE(lhs
dot(value.rgb, luminance_coefficients)") DEFINE_VALUE("REDUCE(lhs
#define ccl_device_forceinline
#define ccl_private
#define ccl_device_inline
#define CCL_NAMESPACE_END
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
#define copysignf(x, y)
static float verts[][3]
ccl_device_inline float inversesqrtf(const float f)
Definition math_base.h:529
ccl_device_inline bool solve_quadratic(const float a, const float b, const float c, ccl_private float &x1, ccl_private float &x2)
Definition math_base.h:842
ccl_device_inline Interval< T > intervals_intersection(const ccl_private Interval< T > &first, const ccl_private Interval< T > &second)
Definition math_base.h:901
CCL_NAMESPACE_BEGIN ccl_device_inline float madd(const float a, const float b, const float c)
Definition math_fast.h:35
ccl_device_inline float len_squared(const float2 a)
ccl_device_inline float2 normalize_len(const float2 a, ccl_private float *t)
ccl_device_inline float reduce_min(const float2 a)
ccl_device_inline float3 reciprocal(const float3 a)
Definition math_float3.h:36
ccl_device_inline float4 msub(const float4 a, const float4 b, const float4 c)
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_device bool ray_disk_intersect(const float3 ray_P, const float3 ray_D, const float ray_tmin, const float ray_tmax, const float3 disk_P, const float3 disk_N, const float disk_radius, ccl_private float3 *isect_P, ccl_private float *isect_t)
ccl_device bool ray_aabb_intersect(const float3 bbox_min, const float3 bbox_max, const float3 ray_P, const float3 ray_D, 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)
ccl_device_forceinline bool ray_triangle_intersect(const float3 ray_P, const float3 ray_D, const float ray_tmin, const float ray_tmax, const float3 tri_a, const float3 tri_b, const float3 tri_c, ccl_private float *isect_u, ccl_private float *isect_v, ccl_private float *isect_t)
ccl_device_forceinline float ray_triangle_reciprocal(const float x)
ccl_device bool ray_aligned_disk_intersect(const float3 ray_P, const float3 ray_D, const float ray_tmin, const float ray_tmax, const float3 disk_P, const float disk_radius, ccl_private float3 *isect_P, ccl_private float *isect_t)
ccl_device_inline bool ray_infinite_cylinder_intersect(const float3 P, const float3 D, const float len_u, const float len_v, ccl_private Interval< float > *t_range)
ccl_device bool ray_plane_intersect(const float3 N, const float3 P, const float3 ray_D, ccl_private Interval< float > *t_range)
ccl_device_forceinline bool ray_triangle_intersect_self(const float3 ray_P, const float3 ray_D, const float3 verts[3])
ccl_device_inline float3 ray_triangle_cross(const float3 a, const float3 b)
ccl_device bool ray_quad_intersect(const float3 ray_P, const float3 ray_D, const float ray_tmin, const float ray_tmax, const float3 quad_P, const float3 inv_quad_u, const float3 inv_quad_v, const float3 quad_n, ccl_private float3 *isect_P, ccl_private float *isect_t, ccl_private float *isect_u, ccl_private float *isect_v, bool ellipse)
ccl_device_inline float ray_triangle_dot(const float3 a, const float3 b)
#define N
#define T
const btScalar eps
Definition poly34.cpp:11
#define sqr
#define fabsf
#define sqrtf
#define ccl_device
#define fmaxf
#define make_float2
#define make_float4
#define fminf
#define min(a, b)
Definition sort.cc:36
#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
max
Definition text_draw.cc:251
CCL_NAMESPACE_BEGIN struct Window V