Photon Mapping : Photon Emission

Started by
15 comments, last by cwhite 18 years, 10 months ago
By reading Jensen's book, I found out I was doing something wrong in my renderers. Whenever I was emitting photons off of a flat surface, or diffusely reflecting them, I was simply picking a direction completely at random in the unit sphere, and inverting it if it was opposed to the surface normal. Jensen says that instead, the direction should have a probability that is inversely proportional to the cosine between the direction and the surface normal. He also proposed a function to obtain two random polar angles for the reflected direction from two random values... What I am looking for is an efficient way to get a random unit vector for this direction. His method gives polar angles, and it does not take into account the surface normal. It only gives you rotational angles from this normal, which seems quite inefficient to me (do you use quaternions to rotate the surface normal?). Surely there must be a more practical way of obtaining the direction vector. If anybody is willing to share the trick he/she uses, that is what I am looking for. EDIT: Second question. It seems Jensen does not scale the photon power by the dot product when doing the irradiance estimate. I typically multiply the photon power by the dot product of the surface normal and the photon's incoming direction (away from the surface)... Is this probabilistic emission scheme just another way to do that? If so, it seems weird to me that its being done at the emitting surface, and not at the receiving surface. [Edited by - Max_Payne on June 6, 2005 11:53:01 AM]

Looking for a serious game project?
www.xgameproject.com
Advertisement
Question 1:

I don't know if this is the trick Jensen uses, but if you find a uniformly distributed point on the base of the unit hemisphere and project that point upwards onto the surface of the hemisphere, that point will be cosine weighted.

Question 2:

I'm not sure if I've interpreted your question correctly, so I'll give you two answers corresponding to two interpretations. Interpretation 1: you are multiplying by the dot product to take into account foreshortening, the geometric property that dictates that less light falls onto a surface when the angle between the normal of that surface and the direction of the light ray increases. In this case, you don't multiply by the dot product with a photon mapper because foreshortening is already taken into account by the fact that photons will fall onto a surface less densely if the angle between that surface and the direction of the light is large.

Interpretation 2: you are multiplying by the dot product because the dot product is your diffuse BRDF. In this case, you're confusing irradiance with radiance. The code in the back of Jensen's book is meant to calculate the irradiance, which is the incoming light at that point. I think you're more concerned with radiance, which is the reflected radiance back in a given direction. If you are calculating radiance, then you change the summation loop of Jensen's irradiance function to multiply each photon power by the BRDF. If you are using the dot product as a diffuse BRDF, then you multiply by the dot product. Both your code and his code are correct, they're just meant to do different things.
Quote:Question 1:

I don't know if this is the trick Jensen uses, but if you find a uniformly distributed point on the base of the unit hemisphere and project that point upwards onto the surface of the hemisphere, that point will be cosine weighted.


Thats interesting to know. I'd still like some suggestions on how to actually do it though :)

Quote:Question 2:

I'm not sure if I've interpreted your question correctly, so I'll give you two answers corresponding to two interpretations. Interpretation 1: you are multiplying by the dot product to take into account foreshortening, the geometric property that dictates that less light falls onto a surface when the angle between the normal of that surface and the direction of the light ray increases. In this case, you don't multiply by the dot product with a photon mapper because foreshortening is already taken into account by the fact that photons will fall onto a surface less densely if the angle between that surface and the direction of the light is large.


I believe that answers my question.

Quote:Interpretation 2: you are multiplying by the dot product because the dot product is your diffuse BRDF. In this case, you're confusing irradiance with radiance. The code in the back of Jensen's book is meant to calculate the irradiance, which is the incoming light at that point. I think you're more concerned with radiance, which is the reflected radiance back in a given direction. If you are calculating radiance, then you change the summation loop of Jensen's irradiance function to multiply each photon power by the BRDF. If you are using the dot product as a diffuse BRDF, then you multiply by the dot product. Both your code and his code are correct, they're just meant to do different things.


I'm not sure why you would want a BRDF like that for a diffuse surface though, any particular reason?

Looking for a serious game project?
www.xgameproject.com
Quote:Original post by Max_Payne

Quote:Interpretation 2: you are multiplying by the dot product because the dot product is your diffuse BRDF. In this case, you're confusing irradiance with radiance. The code in the back of Jensen's book is meant to calculate the irradiance, which is the incoming light at that point. I think you're more concerned with radiance, which is the reflected radiance back in a given direction. If you are calculating radiance, then you change the summation loop of Jensen's irradiance function to multiply each photon power by the BRDF. If you are using the dot product as a diffuse BRDF, then you multiply by the dot product. Both your code and his code are correct, they're just meant to do different things.


I'm not sure why you would want a BRDF like that for a diffuse surface though, any particular reason?


You wouldn't, that reply was written in haste as i left the office. I was probably confusing myself with specular, which is based on a dot product between the reflected light vector and the eye vector. The point is, though, that if you want the radiance, you multiply the power of each photon by whatever your BRDF is.

Quote:
Quote:
Question 1:

I don't know if this is the trick Jensen uses, but if you find a uniformly distributed point on the base of the unit hemisphere and project that point upwards onto the surface of the hemisphere, that point will be cosine weighted.



Thats interesting to know. I'd still like some suggestions on how to actually do it though :)


It's easiest to do your calculations in the local coordinate space of the normal and then apply a transformation that moves your new vector back into world space. You can find a uniformly distributed point on a unit circle with the following sampling method:

U1 := rand [0, 1]
U2 := rand [0, 1]

Theta := U1 * 2_PI
Radius := sqrt(U2)

You can then map these to cartesian coordinates and plug them into the equation of the sphere to get the projected point onto the surface of the unit hemisphere. You now have a direction pulled from a cosine weighted distribution in normal space, you just need to transform it to world space.
Well, thanks for your clarifications, they really were helpful ;)

I think I found a decent solution based on what you proposed for the ray emission with cosine probability. I will have two random values to obtain a random point on the unit sphere (angle and center-distance). I will then compute two vectors u and v orthogonal to the normal, and make them orthogonal to one another. I will then normalize them. They will form an orthonormal basis with the normal.

From this point, I will use the x = r cos a and y = r sin a to compute the coordinate of the point on the unit circle. I will then "project" it on the unit sphere by calculating its height z from the x and y coordinates... My random unit direction vector will then be xu + yv + zn.

Its a good bit of computations, three sqrts and a few divides... But it should only be used in the photon tracing phase, which is usually rather fast.

Looking for a serious game project?
www.xgameproject.com
If you wanted to avoid a square root and some trig calls, you could obtain x and y values directly by sampling a square on [-1,1] and rejecting values outside the unit circle. Rejection sampling is not a very good sampling method, but it might be faster. You're basically exchanging wasted work for less square roots and trig calls.
Quote:Original post by cwhite
If you wanted to avoid a square root and some trig calls, you could obtain x and y values directly by sampling a square on [-1,1] and rejecting values outside the unit circle. Rejection sampling is not a very good sampling method, but it might be faster. You're basically exchanging wasted work for less square roots and trig calls.


I suppose thats a valid approach considering the probability that it lies within the circle is 78.5%. I believe it only eliminates the cos and sin calls, however. I still need to obtain an orthonormal basis. I would be curious to know how fast sinf, cosf and rand actually are.

Looking for a serious game project?
www.xgameproject.com
sinf and cosf are not all that slow, especially if you use them sparingly; however, rand() is almost always a very inefficient and ugly function. The Mersenne Twister is often faster (when implemented well) and will give much better sampling results.

In the past I've just used a unit-hemisphere sampling and done some ugly math to bias the vector towards the normal (which is essentially a cosine weighting, give or take). It works fairly fast but it's ugly as sin and makes for some poor results in scenes with lots of perpendicular, flat surfaces. I've considered changing the weighting method and/or outright replacing it with a proper sampling scheme, but I haven't had time to mess with it lately. If you're interested I can dig up the code for that for you to play with.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

I will begin by implementing something that works well for this using polar coordinates tonight... And I will then implement my photon map. Speaking of which. The sample code from Jensen is a bit discouraging.

In order to create a balanced photon map, you constantly need to find the median along multiple dimensions. I suppose the most efficient way to do that is using a partition-based selection algorithm to select the n/2th element. However, can the photon map heap easily be built in-place, without resorting to a temporary tree structure (very bad for memory fragmentation)? If so, how.

And for the irradiance estimate, what exactly is Jensen doing to build his heap? I looked at it briefly and it didn't seem obvious at all from the code.

Looking for a serious game project?
www.xgameproject.com
The median can be found in linear time using an algorithm called quickselect. It's much like quicksort, except you only recurse on the half of the list where you suspect to find the median. The catch, however, lies in when the list has an even number of elements in it. In this case, there are technically two medians, and you have to make sure you choose the correct one, or your tree will have gaps in it. I can't remember the exact criteria for choosing the correct median off the top of my head, nor was I able to figure it out on a scratch piece of paper, but you might be able to figure it out from Jensen's code.

Jensen's heap is quite confusing and I don't at all understand it myself. However, I don't think it would be too hard to implement your own heap. One efficiency suggestion is don't create your heap until you've already found at least the number of photons you're looking for in your original search radius. It's faster to create a heap out of n unsorted photons than it is to update your heap as you go along.

It may be useful to look at another photon mapper besides Jensen's. It'll give you a good idea of what sorts of different implementation options there are. Jensen gives one approach, but there are obviously many others. I would suggest looking at the photon mapper in PBRT (pbrt.org). It has a simple photon mapper and a more complex photon mapper that does some interesting math to decrease variance and increase the speed of the final gathering. Looking at both of them might help you out a little bit. I won't, however, show you my own photon mapper because it's so full of hacks that it would just give you bad ideas [smile].

This topic is closed to new replies.

Advertisement