Given the BSDF function and the Normal
vector of the intersection point in world space, how can I generate a new direction vector wi
that is valid? Does the method for generating valid wi
s change based on the BSDF?
Here's an example of what I'm thinking to do for ideal diffuse material the BSDF: I generate a new direction vector wi
as points on a unit hemisphere as follow and then compute the dot
product of the produced vector with the Normal
vector. If the dot
product result is positive the direction vector wi
is valid. Otherwise I negate wi
as suggested here.
Here's how I get a random wi
:
float theta = 2 * M_PI * uniform01(generator);
float phi = acos(uniform01(generator));
float x = sin(phi) * cos(theta);
float y = sin(phi) * sin(theta);
float z = cos(phi);
Vector3f wi(x, y, z);
if (dot(wi, Normal) > 0){
return wi;
}
else{
return -wi;
}
However, this doesn't seem to be the right approach based on a conversation I had with someone recently. Apparently the new direction vector produced this way is somehow not in the right space (not sure whether it was world or object space) and could only work if my material is ideal diffuse. So I will have to apply some transformations in order to be able to get the right wi
. Is this correct? If so, can someone provide a solution that includes doing such transformation? Also, is there a general way to ensure all of my produced wi
s are valid with respect to the BSDF (not just ideal diffuse)?
You are generating your wi
in tangent space, with z
pointing along the normal. It is neither world nor object space, and you will have to transform into world space or do all your calculations in tangent space (or shading space, they're both the same).
What you should be doing, as it will make your life much easier when doing other calculations, is to transform your wo
to tangent space, and do all calculations in it. Over here, you would choose z
to be your normal, and generate x
and y
vectors orthogonal to it.
A function for generating the coordinate system like this would be:
void GenerateCoordinateSystem(const Vector& normalized, Vector& outFirst, Vector& outSecond)
{
if (std::abs(normalized.x) > std::abs(normalized.y))
{
outFirst = Vector(-normalized.z, 0, normalized.x) /
std::sqrt(normalized.x * normalized.x + normalized.z * normalized.z);
}
else
{
outFirst = Vector(0, normalized.z, -normalized.y) /
std::sqrt(normalized.z * normalized.z + normalized.y * normalized.y);
}
outSecond = Cross(normalized, outFirst);
}
Where normalized
is the normal (z
vector) at the point, and outFirst
and outSecond
are your x
and y
vectors respectively.
Now that you have your tangent space vectors, you transform into them by (wo
is in object space):
Vector x, y;
GenerateCoordinateSystem(normal, x, y);
Vector tangentWo = Vector(Dot(wo, x), Dot(wo, y), Dot(wo, normal));
You would then generate your wi
as you do above.
Then, to get wi
in object space, you would:
Vector objWi = wi.X * x + wi.Y * y + wi.Z * normal;
If you want them in world space, you would obviously have to multiply them by the object's transformation matrix.
Uniform hemisphere sampling does ensure that your wi
is valid for any BSDF, however, you have to ensure that the pdf
for the BSDF takes into account the distribution.