From 9df37ee5c008a77b3fad8c390fc493feccaeeb1f Mon Sep 17 00:00:00 2001 From: Longor1996 Date: Sat, 9 Dec 2023 14:17:58 +0100 Subject: [PATCH] refactor(/wiki/raycasting): dda algorithm impl It *should* be correct now. --- content/wiki/raycasting/dda.glsl | 106 +++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 33 deletions(-) diff --git a/content/wiki/raycasting/dda.glsl b/content/wiki/raycasting/dda.glsl index 123079c..9152677 100644 --- a/content/wiki/raycasting/dda.glsl +++ b/content/wiki/raycasting/dda.glsl @@ -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; }