1 | // Copyright 2009-2021 Intel Corporation |
2 | // SPDX-License-Identifier: Apache-2.0 |
3 | |
4 | #pragma once |
5 | |
6 | #include "triangle.h" |
7 | #include "trianglev.h" |
8 | #include "trianglev_mb.h" |
9 | #include "intersector_epilog.h" |
10 | |
11 | /*! Modified Pluecker ray/triangle intersector. The test first shifts |
12 | * the ray origin into the origin of the coordinate system and then |
13 | * uses Pluecker coordinates for the intersection. Due to the shift, |
14 | * the Pluecker coordinate calculation simplifies and the tests get |
15 | * numerically stable. The edge equations are watertight along the |
16 | * edge for neighboring triangles. */ |
17 | |
18 | namespace embree |
19 | { |
20 | namespace isa |
21 | { |
22 | template<int M, typename UVMapper> |
23 | struct PlueckerHitM |
24 | { |
25 | __forceinline PlueckerHitM(const UVMapper& mapUV) : mapUV(mapUV) {} |
26 | |
27 | __forceinline PlueckerHitM(const vbool<M>& valid, const vfloat<M>& U, const vfloat<M>& V, const vfloat<M>& UVW, const vfloat<M>& t, const Vec3vf<M>& Ng, const UVMapper& mapUV) |
28 | : U(U), V(V), UVW(UVW), mapUV(mapUV), valid(valid), vt(t), vNg(Ng) {} |
29 | |
30 | __forceinline void finalize() |
31 | { |
32 | const vbool<M> invalid = abs(UVW) < min_rcp_input; |
33 | const vfloat<M> rcpUVW = select(invalid,vfloat<M>(0.0f),rcp(UVW)); |
34 | vu = min(U * rcpUVW,1.0f); |
35 | vv = min(V * rcpUVW,1.0f); |
36 | mapUV(vu,vv,vNg); |
37 | } |
38 | |
39 | __forceinline Vec2vf<M> uv() const { return Vec2vf<M>(vu,vv); } |
40 | __forceinline vfloat<M> t () const { return vt; } |
41 | __forceinline Vec3vf<M> Ng() const { return vNg; } |
42 | |
43 | __forceinline Vec2f uv (const size_t i) const { return Vec2f(vu[i],vv[i]); } |
44 | __forceinline float t (const size_t i) const { return vt[i]; } |
45 | __forceinline Vec3fa Ng(const size_t i) const { return Vec3fa(vNg.x[i],vNg.y[i],vNg.z[i]); } |
46 | |
47 | public: |
48 | vfloat<M> U; |
49 | vfloat<M> V; |
50 | vfloat<M> UVW; |
51 | const UVMapper& mapUV; |
52 | |
53 | public: |
54 | vbool<M> valid; |
55 | vfloat<M> vu; |
56 | vfloat<M> vv; |
57 | vfloat<M> vt; |
58 | Vec3vf<M> vNg; |
59 | }; |
60 | |
61 | template<int M, bool early_out = true> |
62 | struct PlueckerIntersector1 |
63 | { |
64 | __forceinline PlueckerIntersector1() {} |
65 | |
66 | __forceinline PlueckerIntersector1(const Ray& ray, const void* ptr) {} |
67 | |
68 | template<typename UVMapper> |
69 | __forceinline bool intersect(const vbool<M>& valid0, |
70 | Ray& ray, |
71 | const Vec3vf<M>& tri_v0, |
72 | const Vec3vf<M>& tri_v1, |
73 | const Vec3vf<M>& tri_v2, |
74 | const UVMapper& mapUV, |
75 | PlueckerHitM<M,UVMapper>& hit) const |
76 | { |
77 | vbool<M> valid = valid0; |
78 | |
79 | /* calculate vertices relative to ray origin */ |
80 | const Vec3vf<M> O = Vec3vf<M>((Vec3fa)ray.org); |
81 | const Vec3vf<M> D = Vec3vf<M>((Vec3fa)ray.dir); |
82 | const Vec3vf<M> v0 = tri_v0-O; |
83 | const Vec3vf<M> v1 = tri_v1-O; |
84 | const Vec3vf<M> v2 = tri_v2-O; |
85 | |
86 | /* calculate triangle edges */ |
87 | const Vec3vf<M> e0 = v2-v0; |
88 | const Vec3vf<M> e1 = v0-v1; |
89 | const Vec3vf<M> e2 = v1-v2; |
90 | |
91 | /* perform edge tests */ |
92 | const vfloat<M> U = dot(cross(e0,v2+v0),D); |
93 | const vfloat<M> V = dot(cross(e1,v0+v1),D); |
94 | const vfloat<M> W = dot(cross(e2,v1+v2),D); |
95 | const vfloat<M> UVW = U+V+W; |
96 | const vfloat<M> eps = float(ulp)*abs(UVW); |
97 | #if defined(EMBREE_BACKFACE_CULLING) |
98 | valid &= max(U,V,W) <= eps; |
99 | #else |
100 | valid &= (min(U,V,W) >= -eps) | (max(U,V,W) <= eps); |
101 | #endif |
102 | if (unlikely(early_out && none(valid))) return false; |
103 | |
104 | /* calculate geometry normal and denominator */ |
105 | const Vec3vf<M> Ng = stable_triangle_normal(e0,e1,e2); |
106 | const vfloat<M> den = twice(dot(Ng,D)); |
107 | |
108 | /* perform depth test */ |
109 | const vfloat<M> T = twice(dot(v0,Ng)); |
110 | const vfloat<M> t = rcp(den)*T; |
111 | valid &= vfloat<M>(ray.tnear()) <= t & t <= vfloat<M>(ray.tfar); |
112 | valid &= den != vfloat<M>(zero); |
113 | if (unlikely(early_out && none(valid))) return false; |
114 | |
115 | /* update hit information */ |
116 | new (&hit) PlueckerHitM<M,UVMapper>(valid,U,V,UVW,t,Ng,mapUV); |
117 | return true; |
118 | } |
119 | |
120 | template<typename UVMapper> |
121 | __forceinline bool intersectEdge(const vbool<M>& valid, |
122 | Ray& ray, |
123 | const Vec3vf<M>& tri_v0, |
124 | const Vec3vf<M>& tri_v1, |
125 | const Vec3vf<M>& tri_v2, |
126 | const UVMapper& mapUV, |
127 | PlueckerHitM<M,UVMapper>& hit) const |
128 | { |
129 | return intersect(valid,ray,tri_v0,tri_v1,tri_v2,mapUV,hit); |
130 | } |
131 | |
132 | template<typename UVMapper> |
133 | __forceinline bool intersectEdge(Ray& ray, |
134 | const Vec3vf<M>& tri_v0, |
135 | const Vec3vf<M>& tri_v1, |
136 | const Vec3vf<M>& tri_v2, |
137 | const UVMapper& mapUV, |
138 | PlueckerHitM<M,UVMapper>& hit) const |
139 | { |
140 | vbool<M> valid = true; |
141 | return intersect(valid,ray,tri_v0,tri_v1,tri_v2,mapUV,hit); |
142 | } |
143 | |
144 | template<typename UVMapper> |
145 | __forceinline bool intersect(Ray& ray, |
146 | const Vec3vf<M>& tri_v0, |
147 | const Vec3vf<M>& tri_v1, |
148 | const Vec3vf<M>& tri_v2, |
149 | const UVMapper& mapUV, |
150 | PlueckerHitM<M,UVMapper>& hit) const |
151 | { |
152 | return intersectEdge(ray,tri_v0,tri_v1,tri_v2,mapUV,hit); |
153 | } |
154 | |
155 | template<typename UVMapper, typename Epilog> |
156 | __forceinline bool intersectEdge(Ray& ray, |
157 | const Vec3vf<M>& v0, |
158 | const Vec3vf<M>& e1, |
159 | const Vec3vf<M>& e2, |
160 | const UVMapper& mapUV, |
161 | const Epilog& epilog) const |
162 | { |
163 | PlueckerHitM<M,UVMapper> hit(mapUV); |
164 | if (likely(intersectEdge(ray,v0,e1,e2,mapUV,hit))) return epilog(hit.valid,hit); |
165 | return false; |
166 | } |
167 | |
168 | template<typename UVMapper, typename Epilog> |
169 | __forceinline bool intersect(Ray& ray, |
170 | const Vec3vf<M>& v0, |
171 | const Vec3vf<M>& v1, |
172 | const Vec3vf<M>& v2, |
173 | const UVMapper& mapUV, |
174 | const Epilog& epilog) const |
175 | { |
176 | PlueckerHitM<M,UVMapper> hit(mapUV); |
177 | if (likely(intersect(ray,v0,v1,v2,mapUV,hit))) return epilog(hit.valid,hit); |
178 | return false; |
179 | } |
180 | |
181 | template<typename Epilog> |
182 | __forceinline bool intersect(Ray& ray, |
183 | const Vec3vf<M>& v0, |
184 | const Vec3vf<M>& v1, |
185 | const Vec3vf<M>& v2, |
186 | const Epilog& epilog) const |
187 | { |
188 | auto mapUV = UVIdentity<M>(); |
189 | PlueckerHitM<M,UVIdentity<M>> hit(mapUV); |
190 | if (likely(intersect(ray,v0,v1,v2,mapUV,hit))) return epilog(hit.valid,hit); |
191 | return false; |
192 | } |
193 | |
194 | template<typename UVMapper, typename Epilog> |
195 | __forceinline bool intersect(const vbool<M>& valid, |
196 | Ray& ray, |
197 | const Vec3vf<M>& v0, |
198 | const Vec3vf<M>& v1, |
199 | const Vec3vf<M>& v2, |
200 | const UVMapper& mapUV, |
201 | const Epilog& epilog) const |
202 | { |
203 | PlueckerHitM<M,UVMapper> hit(mapUV); |
204 | if (likely(intersect(valid,ray,v0,v1,v2,mapUV,hit))) return epilog(hit.valid,hit); |
205 | return false; |
206 | } |
207 | |
208 | }; |
209 | |
210 | template<int K, typename UVMapper> |
211 | struct PlueckerHitK |
212 | { |
213 | __forceinline PlueckerHitK(const UVMapper& mapUV) : mapUV(mapUV) {} |
214 | |
215 | __forceinline PlueckerHitK(const vfloat<K>& U, const vfloat<K>& V, const vfloat<K>& UVW, const vfloat<K>& t, const Vec3vf<K>& Ng, const UVMapper& mapUV) |
216 | : U(U), V(V), UVW(UVW), t(t), Ng(Ng), mapUV(mapUV) {} |
217 | |
218 | __forceinline std::tuple<vfloat<K>,vfloat<K>,vfloat<K>,Vec3vf<K>> operator() () const |
219 | { |
220 | const vbool<K> invalid = abs(UVW) < min_rcp_input; |
221 | const vfloat<K> rcpUVW = select(invalid,vfloat<K>(0.0f),rcp(UVW)); |
222 | vfloat<K> u = min(U * rcpUVW,1.0f); |
223 | vfloat<K> v = min(V * rcpUVW,1.0f); |
224 | Vec3vf<K> vNg = Ng; |
225 | mapUV(u,v,vNg); |
226 | return std::make_tuple(u,v,t,vNg); |
227 | } |
228 | vfloat<K> U; |
229 | vfloat<K> V; |
230 | const vfloat<K> UVW; |
231 | const vfloat<K> t; |
232 | const Vec3vf<K> Ng; |
233 | const UVMapper& mapUV; |
234 | }; |
235 | |
236 | template<int M, int K> |
237 | struct PlueckerIntersectorK |
238 | { |
239 | __forceinline PlueckerIntersectorK() {} |
240 | __forceinline PlueckerIntersectorK(const vbool<K>& valid, const RayK<K>& ray) {} |
241 | |
242 | /*! Intersects K rays with one of M triangles. */ |
243 | template<typename UVMapper> |
244 | __forceinline vbool<K> intersectK(const vbool<K>& valid0, |
245 | RayK<K>& ray, |
246 | const Vec3vf<K>& tri_v0, |
247 | const Vec3vf<K>& tri_v1, |
248 | const Vec3vf<K>& tri_v2, |
249 | const UVMapper& mapUV, |
250 | PlueckerHitK<K,UVMapper> &hit) const |
251 | { |
252 | /* calculate vertices relative to ray origin */ |
253 | vbool<K> valid = valid0; |
254 | const Vec3vf<K> O = ray.org; |
255 | const Vec3vf<K> D = ray.dir; |
256 | const Vec3vf<K> v0 = tri_v0-O; |
257 | const Vec3vf<K> v1 = tri_v1-O; |
258 | const Vec3vf<K> v2 = tri_v2-O; |
259 | |
260 | /* calculate triangle edges */ |
261 | const Vec3vf<K> e0 = v2-v0; |
262 | const Vec3vf<K> e1 = v0-v1; |
263 | const Vec3vf<K> e2 = v1-v2; |
264 | |
265 | /* perform edge tests */ |
266 | const vfloat<K> U = dot(Vec3vf<K>(cross(e0,v2+v0)),D); |
267 | const vfloat<K> V = dot(Vec3vf<K>(cross(e1,v0+v1)),D); |
268 | const vfloat<K> W = dot(Vec3vf<K>(cross(e2,v1+v2)),D); |
269 | const vfloat<K> UVW = U+V+W; |
270 | const vfloat<K> eps = float(ulp)*abs(UVW); |
271 | #if defined(EMBREE_BACKFACE_CULLING) |
272 | valid &= max(U,V,W) <= eps; |
273 | #else |
274 | valid &= (min(U,V,W) >= -eps) | (max(U,V,W) <= eps); |
275 | #endif |
276 | if (unlikely(none(valid))) return valid; |
277 | |
278 | /* calculate geometry normal and denominator */ |
279 | const Vec3vf<K> Ng = stable_triangle_normal(e0,e1,e2); |
280 | const vfloat<K> den = twice(dot(Vec3vf<K>(Ng),D)); |
281 | |
282 | /* perform depth test */ |
283 | const vfloat<K> T = twice(dot(v0,Vec3vf<K>(Ng))); |
284 | const vfloat<K> t = rcp(den)*T; |
285 | valid &= ray.tnear() <= t & t <= ray.tfar; |
286 | valid &= den != vfloat<K>(zero); |
287 | if (unlikely(none(valid))) return valid; |
288 | |
289 | /* calculate hit information */ |
290 | new (&hit) PlueckerHitK<K,UVMapper>(U,V,UVW,t,Ng,mapUV); |
291 | return valid; |
292 | } |
293 | |
294 | template<typename Epilog> |
295 | __forceinline vbool<K> intersectK(const vbool<K>& valid0, |
296 | RayK<K>& ray, |
297 | const Vec3vf<K>& tri_v0, |
298 | const Vec3vf<K>& tri_v1, |
299 | const Vec3vf<K>& tri_v2, |
300 | const Epilog& epilog) const |
301 | { |
302 | UVIdentity<K> mapUV; |
303 | PlueckerHitK<K,UVIdentity<K>> hit(mapUV); |
304 | const vbool<K> valid = intersectK(valid0,ray,tri_v0,tri_v1,tri_v2,mapUV,hit); |
305 | return epilog(valid,hit); |
306 | } |
307 | |
308 | template<typename UVMapper, typename Epilog> |
309 | __forceinline vbool<K> intersectK(const vbool<K>& valid0, |
310 | RayK<K>& ray, |
311 | const Vec3vf<K>& tri_v0, |
312 | const Vec3vf<K>& tri_v1, |
313 | const Vec3vf<K>& tri_v2, |
314 | const UVMapper& mapUV, |
315 | const Epilog& epilog) const |
316 | { |
317 | PlueckerHitK<K,UVMapper> hit(mapUV); |
318 | const vbool<K> valid = intersectK(valid0,ray,tri_v0,tri_v1,tri_v2,mapUV,hit); |
319 | return epilog(valid,hit); |
320 | } |
321 | |
322 | /*! Intersect k'th ray from ray packet of size K with M triangles. */ |
323 | template<typename UVMapper> |
324 | __forceinline bool intersect(RayK<K>& ray, size_t k, |
325 | const Vec3vf<M>& tri_v0, |
326 | const Vec3vf<M>& tri_v1, |
327 | const Vec3vf<M>& tri_v2, |
328 | const UVMapper& mapUV, |
329 | PlueckerHitM<M,UVMapper> &hit) const |
330 | { |
331 | /* calculate vertices relative to ray origin */ |
332 | const Vec3vf<M> O = broadcast<vfloat<M>>(ray.org,k); |
333 | const Vec3vf<M> D = broadcast<vfloat<M>>(ray.dir,k); |
334 | const Vec3vf<M> v0 = tri_v0-O; |
335 | const Vec3vf<M> v1 = tri_v1-O; |
336 | const Vec3vf<M> v2 = tri_v2-O; |
337 | |
338 | /* calculate triangle edges */ |
339 | const Vec3vf<M> e0 = v2-v0; |
340 | const Vec3vf<M> e1 = v0-v1; |
341 | const Vec3vf<M> e2 = v1-v2; |
342 | |
343 | |
344 | /* perform edge tests */ |
345 | const vfloat<M> U = dot(cross(e0,v2+v0),D); |
346 | const vfloat<M> V = dot(cross(e1,v0+v1),D); |
347 | const vfloat<M> W = dot(cross(e2,v1+v2),D); |
348 | |
349 | const vfloat<M> UVW = U+V+W; |
350 | const vfloat<M> eps = float(ulp)*abs(UVW); |
351 | #if defined(EMBREE_BACKFACE_CULLING) |
352 | vbool<M> valid = max(U,V,W) <= eps; |
353 | #else |
354 | vbool<M> valid = (min(U,V,W) >= -eps) | (max(U,V,W) <= eps); |
355 | #endif |
356 | if (unlikely(none(valid))) return false; |
357 | |
358 | /* calculate geometry normal and denominator */ |
359 | const Vec3vf<M> Ng = stable_triangle_normal(e0,e1,e2); |
360 | const vfloat<M> den = twice(dot(Ng,D)); |
361 | |
362 | /* perform depth test */ |
363 | const vfloat<M> T = twice(dot(v0,Ng)); |
364 | const vfloat<M> t = rcp(den)*T; |
365 | valid &= vfloat<M>(ray.tnear()[k]) <= t & t <= vfloat<M>(ray.tfar[k]); |
366 | if (unlikely(none(valid))) return false; |
367 | |
368 | /* avoid division by 0 */ |
369 | valid &= den != vfloat<M>(zero); |
370 | if (unlikely(none(valid))) return false; |
371 | |
372 | /* update hit information */ |
373 | new (&hit) PlueckerHitM<M,UVMapper>(valid,U,V,UVW,t,Ng,mapUV); |
374 | return true; |
375 | } |
376 | |
377 | template<typename UVMapper, typename Epilog> |
378 | __forceinline bool intersect(RayK<K>& ray, size_t k, |
379 | const Vec3vf<M>& tri_v0, |
380 | const Vec3vf<M>& tri_v1, |
381 | const Vec3vf<M>& tri_v2, |
382 | const UVMapper& mapUV, |
383 | const Epilog& epilog) const |
384 | { |
385 | PlueckerHitM<M,UVMapper> hit(mapUV); |
386 | if (intersect(ray,k,tri_v0,tri_v1,tri_v2,mapUV,hit)) |
387 | return epilog(hit.valid,hit); |
388 | return false; |
389 | } |
390 | |
391 | template<typename Epilog> |
392 | __forceinline bool intersect(RayK<K>& ray, size_t k, |
393 | const Vec3vf<M>& tri_v0, |
394 | const Vec3vf<M>& tri_v1, |
395 | const Vec3vf<M>& tri_v2, |
396 | const Epilog& epilog) const |
397 | { |
398 | UVIdentity<M> mapUV; |
399 | PlueckerHitM<M,UVIdentity<M>> hit(mapUV); |
400 | if (intersect(ray,k,tri_v0,tri_v1,tri_v2,mapUV,hit)) |
401 | return epilog(hit.valid,hit); |
402 | return false; |
403 | } |
404 | |
405 | }; |
406 | } |
407 | } |
408 | |