Ray tracing lighting help

Started by
7 comments, last by Skelit 13 years, 6 months ago
Hello all, I'm trying to make a ray tracer, and right now I've got it so that it traces simple rays moving out of the camera. It can see a sphere and draw that to the screen which is neat and all. However, I'm having trouble getting lighting to work.

I got it working once before, but then I started over because my code became a huge mess since I was working on a tiny netbook screen. When I brought it to my desktop it was just awful looking.

So now I redid it, and I can't get lighting to work. Before all I did was take the position at which the ray hit the sphere, then shot it again at the light. Then I checked it against all the objects in the scene to see if it hit any of them, and if so then make it dimmer. If not, make it the color of the sphere.

That's what I tried doing again, but its just not working. I was there would be a simple mistake I'm just not seeing that you'd be able to point out to me.

Here is all my code for the ray tracing. I feel like I did a pretty good job documenting it and making it self documented, but if you're confused on anything then let me know.

http://pastebin.com/iHaxmuFL

The main part that deals with lighting is lines 100-136. Also, I'm not worried about how much shading the points get right now, I just simply want them to be lit up or not.

Thank you if you're wiling to help!

Here is a picture of what happens. It draws the sphere, and it seems like it only shades the outline of the shadow.

http://imgur.com/wCToi.png
Advertisement
I have not the time right now to check all the code, but have you added an epsilon to your shadow ray? If not, then you are probably incourring ins self-shadonwing artifacts, where the shadow ray intersects the same primitive it was generated from due to floating point precision issues.
You should create your shadow ray as follows:
sr = (hitpoint + hitnormal * epsilon, lightdir)

This should ensure that the origin of the shadow ray is always outside the primitive where the hitpoint lies on. The exact value of epsilon is rather small, but you must try several values until it works (start trying with 0.0001 for example)
Cool your writing a ray tracer hey! Ray tracers are good general practice... once you finally get your lighting working I suggest adding 2 things.

a) Area lights

b) global illumination

AREA LIGHTS
-----------
Ill explain a directional area light, to say fake the suns effect.

instead of firing a ray directly in the light direction, fire about 20 rays all offseted by a random amount and collect how many of these rays didnt get occluded by geometry, average the result and you should get an attractive penumbra.

GLOBAL ILLUMINATION
-------------------
First calculate the direct light hitting all the front faces of the geometry and all the back faces.
then you fire rays out of every pixel, youll need about 1024 rays (maybe more) firing out from the hemisphere of the normal of the surface of the pixel.
every time you hit a lit pixel add light to this pixel (its the secondary light) and average by the amount of rays you used.

this will get the "Secondary" light. then do it again for the backfaces, then do "tertiary" light for the front faces and thats about as far as you have to go.

You need to do it for front faces and back faces cause the back faces effect the final result on the front faces.

and youll get a stunning realistic result, add the front face "direct" "secondary" and "tertiary" light together for the finished product.

First, Looking at the code you provided, it seems that the sphere intersection tests have two important problems:
- Ray/Sphere intersection may have 0, 1 or 2 solutions.
- Self-intersetion when computing shadow which leads to wrong shadowing.

For ray/sphere intersection, if D>0 there is two intersection points: -B +/- sqrt(D). You may select only the closest AND positive solution. Negative solution indicates there is an intersection behind the origin of the ray regarding the vector direction.

Added to this, if the origin of the ray is actually on the sphere and you not take this in account, the closest intersection will be the origin itself (obviously). You should had a test to avoid self-intersection. One quick and dirty solution (eventhough commonly used) is to offset the origin of the ray (when on a sphere) by a small epsilon: ray.origin += epsilon*ray.dir. Another solution, as far as you know the currently intersected object when firing again a ray, is to remove/avoid from the list of the objects tested for intersection by the new ray.

Well I tried to add an epsilon to my new position to shoot the shadow ray from, but I'm getting a really weird result. The outline ring that used to be the shadow is still just a ring, but when I increase epsilon it gets bigger.

Epsilon = 0.0001f: http://imgur.com/5Qybz.png
Epsilon = 0.001f: http://imgur.com/LD2iw.png
Epsilon = 0.1f: http://imgur.com/i3ORg.png
Epsilon = 1.0f: http://imgur.com/w4STG.png

The light is up above to the right of the sphere. I'm also not sure why it looks like the shadow is red(the color of the actual sphere), and the part that's supposed to be lit up by the light is darker.

Here is my current code, it changed a little bit from last time because I kept trying to do stuff but it kept messing up, and I ended up with a whole bunch of commented out stuff that eventually I couldn't keep track of, so I redid it at least once.
http://pastebin.com/uwVK2Ywv
I also fixed the problem that was in raySphereIntersection.

Also, xbenech, if you were to ignore the current sphere that you hit when casting the shadow ray(as opposed to giving the intersecting point a small offset), wouldn't that cause it to not cast a shadow on itself? So if you only had one sphere it would also be completely lit, even the part on the other side of the light. Because of that I opted to offset the intersecting point by a small value.
Quote:Also, xbenech, if you were to ignore the current sphere that you hit when casting the shadow ray(as opposed to giving the intersecting point a small offset), wouldn't that cause it to not cast a shadow on itself? So if you only had one sphere it would also be completely lit, even the part on the other side of the light. Because of that I opted to offset the intersecting point by a small value.


Shadow rays should only compute projected shadows (shadows generated by occlusion due to other objects) while points on the dark side of the sphere just require a check to the result of the dot product between the normal and the light vector.

The ring appears right where the surface beginis to point toward the opposite direction of the light, so I would check that the dot product is correct.

EDIT:
#        ArrayList lights = scene.getLights();#        for ( int i = 0; i < lights.size(); i++ ) {#            Light light = (Light)lights.get(i);#            Vector3f lp = light.getPosition();# #            //get direction to light#            Vector3f directionToLight = vu.subVector3f(lp, hitPos);#            directionToLight.normalize();# #            Ray rayToLight = new Ray(hitPos, directionToLight);#            try {#                raySphereIntersection(rayToLight, closestSphere);#                return closestSphere.getColor().darker();#            } catch ( DidNotHitAnythingException e ) {#               #            }#        }


You should do that check: you shoot the shadow ray only if the normal points toward the light.
In addition (if I don't misunderstand your code) you use an exception when you don't hit primitives. If this is correct, then I suggest you to avoid that, as exceptions are meant for errors only...
Ah ok. I finally got it working! Here is what it ended up looking like: http://i.imgur.com/wq2uO.png

I finally built a ray tracer which I've wanted to do for the longest time ever! I'm definitely going to keep going and try to add as many features as I can.

Thanks for everyones help!
Coul you explain which was the problem and how you were able to solve it?
Well first, I think my dot product to see if the light was hitting the point was either wrong or missing. So I added that. Then instead of trying to go for a simple light/dark color, I just went straight to finishing(kinda) the diffuse lighting and multiplied the current color by the angle between the normal and the light ray in order to get it shaded. And then I ran it and ended up with this.

It's not perfect yet. For one, it doesn't get darker the further away the light is, and there is a few more things I remember not being completely right, but I got passed the main problem I was having in this thread, so its good enough for jazz!

This topic is closed to new replies.

Advertisement