Skip to content
This repository was archived by the owner on Oct 24, 2023. It is now read-only.

Commit b73305c

Browse files
committed
Initial commit.
0 parents  commit b73305c

File tree

6 files changed

+366
-0
lines changed

6 files changed

+366
-0
lines changed

CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
cmake_minimum_required(VERSION 3.15)
2+
project(fast_voxel_traversal_algorithm)
3+
4+
set(CMAKE_CXX_STANDARD 20)
5+
6+
add_executable(fast_voxel_traversal_algorithm amanatidesWooAlgorithm.cpp amanatidesWooAlgorithm.h Ray.h Vec3.h Grid3D.h)

Grid3D.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#ifndef FAST_VOXEL_TRAVERSAL_ALGORITHM_GRID3D_H
2+
#define FAST_VOXEL_TRAVERSAL_ALGORITHM_GRID3D_H
3+
4+
#include <cstddef>
5+
#include "Vec3.h"
6+
7+
// Represents a 3 dimensional grid sub-divided with voxels. Provides necessary information to perform
8+
// traversal over the grid system.
9+
struct Grid3D {
10+
public:
11+
[[nodiscard]] constexpr Grid3D(const BoundVec3& min_bound, const BoundVec3& max_bound, size_t num_x_voxels,
12+
size_t num_y_voxels, size_t num_z_voxels) :
13+
min_bound_{min_bound},
14+
max_bound_{max_bound_},
15+
grid_size_{max_bound - min_bound},
16+
num_x_voxels_{num_x_voxels_},
17+
num_y_voxels_{num_y_voxels},
18+
num_z_voxels_{num_z_voxels},
19+
voxel_size_x_{grid_size_.x() / num_x_voxels},
20+
voxel_size_y_{grid_size_.y() / num_y_voxels},
21+
voxel_size_z_{grid_size_.z() / num_z_voxels} {}
22+
23+
[[nodiscard]] constexpr inline size_t numberOfXVoxels() const { return num_x_voxels_; }
24+
[[nodiscard]] constexpr inline size_t numberOfYVoxels() const { return num_y_voxels_; }
25+
[[nodiscard]] constexpr inline size_t numberOfZVoxels() const { return num_z_voxels_; }
26+
[[nodiscard]] constexpr inline BoundVec3 minBound() const { return min_bound_; }
27+
[[nodiscard]] constexpr inline BoundVec3 maxBound() const { return max_bound_; }
28+
[[nodiscard]] constexpr inline FreeVec3 gridSize() const { return grid_size_; }
29+
[[nodiscard]] constexpr inline value_type voxelSizeX() const { return voxel_size_x_; }
30+
[[nodiscard]] constexpr inline value_type voxelSizeY() const { return voxel_size_y_; }
31+
[[nodiscard]] constexpr inline value_type voxelSizeZ() const { return voxel_size_z_; }
32+
33+
private:
34+
// The minimum bound vector of the voxel grid.
35+
const BoundVec3 min_bound_;
36+
// The maximum bound vector of the voxel grid.
37+
const BoundVec3 max_bound_;
38+
// The grid size, determined by (max_bound_ - min_bound_).
39+
const FreeVec3 grid_size_;
40+
// The number of voxels in each of the x, y, z directions.
41+
const size_t num_x_voxels_, num_y_voxels_, num_z_voxels_;
42+
// The size of the voxel's x dimension.
43+
const value_type voxel_size_x_;
44+
// The size of the voxel's y dimension.
45+
const value_type voxel_size_y_;
46+
// The size of the voxel's z dimension.
47+
const value_type voxel_size_z_;
48+
};
49+
50+
#endif //FAST_VOXEL_TRAVERSAL_ALGORITHM_GRID3D_H

Ray.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#ifndef FAST_VOXEL_TRAVERSAL_ALGORITHM_RAY_H
2+
#define FAST_VOXEL_TRAVERSAL_ALGORITHM_RAY_H
3+
4+
#include "Vec3.h"
5+
6+
// Encapsulates the functionality of a ray.
7+
// This consists of two components, the origin of the ray,
8+
// and the direction of the ray.
9+
struct Ray {
10+
[[nodiscard]] constexpr Ray(const BoundVec3& origin, const UnitVec3& direction)
11+
: origin_{origin}, direction_{direction} {}
12+
13+
// Represents the function p(t) = origin + t * direction,
14+
// where p is a 3-dimensional position, and t is a scalar.
15+
[[nodiscard]] inline constexpr BoundVec3 point_at_parameter(const value_type t) const {
16+
return this->origin_ + (this->direction_ * t);
17+
}
18+
19+
[[nodiscard]] inline constexpr BoundVec3 origin() const { return this->origin_; }
20+
[[nodiscard]] inline constexpr UnitVec3 direction() const { return this->direction_; }
21+
private:
22+
// The origin of the ray.
23+
const BoundVec3 origin_;
24+
// The normalized direction of the ray.
25+
const UnitVec3 direction_;
26+
};
27+
28+
#endif //FAST_VOXEL_TRAVERSAL_ALGORITHM_RAY_H

Vec3.h

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
#ifndef FAST_VOXEL_TRAVERSAL_ALGORITHM_VEC3_H
2+
#define FAST_VOXEL_TRAVERSAL_ALGORITHM_VEC3_H
3+
4+
#include <cmath>
5+
6+
// The type used for the 3-dimensional vectors.
7+
// In most cases, this will be either double or float.
8+
using value_type = double;
9+
10+
// Represents a Euclidean vector in 3-dimensional space.
11+
// Assumes vectors take the form of:
12+
// [x]
13+
// [y]
14+
// [z]
15+
struct Vec3 {
16+
public:
17+
constexpr explicit Vec3(const value_type x, const value_type y, const value_type z)
18+
: x_{x}, y_{y}, z_{z} {}
19+
20+
[[nodiscard]] inline constexpr value_type x() const { return this->x_; }
21+
[[nodiscard]] inline constexpr value_type y() const { return this->y_; }
22+
[[nodiscard]] inline constexpr value_type z() const { return this->z_; }
23+
24+
[[nodiscard]] inline constexpr value_type& x() { return this->x_; }
25+
[[nodiscard]] inline constexpr value_type& y() { return this->y_; }
26+
[[nodiscard]] inline constexpr value_type& z() { return this->z_; }
27+
28+
[[nodiscard]] inline value_type length() const {
29+
return std::hypot(this->x(), this->y(), this->z());
30+
}
31+
32+
[[nodiscard]] inline value_type squared_length() const {
33+
return x() * x() + y() * y() + z() * z();
34+
}
35+
36+
private:
37+
// Represents the x-dimension value of the vector.
38+
value_type x_;
39+
// Represents the y-dimension value of the vector.
40+
value_type y_;
41+
// Represents the z-dimension value of the vector.
42+
value_type z_;
43+
};
44+
45+
// A 3-dimensional free vector, which has no initial point. It has two main criteria:
46+
// (1) direction, and (2) magnitude.
47+
struct FreeVec3 : Vec3 {
48+
using Vec3::Vec3;
49+
50+
[[nodiscard]] constexpr explicit FreeVec3(const Vec3& vec3) : Vec3::Vec3{vec3} {}
51+
52+
[[nodiscard]] inline constexpr value_type dot(const Vec3& other) const {
53+
return this->x() * other.x() + this->y() * other.y() + this->z() * other.z();
54+
}
55+
56+
[[nodiscard]] inline constexpr FreeVec3 cross(const Vec3& other) const {
57+
return FreeVec3{this->y() * other.z() - this->z() * other.y(),
58+
this->z() * other.x() - this->x() * other.z(),
59+
this->x() * other.y() - this->y() * other.x()};
60+
}
61+
62+
[[nodiscard]] inline constexpr FreeVec3& operator+=(const FreeVec3& other) {
63+
this->x() += other.x();
64+
this->y() += other.y();
65+
this->z() += other.z();
66+
return *this;
67+
}
68+
69+
[[nodiscard]] inline constexpr FreeVec3& operator-=(const FreeVec3& other) {
70+
this->x() -= other.x();
71+
this->y() -= other.y();
72+
this->z() -= other.z();
73+
return *this;
74+
}
75+
76+
[[nodiscard]] inline constexpr FreeVec3& operator*=(const value_type scalar) {
77+
this->x() *= scalar;
78+
this->y() *= scalar;
79+
this->z() *= scalar;
80+
return *this;
81+
}
82+
83+
[[nodiscard]] inline constexpr FreeVec3& operator/=(const value_type scalar) {
84+
this->x() /= scalar;
85+
this->y() /= scalar;
86+
this->z() /= scalar;
87+
return *this;
88+
}
89+
};
90+
91+
[[nodiscard]] inline constexpr FreeVec3 operator+(const FreeVec3& v) { return v; }
92+
93+
[[nodiscard]] inline constexpr FreeVec3 operator-(const FreeVec3& v) {
94+
return FreeVec3{-v.x(), -v.y(), -v.z()};
95+
}
96+
97+
[[nodiscard]] inline constexpr FreeVec3 operator+(FreeVec3 v1, const FreeVec3& v2) {
98+
return v1 += v2;
99+
}
100+
101+
[[nodiscard]] inline constexpr FreeVec3 operator-(FreeVec3 v1, const FreeVec3& v2) {
102+
return v1 -= v2;
103+
}
104+
105+
[[nodiscard]] inline constexpr FreeVec3 operator*(FreeVec3 v, const value_type scalar) {
106+
return v *= scalar;
107+
}
108+
109+
[[nodiscard]] inline constexpr FreeVec3 operator/(FreeVec3 v, const value_type scalar) {
110+
return v /= scalar;
111+
}
112+
113+
// A 3-dimensional bounded vector has a fixed start and end point. It represents a fixed point
114+
// in space, relative to some frame of reference.
115+
struct BoundVec3 : Vec3 {
116+
[[nodiscard]]constexpr explicit BoundVec3(const Vec3& vec3) : Vec3::Vec3{vec3} {}
117+
118+
[[nodiscard]] inline constexpr value_type dot(const Vec3& other) const {
119+
return this->x() * other.x() + this->y() * other.y() + this->z() * other.z();
120+
}
121+
122+
[[nodiscard]] inline constexpr BoundVec3& operator+=(const FreeVec3& other) {
123+
this->x() += other.x();
124+
this->y() += other.y();
125+
this->z() += other.z();
126+
return *this;
127+
}
128+
129+
[[nodiscard]] inline constexpr BoundVec3& operator-=(const FreeVec3& other) {
130+
return *this += (-other);
131+
}
132+
};
133+
134+
[[nodiscard]] inline constexpr FreeVec3 operator-(const BoundVec3& v1, const BoundVec3& v2) {
135+
return FreeVec3{v1.x() - v2.x(), v1.y() - v2.y(), v1.z() - v2.z()};
136+
}
137+
138+
[[nodiscard]] inline constexpr BoundVec3 operator+(BoundVec3 v1, const FreeVec3& v2) {
139+
return v1 += v2;
140+
}
141+
142+
[[nodiscard]] inline constexpr BoundVec3 operator-(BoundVec3 v1, const FreeVec3& v2) {
143+
return v1 -= v2;
144+
}
145+
146+
// Represents a 3-dimensional unit vector, an abstraction over free vectors that guarantees
147+
// a length of 1. To prevent its length from changing, UnitVec3 does not allow
148+
// for mutations.
149+
struct UnitVec3 {
150+
UnitVec3(value_type x, value_type y, value_type z)
151+
: UnitVec3{FreeVec3{x, y, z}} {}
152+
[[nodiscard]] constexpr explicit UnitVec3(const Vec3& vec3) : UnitVec3{FreeVec3{vec3}} {}
153+
[[nodiscard]] constexpr explicit UnitVec3(const FreeVec3& free_vec3) :
154+
inner_{free_vec3 / free_vec3.length()} {}
155+
156+
[[nodiscard]] inline constexpr value_type x() const { return this->to_free().x(); }
157+
[[nodiscard]] inline constexpr value_type y() const { return this->to_free().y(); }
158+
[[nodiscard]] inline constexpr value_type z() const { return this->to_free().z(); }
159+
[[nodiscard]] inline constexpr const FreeVec3& to_free() const { return inner_; }
160+
private:
161+
const FreeVec3 inner_;
162+
};
163+
164+
[[nodiscard]] inline constexpr FreeVec3 operator*(const UnitVec3& v, const value_type scalar) {
165+
return v.to_free() * scalar;
166+
}
167+
168+
[[nodiscard]] inline constexpr FreeVec3 operator/(const UnitVec3& v, const value_type scalar) {
169+
return v.to_free() / scalar;
170+
}
171+
172+
#endif //FAST_VOXEL_TRAVERSAL_ALGORITHM_VEC3_H

amanatidesWooAlgorithm.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#include "amanatidesWooAlgorithm.h"
2+
#include <numeric>
3+
4+
// Macros defined to avoid unnecessary checks with NaNs when using std::max and std::min
5+
#define MAX(a,b) ((a > b ? a : b))
6+
#define MIN(a,b) ((a < b ? a : b))
7+
8+
void amanatidesWooAlgorithm(const Ray& ray, const Grid3D& grid) noexcept {
9+
10+
// TODO: Assuming the ray's origin is within the voxel grid. To fix, add Smit's algorithm.
11+
const value_type tMin = 0.0;
12+
const value_type tMax = 1.0;
13+
const BoundVec3 ray_start = ray.origin() + ray.direction() * tMin;
14+
const BoundVec3 ray_end = ray.origin() + ray.direction() * tMax;
15+
16+
size_t current_X_index = MAX(1, std::ceil(ray_start.x() - grid.minBound().x() / grid.voxelSizeX()));
17+
const size_t end_X_index = MAX(1, std::ceil(ray_end.x() - grid.minBound().x() / grid.voxelSizeX()));
18+
int stepX;
19+
value_type tDeltaX;
20+
value_type tMaxX;
21+
if (ray.direction().x() > 0.0) {
22+
stepX = 1;
23+
tDeltaX = grid.voxelSizeX() / ray.direction().x();
24+
tMaxX = tMin + (grid.minBound().x() + current_X_index * grid.voxelSizeX()
25+
- ray_start.x()) / ray.direction().x();
26+
} else if (ray.direction().x() < 0.0) {
27+
stepX = -1;
28+
tDeltaX = grid.voxelSizeX() / -ray.direction().x();
29+
const size_t previous_X_index = current_X_index - 1;
30+
tMaxX = tMin + (grid.minBound().x() + previous_X_index * grid.voxelSizeX()
31+
- ray_start.x()) / ray.direction().x();
32+
} else {
33+
stepX = 0;
34+
tDeltaX = tMax;
35+
tMaxX = tMax;
36+
}
37+
38+
size_t current_Y_index = MAX(1, std::ceil(ray_start.y() - grid.minBound().y() / grid.voxelSizeY()));
39+
const size_t end_Y_index = MAX(1, std::ceil(ray_end.y() - grid.minBound().y() / grid.voxelSizeY()));
40+
int stepY;
41+
value_type tDeltaY;
42+
value_type tMaxY;
43+
if (ray.direction().y() > 0.0) {
44+
stepY = 1;
45+
tDeltaY = grid.voxelSizeY() / ray.direction().y();
46+
tMaxY = tMin + (grid.minBound().y() + current_Y_index * grid.voxelSizeY()
47+
- ray_start.y()) / ray.direction().y();
48+
} else if (ray.direction().y() < 0.0) {
49+
stepY= -1;
50+
tDeltaY = grid.voxelSizeY() / -ray.direction().y();
51+
const size_t previous_Y_index = current_Y_index - 1;
52+
tMaxY = tMin + (grid.minBound().y() + previous_Y_index * grid.voxelSizeY()
53+
- ray_start.y()) / ray.direction().y();
54+
} else {
55+
stepY = 0;
56+
tDeltaY = tMax;
57+
tMaxY = tMax;
58+
}
59+
60+
size_t current_Z_index = MAX(1, std::ceil(ray_start.z() - grid.minBound().z() / grid.voxelSizeZ()));
61+
const size_t end_Z_index = MAX(1, std::ceil(ray_end.z() - grid.minBound().z() / grid.voxelSizeZ()));
62+
int stepZ;
63+
value_type tDeltaZ;
64+
value_type tMaxZ;
65+
if (ray.direction().z() > 0.0) {
66+
stepZ = 1;
67+
tDeltaZ = grid.voxelSizeZ() / ray.direction().z();
68+
tMaxZ = tMin + (grid.minBound().z() + current_Z_index * grid.voxelSizeZ()
69+
- ray_start.z()) / ray.direction().z();
70+
} else if (ray.direction().z() < 0.0) {
71+
stepZ = -1;
72+
tDeltaZ = grid.voxelSizeZ() / -ray.direction().z();
73+
const size_t previous_Z_index = current_Z_index - 1;
74+
tMaxZ = tMin + (grid.minBound().z() + previous_Z_index * grid.voxelSizeZ()
75+
- ray_start.z()) / ray.direction().z();
76+
} else {
77+
stepZ = 0;
78+
tDeltaZ = tMax;
79+
tMaxZ = tMax;
80+
}
81+
82+
while (current_X_index != end_X_index || current_Y_index != end_Y_index || current_Z_index != end_Z_index) {
83+
if (tMaxX < tMaxY && tMaxX < tMaxZ) {
84+
// X-axis traversal.
85+
current_X_index += stepX;
86+
tMaxX += tDeltaX;
87+
} else if (tMaxY < tMaxZ) {
88+
// Y-axis traversal.
89+
current_Y_index += stepY;
90+
tMaxY += tDeltaY;
91+
} else {
92+
// Z-axis traversal.
93+
current_Z_index += stepZ;
94+
tMaxZ += tDeltaZ;
95+
}
96+
}
97+
}

amanatidesWooAlgorithm.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#ifndef FAST_VOXEL_TRAVERSAL_ALGORITHM_AMANATIDESWOOALGORITHM_H
2+
#define FAST_VOXEL_TRAVERSAL_ALGORITHM_AMANATIDESWOOALGORITHM_H
3+
4+
#include "Ray.h"
5+
#include "Grid3D.h"
6+
7+
// Provides a basis for Amanatides & Woo's "A Fast Voxel Traversal Algorithm for Ray Tracing."
8+
// See: https://www.researchgate.net/publication/2611491_A_Fast_Voxel_Traversal_Algorithm_for_Ray_Tracing
9+
// Assumes that indices for voxel coordinates begin at 1.
10+
// If the ray origin is outside the voxel grid, uses Smit's algorithm to determine intersection.
11+
void amanatidesWooAlgorithm(const Ray& ray, const Grid3D& grid) noexcept;
12+
13+
#endif //FAST_VOXEL_TRAVERSAL_ALGORITHM_AMANATIDESWOOALGORITHM_H

0 commit comments

Comments
 (0)