Context: I have a CPU based raytracing engine written in Pygame, it works by having rays move 1 unit per loop in the direction of their velocity and detecting any voxel at that integer position. I recently added chunks to boost performance, voxels in each area are stored in a container of a fixed size which is read first. For example: If the chunk size is 16 the chunk at position (0, -16, 32)
will store all data from it to position (16, 0, 48)
. Valid chunks are stored in a dictionary indexed by their start corner tuple, the end corner can be obtained by adding the size to it. Here’s an example of the data structure, in this case the chunks are None since their data and how it’s used is irrelevant to my question.
chunks = {
(0, 0, 0): None,
(64, 0, 32): None,
(-96, 48, 16): None,
(-128, -96, 0): None,
}
I noticed that scanning for chunks is more costly than it could be. Most lag appears to originate from checking the position and snapping it to the chunk size to get the appropriate index: Each time the ray moves I need to check if it’s in the area of another chunk and fetch its data. For example if the ray moved from position (-0.5, 0.25, 0)
to (0.5, 1.25, 1)
given the velocity (1, 1, 1)
I now need to get the data of chunk (0, 0, 0)
. This is a representation of my ray loop so far:
pos = (0, 0, 0)
chunk_min = (0, 0, 0)
chunk_max = (0, 0, 0)
chunk_size = 16
chunk = None
while True:
if pos[0] < chunk_min[0] or pos[1] < chunk_min[1] or pos[2] < chunk_min[2] or pos[0] > chunk_max[0] or pos[1] > chunk_max[1] or pos[2] > chunk_max[2]:
pos_min = ((self[0] // chunk_size) * chunk_size, (self[1] // chunk_size) * chunk_size, (self[2] // chunk_size) * chunk_size)
pos_max = (pos_min[0] + chunk_size, pos_min[1] + chunk_size, pos_min[2] + chunk_size)
chunk = chunks[pos_min] if pos_min in chunks else None
# Do things with the data in chunk, advance pos by -1 or +1 on at least one axis, and break out of the loop when tracing is over
That loop runs dozens of times per pixel thus thousands of times in total, every check must be very efficient or FPS drops drastically. I get a performance boost by caching the boundaries of the last chunk then checking when the ray’s position is no longer within those bounds to only change the chunk once, but this check is itself costly and snapping the position to the chunk size is even more expensive. How can this be done most optimally, what are the cheapest operations to detect the position entering a new cubic area?