1
votes

l need help figuring out how to calculate (Lambertian) diffuse reflectance - that is when a light ray hits a surface and then bounces off in a random direction. So if I have a light vector L originating from the light source, a 3D point X and a normal vector N at point X, how do I calculate a randomly reflected ray?

In one book I am reading on the subject they say to use this equation:

Wd = (theta, phi) = cos-1(sqrt(rand1), 2*pi*rand2) where Wd is the reflected ray, and rand1 and rand2 are random numbers in the range [0,1].

This didn't work for me. I used that equation and then converted the spherical coordinates to cartesian coordinates but this made it so the rays were always being reflected in the same few directions, regardless of the orientation of the normal.

Any help is appreciated!

2

2 Answers

1
votes

Choosing theta to be arccos(sqrt(rand1)) would not give you randomly distributed points on the surface of a unit sphere since the distribution of arccos(sqrt(rand1)) is nowhere uniform as it should be.

Given two random variates u and v, uniformly sampled from (0, 1), in spherical coordinates a random point would be:

theta = 2*pi * u   <--- note, no arccos here
phi = arccos(2*v - 1)

Since it's a reflection from a surface, you only have to sample half of the spherical surface, so theta would rather be theta = pi * u.

Since the angle of reflection is random, it doesn't really matter where the light source is located, i.e. the result does not depend on the light vector L. You only have to chose the spherical coordinates so that the sampled hemisphere is on the outer side of the surface.

Another option:

  1. Draw a random point on the surface of the entire unit sphere (i.e. theta = 2 * pi * u)
  2. Convert to Cartesian point vector coordinates
  3. Take the dot product of the normal and the point vector. If it's negative, repeat from step 1.

This would guarantee that you only sample from the hemisphere that faces the outer side of the surface.

You could also skip the spherical coordinates altogether and directly compute the Cartesian coordinates:

x = sqrt(1 - u^2) * cos(theta)
y = sqrt(1 - u^2) * sin(theta)
z = u

Here u is uniformly sampled from [-1, 1] (e.g. u = 2*v - 1 where v is uniformly sampled in [0, 1]) and theta is uniformly sampled from [0, 2*pi).

There is another method that does not use trigonometric functions at all (taken from here):

Take x1 and x2 uniformly sampled from [-1, 1]. If x1^2 + x2^2 < 1, then (if not, repeat the sampling):

x = 2 * x1 * sqrt(1 - x1^2 - x2^2)
y = 2 * x2 * sqrt(1 - x1^2 - x2^2)
z = 1 - 2 * (x1^2 + x2^2)
0
votes

I'm also looking at Lambertian reflection, and the code you provide does work, it's simply referenced from the normal vector of the surface. To get the correct vector you should:

  1. rotate the surface normal vector N about (any) axis in the plane of the surface by the angle phi
  2. rotate that resulting vector about the surface normal vector by the angle theta

Code for rotating vectors about arbitrary axes can be generated from here: http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/