PDA

View Full Version : Raytracer WIP


DeFi
04-23-2006, 11:53 AM
Hello,
at the moment I'm implementing my own raytracer in Visual C++ 8.0 to play arount with all the stuff my professor talks about in my image rendering lectures. I managed to implement a small framework with all the vector math stuff and some screen drawing and file output procedures.

Yesterday I got my first raytraced image with ambient lighting and antialiasing, so I thought it would be a good idea to implement diffuse, specular lighting and shadows today but I got stuck with some strange rendering artifacts that look like numeric errors while implementing shadows.

shadow errors without antialiasing:
http://www.defi1.de/showcase/rt_shadow_errors1.png
http://www.defi1.de/showcase/rt_shadow_errors2.png

There seems to be a problem with the distance comparison of the shadow rays. On the plane the artifacts are periodic in distance of the lightsoure. On the spheres they seem to be random. If I add a small threshold to the distance, the artifacts disappear. At the moment my shadow rays are shot from the light source to the point to be tested. If I invert the ray direction and cast them from the point to be tested to the light source the artifacts dissapear only on the plane but not on the spheres.

My next try was to increase the precision of my floatingpoint values but useing doubles instead of floats had no effect after all.

So my question is: Has anybody an idea what this problem may be caused by? Since it's a strange problem I would be grateful for any help on this.

Greetings
DeFi

relief7
04-23-2006, 12:38 PM
DeFi,

it seems to me that this is a self-intersection problem. It appears if the calculated intersection point lies a tiny bit below the spherical surface (which means inside the sphere). Thus, any shadow rays that are cast will return an intersection (with the original object actually) but the information that this point is shadowed is wrong. I think the common way to correct this is to add a very small value to make sure the point is outside the object. Please correct me if I am wrong.

I just don't quite understand why your spheres behave differently from your planes when you reverse the direction of the ray.

Btw, for your first raytraced image this is very nice ! :-)

montclaris
04-23-2006, 02:08 PM
Definitely self-intersection. Do as relief7 suggests and it should solve the prob, eg either translate your ray origin by epsilon along the geometric normal of the surface (if you trace surface -> light) or along the ray direction (if you trace from the light). A better approach IMO, would be to modify your ray-primitive intersection code. When you check ray_length is a positive value, test against epsilon rather than 0.

DeFi
04-23-2006, 04:07 PM
Ahh, so you think there's no other solution than using an epsilon corrector or excluding the primitives from self-shadow-ray-intersetion. Well, I personally dont like those unclean epsilon-fakes, so I implemented the second solution. (However I found out, that my numeric bug can detect Intel and AMD processors by the type of noise they produce.) ;)
It worked fine and I was able to complete Blinn-shading with diffuse and specular lighting.

Thank's for the hints.

Here are two shots using my own hand-tuned 8x anialiasing mask:
http://www.defi1.de/showcase/rt_blinn_8xaa_1.png
http://www.defi1.de/showcase/rt_blinn_8xaa_2.png

MattTheMan
04-23-2006, 06:06 PM
Wow, cool!

Im making a raytracer too (you may have seen my thread here too) and I'm writing a photon mapper now.

But good job!

montclaris
04-23-2006, 06:42 PM
I don't understand what you mean by "unclean fake" since a raytracer is only faking light behaviour ? Anyways you can't spare using epsilons in one form or another as soon as you're doing floating point arithmetics because of round-off errors. Excluding traced surface from intersection tests will also result in the object being unable to occlude light from itself (no self shadowing) unless you resort to conditonal statements which are even more unclean IMO. But to each one its own, of course :)
Nice renders, anyways.

DeFi
04-24-2006, 07:40 AM
By unclean I mean, that it's a solution that is heavyly influenced by the accuricy of your data types and you have to guess the right size of the epsilon.

However self shadowing is a point I didn't think of(thanks for reminding me). I tried an epsilon solution, deaktivated all illumination except shadows and got nice self shadows on the spheres. As for the plane and all other infinitely thin object this doesnt work without determining and comparing on which side(normal wise) of the object the camera and the light are. At the moment surface illumination(not the light itself) and shadows just break through my planes, if I place the camera on one side and a light on the other.

Perhaps a solution of this can be to define an inner and an outer objectspace for each object. Even if you deal with polygons you can do this safely by takeing the plane defined by the three vertices. Then it might be possible to compare if camera and light are in different object spaces and react accordingly to get correct self-shadows on planes and on solid objects if you move either the camera or the light inside.

Self-shadowing spheres:
http://www.defi1.de/showcase/rt_self_shadow1.png
Break trough illumination on the plane, if the camera is moved below(notice correct lighting on the small part of the green sphere, thats below the plane):
http://www.defi1.de/showcase/rt_self_shadow2.png

montclaris
04-24-2006, 02:31 PM
You are right self shadowing may be handled differently depending on the side of the object you're looking at. It would be visible in your spheres too if the camera was inside the volume of one of them.
I think this is usually addressed using the following method or a variant :
1 - switch surface normal's sign so that dot(incoming_ray, normal) > 0,
eg normal = dot(incoming_ray, normal) ? normal : -normal
Typically, this would done by the engine prior to invoking the shader to handle two sided surfaces correctly.
2 - in the surface shader. Trace your shadow ray, iff dot(switched_normal, light_dir) > 0 where light dir is the direction of the shadow ray surface-> light,
eg boolean occlusion = dot(normal, light_dir) > 0 ? ShadowRayTrace() : true

Note the test of dot(normal, light_dir) would be done in a Lambert illumination model anyways to prevent backward lighting to result in a negative irradiance contribution (a self-occluded light substracting irradiance to the surface instead of having 0 contribution).

Hope i didn't do any mistakes with my math :)

kabojnk
04-25-2006, 09:56 AM
U R TEH CHAMp!!! KEP UP THE GOOD WARK!!!11

DeFi
04-25-2006, 11:56 AM
@montclaris: I was able to fix the shadows with something similar to your approach. Thank's.

Today I implemented various shading models besides the Blinn including Phong, Ward-anisotrop and Cook-Torrance. However Blinn and Ward-anisotrop seem to look better without self shadowing. In addition the formula for the Cook-Torrance shader didn't work as expected(had nothing to to with the BRDF<->BSF coversion). So I had to modify it a little bit and now I'm not sure, if the result is still the Cook-Torrance model or something different(maybe it's only the strange Cook-Torrance-DeFi Shader ;) ).

Here are some images:
Phong Shading (http://www.defi1.de/showcase/rt_phong.png)
Blinn Shading (http://www.defi1.de/showcase/rt_blinn.png)
Ward anisotrop (http://www.defi1.de/showcase/rt_ward_anisotrop.png)
Ward anisotrop without self shadowing (http://www.defi1.de/showcase/rt_ward_anisotrop2.png)
Cook-Torrance with different roughnesses (http://www.defi1.de/showcase/rt_cook_torrance.png)

rendermaniac
04-25-2006, 10:11 PM
Definitely self-intersection. Do as relief7 suggests and it should solve the prob, eg either translate your ray origin by epsilon along the geometric normal of the surface (if you trace surface -> light) or along the ray direction (if you trace from the light). A better approach IMO, would be to modify your ray-primitive intersection code. When you check ray_length is a positive value, test against epsilon rather than 0.

Why can't you shift your ray origin along the ray direction inthe surface -> light situation? It also seems several renderers do this for reflections which means that the reflection changes if the bias does - which seems wrong to me. Or is there something I am missing?

Simon

montclaris
04-25-2006, 10:34 PM
Why can't you shift your ray origin along the ray direction inthe surface -> light situation? It also seems several renderers do this for reflections which means that the reflection changes if the bias does - which seems wrong to me. Or is there something I am missing?

I think you can do whatever works best for you ;) . I never programed a raytracer, so i'm probably not the best candidate to answer. It was only a suggestion. I think the generic method is to shift along the ray direction, not along normal. But both methods are lame IMO, because they are supposed to compensate for an error in the incoming ray length, and they therefore should take into account the incoming ray direction (which they do not) to be numerically accurate.
Suppose for instance the incoming ray was almost colinear to the surface normal, whereas the shadow ray is almost perpendicular. Your epsilon would have to be huge to compensate for an error on the incoming ray-length (provided you're using epsilon to slide the shadow ray). Shifting along normalized normal on the other hand ensures the error is correctly bound by a fixed epsilon.

edit : the numerically correct method would be to shift "back" along the incoming ray, by an epsilon that bounds accumulated rounding errors in your ray-length calculations ?
Does it make sense ? Please someone correct me if i'm seriously wrong.

montclaris
04-25-2006, 10:55 PM
@rendermaniac : as for reflection rays, translating the reflected ray along the normal by a very small amount while preserving the ray's direction is unlikely to change the reflection. Unless possibly if these errors sum up after many reflections on different objects.

rendermaniac
04-28-2006, 01:45 PM
I have seen it change the image - although admittedly it was on alarge array of perfectly flat mirrors. Blurred reflections or a broken up surface may be less noticeable, however it would still be there.

Simon

DeFi
04-30-2006, 06:30 PM
I managed to get reflections and refractions working(had to use 3 evil epsilons ;) ). The reflections have a fresnel option in the shader but don't use diffuse component darkening. As for the refraction it took me half a day to figure out how to compute the total reflection angle without using any trigonometric functions, but now it seems to work. :cool:

Next thing will be, to add triangle primitive support, area lights and some better shadows.

http://www.defi1.de/showcase/rt_refl_refr.png

cignox1
05-03-2006, 08:27 AM
As suggested, when you compare floating point values always use an epsilon: Don't do if(x > 0) but if(x > epsilon) (if x is supposed to be the result of a computation, because if it is assigned the value 0 explicitly it doeasn't matter). Again, don't do if(x == 0) but if(x >= -epsilon && x <= epsilon).
To avoid self intersection errors, the standard way (as far as I know, and the one I used) is to shift the origin of the shado/reflection/refraction ray a bit in the direction of the ray before testing for an intersection with the scene.
if i_p = (x,y,z) is the intersection point with your primitive and you want to shoot the shadow ray, don't build your ray as follows:

vec3 dir = normalize(light - i_p);
ray r(i_p, dir);

but:
vec3 dir = normalize(light - i_p);
ray r(i_p + dir*epsilon, dir);

This should remove your problems. Search epsilon by trying many values: I used 0.0005 (or something like this) for float and 0.000005 (or something like this) for double.

DeFi
05-04-2006, 08:29 AM
@cignox1: Epsilon-ray-shifting is exactly what I did for all normal ray intersection calculations. Only my shadow rays are shot in the opposite direction from the lights to the objects for practical reasons. In that case I simply shorten the rays by epsilon. And the value that works for doubles in my case is 0.000000001.

dlarsson
05-28-2006, 08:44 PM
There can be problems using constant epsilons since floating point values have different precision depending on their value. Typically what can happen if someone makes a huge scene the epsilon won't be large enough to displace the ray out of the primitive.
One way of approaching this is make the epsilon related to the distance a ray has travelled making it larger for distant hits, however it relies on that the most interesting parts of the scene is centered in origin where the precision is best. Check out the part about surface acne in http://jedi.ks.uiuc.edu/~johns/raytracer/rtn/rtnv4n2.html#art4 (http://jedi.ks.uiuc.edu/%7Ejohns/raytracer/rtn/rtnv4n2.html#art4) . Perhaps using ray differentials (tracing ray differentials (http://graphics.stanford.edu/papers/trd/)) to calculate a projected pixel area would be useful.


Have fun.

CGTalk Moderation
05-28-2006, 08:44 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.