Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using other arrays in the generate function #317

Closed
LukaHorvat opened this issue May 23, 2016 · 8 comments
Closed

Using other arrays in the generate function #317

LukaHorvat opened this issue May 23, 2016 · 8 comments

Comments

@LukaHorvat
Copy link

LukaHorvat commented May 23, 2016

I'm working on a ray tracer. I'm using the generate function to construct the array of pixels that will be displayed. Each pixel casts a ray. The casting is done by iterating through the scene of objects. I've put those objects into an accelerated array but my code fails with the inconsistent valuation @ shared 'Exp' error. I suspect it's because I'm iterating through one array while generating another, but how do I get around this?

@tmcdonell
Copy link
Member

Just guessing by what you wrote, your conclusion is probably correct. Maybe you could sfold through the array of objects, looking for the closest intersection? If you can share some sample code, we can try and give more specific advice / suggestions.

There is also a very simple ray tracer in accelerate-examples which may provide some inspiration.

@LukaHorvat
Copy link
Author

LukaHorvat commented May 23, 2016

The project is on GitHub here: https://github.com/LukaHorvat/RayTracer

The relevant bits are cast function here https://github.com/LukaHorvat/RayTracer/blob/master/src/Ray.hs#L96 and the render function here https://github.com/LukaHorvat/RayTracer/blob/master/src/Main.hs#L67

I don't know if you can see anything immediately wrong here. In essence what is happening is the expanded in render generates a new array using the generateCell function. This function basically just calls cast.

Inside of cast I map over the scene (called spheres in this case because it's the only primitive I have at the moment) to get intersections, then find the nearest one and all that ray tracing stuff.

I'm guessing I have a fundamental misunderstanding of how things work. I assumed that if I use an array, I could just index it from anywhere else later (though I don't explicitly index anything; just maps and folds).

I should note that I'm not a 100% sure that the error really is where I think it is, but since it's the error mentioned in the docs related to the generate function, and this is the only place I'm using the generate function, I assume it's so.

Also, the reason for that generate' function is because I thought it might make a difference if I first create an array and then map over it to set the values instead of just doing it through generate.

@tmcdonell
Copy link
Member

Yes, I think specifically it is the use of the on this line.

The general approach to tackle this is to, instead of working on a single ray at a time, try to compute all rays at once, using multidimensional arrays to your advantage. For your program however this is a bit tricky since you use filter, which we don't have a multidimensional equivalent of at the moment.

I think the most straightforward approach would be to compute the nearest intersection using sfoldl to iterate over the array of spheres, computing for each object whether the array intersects it, and returning the nearest intersection at each iteration of the loop. This is a bit unsatisfying, but should work for small scenes at least...

@tmcdonell
Copy link
Member

If you want to instead do the all-rays-at-once approach, since you've defined a nice Optional type one thing you could do is to drop the filter and have the downstream bits check whether it is some/none before doing anything. That would keep everything regular, so you'd end up with a reduction over an array of size (Z :. height :. width :. objects) to compute the nearest intersection (if any) for every array at once.

That is just for a single ray cast. Doing reflections is tricky: you can't do recursion in the Accelerate language, so you need to either find a way to express that in terms of iteration using while or awhile, or make clever use of metaprogramming.

@LukaHorvat
Copy link
Author

Both approaches sound interesting. I have a few questions if you don't mind
answering.

With the sfoldl, what exactly do you mean by "computing for each object
whether the array intersects it". Do you mean that instead of "for each ray
-> for each object -> ..." I do "for each object -> for each ray"? Would
that make a difference?
Or do you just mean that I should use sfoldl to do what I currently do
instead of the map/fold approach? Why is that different?

The other approach seems pretty nice but it means that I'll have a copy of
the whole scene for each pixel, doesn't it? For my current use case this is
obviously a non issue, but it doesn't sound like it would scale well.

As far as reflections go, currently I don't do them directly but instead
output the bounce ray along with the ray cast result. The idea is that I
can cast all the rays at once, then in the next iteration do their
reflections and so on.

On Tue, May 24, 2016 at 7:29 AM Trevor L. McDonell notifications@github.com
wrote:

If you want to instead do the all-rays-at-once approach, since you've
defined a nice Optional type one thing you could do is to drop the filter
and have the downstream bits check whether it is some/none before doing
anything. That would keep everything regular, so you'd end up with a
reduction over an array of size (Z :. height :. width :. objects) to
compute the nearest intersection (if any) for every array at once.

That is just for a single ray cast. Doing reflections is tricky: you can't
do recursion in the Accelerate language, so you need to either find a way
to express that in terms of iteration using while or awhile, or make
clever use of metaprogramming.


You are receiving this because you authored the thread.
Reply to this email directly or view it on GitHub
#317 (comment)

@tmcdonell
Copy link
Member

For the use of sfoldl, you would have a loop that for each ray (the cast function) iterate over each object. i.e. use (sequential) sfoldl to replace the current use of (nested parallel) map and foldAll. Your cast function would look something like...

cast :: Acc Scene -> Exp Color -> Exp Ray -> Exp (Color, Optional Ray)
cast scene color ray = ...
  where
    intersect :: Exp (Optional Sphere)
    intersect = sfoldl nearest none Z scene

    nearest :: Exp (Optional Sphere) -> Exp Sphere -> Exp (Optional Sphere)
    nearest old new = ...

For the second approach, you would probably need to replicate the objects in the scene. While conceptually that sounds like having a copy of the scene for each pixel (exactly as you say) in practice that should be fused in together into the reduction over all pixels, so that large array will never actually be created.

By the way, I created these after I wrote that toy ray tracer, but you may find these auxiliary packages of mine useful, particularly the first:

https://github.com/tmcdonell/linear-accelerate
https://github.com/tmcdonell/colour-accelerate

@LukaHorvat
Copy link
Author

I'll close this since it's not an issue. Also, I'm happy to report that I've managed to get it working with sfoldl. With reflections even.

I don't want to open a separate issue for this but how come there are so many unimplemented class methods? Most of the errors I've encountered were because some part of my code decided to be polymorphic over standard numeric classes and instead of that being a type error it just crashed at runtime with things like 'no implmentation of toInteger'

@tmcdonell
Copy link
Member

Arbitrary precision types like Integer and Rational aren't supported in Accelerate at the moment (and, arguably, don't make sense).

Some prelude functions such as (==) :: a -> a -> Bool we can not overload, because their types are fixed (in this case Bool, whereas we require Exp Bool). Hence, we need to define our own operator (==*).

I have recreated the relevant parts of the type class hierarchy in Accelerate, so use that instead and import only the parts of the Prelude that you require.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants