Last active
April 13, 2021 00:03
-
-
Save rbnelr/3e3beb36a1d7528c5f114ac4b6a8e409 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
bool trace_ray (vec3 pos, vec3 dir, float max_dist, out Hit hit, bool sunray) { | |
// make ray relative to world texture and bring coordinates into [1.0, 2.0] float space | |
// for float mantissa optimization (avoid int -> float conversion for projection) | |
// basially treat float mantissa like integer for stepping but use the whole float for projection calculations | |
vec3 coord = pos + WORLD_SIZE_HALF; | |
coord = coord * INV_WORLD_SIZEf + 1.0; // to [1.0, 2.0] | |
coord = clamp(coord, 1.0, 2.0); | |
// flip coordinate space such that ray is always positive (simplifies stepping logic) | |
// keep track of flip via flipmask | |
bvec3 ray_neg = lessThan(dir, vec3(0.0)); | |
coord = mix(coord, 3.0 - coord, ray_neg); | |
uvec3 flipmask = mix(uvec3(0u), uvec3(MANTISSA_MASK), ray_neg); | |
// precompute part of plane projection equation | |
// prefer 'pos * inv_dir + bias' over 'inv_dir * (pos - ray_pos)' | |
// due to mad instruction | |
// multiply in WORLD_SIZEf to make distances be in world space | |
vec3 inv_dir = mix(1.0 / abs(dir), vec3(INF), equal(dir, vec3(0.0))) * WORLD_SIZEf; | |
vec3 bias = inv_dir * -coord; | |
// starting cell is where ray is | |
// start at some level of octree | |
// -best to start at 0 if camera on surface | |
// -best at higher levels if camera were in a large empty region | |
uint mip = 0; | |
//uint mip = uint(OCTREE_MIPS-1); | |
// round down to start cell of octree | |
coord = TOFLOAT(TOUINT(coord) & (ROUND_MASK << mip)); | |
bvec3 axismask = bvec3(false); | |
float dist = 0.0; | |
for (;;) { | |
// get current original coordinate space | |
int bits = OCTREE_MIPS - int(mip); | |
int offs = MANTISSA_BITS - bits; | |
uvec3 flipped = bitfieldExtract(TOUINT(coord) ^ flipmask, offs, bits); | |
// read octree cell | |
uint childmask = texelFetch(octree, ivec3(flipped >> 1u), int(mip)).r; | |
//flipped &= 1u; | |
//uint i = flipped.z*4u + (flipped.y*2u + flipped.x); | |
uint i = flipped.x & 1u; | |
i = bitfieldInsert(i, flipped.y, 1, 1); | |
i = bitfieldInsert(i, flipped.z, 2, 1); | |
if ((childmask & (1u << i)) != 0) { | |
// non-air octree cell | |
if (mip == 0u) | |
break; // found solid leaf voxel | |
// decend octree | |
mip--; | |
vec3 next_coord = TOFLOAT(TOUINT(coord) + (FLOAT_OCTREE_SIZE << mip)); | |
// upate coord by determining which child octant is entered first | |
// by comparing ray hit against middle plane hits | |
vec3 tmidv = inv_dir * next_coord + bias; | |
coord = mix(coord, next_coord, lessThan(tmidv, vec3(dist))); | |
} else { | |
// air octree cell, continue stepping | |
vec3 next_coord = TOFLOAT(TOUINT(coord) + (FLOAT_OCTREE_SIZE << mip)); | |
// calculate entry distances of next octree cell | |
vec3 t0v = inv_dir * next_coord + bias; | |
dist = min(min(t0v.x, t0v.y), t0v.z); | |
// step into next cell via relevant axis | |
axismask.x = t0v.x == dist; | |
axismask.y = t0v.y == dist && !axismask.x; | |
axismask.z = !axismask.x && !axismask.y; | |
coord = mix(coord, next_coord, axismask); | |
uint stepcoord = axismask.x ? TOUINT(coord.x) : TOUINT(coord.z); | |
stepcoord = axismask.y ? TOUINT(coord.y) : stepcoord; | |
mip = findLSB(stepcoord) - MANTISSA_SHIFT; | |
coord = TOFLOAT(TOUINT(coord) & (ROUND_MASK << mip)); | |
if (mip >= uint(OCTREE_MIPS) || dist >= max_dist) | |
return false; | |
} | |
} | |
if (!sunray) { | |
int bits = OCTREE_MIPS - int(mip); | |
int offs = MANTISSA_BITS - bits; | |
uvec3 flipped = bitfieldExtract(TOUINT(coord) ^ flipmask, offs, bits); | |
// arrived at solid leaf voxel, read block id from seperate data structure | |
hit.bid = read_bid(flipped); | |
hit.prev_bid = 0; // don't know, could read | |
// calcualte surface hit info | |
hit.dist = dist; | |
hit.pos = pos + dir * dist; | |
hit.normal = mix(vec3(0.0), -sign(dir), axismask); | |
uint entry_face = get_step_face(axismask, dir); | |
vec2 uv = calc_uv(fract(hit.pos), axismask, entry_face); | |
float texid = float(block_tiles[hit.bid].sides[entry_face]); | |
hit.col = texture(tile_textures, vec3(uv, texid)).rgb; | |
hit.emiss = hit.col * get_emmisive(hit.bid); | |
} | |
return true; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment