Skip to content

Commit

Permalink
refactor(/wiki/raycasting): dda algorithm impl
Browse files Browse the repository at this point in the history
It *should* be correct now.
  • Loading branch information
Longor1996 committed Dec 9, 2023
1 parent f1a943f commit 9df37ee
Showing 1 changed file with 73 additions and 33 deletions.
106 changes: 73 additions & 33 deletions content/wiki/raycasting/dda.glsl
Original file line number Diff line number Diff line change
@@ -1,42 +1,82 @@
bool DDATraversal(vec3 origin, vec3 dir, out vec3 o_hitPos, out vec3 o_hitNormal)
{
dir = normalize(dir);
#define MAX_RAY_STEPS 64

bool raycast(
// Inputs
vec3 ray_origin,
vec3 ray_direction,

// Outputs
// out bool RETURN, // was any voxel hit at all?
out bvec3 o_hit_axis, // Which axis did the hit occur on.
out float o_hit_dist, // Distance to the hit position.
out ivec3 o_hit_vox, // Which voxel was hit.
out vec3 o_hit_pos, // Position of the hit, globally.
out vec3 o_hit_uvw, // Position of the hit, on voxel.
out vec3 o_hit_nor // Normal of the face that was hit.
) {
ray_direction = normalize(ray_direction);

// We don't want to deal with this in the loop...
if (rayDir.x == 0)
rayDir.x = 0.001;
if (rayDir.y == 0)
rayDir.y = 0.001;
if (rayDir.z == 0)
rayDir.z = 0.001;

ivec3 raySign = ivec3(sign(dir));
ivec3 rayPositivity = (1 + raySign) >> 1;
vec3 rayInverse = 1 / dir;

ivec3 gridCoords = ivec3(origin);
vec3 withinVoxelCoords = origin - gridCoords;

while (true)
{
if (!isCoordInBounds(gridCoords))
return false;
if(ray_direction.x == 0.0)
ray_direction.x = 0.0001;
if(ray_direction.y == 0.0)
ray_direction.y = 0.0001;
if(ray_direction.z == 0.0)
ray_direction.z = 0.0001;

vec3 ray_signf = sign(ray_direction); // only for init
ivec3 ray_sign = ivec3(ray_signf); // used in loop
vec3 ray_step = 1.0 / ray_direction;

vec3 ray_origin_grid = floor(ray_origin);
ivec3 voxel_coords = ivec3(ray_origin_grid);

vec3 side_distance = ray_origin_grid - ray_origin;
side_distance += 0.5;
side_distance += ray_signf * 0.5;
side_distance *= ray_step;

bvec3 mask; // of side_distance's largest axis

for (int i = 0; i < MAX_RAY_STEPS; i++) {

if (isVoxelFilled(gridCoords))
{
o_hitPos = gridCoords + withinVoxelCoords;
vec3 normal = vec3(0);
normal[minIdx] = -raySign[minIdx];
o_hitNormal = normal;
if(is_voxel_filled(voxel_coords)) {
o_hit_axis = mask;

// Determine final hit position in global space.
o_hit_pos = side_distance - ray_origin;
o_hit_pos += 0.5;
o_hit_pos -= ray_signf * 0.5; // MINUS=
o_hit_pos *= ray_step;
//o_hit_pos = ray_origin + ray_direction * o_hit_dist;

// The voxel that was hit.
o_hit_vox = voxel_coords;

// The distance to the hit.
o_hit_dist = max(o_hit_pos.x, max(o_hit_pos.y, o_hit_pos.z));
//o_hit_dist = length(vec3(mask) * (side_distance - ray_step));

// The normal of the face that was hit.
o_hit_nor = vec3(mask) * -ray_sign;

// The position of the hit on the voxel.
o_hit_uvw = o_hit_pos - o_hit_vox;

return true; // We hit a voxel!
}

// Do a step...
vec3 t = (rayPositivity - withinVoxelCoords) * rayInverse;
int minIdx = t.x < t.y ? (t.x < t.z ? 0 : 2) : (t.y < t.z ? 1 : 2);
// Determine the mask without branching...
// (Idea by https://www.shadertoy.com/user/kzy)
mask = lessThanEqual(side_distance.xyz, min(side_distance.yzx, side_distance.zxy));

gridCoords[minIdx] += raySign[minIdx];
withinVoxelCoords += rayDir * t[minIdx];
withinVoxelCoords[minIdx] = 1 - rayPositivity[minIdx];
// All components of `mask` are false,
// EXCEPT for the corresponding largest component of sideDist,
// which is the axis along which the ray should be incremented.

side_distance += vec3(mask) * ray_step;
voxel_coords += ivec(vec3(mask)) * ray_sign;
}

return false;
}

0 comments on commit 9df37ee

Please sign in to comment.