Sign in to follow this  
sjf

ray tracing - soft shadows

Recommended Posts

Just me again with yet another annoying distribution question. Thanks to the simple advice of the friendly community here, I have been successful in my implementation of glossy reflections and my friend with the depth of field effect. Now I wish to implement soft shadowing, my current shadows are basic and thus hard! This is some information I already know: * Represent light source as area rather than point. * For each shadow calculation, send multiple rays aimed at different points on the light. * Points near the border of the shadow region will be partially lit. I currently have point source light support – which produces hard shadows due to the light being a humble point with no (surface) area. Can someone guide me through the process of allowing my point lights to have area? How do I represent my point light as an area light? And the whole soft shadowing algorithm for a simple ray tracer??? PS: Timw, Yes, ray tracer is simple and unaccelerated. This is to understand the basics from the ground up, before I hand over spec’s and other details to the hardware dept. of our university. Thanks in advance to all. And thanks to timw.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Point lights in real world also cause hard shadows. If you want to get rid of aliasing in the hard shadows for point lights, you have to use some kind of Antialiasing.

Now, if you want real soft shadows, then you must have a lightsource with an area. So you can model your lightsource as a square or sphere. To get the direct contribution of a light at any point, you need to see how much of the light is visible from that point. This can be approximated by the number of rays that intersect the light first, in the set of the sample shadow rays all of which intersect the light.

Share this post


Link to post
Share on other sites
Well, getting fast results is not so complex. Assuming you already support point lights, an easy way is to model your area light as n point lights on the same plane, each with a energy power of p/n (where p is the total power you want the light to have).
For each point you want to lit:
-generate the ray with origin in hit_point and direction light_sample-hit_point.
-Apply your preferred lighting equation.
-sum all the samples contribute. So a point that can see all the samples on the light surface is fully lit. Points than are completed covered by other objects and cannot see any samples are shadowed (shadow) and point that can see only some samples are partially lit (penumbra).

EDIT: just to be clear: hit_point is the point you want to light (i.e. intersection point between geometry and primary rays). light_sample is a sample of the light (intuitive, isn't it?): a square light can be subdivided into 4, 8, or 16 samples (actually you can choose the number of them). Only remember that each sample has p/num_samples power.

Share this post


Link to post
Share on other sites
cignox1, thankyou for your reply. This the method you describe a distribution ray tracing alorithm, or is it the one which produces those visible and discrete bands?

"is to model your area light as n point lights on the same plane" - How do I do this? I only have a point remember? How should I turn my point into a plane?

Share this post


Link to post
Share on other sites
you have support for spheres in your engine? if so, create spherical lights. remember, lightbulbs are (nearly) spherical as well, so it'll be great to have those anyways.

the trick is to not look at your lightsources as anything special. ANY geometrical object can emit light, so just use that knowledge: use models. your engine can handle spheres? use spheres as lights. your engine can handle planes? use planes as lights (will be quite big, though.. :D). your engine can handle polygons? great, use them (if you develop something with model support, your lights will gonna be polygonal one day anyways..).

just use a flag per mesh, wether it's a light, or not. and moving from a pointlight to a spherical light is a quite simple thing: add a radius to it.

and then yes, generate some random points on the sphere, trace towards them, and see if something is inbetween. and shade and shadow aaccordingly.

Share this post


Link to post
Share on other sites
there are a couple ways to do this. they're basically all the same idea, but differ in their physical interpretation of what the light geometry is.

1) area planer lights.
2) area spherical lights.

the planer light is a bit easier. it just depends on what you want. if you want the light to act like a point light with soft shadows, ie(a spherical light) or if you want the light to act like planer light, like the stuff in the cornell box.

I'm gonna assume you don't want to change your lighting model, that is to say, you wanna keep all the lighting equations as if everything is a point light. the general formula for the contribution of light from a diffuse planer surface is actully quite horrifying.

so I'm assuming you want just soft shadows. so to do this you need a way to see what percentage of the light is visible from a particular point. Say I pick 100 random positions on it(the plane light) and I trace the rays from the surface to the random point on the light and find out that only 20 of them hit the light before they hit anything else. that tells me that about 20/100 or 1/5th of the light is visible from that point. this is the basic concept. Now, for every ray that hits the light, you treat that point(the random point on the light) as a point light with 1/100th the power of the original light.

you can sample the light in 3 ways.

1) pick random positions on the plane
2) divide the plane to say 10x10 squares and pick the center of each square to send rays
3) use jittered sampling (1 + 2)

of the 3 only choice (2) will produce banding effects.

I strongly suggest using (3). it's so much better. stratification is necessary for fast convergance. If you care, (3) converges as 1/N, (1) converges as 1/sqrt(N). it's the difference between night and day.

now consider the spherical light.

If you're shading a point on a surface, from the points point of view(lol) the light looks like a circle. from any point of view, a spherical light looks like a circle with normal pointing to the surface point. that is to say:

from a point X.
the sphere centered at P, with radius r can be replaced by a circle centerd at P with radius r and normal X-P. does it make sense?

so instead of picking random points on a sphere, we only need to pick random points on a circle. sampling a circle is a little bit harder then sampling a plane. the way I do it is sample a circle centered at 0,0,0 with radius 1 and normal 0,0,1. this gives you a point (x,y,0) that you can transform with the appropriate matrix. or simply use some vector math. The point is, it's a little harder then sampling a plane light.

now, simply send n sample rays to the circle and determin visibility

VIS = #samples that hit sphere/n

and if the surface is reasonably far from the spherical light, the speherical light acts just like a point light. so simply shade it like one, just multiply the point lights contribution by VIS. note this is slightly different then the area light, in the area light, we considered each sample ray as a point light. that is to say, we modeled a plane light with n point lights, each point light has 1/nth of the total power of the light. here we consider the spherical light simply as one point light, we just multiply the point light's contribution by VIS. also, instead of sampling a circle, you could also sample a square if you think it's easier. just make sure the area of the square is approximitly the same as the area of the disk you're trying to approximate. you won't notice much difference in the shadow. what matters for shadows is not the so much the shape of the light as the size(unless of course the shape is something crazy)

Tim

[Edited by - timw on October 22, 2005 11:41:25 PM]

Share this post


Link to post
Share on other sites
Before you posted, I did do this to create soft shadows:


void SoftLight::GetIllumination(vector3f p, vector3f& d, vector3f& c, float& distance_to_light) const
{
vector3f random(((rand() / (float) RAND_MAX) - 0.5f), ((rand() / (float) RAND_MAX) - 0.5f), ((rand() / (float) RAND_MAX) - 0.5f));
d = (position + random) - p;

/* Grab the length before the direction is normalized. */
distance_to_light = d.Length();
d.Normalize();

float attenuation = 1.0f / (attenuation_1 + attenuation_2 * distance_to_light + attenuation_3 * distance_to_light * distance_to_light);

if (attenuation < 0.0f) attenuation = 0.0f;

c = color * attenuation;

return;
}



It works, but is this the best method? I will reread your posting!

Share this post


Link to post
Share on other sites
I'm not sure exactly what you're doing. it doesnt' look correct. this thing may produce some sort of soft shadow, but it's not a plane light. it's more of a sphere light, but it's not a sphere either. it's biased, your randomness isn't constrained in any reasonable way as far as I can tell. it's not even a uniformly random direction. I can see how this could kinda give you soft shadows, if you're happy with the results then I guess it's cool. If you're gonna do this, may I suggest one simple modification?

pick random uniformly, remove the directional bias by doing this.

do{
X = rand[-1,1]
Y = rand[-1,1]
Z = rand[-1,1]

}while(X*X + Y*Y + Z*Z > 1)

random = (X, Y, Z)

random is now a uniform random direction, ie every direction has an equal chance of being picked. it basically picks random points from within the unit sphere. what you were doing before was picking random points within a unit cube, if you look at a cube the perpendicular distance from the center to a face is .5 units but the distance from the center to a corner is sqrt(.5^2 + .5^2) thus, it's based to the corners. more random vectors will be near the corners then near other directions. sampling within the unit sphere removes that bias. this technique for picking random directions is called rejection sampling, simple but inefficent. in any case, it's not the correct way to do it anyway, but I think it'll give you slightly better results if you want to stick with this implimentation.

Tim

Share this post


Link to post
Share on other sites
come to think of it, the more I think about your solution the more reasonable I think it is. one thing tho, I'm sure using the above random procedure would produce better results, you might notice the bias when shading large shadows.
I have one more suggestion, since your're trying to simulate a spherical light, spherical lights behave like a point light centered at the center of the sphere. so when you calculate the distance for attenuation, just use the center of the sphere, and not the random point. I'm not sure how much it matters. One thing is certian though, it will take longer to converge if the attenuation is different for every sample. so id suggest it on that fact alone.

Share this post


Link to post
Share on other sites
Most probably you already got the right answer, but anyway:
Quote:

This the method you describe a distribution ray tracing alorithm, or is it the one which produces those visible and discrete bands?

this only depends on how you choose the lighting samples: if you choose them splitting the light surface like a grid, you get bands. If you choose them randomly you get a blurred shadow, but you can have problems if the random coordinates are not well distributed (i.e. all them cre gruped in a small portion of the light surface). A better solution is to use a grid to split the light, and then randomly jitter the samples in order to get a good but not too regular distribution.
Quote:

How do I do this? I only have a point remember? How should I turn my point into a plane?

Simply create another type of lights. If yopu use a oo design, then you can derive both pointlights and arealights from a common 'light' class. Using multiple pointlights to get an area light is not a good idea (IMHO), even if the concept is similar.
One way to model an area light is to store the four vertices of the surface, with a position and a direction (to transform the vertices from local to world spaces). Or you can simply store the upper-left corner and then store the width and the height. This is up to you. If you can get access to the tutorials on flipcode then look at the chapter about soft shadows.

Share this post


Link to post
Share on other sites
Quote:
Original post by timw
it's more of a sphere light, but it's not a sphere either. it's biased, your randomness isn't


How 'bout I just Normalize my random "unit cube vector"???

Share this post


Link to post
Share on other sites
ya it makes more sense to have the vector to be the same length as the radius of your sphere. but you can't sample a sphere uniformly with that unit cube vector. as I said, you'll end up having more vectors in the directions of the corners of the cube then other directions. to uniformly sample a sphere, every direction has to have an equal chance of being picked. I'd draw you a pic if I had my computer, but I dont unfortunately.

//edit
if it doestn' make sense, you can show yourself the bias with this experiment.

1)
sample 1000 or so points using your method, then normalize all of them. now you have 1000 points on the unit sphere. plot those points with open gl GL_POINTS.
2)
sample 1000 points using the rejection sampling method, normalize them and plot them.

you'll see that 1 is not uniform, you'll see the bias toward the edges.
2 would look reasonably random.

[Edited by - timw on October 23, 2005 11:31:57 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this