Library of Assembled Shared Sources
ray_3d_sphere_3d.h
Go to the documentation of this file.
1/** @file
2 * @author Bram de Greve (bram@cocamware.com)
3 * @author Tom De Muer (tom@cocamware.com)
4 *
5 * *** BEGIN LICENSE INFORMATION ***
6 *
7 * The contents of this file are subject to the Common Public Attribution License
8 * Version 1.0 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://lass.sourceforge.net/cpal-license. The License is based on the
11 * Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover
12 * use of software over a computer network and provide for limited attribution for
13 * the Original Developer. In addition, Exhibit A has been modified to be consistent
14 * with Exhibit B.
15 *
16 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
17 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
18 * language governing rights and limitations under the License.
19 *
20 * The Original Code is LASS - Library of Assembled Shared Sources.
21 *
22 * The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
23 * The Original Developer is the Initial Developer.
24 *
25 * All portions of the code written by the Initial Developer are:
26 * Copyright (C) 2004-2025 the Initial Developer.
27 * All Rights Reserved.
28 *
29 * Contributor(s):
30 *
31 * Alternatively, the contents of this file may be used under the terms of the
32 * GNU General Public License Version 2 or later (the GPL), in which case the
33 * provisions of GPL are applicable instead of those above. If you wish to allow use
34 * of your version of this file only under the terms of the GPL and not to allow
35 * others to use your version of this file under the CPAL, indicate your decision by
36 * deleting the provisions above and replace them with the notice and other
37 * provisions required by the GPL License. If you do not delete the provisions above,
38 * a recipient may use your version of this file under either the CPAL or the GPL.
39 *
40 * *** END LICENSE INFORMATION ***
41 */
42
43#ifndef LASS_GUARDIAN_OF_INCLUSION_PRIM_RAY_3D_SPHERE_3D_H
44#define LASS_GUARDIAN_OF_INCLUSION_PRIM_RAY_3D_SPHERE_3D_H
45
46#include "prim_common.h"
47#include "ray_3d.h"
48#include "sphere_3d.h"
49#include "../num/basic_ops.h"
51
52namespace lass
53{
54namespace prim
55{
56
57namespace impl
58{
59 /** @internal
60 */
61 template <class NormalizingPolicy>
62 struct RaySphere
63 {
64 template <typename T, class PP>
65 static Result intersect(
66 const Sphere3D<T>& sphere, const Ray3D<T, NormalizingPolicy, PP>& ray,
67 T& t, const T& tMin)
68 {
69 using TValue = T;
70 using TVector = Vector3D<TValue>;
71 using TNumTraits = typename TVector::TNumTraits;
72
73 using TDouble = std::conditional_t<std::is_same_v<T, float>, double, T>;
74 using TVectorDouble = Vector3D<TDouble>;
75
76 const TVector cs = ray.support() - sphere.center();
77 const TVector& d = ray.direction();
78 const TValue r2 = num::sqr(sphere.radius());
79
80 // at² + bt + c == 0
81 const TValue a = d.squaredNorm();
82 const TValue b = dot(cs, d);
83 const TValue c = static_cast<TValue>(
84 static_cast<TVectorDouble>(cs).squaredNorm() -
85 num::sqr(static_cast<TDouble>(sphere.radius())));
86 // const TValue discriminant = num::sqr(b) - a * c;
87
88 // Hearn and Baker's geometrical reformulation:
89 const TVector l = cs - (b / a) * d;
90 const TValue discriminant = r2 - l.squaredNorm();
91
92 if (discriminant > TNumTraits::zero)
93 {
94 // Press et al. Numerical Recipes
95 const TValue q = -(b + std::copysign(num::sqrt(a * discriminant), b));
96 TValue t1 = c / q;
97 TValue t2 = q / a;
98 if (t1 > t2)
99 {
100 std::swap(t1, t2);
101 }
102 if (t1 > tMin)
103 {
104 t = t1;
105 return rOne;
106 }
107 if (t2 > tMin)
108 {
109 t = t2;
110 return rOne;
111 }
112 }
113 else if (discriminant == TNumTraits::zero)
114 {
115 const TValue t1 = - b / a;
116 if (t1 > tMin)
117 {
118 t = t1;
119 return rOne;
120 }
121 }
122 return rNone;
123 }
124 };
125
126 /** @internal
127 */
128 template <>
129 struct RaySphere<Normalized>
130 {
131 template <typename T, class PP>
132 static Result intersect(const Sphere3D<T>& sphere,
133 const Ray3D<T, Normalized, PP>& ray,
134 T& t, const T& tMin)
135 {
136 using TValue = T;
137 using TVector = Vector3D<TValue>;
138 using TNumTraits = typename TVector::TNumTraits;
139
140 using TDouble = std::conditional_t<std::is_same_v<T, float>, double, T>;
141 using TVectorDouble = Vector3D<TDouble>;
142
143 const TVector cs = ray.support() - sphere.center();
144 const TVector& d = ray.direction();
145 const TValue r2 = num::sqr(sphere.radius());
146
147 // at² + bt + c == 0
148 // const TValue a = d.squaredNorm() == 1;
149 const TValue b = dot(cs, d);
150 const TValue c = static_cast<TValue>(
151 static_cast<TVectorDouble>(cs).squaredNorm() -
152 num::sqr(static_cast<TDouble>(sphere.radius())));
153 // const TValue discriminant = num::sqr(b) - c;
154
155 // Hearn and Baker's geometrical reformulation:
156 const TVector l = cs - b * d;
157 const TValue discriminant = r2 - l.squaredNorm();
158
159 if (discriminant > TNumTraits::zero)
160 {
161 // Press et al. Numerical Recipes
162 const TValue q = -(b + std::copysign(num::sqrt(discriminant), b));
163 TValue t1 = c / q;
164 TValue t2 = q;
165 if (t1 > t2)
166 {
167 std::swap(t1, t2);
168 }
169 if (t1 > tMin)
170 {
171 t = t1;
172 return rOne;
173 }
174 if (t2 > tMin)
175 {
176 t = t2;
177 return rOne;
178 }
179 }
180 else if (discriminant == TNumTraits::zero)
181 {
182 const TValue t1 = -b;
183 if (t1 > tMin)
184 {
185 t = t1;
186 return rOne;
187 }
188 }
189 return rNone;
190 }
191 };
192}
193
194
195
196
197/** Find the intersection of a ray and a sphere by their parameter t on the ray.
198 * @relates lass::prim::Ray3D
199 * @relates lass::prim:Sphere3D
200 *
201 * @param sphere [in] the sphere
202 * @param ray [in] the ray
203 * @param t [out] the parameter of the intersection point > @a tMin.
204 * @param tMin [in] the minimum t that may be returned as valid intersection.
205 * @return @arg rNone no intersections with @a t > @a tMin found
206 * @a t is not assigned.
207 * @arg rOne a intersection with @a t > @a tMin is found
208 * @a t is assigned.
209 *
210 * For implementation details on numerical precision:
211 *
212 * Haines, E., Günther, J., Akenine-Möller, T. (2019). Precision Improvements for Ray/Sphere Intersection.
213 * In: Haines, E., Akenine-Möller, T. (eds) Ray Tracing Gems. Apress, Berkeley, CA.
214 * https://doi.org/10.1007/978-1-4842-4427-2_7
215 */
216template<typename T, class NP, class PP> inline
217Result intersect(
218 const Sphere3D<T>& sphere, const Ray3D<T, NP, PP>& ray,
219 T& t, const T& tMin = T())
220{
221#ifdef NDEBUG
222 return impl::RaySphere<NP>::intersect(sphere, ray, t, tMin);
223#else
224 const Result result = impl::RaySphere<NP>::intersect(sphere, ray, t, tMin);
225 LASS_ASSERT(t > tMin || result == rNone);
226 return result;
227#endif
228}
229
230}
231}
232
233#endif
234
235// EOF
T sqr(const T &x)
return x * x
Definition basic_ops.h:162
implementation details of lass::prim
set of geometrical primitives
Definition aabb_2d.h:81
Result
meta information on the result you have from an operation like an intersection ...
Definition result.h:74
@ rNone
operation has no answer, output arguments are meaningless
Definition result.h:76
@ rOne
there's exactly one answer, 1 output argument contains the answer
Definition result.h:77
Library for Assembled Shared Sources.
Definition config.h:53