Path Tracing Weights

Started by
5 comments, last by jameszhao00 11 years, 8 months ago
Hi,

I'm trying to write the simplest possible (no importance sampling, etc.) path tracer from scratch. 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

[size="1"]And a Unix user said rm -rf *.* and all was null and void...|There's no place like 127.0.0.1|The Application "Programmer" has unexpectedly quit. An error of type A.M. has occurred.
[size="2"]

Advertisement
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 self-explanatory OpenCL code 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 unweighted rays over a hemisphere, how would I weight each ray?

Thanks,
Ian

[size="1"]And a Unix user said rm -rf *.* and all was null and void...|There's no place like 127.0.0.1|The Application "Programmer" has unexpectedly quit. An error of type A.M. has occurred.
[size="2"]

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 weight of a scatter in any direction on the hemisphere must be 1.0 (that is, the integral of the weights over the hemisphere must be 2*pi). 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.

2004importancevsweighte.png

Thanks,
Ian

[size="1"]And a Unix user said rm -rf *.* and all was null and void...|There's no place like 127.0.0.1|The Application "Programmer" has unexpectedly quit. An error of type A.M. has occurred.
[size="2"]

. . . 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.

1001rrvsweighted.png

Thanks,
Ian

[size="1"]And a Unix user said rm -rf *.* and all was null and void...|There's no place like 127.0.0.1|The Application "Programmer" has unexpectedly quit. An error of type A.M. has occurred.
[size="2"]

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?

Russian roulette needs at least as many rays as there are depths[/quote]
This statement doesn't quite make sense.
By weighting scheme, I mean I am weighting the ray so as to simulate 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).

[size="1"]And a Unix user said rm -rf *.* and all was null and void...|There's no place like 127.0.0.1|The Application "Programmer" has unexpectedly quit. An error of type A.M. has occurred.
[size="2"]

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

float3 color = blah blah blah
r = 0.3 (or some other ratio)
if(rand() < r)
color /= r <---- key
else
terminate

next bounce...


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

This topic is closed to new replies.

Advertisement