Sign in to follow this  

Path Tracing Weights

This topic is 1953 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi,

I'm trying to write the simplest possible (no importance sampling, etc.) path tracer from [i]scratch[/i]. It will support only diffuse surfaces and area light sources.

A random path should be generated from the camera. Each ray is chosen uniformly from a hemisphere. The path is a maximum length (I realize this introduces bias, but it's a hard constraint. This maximum length can be very large, so the bias can be small).

How can I weight the each step of the path so that this works?

Thanks,
Ian

Share this post


Link to post
Share on other sites
I've made some progress. The following algorithm works:
-For each point:
....-Add the emissive color
....-Continue bouncing the ray with probability proportional to the diffuse color. The bounced ray is generated from a cosine-weighted distribution. You can either weight the ray based on the color's overall intensity, xor consider each channel separately).

This [url="http://pastebin.com/kcyY5xBJ"]self-explanatory OpenCL code[/url] I wrote implements both algorithms. My question now is, given that the algorithm works, how can I change it to use weighting? Most simply, if I change the generate_path(...) function to generate [i]unweighted[/i] rays over a hemisphere, how would I weight each ray?

Thanks,
Ian Edited by Geometrian

Share this post


Link to post
Share on other sites
I have made some more progress. Using unweighted paths, versus cosine weighted, one must correct by a factor of two. So, you sample rays from a random hemisphere. Weight each ray by 2.0*cos(theta) where theta is the angle of outgoing ray from the normal. One way I derived this by comparing the integrals of a standard hemisphere (integrate f(theta)=1 to get 2*pi, which is surface area) to that of a cosine weighted hemisphere (integrate f(theta)=cos(theta) to get pi). It took a while to prove this was the right answer, because a stupid bug prevented it from working.

Naturally, hemispherical sampling makes the convergence worse, because it no longer does importance sampling. However, I think this at least shows that my methodology is sound, and it's an important precursor to eventually making bidirectional path tracing. The below picture shows importance sampling on left versus hemispherical sampling with 2.0*cos(theta) weighting on right. The image was rendered with 2004 samples. The key thing to note is that the illumination is the same, even if the hemispherical sampling has higher variance.

Assuming that the ray scatters, it must go on the hemisphere. I speculate that, on average, the [i]weight[/i] of a scatter in any direction on the hemisphere must be 1.0 (that is, [i]the integral of the weights over the hemisphere must be 2*pi[/i]). For importance sampling, all the weights are one, but the probability of scattering away from the normal is lower. The integral is 2*pi. For cosine weighted, all the weights are cos(theta), but they need to be scaled by 2 to that the integral is 2*pi. This provides a nice way to make any BRDF--without importance sampling, simply weight each sample by the BRDF(theta), and then scale to ensure that the integral over the hemisphere is 2*pi.

[img]http://img109.imageshack.us/img109/8839/2004importancevsweighte.png[/img]

Thanks,
Ian Edited by Geometrian

Share this post


Link to post
Share on other sites
. . . I guess this is turning into a how-to log for making a path tracer.

More progress: you can collapse all that Russian roulette into a simple weighting scheme. This means that, instead of simulating a bunch of rays conditionally stopping, or whatnot, you simulate a single ray, but weight contributions further along geometrically with reflectance. I feel like I tried this before, but for some reason it didn't work.

This self-explanatory OpenCL code works, though. Note that this uses an importance sampled path--one could generate the same result by sampling uniformly from a hemisphere, but weighting each sample by an extra 2.0*cos(theta), as I discovered in my last post.[source lang="c"]HitRecord hits_camera[N];
int max_depth = generate_path(ray,N, scene,rvec, hits_camera,NULL,true); //Generate importance sampled path
float3 mask = (float3)(1.0f,1.0f,1.0f);
for (int i=0;i<N;++i) {
const HitRecord* hit = hits_camera + i;
accumulated += mask * hit->material->color_emission;
float3 f = hit->material->color_diffuse;
mask *= f;
}[/source]On left, standard path tracing with Russian roulette ray termination. On right, weighting (the above code). Notice that the above code converges more quickly. Intuitively, this is because the weighting scheme needs only one ray to sample all path depths, whereas the Russian roulette needs at least as many rays as there are depths--and the variance for doing this is higher; for weighting, we use the expected value of the reflectance function.

[img]http://img576.imageshack.us/img576/265/1001rrvsweighted.png[/img]

Thanks,
Ian Edited by Geometrian

Share this post


Link to post
Share on other sites
Hmm but the point of RR is unbiased results... whereas your weighting scheme (btw that doesn't look like a weighting scheme... just a simplified normal lighting bounce calculation) stops at N bounces... what about N+1 bounces?

[quote]Russian roulette needs at least as many rays as there are depths[/quote]
This statement doesn't quite make sense. Edited by jameszhao00

Share this post


Link to post
Share on other sites
By weighting scheme, I mean I am weighting the ray so as to [i]simulate[/i] a number of RR rays. The ray goes as far as possible (it can't go forever; I'm on the GPU), and then it's weighted. The result of shooting one ray, weighting each step is thus the same as shooting many RR rays and not weighting any (notice I get the same result either way, but shooting only one ray is faster, so it converges better).

Share this post


Link to post
Share on other sites
By rays, do you mean a path? So are you generating only 1 path per pixel? (aka 1 sample per pixel?)

Also, a key point of RR is
[code]
float3 color = blah blah blah
r = 0.3 (or some other ratio)
if(rand() < r)
color /= r <---- key
else
terminate

next bounce...
[/code]

Unless I'm missing some context, your weighting scheme is what everyone does, as part of the standard lighting calculating. Edited by jameszhao00

Share this post


Link to post
Share on other sites

This topic is 1953 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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