Skip to content

Commit dd88bab

Browse files
authored
Fix bug in CustomImplicitSurface2 and 3 (#110)
This revision includes: * Fix the calculation of surface location using SDF * Improve the ray marching algorithm by taking adaptive step
1 parent ad1c801 commit dd88bab

6 files changed

Lines changed: 112 additions & 58 deletions

include/jet/custom_implicit_surface2.h

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,24 @@ class CustomImplicitSurface2 final : public ImplicitSurface2 {
1717
public:
1818
class Builder;
1919

20+
//!
2021
//! Constructs an implicit surface using the given signed-distance function.
21-
CustomImplicitSurface2(
22-
const std::function<double(const Vector2D&)>& func,
23-
const BoundingBox2D& domain = BoundingBox2D(),
24-
double resolution = 1e-3,
25-
unsigned int numberOfIterations = 5,
26-
const Transform2& transform = Transform2(),
27-
bool isNormalFlipped = false);
22+
//!
23+
//! \param func Custom SDF function object.
24+
//! \param domain Bounding box of the SDF if exists.
25+
//! \param resolution Finite differencing resolution for derivatives.
26+
//! \param rayMarchingResolution Ray marching resolution for ray tests.
27+
//! \param maxNumOfIterations Number of iterations for closest point search.
28+
//! \param transform Local-to-world transform.
29+
//! \param isNormalFlipped True if normal is flipped.
30+
//!
31+
CustomImplicitSurface2(const std::function<double(const Vector2D&)>& func,
32+
const BoundingBox2D& domain = BoundingBox2D(),
33+
double resolution = 1e-3,
34+
double rayMarchingResolution = 1e-6,
35+
unsigned int numberOfIterations = 5,
36+
const Transform2& transform = Transform2(),
37+
bool isNormalFlipped = false);
2838

2939
//! Destructor.
3040
virtual ~CustomImplicitSurface2();
@@ -36,6 +46,7 @@ class CustomImplicitSurface2 final : public ImplicitSurface2 {
3646
std::function<double(const Vector2D&)> _func;
3747
BoundingBox2D _domain;
3848
double _resolution = 1e-3;
49+
double _rayMarchingResolution = 1e-6;
3950
unsigned int _maxNumOfIterations = 5;
4051

4152
Vector2D closestPointLocal(const Vector2D& otherPoint) const override;
@@ -57,7 +68,6 @@ class CustomImplicitSurface2 final : public ImplicitSurface2 {
5768
//! Shared pointer type for the CustomImplicitSurface2.
5869
typedef std::shared_ptr<CustomImplicitSurface2> CustomImplicitSurface2Ptr;
5970

60-
6171
//!
6272
//! \brief Front-end to create CustomImplicitSurface2 objects step by step.
6373
//!
@@ -71,10 +81,15 @@ class CustomImplicitSurface2::Builder final
7181
//! Returns builder with domain.
7282
Builder& withDomain(const BoundingBox2D& domain);
7383

74-
//! Returns builder with resolution.
84+
//! Returns builder with finite differencing resolution.
7585
Builder& withResolution(double resolution);
7686

77-
//! Returns builder with number of iterations.
87+
//! Returns builder with ray marching resolution which determines the ray
88+
//! intersection quality.
89+
Builder& withRayMarchingResolution(double rayMarchingResolution);
90+
91+
//! Returns builder with number of iterations for closest point/normal
92+
//! searches.
7893
Builder& withMaxNumberOfIterations(unsigned int numIter);
7994

8095
//! Builds CustomImplicitSurface2.
@@ -87,6 +102,7 @@ class CustomImplicitSurface2::Builder final
87102
std::function<double(const Vector2D&)> _func;
88103
BoundingBox2D _domain;
89104
double _resolution = 1e-3;
105+
double _rayMarchingResolution = 1e-6;
90106
unsigned int _maxNumOfIterations = 5;
91107
};
92108

include/jet/custom_implicit_surface3.h

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,24 @@ class CustomImplicitSurface3 final : public ImplicitSurface3 {
1717
public:
1818
class Builder;
1919

20+
//!
2021
//! Constructs an implicit surface using the given signed-distance function.
21-
CustomImplicitSurface3(
22-
const std::function<double(const Vector3D&)>& func,
23-
const BoundingBox3D& domain = BoundingBox3D(),
24-
double resolution = 1e-3,
25-
unsigned int maxNumOfIterations = 5,
26-
const Transform3& transform = Transform3(),
27-
bool isNormalFlipped = false);
22+
//!
23+
//! \param func Custom SDF function object.
24+
//! \param domain Bounding box of the SDF if exists.
25+
//! \param resolution Finite differencing resolution for derivatives.
26+
//! \param rayMarchingResolution Ray marching resolution for ray tests.
27+
//! \param maxNumOfIterations Number of iterations for closest point search.
28+
//! \param transform Local-to-world transform.
29+
//! \param isNormalFlipped True if normal is flipped.
30+
//!
31+
CustomImplicitSurface3(const std::function<double(const Vector3D&)>& func,
32+
const BoundingBox3D& domain = BoundingBox3D(),
33+
double resolution = 1e-3,
34+
double rayMarchingResolution = 1e-6,
35+
unsigned int maxNumOfIterations = 5,
36+
const Transform3& transform = Transform3(),
37+
bool isNormalFlipped = false);
2838

2939
//! Destructor.
3040
virtual ~CustomImplicitSurface3();
@@ -36,6 +46,7 @@ class CustomImplicitSurface3 final : public ImplicitSurface3 {
3646
std::function<double(const Vector3D&)> _func;
3747
BoundingBox3D _domain;
3848
double _resolution = 1e-3;
49+
double _rayMarchingResolution = 1e-6;
3950
unsigned int _maxNumOfIterations = 5;
4051

4152
Vector3D closestPointLocal(const Vector3D& otherPoint) const override;
@@ -57,7 +68,6 @@ class CustomImplicitSurface3 final : public ImplicitSurface3 {
5768
//! Shared pointer type for the CustomImplicitSurface3.
5869
typedef std::shared_ptr<CustomImplicitSurface3> CustomImplicitSurface3Ptr;
5970

60-
6171
//!
6272
//! \brief Front-end to create CustomImplicitSurface3 objects step by step.
6373
//!
@@ -71,10 +81,15 @@ class CustomImplicitSurface3::Builder final
7181
//! Returns builder with domain.
7282
Builder& withDomain(const BoundingBox3D& domain);
7383

74-
//! Returns builder with resolution.
84+
//! Returns builder with finite differencing resolution.
7585
Builder& withResolution(double resolution);
7686

77-
//! Returns builder with number of iterations.
87+
//! Returns builder with ray marching resolution which determines the ray
88+
//! intersection quality.
89+
Builder& withRayMarchingResolution(double rayMarchingResolution);
90+
91+
//! Returns builder with number of iterations for closest point/normal
92+
//! searches.
7893
Builder& withMaxNumberOfIterations(unsigned int numIter);
7994

8095
//! Builds CustomImplicitSurface3.
@@ -87,6 +102,7 @@ class CustomImplicitSurface3::Builder final
87102
std::function<double(const Vector3D&)> _func;
88103
BoundingBox3D _domain;
89104
double _resolution = 1e-3;
105+
double _rayMarchingResolution = 1e-6;
90106
unsigned int _maxNumOfIterations = 5;
91107
};
92108

src/jet/custom_implicit_surface2.cpp

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// property of any third parties.
66

77
#include <pch.h>
8+
89
#include <jet/constants.h>
910
#include <jet/custom_implicit_surface2.h>
1011
#include <jet/level_set_utils.h>
@@ -14,12 +15,13 @@ using namespace jet;
1415
CustomImplicitSurface2::CustomImplicitSurface2(
1516
const std::function<double(const Vector2D&)>& func,
1617
const BoundingBox2D& domain, double resolution,
17-
unsigned int maxNumOfIterations, const Transform2& transform,
18-
bool isNormalFlipped)
18+
double rayMarchingResolution, unsigned int maxNumOfIterations,
19+
const Transform2& transform, bool isNormalFlipped)
1920
: ImplicitSurface2(transform, isNormalFlipped),
2021
_func(func),
2122
_domain(domain),
2223
_resolution(resolution),
24+
_rayMarchingResolution(rayMarchingResolution),
2325
_maxNumOfIterations(maxNumOfIterations) {}
2426

2527
CustomImplicitSurface2::~CustomImplicitSurface2() {}
@@ -54,17 +56,18 @@ bool CustomImplicitSurface2::intersectsLocal(const Ray2D& ray) const {
5456

5557
double t = tStart;
5658
Vector2D pt = ray.pointAt(t);
57-
double prevSign = sign(_func(pt));
58-
59+
double prevPhi = _func(pt);
5960
while (t <= tEnd) {
6061
pt = ray.pointAt(t);
61-
double newSign = sign(_func(pt));
62+
const double newPhi = _func(pt);
63+
const double newPhiAbs = std::fabs(newPhi);
6264

63-
if (newSign * prevSign < 0.0) {
65+
if (newPhi * prevPhi < 0.0) {
6466
return true;
6567
}
6668

67-
t += _resolution;
69+
t += std::max(newPhiAbs, _rayMarchingResolution);
70+
prevPhi = newPhi;
6871
}
6972
}
7073

@@ -118,11 +121,12 @@ SurfaceRayIntersection2 CustomImplicitSurface2::closestIntersectionLocal(
118121

119122
while (t <= tEnd) {
120123
pt = ray.pointAt(t);
121-
double newPhi = _func(pt);
124+
const double newPhi = _func(pt);
125+
const double newPhiAbs = std::fabs(newPhi);
122126

123127
if (newPhi * prevPhi < 0.0) {
124-
double frac = fractionInsideSdf(prevPhi, newPhi);
125-
double tSub = t + _resolution * frac;
128+
const double frac = prevPhi / (prevPhi - newPhi);
129+
const double tSub = t + _rayMarchingResolution * frac;
126130

127131
result.isIntersecting = true;
128132
result.distance = tSub;
@@ -135,7 +139,8 @@ SurfaceRayIntersection2 CustomImplicitSurface2::closestIntersectionLocal(
135139
return result;
136140
}
137141

138-
t += _resolution;
142+
t += std::max(newPhiAbs, _rayMarchingResolution);
143+
prevPhi = newPhi;
139144
}
140145
}
141146

@@ -174,6 +179,12 @@ CustomImplicitSurface2::Builder::withResolution(double resolution) {
174179
return *this;
175180
}
176181

182+
CustomImplicitSurface2::Builder&
183+
CustomImplicitSurface2::Builder::withRayMarchingResolution(double resolution) {
184+
_rayMarchingResolution = resolution;
185+
return *this;
186+
}
187+
177188
CustomImplicitSurface2::Builder&
178189
CustomImplicitSurface2::Builder::withMaxNumberOfIterations(
179190
unsigned int numIter) {
@@ -183,14 +194,14 @@ CustomImplicitSurface2::Builder::withMaxNumberOfIterations(
183194

184195
CustomImplicitSurface2 CustomImplicitSurface2::Builder::build() const {
185196
return CustomImplicitSurface2(_func, _domain, _resolution,
186-
_maxNumOfIterations, _transform,
187-
_isNormalFlipped);
197+
_rayMarchingResolution, _maxNumOfIterations,
198+
_transform, _isNormalFlipped);
188199
}
189200

190201
CustomImplicitSurface2Ptr CustomImplicitSurface2::Builder::makeShared() const {
191202
return std::shared_ptr<CustomImplicitSurface2>(
192203
new CustomImplicitSurface2(_func, _domain, _resolution,
193-
_maxNumOfIterations, _transform,
194-
_isNormalFlipped),
204+
_rayMarchingResolution, _maxNumOfIterations,
205+
_transform, _isNormalFlipped),
195206
[](CustomImplicitSurface2* obj) { delete obj; });
196207
}

src/jet/custom_implicit_surface3.cpp

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// property of any third parties.
66

77
#include <pch.h>
8+
89
#include <jet/constants.h>
910
#include <jet/custom_implicit_surface3.h>
1011
#include <jet/level_set_utils.h>
@@ -14,12 +15,13 @@ using namespace jet;
1415
CustomImplicitSurface3::CustomImplicitSurface3(
1516
const std::function<double(const Vector3D&)>& func,
1617
const BoundingBox3D& domain, double resolution,
17-
unsigned int maxNumOfIterations, const Transform3& transform,
18-
bool isNormalFlipped)
18+
double rayMarchingResolution, unsigned int maxNumOfIterations,
19+
const Transform3& transform, bool isNormalFlipped)
1920
: ImplicitSurface3(transform, isNormalFlipped),
2021
_func(func),
2122
_domain(domain),
2223
_resolution(resolution),
24+
_rayMarchingResolution(rayMarchingResolution),
2325
_maxNumOfIterations(maxNumOfIterations) {}
2426

2527
CustomImplicitSurface3::~CustomImplicitSurface3() {}
@@ -54,17 +56,18 @@ bool CustomImplicitSurface3::intersectsLocal(const Ray3D& ray) const {
5456

5557
double t = tStart;
5658
Vector3D pt = ray.pointAt(t);
57-
double prevSign = sign(_func(pt));
58-
59+
double prevPhi = _func(pt);
5960
while (t <= tEnd) {
6061
pt = ray.pointAt(t);
61-
double newSign = sign(_func(pt));
62+
const double newPhi = _func(pt);
63+
const double newPhiAbs = std::fabs(newPhi);
6264

63-
if (newSign * prevSign < 0.0) {
65+
if (newPhi * prevPhi < 0.0) {
6466
return true;
6567
}
6668

67-
t += _resolution;
69+
t += std::max(newPhiAbs, _rayMarchingResolution);
70+
prevPhi = newPhi;
6871
}
6972
}
7073

@@ -118,11 +121,12 @@ SurfaceRayIntersection3 CustomImplicitSurface3::closestIntersectionLocal(
118121

119122
while (t <= tEnd) {
120123
pt = ray.pointAt(t);
121-
double newPhi = _func(pt);
124+
const double newPhi = _func(pt);
125+
const double newPhiAbs = std::fabs(newPhi);
122126

123127
if (newPhi * prevPhi < 0.0) {
124-
double frac = fractionInsideSdf(prevPhi, newPhi);
125-
double tSub = t + _resolution * frac;
128+
const double frac = prevPhi / (prevPhi - newPhi);
129+
const double tSub = t + _rayMarchingResolution * frac;
126130

127131
result.isIntersecting = true;
128132
result.distance = tSub;
@@ -135,7 +139,8 @@ SurfaceRayIntersection3 CustomImplicitSurface3::closestIntersectionLocal(
135139
return result;
136140
}
137141

138-
t += _resolution;
142+
t += std::max(newPhiAbs, _rayMarchingResolution);
143+
prevPhi = newPhi;
139144
}
140145
}
141146

@@ -177,6 +182,12 @@ CustomImplicitSurface3::Builder::withResolution(double resolution) {
177182
return *this;
178183
}
179184

185+
CustomImplicitSurface3::Builder&
186+
CustomImplicitSurface3::Builder::withRayMarchingResolution(double resolution) {
187+
_rayMarchingResolution = resolution;
188+
return *this;
189+
}
190+
180191
CustomImplicitSurface3::Builder&
181192
CustomImplicitSurface3::Builder::withMaxNumberOfIterations(
182193
unsigned int numIter) {
@@ -186,14 +197,14 @@ CustomImplicitSurface3::Builder::withMaxNumberOfIterations(
186197

187198
CustomImplicitSurface3 CustomImplicitSurface3::Builder::build() const {
188199
return CustomImplicitSurface3(_func, _domain, _resolution,
189-
_maxNumOfIterations, _transform,
190-
_isNormalFlipped);
200+
_rayMarchingResolution, _maxNumOfIterations,
201+
_transform, _isNormalFlipped);
191202
}
192203

193204
CustomImplicitSurface3Ptr CustomImplicitSurface3::Builder::makeShared() const {
194205
return std::shared_ptr<CustomImplicitSurface3>(
195206
new CustomImplicitSurface3(_func, _domain, _resolution,
196-
_maxNumOfIterations, _transform,
197-
_isNormalFlipped),
207+
_rayMarchingResolution, _maxNumOfIterations,
208+
_transform, _isNormalFlipped),
198209
[](CustomImplicitSurface3* obj) { delete obj; });
199210
}

src/tests/unit_tests/custom_implicit_surface2_tests.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,16 @@ TEST(CustomImplicitSurface2, ClosestIntersection) {
8888

8989
CustomImplicitSurface2 cis1(
9090
[&](const Vector2D& pt) { return refSurf.signedDistance(pt); },
91-
BoundingBox2D({0, 0}, {1, 1}), 1e-3);
91+
BoundingBox2D({0, 0}, {1, 1}), 1e-3, 1e-6);
9292

9393
for (size_t i = 0; i < getNumberOfSamplePoints2(); ++i) {
9494
auto x = getSamplePoints2()[i];
9595
auto d = getSampleDirs2()[i];
9696
auto refAns = refSurf.closestIntersection(Ray2D(x, d));
9797
auto actAns = cis1.closestIntersection(Ray2D(x, d));
9898
EXPECT_EQ(refAns.isIntersecting, actAns.isIntersecting);
99-
EXPECT_NEAR(refAns.distance, actAns.distance, 1e-2);
100-
EXPECT_VECTOR2_NEAR(refAns.point, actAns.point, 1e-2);
101-
EXPECT_VECTOR2_NEAR(refAns.normal, actAns.normal, 1e-2);
99+
EXPECT_NEAR(refAns.distance, actAns.distance, 1e-5);
100+
EXPECT_VECTOR2_NEAR(refAns.point, actAns.point, 1e-5);
101+
EXPECT_VECTOR2_NEAR(refAns.normal, actAns.normal, 1e-5);
102102
}
103103
}

0 commit comments

Comments
 (0)