Blender V5.0
curve_intersect.h
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009-2021 Intel Corporation
2 *
3 * SPDX-License-Identifier: Apache-2.0
4 *
5 * Adapted from Embree with modifications. */
6
7#pragma once
8
10#include "kernel/geom/object.h"
11
13
14/* Curve primitive intersection functions.
15 *
16 * The code here was adapted from curve_intersector_sweep.h in Embree, to get
17 * an exact match between Embree CPU ray-tracing and our GPU ray-tracing. */
18
19// NOLINTBEGIN
20#define CURVE_NUM_BEZIER_SUBDIVISIONS 3
21#define CURVE_NUM_BEZIER_SUBDIVISIONS_UNSTABLE (CURVE_NUM_BEZIER_SUBDIVISIONS + 1)
22#define CURVE_NUM_BEZIER_STEPS 2
23#define CURVE_NUM_JACOBIAN_ITERATIONS 5
24// NOLINTEND
25
26#ifdef __HAIR__
27
28/* Catmull-rom curve evaluation. */
29
30ccl_device_inline float4 catmull_rom_basis_eval(const float4 curve[4], float u)
31{
32 const float t = u;
33 const float s = 1.0f - u;
34 const float n0 = -t * s * s;
35 const float n1 = 2.0f + t * t * (3.0f * t - 5.0f);
36 const float n2 = 2.0f + s * s * (3.0f * s - 5.0f);
37 const float n3 = -s * t * t;
38 return 0.5f * (curve[0] * n0 + curve[1] * n1 + curve[2] * n2 + curve[3] * n3);
39}
40
41ccl_device_inline float4 catmull_rom_basis_derivative(const float4 curve[4], float u)
42{
43 const float t = u;
44 const float s = 1.0f - u;
45 const float n0 = -s * s + 2.0f * s * t;
46 const float n1 = 2.0f * t * (3.0f * t - 5.0f) + 3.0f * t * t;
47 const float n2 = 2.0f * s * (3.0f * t + 2.0f) - 3.0f * s * s;
48 const float n3 = -2.0f * s * t + t * t;
49 return 0.5f * (curve[0] * n0 + curve[1] * n1 + curve[2] * n2 + curve[3] * n3);
50}
51
52ccl_device_inline float4 catmull_rom_basis_derivative2(const float4 curve[4], float u)
53{
54 const float t = u;
55 const float n0 = -3.0f * t + 2.0f;
56 const float n1 = 9.0f * t - 5.0f;
57 const float n2 = -9.0f * t + 4.0f;
58 const float n3 = 3.0f * t - 1.0f;
59 return (curve[0] * n0 + curve[1] * n1 + curve[2] * n2 + curve[3] * n3);
60}
61
62/* Thick Curve */
63
64ccl_device_inline float3 dnormalize(const float3 p, const float3 dp)
65{
66 const float pp = dot(p, p);
67 const float pdp = dot(p, dp);
68 return (pp * dp - pdp * p) / (pp * sqrtf(pp));
69}
70
71ccl_device_inline float sqr_point_to_line_distance(const float3 PmQ0, const float3 Q1mQ0)
72{
73 const float3 N = cross(PmQ0, Q1mQ0);
74 const float3 D = Q1mQ0;
75 return dot(N, N) / dot(D, D);
76}
77
78ccl_device_inline bool cylinder_intersect(const float3 cylinder_start,
79 const float3 cylinder_end,
80 const float cylinder_radius,
81 const float3 ray_D,
83 ccl_private float *u0_o,
84 ccl_private float3 *Ng0_o,
85 ccl_private float *u1_o,
86 ccl_private float3 *Ng1_o)
87{
88 /* Calculate quadratic equation to solve. */
89 const float rl = 1.0f / len(cylinder_end - cylinder_start);
90 const float3 P0 = cylinder_start;
91 const float3 dP = (cylinder_end - cylinder_start) * rl;
92 const float3 O = -P0;
93 const float3 dO = ray_D;
94
95 const float dOdO = dot(dO, dO);
96 const float OdO = dot(dO, O);
97 const float OO = dot(O, O);
98 const float dOz = dot(dP, dO);
99 const float Oz = dot(dP, O);
100
101 const float A = dOdO - sqr(dOz);
102 const float B = 2.0f * (OdO - dOz * Oz);
103 const float C = OO - sqr(Oz) - sqr(cylinder_radius);
104
105 /* We miss the cylinder if determinant is smaller than zero. */
106 const float D = B * B - 4.0f * A * C;
107 if (!(D >= 0.0f)) {
108 *t_o = make_float2(FLT_MAX, -FLT_MAX);
109 return false;
110 }
111
112 /* Special case for rays that are parallel to the cylinder. */
113 const float eps = 16.0f * FLT_EPSILON * max(fabsf(dOdO), fabsf(sqr(dOz)));
114 if (fabsf(A) < eps) {
115 if (C <= 0.0f) {
116 *t_o = make_float2(-FLT_MAX, FLT_MAX);
117 return true;
118 }
119 *t_o = make_float2(-FLT_MAX, FLT_MAX);
120 return false;
121 }
122
123 /* Standard case for rays that are not parallel to the cylinder. */
124 const float Q = sqrtf(D);
125 const float rcp_2A = 1.0f / (2.0f * A);
126 const float t0 = (-B - Q) * rcp_2A;
127 const float t1 = (-B + Q) * rcp_2A;
128
129 /* Calculates u and Ng for near hit. */
130 {
131 *u0_o = (t0 * dOz + Oz) * rl;
132 const float3 Pr = t0 * ray_D;
133 const float3 Pl = (*u0_o) * (cylinder_end - cylinder_start) + cylinder_start;
134 *Ng0_o = Pr - Pl;
135 }
136
137 /* Calculates u and Ng for far hit. */
138 {
139 *u1_o = (t1 * dOz + Oz) * rl;
140 const float3 Pr = t1 * ray_D;
141 const float3 Pl = (*u1_o) * (cylinder_end - cylinder_start) + cylinder_start;
142 *Ng1_o = Pr - Pl;
143 }
144
145 *t_o = make_float2(t0, t1);
146
147 return true;
148}
149
150ccl_device_inline float2 half_plane_intersect(const float3 P, const float3 N, const float3 ray_D)
151{
152 const float3 O = -P;
153 const float3 D = ray_D;
154 const float ON = dot(O, N);
155 const float DN = dot(D, N);
156 const float min_rcp_input = 1e-18f;
157 const bool eps = fabsf(DN) < min_rcp_input;
158 const float t = -ON / DN;
159 const float lower = (eps || DN < 0.0f) ? -FLT_MAX : t;
160 const float upper = (eps || DN > 0.0f) ? FLT_MAX : t;
161 return make_float2(lower, upper);
162}
163
164ccl_device bool curve_intersect_iterative(const float3 ray_D,
165 const float ray_tmin,
166 ccl_private float *ray_tmax,
167 const float dt,
168 const float4 curve[4],
169 float u,
170 float t,
171 const bool use_backfacing,
173{
174 const float length_ray_D = len(ray_D);
175
176 /* Error of curve evaluations is proportional to largest coordinate. */
177 const float4 box_min = min(min(curve[0], curve[1]), min(curve[2], curve[3]));
178 const float4 box_max = max(min(curve[0], curve[1]), max(curve[2], curve[3]));
179 const float4 box_abs = max(fabs(box_min), fabs(box_max));
180 const float P_err = 16.0f * FLT_EPSILON *
181 max(box_abs.x, max(box_abs.y, max(box_abs.z, box_abs.w)));
182 const float radius_max = box_max.w;
183
184 for (int i = 0; i < CURVE_NUM_JACOBIAN_ITERATIONS; i++) {
185 const float3 Q = ray_D * t;
186 const float3 dQdt = ray_D;
187 const float Q_err = 16.0f * FLT_EPSILON * length_ray_D * t;
188
189 const float4 P4 = catmull_rom_basis_eval(curve, u);
190 const float4 dPdu4 = catmull_rom_basis_derivative(curve, u);
191
192 const float3 P = make_float3(P4);
193 const float3 dPdu = make_float3(dPdu4);
194 const float radius = P4.w;
195 const float dradiusdu = dPdu4.w;
196
197 const float3 ddPdu = make_float3(catmull_rom_basis_derivative2(curve, u));
198
199 const float3 R = Q - P;
200 const float len_R = len(R);
201 const float R_err = max(Q_err, P_err);
202 const float3 dRdu = -dPdu;
203 const float3 dRdt = dQdt;
204
205 const float3 T = normalize(dPdu);
206 const float3 dTdu = dnormalize(dPdu, ddPdu);
207 const float cos_err = P_err / len(dPdu);
208
209 const float f = dot(R, T);
210 const float f_err = len_R * P_err + R_err + cos_err * (1.0f + len_R);
211 const float dfdu = dot(dRdu, T) + dot(R, dTdu);
212 const float dfdt = dot(dRdt, T);
213
214 const float K = dot(R, R) - sqr(f);
215 const float dKdu = (dot(R, dRdu) - f * dfdu);
216 const float dKdt = (dot(R, dRdt) - f * dfdt);
217 const float rsqrt_K = inversesqrtf(K);
218
219 const float g = sqrtf(K) - radius;
220 const float g_err = R_err + f_err + 16.0f * FLT_EPSILON * radius_max;
221 const float dgdu = dKdu * rsqrt_K - dradiusdu;
222 const float dgdt = dKdt * rsqrt_K;
223
224 const float invdet = 1.0f / (dfdu * dgdt - dgdu * dfdt);
225 u -= (dgdt * f - dfdt * g) * invdet;
226 t -= (-dgdu * f + dfdu * g) * invdet;
227
228 if (fabsf(f) < f_err && fabsf(g) < g_err) {
229 t += dt;
230 if (!(t >= ray_tmin && t <= *ray_tmax)) {
231 return false; /* Rejects NaNs */
232 }
233 if (!(u >= 0.0f && u <= 1.0f)) {
234 return false; /* Rejects NaNs */
235 }
236
237 /* Back-face culling. */
238 const float3 R = normalize(Q - P);
239 const float3 U = dradiusdu * R + dPdu;
240 const float3 V = cross(dPdu, R);
241 const float3 Ng = cross(V, U);
242 if (!use_backfacing && dot(ray_D, Ng) > 0.0f) {
243 return false;
244 }
245
246 /* Record intersection. */
247 *ray_tmax = t;
248 isect->t = t;
249 isect->u = u;
250 isect->v = 0.0f;
251
252 return true;
253 }
254 }
255 return false;
256}
257
258ccl_device bool curve_intersect_recursive(const float3 ray_P,
259 const float3 ray_D,
260 const float ray_tmin,
261 float ray_tmax,
262 float4 curve[4],
264{
265 /* Move ray closer to make intersection stable. */
266 const float3 center = make_float3(0.25f * (curve[0] + curve[1] + curve[2] + curve[3]));
267 const float dt = dot(center - ray_P, ray_D) / dot(ray_D, ray_D);
268 const float3 ref = ray_P + ray_D * dt;
269 const float4 ref4 = make_float4(ref, 0.0f);
270 curve[0] -= ref4;
271 curve[1] -= ref4;
272 curve[2] -= ref4;
273 curve[3] -= ref4;
274
275 const bool use_backfacing = false;
276 const float step_size = 1.0f / (float)(CURVE_NUM_BEZIER_STEPS);
277
278 int depth = 0;
279
280 /* todo: optimize stack for GPU somehow? Possibly some bitflags are enough, and
281 * u0/u1 can be derived from the depth. */
282 struct {
283 float u0, u1;
284 int i;
286
287 bool found = false;
288
289 float u0 = 0.0f;
290 float u1 = 1.0f;
291 int i = 0;
292
293 while (true) {
294 for (; i < CURVE_NUM_BEZIER_STEPS; i++) {
295 const float step = i * step_size;
296
297 /* Subdivide curve. */
298 const float dscale = (u1 - u0) * (1.0f / 3.0f) * step_size;
299 const float vu0 = mix(u0, u1, step);
300 const float vu1 = mix(u0, u1, step + step_size);
301
302 const float4 P0 = catmull_rom_basis_eval(curve, vu0);
303 const float4 dP0du = dscale * catmull_rom_basis_derivative(curve, vu0);
304 const float4 P3 = catmull_rom_basis_eval(curve, vu1);
305 const float4 dP3du = dscale * catmull_rom_basis_derivative(curve, vu1);
306
307 const float4 P1 = P0 + dP0du;
308 const float4 P2 = P3 - dP3du;
309
310 /* Calculate bounding cylinders. */
311 const float rr1 = sqr_point_to_line_distance(make_float3(dP0du), make_float3(P3 - P0));
312 const float rr2 = sqr_point_to_line_distance(make_float3(dP3du), make_float3(P3 - P0));
313 const float maxr12 = sqrtf(max(rr1, rr2));
314 const float one_plus_ulp = 1.0f + 2.0f * FLT_EPSILON;
315 const float one_minus_ulp = 1.0f - 2.0f * FLT_EPSILON;
316 float r_outer = max(max(P0.w, P1.w), max(P2.w, P3.w)) + maxr12;
317 float r_inner = min(min(P0.w, P1.w), min(P2.w, P3.w)) - maxr12;
318 r_outer = one_plus_ulp * r_outer;
319 r_inner = max(0.0f, one_minus_ulp * r_inner);
320 bool valid = true;
321
322 /* Intersect with outer cylinder. */
323 float2 tc_outer;
324 float u_outer0;
325 float u_outer1;
326 float3 Ng_outer0;
327 float3 Ng_outer1;
328 valid = cylinder_intersect(make_float3(P0),
329 make_float3(P3),
330 r_outer,
331 ray_D,
332 &tc_outer,
333 &u_outer0,
334 &Ng_outer0,
335 &u_outer1,
336 &Ng_outer1);
337 if (!valid) {
338 continue;
339 }
340
341 /* Intersect with cap-planes. */
342 float2 tp = make_float2(ray_tmin - dt, ray_tmax - dt);
343 tp = make_float2(max(tp.x, tc_outer.x), min(tp.y, tc_outer.y));
344 const float2 h0 = half_plane_intersect(make_float3(P0), make_float3(dP0du), ray_D);
345 tp = make_float2(max(tp.x, h0.x), min(tp.y, h0.y));
346 const float2 h1 = half_plane_intersect(make_float3(P3), -make_float3(dP3du), ray_D);
347 tp = make_float2(max(tp.x, h1.x), min(tp.y, h1.y));
348 valid = tp.x <= tp.y;
349 if (!valid) {
350 continue;
351 }
352
353 /* Clamp and correct u parameter. */
354 u_outer0 = clamp(u_outer0, 0.0f, 1.0f);
355 u_outer1 = clamp(u_outer1, 0.0f, 1.0f);
356 u_outer0 = mix(u0, u1, (step + u_outer0) * (1.0f / (float)(CURVE_NUM_BEZIER_STEPS + 1)));
357 u_outer1 = mix(u0, u1, (step + u_outer1) * (1.0f / (float)(CURVE_NUM_BEZIER_STEPS + 1)));
358
359 /* Intersect with inner cylinder. */
360 float2 tc_inner;
361 float u_inner0;
362 float u_inner1;
363 float3 Ng_inner0;
364 float3 Ng_inner1;
365 const bool valid_inner = cylinder_intersect(make_float3(P0),
366 make_float3(P3),
367 r_inner,
368 ray_D,
369 &tc_inner,
370 &u_inner0,
371 &Ng_inner0,
372 &u_inner1,
373 &Ng_inner1);
374
375 /* At the unstable area we subdivide deeper. */
376# if 0
377 const bool unstable0 = (!valid_inner) |
378 (fabsf(dot(normalize(ray_D), normalize(Ng_inner0))) < 0.3f);
379 const bool unstable1 = (!valid_inner) |
380 (fabsf(dot(normalize(ray_D), normalize(Ng_inner1))) < 0.3f);
381# else
382 /* On the GPU appears to be a little faster if always enabled. */
383 (void)valid_inner;
384
385 const bool unstable0 = true;
386 const bool unstable1 = true;
387# endif
388
389 /* Subtract the inner interval from the current hit interval. */
390 const float eps = 0.001f;
391 const float2 tp0 = make_float2(tp.x, min(tp.y, tc_inner.x));
392 const float2 tp1 = make_float2(max(tp.x, tc_inner.y), tp.y);
393 /* The X component should be less than the Y component for a valid intersection,
394 * but due to precision issues, the X component can sometimes be greater than
395 * Y by a small amount, leading to missing intersections. */
396 const bool valid0 = valid && ((tp0.x - tp0.y) < eps);
397 const bool valid1 = valid && ((tp1.x - tp1.y) < eps);
398 if (!(valid0 || valid1)) {
399 continue;
400 }
401
402 /* Process one or two hits. */
403 bool recurse = false;
404 if (valid0) {
405 const int termDepth = unstable0 ? CURVE_NUM_BEZIER_SUBDIVISIONS_UNSTABLE :
407 if (depth >= termDepth) {
408 found |= curve_intersect_iterative(
409 ray_D, ray_tmin, &ray_tmax, dt, curve, u_outer0, tp0.x, use_backfacing, isect);
410 }
411 else {
412 recurse = true;
413 }
414 }
415
416 const float t1 = tp1.x + dt;
417 if (valid1 && (t1 >= ray_tmin && t1 <= ray_tmax)) {
418 const int termDepth = unstable1 ? CURVE_NUM_BEZIER_SUBDIVISIONS_UNSTABLE :
420 if (depth >= termDepth) {
421 found |= curve_intersect_iterative(
422 ray_D, ray_tmin, &ray_tmax, dt, curve, u_outer1, tp1.y, use_backfacing, isect);
423 }
424 else {
425 recurse = true;
426 }
427 }
428
429 if (recurse) {
430 stack[depth].u0 = u0;
431 stack[depth].u1 = u1;
432 stack[depth].i = i + 1;
433 depth++;
434
435 u0 = vu0;
436 u1 = vu1;
437 i = -1;
438 }
439 }
440
441 if (depth > 0) {
442 depth--;
443 u0 = stack[depth].u0;
444 u1 = stack[depth].u1;
445 i = stack[depth].i;
446 }
447 else {
448 break;
449 }
450 }
451
452 return found;
453}
454
455/* Ribbons */
456
457ccl_device_inline bool cylinder_culling_test(const float2 p1, const float2 p2, const float r)
458{
459 /* Performs culling against a cylinder. */
460 const float2 dp = p2 - p1;
461 const float num = dp.x * p1.y - dp.y * p1.x;
462 const float den2 = dot(dp, dp);
463 return num * num <= r * r * den2;
464}
465
472ccl_device_inline bool ribbon_intersect_quad(const float ray_tmin,
473 const float ray_tmax,
474 const float3 quad_v0,
475 const float3 quad_v1,
476 const float3 quad_v2,
477 const float3 quad_v3,
478 ccl_private float *u_o,
479 ccl_private float *v_o,
480 ccl_private float *t_o)
481{
482 /* Calculate vertices relative to ray origin? */
483 const float3 O = make_float3(0.0f, 0.0f, 0.0f);
484 const float3 D = make_float3(0.0f, 0.0f, 1.0f);
485 const float3 va = quad_v0 - O;
486 const float3 vb = quad_v1 - O;
487 const float3 vc = quad_v2 - O;
488 const float3 vd = quad_v3 - O;
489
490 const float3 edb = vb - vd;
491 const float WW = dot(cross(vd, edb), D);
492 const float3 v0 = (WW <= 0.0f) ? va : vc;
493 const float3 v1 = (WW <= 0.0f) ? vb : vd;
494 const float3 v2 = (WW <= 0.0f) ? vd : vb;
495
496 /* Calculate edges? */
497 const float3 e0 = v2 - v0;
498 const float3 e1 = v0 - v1;
499
500 /* perform edge tests */
501 const float U = dot(cross(v0, e0), D);
502 const float V = dot(cross(v1, e1), D);
503 if (!(max(U, V) <= 0.0f)) {
504 return false;
505 }
506
507 /* Calculate geometry normal and denominator? */
508 const float3 Ng = cross(e1, e0);
509 const float den = dot(Ng, D);
510 const float rcpDen = 1.0f / den;
511
512 /* Perform depth test? */
513 const float t = rcpDen * dot(v0, Ng);
514 if (!(t >= ray_tmin && t <= ray_tmax)) {
515 return false;
516 }
517
518 /* Avoid division by 0? */
519 if (!(den != 0.0f)) {
520 return false;
521 }
522
523 /* Update hit information? */
524 *t_o = t;
525 *u_o = U * rcpDen;
526 *v_o = V * rcpDen;
527 *u_o = (WW <= 0.0f) ? *u_o : 1.0f - *u_o;
528 *v_o = (WW <= 0.0f) ? *v_o : 1.0f - *v_o;
529 return true;
530}
531
532ccl_device_inline void ribbon_ray_space(const float3 ray_D,
533 const float ray_D_invlen,
534 float3 ray_space[3])
535{
536 const float3 D = ray_D * ray_D_invlen;
537 const float3 dx0 = make_float3(0, D.z, -D.y);
538 const float3 dx1 = make_float3(-D.z, 0, D.x);
539 ray_space[0] = normalize(dot(dx0, dx0) > dot(dx1, dx1) ? dx0 : dx1);
540 ray_space[1] = normalize(cross(D, ray_space[0]));
541 ray_space[2] = D * ray_D_invlen;
542}
543
544ccl_device_inline float4 ribbon_to_ray_space(const float3 ray_space[3],
545 const float3 ray_org,
546 const float4 P4)
547{
548 const float3 P = make_float3(P4) - ray_org;
549 return make_float4(dot(ray_space[0], P), dot(ray_space[1], P), dot(ray_space[2], P), P4.w);
550}
551
552ccl_device_inline bool ribbon_intersect(const float3 ray_org,
553 const float3 ray_D,
554 const float ray_tmin,
555 float ray_tmax,
556 const int N,
557 float4 curve[4],
559{
560 /* Transform control points into ray space. */
561 const float ray_D_invlen = 1.0f / len(ray_D);
562 float3 ray_space[3];
563 ribbon_ray_space(ray_D, ray_D_invlen, ray_space);
564
565 curve[0] = ribbon_to_ray_space(ray_space, ray_org, curve[0]);
566 curve[1] = ribbon_to_ray_space(ray_space, ray_org, curve[1]);
567 curve[2] = ribbon_to_ray_space(ray_space, ray_org, curve[2]);
568 curve[3] = ribbon_to_ray_space(ray_space, ray_org, curve[3]);
569
570 const float4 mx = max(max(fabs(curve[0]), fabs(curve[1])), max(fabs(curve[2]), fabs(curve[3])));
571 const float eps = 4.0f * FLT_EPSILON * max(max(mx.x, mx.y), max(mx.z, mx.w));
572 const float step_size = 1.0f / (float)N;
573
574 /* Evaluate first point and radius scaled normal direction. */
575 float4 p0 = catmull_rom_basis_eval(curve, 0.0f);
576 float3 dp0dt = make_float3(catmull_rom_basis_derivative(curve, 0.0f));
577 if (reduce_max(fabs(dp0dt)) < eps) {
578 const float4 p1 = catmull_rom_basis_eval(curve, step_size);
579 dp0dt = make_float3(p1 - p0);
580 }
581 float3 wn0 = normalize(make_float3(dp0dt.y, -dp0dt.x, 0.0f)) * p0.w;
582
583 /* Evaluate the bezier curve. */
584 for (int i = 0; i < N; i++) {
585 const float u = i * step_size;
586 const float4 p1 = catmull_rom_basis_eval(curve, u + step_size);
587 const bool valid = cylinder_culling_test(
588 make_float2(p0.x, p0.y), make_float2(p1.x, p1.y), max(p0.w, p1.w));
589
590 /* Evaluate next point. */
591 float3 dp1dt = make_float3(catmull_rom_basis_derivative(curve, u + step_size));
592 dp1dt = (reduce_max(fabs(dp1dt)) < eps) ? make_float3(p1 - p0) : dp1dt;
593 const float3 wn1 = normalize(make_float3(dp1dt.y, -dp1dt.x, 0.0f)) * p1.w;
594
595 if (valid) {
596 /* Construct quad coordinates. */
597 const float3 lp0 = make_float3(p0) + wn0;
598 const float3 lp1 = make_float3(p1) + wn1;
599 const float3 up0 = make_float3(p0) - wn0;
600 const float3 up1 = make_float3(p1) - wn1;
601
602 /* Intersect quad. */
603 float vu;
604 float vv;
605 float vt;
606 bool valid0 = ribbon_intersect_quad(ray_tmin, ray_tmax, lp0, lp1, up1, up0, &vu, &vv, &vt);
607
608 if (valid0) {
609 /* ignore self intersections */
610 const float avoidance_factor = 2.0f;
611 if (avoidance_factor != 0.0f) {
612 const float r = mix(p0.w, p1.w, vu);
613 valid0 = vt > avoidance_factor * r * ray_D_invlen;
614 }
615
616 if (valid0) {
617 vv = 2.0f * vv - 1.0f;
618
619 /* Record intersection. */
620 ray_tmax = vt;
621 isect->t = vt;
622 isect->u = u + vu * step_size;
623 isect->v = vv;
624 return true;
625 }
626 }
627 }
628
629 /* Store point for next step. */
630 p0 = p1;
631 wn0 = wn1;
632 }
633 return false;
634}
635
636/* Linear curve evaluation. */
637
638ccl_device_inline float4 linear_basis_eval(const float4 curve[4], float u)
639{
640 return mix(curve[1], curve[2], u);
641}
642
643ccl_device_inline float4 linear_basis_derivative(const float4 curve[4], float)
644{
645 return curve[2] - curve[1];
646}
647
648/* Linear Thick Curve */
649
650ccl_device_inline bool cone_sphere_intersect(const float4 curve[4],
651 const float3 ray_D,
652 ccl_private float *t_o,
653 ccl_private float *u_o,
654 ccl_private float3 *Ng_o)
655{
656 /* Calculate quadratic equation to solve. */
657 const float r0 = curve[1].w;
658 const float r1 = curve[2].w;
659 const float dr = r1 - r0;
660 const float r0dr = r0 * dr;
661
662 const float3 P0 = make_float3(curve[1]);
663 const float3 P1 = make_float3(curve[2]);
664 const float3 dP = P1 - P0;
665 const float3 O = -P0;
666 const float3 dO = ray_D;
667
668 const float dOdO = dot(dO, dO);
669 const float OdO = dot(dO, O);
670 const float OO = dot(O, O);
671 const float dOz = dot(dP, dO);
672 const float Oz = dot(dP, O);
673
674 const float dPdP = dot(dP, dP);
675 const float yp = Oz + r0dr;
676 const float g = dPdP - sqr(dr);
677
678 const float A = g * dOdO - sqr(dOz);
679 const float B = 2.0f * (g * OdO - dOz * yp);
680 const float C = g * OO - sqr(Oz) - sqr(r0) * dPdP - 2.0f * r0dr * Oz;
681
682 /* We miss the cone if determinant is smaller than zero. */
683 const float D = B * B - 4.0f * A * C;
684 if (!(D >= 0.0f)) {
685 *t_o = FLT_MAX;
686 return false;
687 }
688
689 /* Special case for rays that are parallel to the cone. */
690 const float eps = 1e-18f;
691 if (fabsf(A) < eps) {
692 *t_o = -FLT_MAX;
693 return false;
694 }
695
696 /* Standard case for rays that are not parallel to the cone. */
697 const float Q = sqrtf(D);
698 const float rcp_2A = 1.0f / (2.0f * A);
699 const float t0 = (-B - Q) * rcp_2A;
700 const float y0 = yp + t0 * dOz;
701
702 float t = FLT_MAX;
703
704 /* Calculates u and Ng for near hit. */
705 if ((y0 > -FLT_EPSILON) && (y0 <= g) && (g > 0.0f)) {
706 t = t0;
707 *u_o = clamp(y0 / g, 0.0f, 1.0f);
708 const float3 Pr = O + t0 * dO;
709 const float3 Pl = (*u_o) * dP;
710 *Ng_o = Pr - Pl;
711 }
712
713 /* Intersect ending sphere. */
714 {
715 const float3 O1 = -P1;
716 const float O1dO = dot(O1, dO);
717 const float h2 = sqr(O1dO) - dOdO * (dot(O1, O1) - sqr(r1));
718 if (h2 >= 0.0f) {
719 const float rhs1 = sqrt(h2);
720
721 /* Clip away near hit if it is inside next cone segment. */
722 const float t_sph1 = (-O1dO - rhs1) * (1.0f / dOdO);
723
724 const float r2 = curve[3].w;
725 const float3 P2 = make_float3(curve[3]);
726 const float y2 = dot((t_sph1 * dO) - P1, (P2 - P1));
727 const float cap2 = -(r1 * (r2 - r1));
728
729 if ((t_sph1 <= t) && (yp + t_sph1 * dOz) > g && !(y2 > cap2)) {
730 t = t_sph1;
731 *u_o = 1.0f;
732 *Ng_o = t * dO - P1;
733 }
734 }
735 }
736
737 /* Intersect start sphere. */
738 if (isequal(curve[0], curve[1])) {
739 const float h2 = sqr(OdO) - dOdO * (dot(O, O) - sqr(r0));
740 if (h2 >= 0.0f) {
741 const float rhs1 = sqrt(h2);
742
743 /* Clip away near hit if it is inside next cone segment. */
744 const float t_sph0 = (-OdO - rhs1) * (1.0f / dOdO);
745
746 if ((t_sph0 <= t) && (yp + t_sph0 * dOz) < 0) {
747 t = t_sph0;
748 *u_o = 0.0f;
749 *Ng_o = t * dO - P0;
750 }
751 }
752 }
753
754 *t_o = t;
755
756 return t != FLT_MAX;
757}
758
759ccl_device bool linear_curve_intersect(const float3 ray_P,
760 const float3 ray_D,
761 const float ray_tmin,
762 float ray_tmax,
763 float4 curve[4],
765{
766 /* Move ray closer to make intersection stable. */
767 const float3 center = make_float3(0.5f * (curve[1] + curve[2]));
768 const float dt = dot(center - ray_P, ray_D) / dot(ray_D, ray_D);
769 const float3 ref = ray_P + ray_D * dt;
770 const float4 ref4 = make_float4(ref, 0.0f);
771 curve[0] -= ref4;
772 curve[1] -= ref4;
773 curve[2] -= ref4;
774 curve[3] -= ref4;
775
776 /* Intersect with cone sphere. */
777 float t;
778 float u;
779 float3 Ng;
780 if (!cone_sphere_intersect(curve, ray_D, &t, &u, &Ng)) {
781 return false;
782 }
783
784 t += dt;
785
786 if (!(t >= ray_tmin && t <= ray_tmax)) {
787 return false; /* Rejects NaNs */
788 }
789
790 /* Record intersection. */
791 isect->t = t;
792 isect->u = u;
793 isect->v = 0.0f;
794
795 return true;
796}
797
798ccl_device_forceinline bool curve_intersect(KernelGlobals kg,
800 const float3 ray_P,
801 const float3 ray_D,
802 const float tmin,
803 const float tmax,
804 const int object,
805 const int prim,
806 const float time,
807 const int type)
808{
809 const bool is_motion = (type & PRIMITIVE_MOTION);
810
811 const KernelCurve kcurve = kernel_data_fetch(curves, prim);
812
813 const int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(type);
814 const int k1 = k0 + 1;
815 const int ka = max(k0 - 1, kcurve.first_key);
816 const int kb = min(k1 + 1, kcurve.first_key + kcurve.num_keys - 1);
817
818 float4 curve[4];
819 if (!is_motion) {
820 curve[0] = kernel_data_fetch(curve_keys, ka);
821 curve[1] = kernel_data_fetch(curve_keys, k0);
822 curve[2] = kernel_data_fetch(curve_keys, k1);
823 curve[3] = kernel_data_fetch(curve_keys, kb);
824 }
825 else {
826 motion_curve_keys(kg, object, time, ka, k0, k1, kb, curve);
827 }
828
829 switch (type & PRIMITIVE_CURVE) {
831 /* todo: adaptive number of subdivisions could help performance here. */
832 const int subdivisions = kernel_data.bvh.curve_subdivisions;
833 if (ribbon_intersect(ray_P, ray_D, tmin, tmax, subdivisions, curve, isect)) {
834 isect->prim = prim;
835 isect->object = object;
836 isect->type = type;
837 return true;
838 }
839 break;
840 }
842 if (curve_intersect_recursive(ray_P, ray_D, tmin, tmax, curve, isect)) {
843 isect->prim = prim;
844 isect->object = object;
845 isect->type = type;
846 return true;
847 }
848 break;
849 }
851 if (linear_curve_intersect(ray_P, ray_D, tmin, tmax, curve, isect)) {
852 isect->prim = prim;
853 isect->object = object;
854 isect->type = type;
855 return true;
856 }
857 break;
858 }
859 }
860
861 return false;
862}
863
864ccl_device_inline void curve_shader_setup(KernelGlobals kg,
865 ccl_private ShaderData *sd,
866 float3 P,
867 float3 D,
868 float t,
869 const int isect_prim)
870{
871 if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
872 const Transform tfm = object_get_inverse_transform(kg, sd);
873
874 P = transform_point(&tfm, P);
875 D = transform_direction(&tfm, D * t);
876 D = safe_normalize_len(D, &t);
877 }
878
879 const KernelCurve kcurve = kernel_data_fetch(curves, isect_prim);
880
881 const int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type);
882 const int k1 = k0 + 1;
883 const int ka = max(k0 - 1, kcurve.first_key);
884 const int kb = min(k1 + 1, kcurve.first_key + kcurve.num_keys - 1);
885
886 float4 P_curve[4];
887
888 if (!(sd->type & PRIMITIVE_MOTION)) {
889 P_curve[0] = kernel_data_fetch(curve_keys, ka);
890 P_curve[1] = kernel_data_fetch(curve_keys, k0);
891 P_curve[2] = kernel_data_fetch(curve_keys, k1);
892 P_curve[3] = kernel_data_fetch(curve_keys, kb);
893 }
894 else {
895 motion_curve_keys(kg, sd->object, sd->time, ka, k0, k1, kb, P_curve);
896 }
897
898 P = P + D * t;
899
900 const float4 dPdu4 = (sd->type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_THICK_LINEAR ?
901 linear_basis_derivative(P_curve, sd->u) :
902 catmull_rom_basis_derivative(P_curve, sd->u);
903 const float3 dPdu = make_float3(dPdu4);
904
905 if ((sd->type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_RIBBON) {
906 /* Rounded smooth normals for ribbons, to approximate thick curve shape. */
907 const float3 tangent = normalize(dPdu);
908 const float3 bitangent = normalize(cross(tangent, -D));
909 const float sine = sd->v;
910 const float cosine = cos_from_sin(sine);
911
912 sd->N = normalize(sine * bitangent - cosine * normalize(cross(tangent, bitangent)));
913# if 0
914 /* This approximates the position and geometric normal of a thick curve too,
915 * but gives too many issues with wrong self intersections. */
916 const float dPdu_radius = dPdu4.w;
917 sd->Ng = sd->N;
918 P += sd->N * dPdu_radius;
919# endif
920 }
921 else {
922 /* Thick curves, compute normal using direction from inside the curve.
923 * This could be optimized by recording the normal in the intersection,
924 * however for Optix this would go beyond the size of the payload. */
925 /* NOTE: It is possible that P will be the same as P_inside (precision issues, or very small
926 * radius). In this case use the view direction to approximate the normal. */
927 const float3 P_inside = (sd->type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_THICK_LINEAR ?
928 make_float3(linear_basis_eval(P_curve, sd->u)) :
929 make_float3(catmull_rom_basis_eval(P_curve, sd->u));
930 const float3 N = (!isequal(P, P_inside)) ? normalize(P - P_inside) : -sd->wi;
931
932 sd->N = N;
933 sd->v = 0.0f;
934 }
935
936# ifdef __DPDU__
937 /* dPdu/dPdv */
938 sd->dPdu = dPdu;
939# endif
940
941 /* Convert to world space. */
942 if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
944 object_normal_transform_auto(kg, sd, &sd->N);
945 object_dir_transform_auto(kg, sd, &sd->dPdu);
946 }
947
948 sd->P = P;
949 sd->Ng = ((sd->type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_RIBBON) ? sd->wi : sd->N;
950 sd->dPdv = cross(sd->dPdu, sd->Ng);
951 sd->shader = kernel_data_fetch(curves, sd->prim).shader_id;
952}
953
954#endif
955
#define D
ATTR_WARN_UNUSED_RESULT const size_t num
#define K(key)
#define C
Definition RandGen.cpp:29
#define U
ATTR_WARN_UNUSED_RESULT const BMVert * v2
#define A
nullptr float
reduce_max(value.rgb)") DEFINE_VALUE("REDUCE(lhs
dot(value.rgb, luminance_coefficients)") DEFINE_VALUE("REDUCE(lhs
#define CURVE_NUM_BEZIER_SUBDIVISIONS_UNSTABLE
#define CURVE_NUM_BEZIER_STEPS
#define CURVE_NUM_JACOBIAN_ITERATIONS
#define CURVE_NUM_BEZIER_SUBDIVISIONS
#define kernel_data
#define ccl_device_forceinline
#define kernel_data_fetch(name, index)
#define PRIMITIVE_UNPACK_SEGMENT(type)
#define ccl_private
const ThreadKernelGlobalsCPU * KernelGlobals
#define ccl_device_inline
#define CCL_NAMESPACE_END
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
VecBase< float, D > normalize(VecOp< float, D >) RET
#define sqrt
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
VecBase< float, 3 > cross(VecOp< float, 3 >, VecOp< float, 3 >) RET
constexpr T clamp(T, U, U) RET
ccl_device_inline Transform object_get_inverse_transform(KernelGlobals kg, const ccl_private ShaderData *sd)
#define object_normal_transform_auto
#define object_position_transform_auto
#define object_dir_transform_auto
@ PRIMITIVE_CURVE_THICK_LINEAR
@ PRIMITIVE_MOTION
@ PRIMITIVE_CURVE_RIBBON
@ PRIMITIVE_CURVE
@ PRIMITIVE_CURVE_THICK
@ SD_OBJECT_TRANSFORM_APPLIED
ccl_device_inline float inversesqrtf(const float f)
Definition math_base.h:529
ccl_device_inline float cos_from_sin(const float s)
Definition math_base.h:614
ccl_device_inline float2 fabs(const float2 a)
ccl_device_inline bool isequal(const float2 a, const float2 b)
ccl_device_inline float3 safe_normalize_len(const float3 a, ccl_private float *t)
#define N
#define T
#define B
#define R
const btScalar eps
Definition poly34.cpp:11
#define mix
#define sqr
#define fabsf
#define sqrtf
#define ccl_device
#define make_float2
#define make_float4
#define min(a, b)
Definition sort.cc:36
#define FLT_MAX
Definition stdcycles.h:14
float x
float y
float y
Definition sky_math.h:136
float x
Definition sky_math.h:136
float y
Definition sky_math.h:225
float z
Definition sky_math.h:225
float x
Definition sky_math.h:225
float w
Definition sky_math.h:225
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
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
uint len
CCL_NAMESPACE_BEGIN struct Window V