I have a 1920 by 1080 array canvas with each element acting as a pixel, with default value RGB(0, 0, 0), for an image.
What my program (a very simple raytracer) currently does is:
- Use the index of each "pixel" to get the corresponding position on the camera frame
- store the ray from the center of the camera to that position as a three-dimensional vector
- find the closest object that the ray intersects with
- set the value of that pixel to match the color of that object (which again require the indices of that pixel)
The full code is too large to share, but most of the code relevant to the question is as follows:
world = World(RGB(0, 0, 0), 5e-6, objects, lights, 0.2, 4)
camera = Camera((0, -5000, -5000), 1000, (0, 0, 0), 1920, 1080)
canvas = CUDA.fill(world.background, camera.height, camera.width)
for i in 1:camera.width
for j in 1:camera.height
ray = [([i, j] - [camera.width / 2, camera.height / 2])..., camera.depth]
(ray[2], ray[3]) = (cos(camera.rotation[1] + atan(ray[3], ray[2])), sin(camera.rotation[1] + atan(ray[3], ray[2]))) .* sqrt(ray[2]^2 + ray[3]^2)
(ray[1], ray[3]) = (cos(camera.rotation[2] + atan(ray[3], ray[1])), sin(camera.rotation[2] + atan(ray[3], ray[1]))) .* sqrt(ray[1]^2 + ray[3]^2)
(ray[1], ray[2]) = (cos(camera.rotation[3] + atan(ray[2], ray[1])), sin(camera.rotation[3] + atan(ray[2], ray[1]))) .* sqrt(ray[2]^2 + ray[1]^2)
v = (Inf, nothing, nothing)
for object in world.objects
# traceray returns a tuple containing the distance of the point of intersection, and the normal to the surface at that point if the ray intersects the object, or nothing otherwise
t = traceray(ray, camera.position, object, mindistance=camera.depth)
t !== nothing && t[1] < v[1] && (v = (t[1], t[2], object))
end
# computecolor gets the color of the object based on lighting etc.
v[1] != Inf && (canvas[j, i] = computecolor(v[3].material, ray, v[1], v[2], world, camera.position .+ v[1] * ray, v[3]))
end
end
Currently, I do this by using nested for loops to loop over all the indices. However, I wanted to improve performance by using CUDA.jl. I would like to avoid writing a GPU kernel and make do with Array Programming, if possible. However, in that case, indexing over the array is not possible since scalar indexing has to be avoided, and from what it seems to me, broadcasting over an array is the preferred method.
From my understanding, a broadcasted function can return a value based on the elements' value (in this case RGB(0, 0, 0)) and not the indices (which is what I require to compute the color).
Is there any way to have a function that wraps the steps mentioned above receive the indices of the element it is acting upon via broadcasting or any alternative method that achieves what I want?
PS: I did look into Computing array values based on their index using Julia Broadcasting in julia but that approach alters only a row (which is a vector by itself), and the expression is quite simple, and I could not get something similar working for a matrix and the large number of statements my program uses.