9#include <Common/assertion.h>
28 PH_ASSERT_GE(radius, T(0));
34 real*
const out_hitT)
const
37 return isIntersectingHearnBaker(segment, out_hitT);
72 real*
const out_hitT)
const
74 PH_ASSERT_GE(m_radius, T(0));
103 const float64 a = rayD.dot(rayD);
106 const float64 b = rayD.dot(oc);
109 const float64 c = oc.
dot(oc) -
static_cast<float64
>(m_radius) * m_radius;
111 float64 D = b * b - a * c;
120 const float64 rcpA = 1.0 / a;
124 const float64 t1 = (b - D) * rcpA;
125 const float64 t2 = (b + D) * rcpA;
127 PH_ASSERT_MSG(t1 <= t2,
"\n"
128 "t1 = " + std::to_string(t1) +
"\n"
129 "t2 = " + std::to_string(t2) +
"\n"
130 "(a, b, c) = (" + std::to_string(a) +
", " + std::to_string(b) +
", " + std::to_string(c) +
")\n"
131 "ray-origin = " + rayO.toString() +
"\n"
132 "ray-direction = " + rayD.toString());
155 PH_ASSERT_IN_RANGE_INCLUSIVE(tClosest, segment.
getMinT(), segment.
getMaxT());
157 *out_hitT =
static_cast<real
>(tClosest);
169inline bool TSphere<T>::isIntersectingHearnBaker(
170 const TLineSegment<T>& segment,
171 real*
const out_hitT)
const
173 PH_ASSERT_GE(m_radius, T(0));
178 const Vector3D rayO(segment.getOrigin());
179 const Vector3D rayD(segment.getDir());
180 const Vector3D unitRayD = rayD.normalize();
186 const float64 r2 =
static_cast<float64
>(m_radius) * m_radius;
190 const float64 a = rayD.dot(rayD);
193 const float64 b = co.dot(rayD);
196 const Vector3D fd = co - unitRayD * co.dot(unitRayD);
199 const float64 D = a * (r2 - fd.dot(fd));
208 const float64 c = co.dot(co) - r2;
210 const float64 sqrtD = std::sqrt(D);
213 const float64 q = (b >= 0.0) ? -sqrtD - b : sqrtD - b;
224 PH_ASSERT_MSG(t0 <= t1,
"\n"
225 "t0 = " + std::to_string(t0) +
"\n"
226 "t1 = " + std::to_string(t1) +
"\n"
227 "(a, b, c) = (" + std::to_string(a) +
", " + std::to_string(b) +
", " + std::to_string(c) +
")\n"
228 "ray-origin = " + rayO.toString() +
"\n"
229 "ray-direction = " + rayD.toString());
237 if(segment.getMinT() <= t0 && t0 <= segment.getMaxT())
242 else if(segment.getMinT() <= t1 && t1 <= segment.getMaxT())
252 PH_ASSERT_IN_RANGE_INCLUSIVE(tClosest, segment.getMinT(), segment.getMaxT());
254 *out_hitT =
static_cast<real
>(tClosest);
274 constexpr auto EXPANSION_RATIO = T(0.0001);
299 maxDist2 += std::max(a, b);
301 else if(T(0) > volume.
getMaxVertex().x()) minDist2 += b;
305 maxDist2 += std::max(a, b);
307 else if(T(0) > volume.
getMaxVertex().y()) minDist2 += b;
311 maxDist2 += std::max(a, b);
313 else if(T(0) > volume.
getMaxVertex().z()) minDist2 += b;
315 return minDist2 <= radius2 && radius2 <= maxDist2;
321 PH_ASSERT_IN_RANGE_INCLUSIVE(sample[0],
static_cast<T
>(0),
static_cast<T
>(1));
322 PH_ASSERT_IN_RANGE_INCLUSIVE(sample[1],
static_cast<T
>(0),
static_cast<T
>(1));
323 PH_ASSERT_GE(m_radius,
static_cast<T
>(0));
325 const T y =
static_cast<T
>(2) * (sample[0] -
static_cast<T
>(0.5));
327 const T r = std::sqrt(std::max(
static_cast<T
>(1) - y * y,
static_cast<T
>(0)));
334 return localUnitPos * m_radius;
339 const std::array<T, 2>& sample, T*
const out_pdfA)
const
343 *out_pdfA = uniformSurfaceSamplePdfA();
345 return sampleToSurfaceArchimedes(sample);
351 PH_ASSERT_LE(
static_cast<T
>(0), sample[0]); PH_ASSERT_LE(sample[0],
static_cast<T
>(1));
352 PH_ASSERT_LE(
static_cast<T
>(0), sample[1]); PH_ASSERT_LE(sample[1],
static_cast<T
>(1));
354 const auto ySign =
math::sign(sample[1] -
static_cast<T
>(0.5));
355 const auto reusedSample1 = ySign *
static_cast<T
>(2) * (sample[1] -
static_cast<T
>(0.5));
358 const T yValue = ySign * std::sqrt(reusedSample1);
359 const T yRadius = std::sqrt(
static_cast<T
>(1) - yValue * yValue);
362 std::sin(phi) * yRadius,
364 std::cos(phi) * yRadius);
366 return localUnitPos.mul(m_radius);
371 const std::array<T, 2>& sample, T*
const out_pdfA)
const
373 const auto localPos = sampleToSurfaceAbsCosThetaWeighted(sample);
375 PH_ASSERT_GE(localPos.y(),
static_cast<T
>(0));
376 const T cosTheta = localPos.y() / m_radius;
389 return static_cast<T
>(1) / getArea();
395 using namespace math::constant;
397 const TVector2<T>& phiTheta = surfaceToPhiTheta(surface);
400 phiTheta.
x() / two_pi<T>,
401 (pi<T> - phiTheta.
y()) / pi<T>};
407 using namespace math::constant;
409 const T phi = latLong01.
x() * two_pi<T>;
410 const T theta = (
static_cast<T
>(1) - latLong01.
y()) * pi<T>;
418 return phiThetaToSurface(latLong01ToPhiTheta(latLong01));
424 using namespace math::constant;
426 PH_ASSERT_GT(m_radius, 0);
430 const T cosTheta =
math::clamp(unitDir.
y(),
static_cast<T
>(-1),
static_cast<T
>(1));
432 const T theta = std::acos(cosTheta);
433 const T phiRaw = std::atan2(unitDir.
x(), unitDir.
z());
434 const T phi = phiRaw >=
static_cast<T
>(0) ? phiRaw : two_pi<T> + phiRaw;
442 const T phi = phiTheta.
x();
443 const T theta = phiTheta.
y();
445 const T zxPlaneRadius = std::sin(theta);
448 zxPlaneRadius * std::sin(phi),
450 zxPlaneRadius * std::cos(phi));
452 return localUnitPos * m_radius;
456template<
typename SurfaceToUv>
459 SurfaceToUv surfaceToUv,
462 static_assert(std::is_invocable_r_v<TVector2<T>, SurfaceToUv,
TVector3<T>>,
463 "A surface to UV mapper must accept Vector3 position and return Vector2 "
464 "UV or any type that is convertible to the mentioned ones");
466 PH_ASSERT_GT(hInRadians, 0);
474 const T delta = m_radius * std::tan(hInRadians);
495 posXuv.
x() - negXuv.
x(), posXuv.
y() - negXuv.
y(),
496 posZuv.
x() - negZuv.
x(), posZuv.
y() - negZuv.
y());
497 const auto xDiff = posX - negX;
498 const auto zDiff = posZ - negZ;
499 const std::array<std::array<T, 2>, 3> bs = {
500 xDiff.
x(), zDiff.x(),
501 xDiff.y(), zDiff.y(),
502 xDiff.z(), zDiff.z() };
506 std::array<std::array<T, 2>, 3> xs;
507 if(uvwDiff.
solve(bs, &xs))
509 dPdU.
x() = xs[0][0]; dPdV.
x() = xs[0][1];
510 dPdU.
y() = xs[1][0]; dPdV.
y() = xs[1][1];
511 dPdU.
z() = xs[2][0]; dPdV.
z() = xs[2][1];
515 dPdU = hitBasis.getZAxis();
516 dPdV = hitBasis.getXAxis();
A 3-D Axis-Aligned Bounding Box (AABB).
Definition TAABB3D.h:32
const TVector3< T > & getMaxVertex() const
Get the corner vertex of the maximum (+++) octant.
Definition TAABB3D.ipp:152
TAABB3D & expand(const TVector3< T > &amount)
Makes the bounds grow by some amount.
Definition TAABB3D.ipp:225
const TVector3< T > & getMinVertex() const
Get the corner vertex of the minimum (—) octant.
Definition TAABB3D.ipp:146
Represents a line segment in space.
Definition TLineSegment.h:25
T getMinT() const
Definition TLineSegment.ipp:94
const TVector3< T > & getOrigin() const
Definition TLineSegment.ipp:82
const TVector3< T > & getDir() const
Definition TLineSegment.ipp:88
T getMaxT() const
Definition TLineSegment.ipp:100
Represents a 2x2 matrix.
Definition TMatrix2.h:16
bool solve(const std::array< T, 2 > &b, std::array< T, 2 > *out_x) const
Solves linear systems of the form Ax = b.
Definition TMatrix2.ipp:103
static TOrthonormalBasis3 makeFromUnitY(const TVector3< T > &unitYAxis)
Definition TOrthonormalBasis3.ipp:16
A sphere in 3-D space.
Definition TSphere.h:20
static TSphere makeUnit()
Definition TSphere.ipp:19
TVector2< T > surfaceToPhiTheta(const TVector3< T > &surface) const
Map Cartesian to spherical coordinates on the surface of the sphere.
Definition TSphere.ipp:422
bool isInside(const TVector3< T > &point) const
Same as isIntersecting(), except the cost is slightly higher to reduce numerical error.
Definition TSphere.ipp:63
TVector3< T > latLong01ToSurface(const TVector2< T > &latLong01) const
Definition TSphere.ipp:416
TVector2< T > surfaceToLatLong01(const TVector3< T > &surface) const
Definition TSphere.ipp:393
T getArea() const
Definition TSphere.ipp:266
TAABB3D< T > getAABB() const
Definition TSphere.ipp:272
TVector3< T > phiThetaToSurface(const TVector2< T > &phiTheta) const
Map spherical to Cartesian coordinates on the surface of the sphere.
Definition TSphere.ipp:440
TVector2< T > latLong01ToPhiTheta(const TVector2< T > &latLong01) const
Definition TSphere.ipp:405
T uniformSurfaceSamplePdfA() const
Definition TSphere.ipp:386
T getRadius() const
Definition TSphere.ipp:260
TVector3< T > sampleToSurfaceArchimedes(const std::array< T, 2 > &sample) const
Definition TSphere.ipp:319
TVector3< T > sampleToSurfaceAbsCosThetaWeighted(const std::array< T, 2 > &sample) const
Definition TSphere.ipp:349
bool mayOverlapVolume(const TAABB3D< T > &volume) const
Conservatively checks whether this sphere overlaps a volume. By conservative, it means true can be re...
Definition TSphere.ipp:283
std::pair< TVector3< T >, TVector3< T > > surfaceDerivativesWrtUv(const TVector3< T > &surface, SurfaceToUv surfaceToUv, T hInRadians=to_radians< T >(1)) const
Calculate dPdU and dPdV with finite difference.
Definition TSphere.ipp:457
bool isIntersecting(const TLineSegment< T > &segment, real *out_hitT) const
Checks whether the segment is interseting with this sphere.
Definition TSphere.ipp:32
Represents a 2-D vector.
Definition TVector2.h:19
T & x()
Definition TVector2.ipp:38
T & y()
Definition TVector2.ipp:44
Represents a 3-D vector.
Definition TVector3.h:17
T & y()
Definition TVector3.ipp:189
T & z()
Definition TVector3.ipp:195
T & x()
Definition TVector3.ipp:183
Derived mul(const Derived &rhs) const
Definition TArithmeticArrayBase.ipp:98
T dot(const Derived &rhs) const
Definition TVectorNBase.ipp:14
Derived sub(const Derived &rhs) const
Definition TArithmeticArrayBase.ipp:62
Derived div(const Derived &rhs) const
Definition TArithmeticArrayBase.ipp:134
T lengthSquared() const
Definition TVectorNBase.ipp:44
Derived add(const Derived &rhs) const
Definition TArithmeticArrayBase.ipp:26
constexpr T four_pi
Value of .
Definition constant.h:39
constexpr T two_pi
Value of .
Definition constant.h:27
constexpr T pi
Value of .
Definition constant.h:15
Math functions and utilities.
Definition TransformInfo.h:10
T squared(const T value)
Definition math.h:59
int sign(const T value)
Extract the sign of value.
Definition math.h:154
T to_radians(const T degrees)
Convert degrees to radians.
Definition math.h:140
TVector3< float64 > Vector3D
Definition math_fwd.h:54
T clamp(const T value, const T lowerBound, const T upperBound)
Clamps a value to [lowerBound, upperBound]. None of value, lowerBound and upperBound can be NaN,...
Definition math.h:77