I am writing a forward monte carlo path tracer with the added feature of specular refractions for transparent materials, I have managed to come this far but something is not quite right and I am having a hard time figuring out what. I have looked at the following guide and I make sure to invert the normal, switch the refractive indices etc depending on whether we are entering a material with a lower or higher refractive index. It seems like the reflection part when I get total internal reflection works great but the problem is the refraction direction I think but I don't see the problem with it. I am using a russian roulette approach to choose either reflection or refraction and the variable p is the pdf.
This is what I achieve right now with Transparency = 1.0f, refractive index = 1.5f:
My code for calculating the sample direction looks like this:
vec3 BlinnPhongGlass::sample_wi(vec3 & wi, const vec3 & wo, vec3 & n, float & p) {
vec3 tangent = normalize(perpendicular(n));
vec3 bitangent = normalize(cross(tangent, n));
float phi = 2.0f * M_PI * randf();
float cos_theta = pow(randf(), 1.0f / (shininess + 1));
float sin_theta = sqrt(max(0.0f, 1.0f - cos_theta * cos_theta));
vec3 wh = normalize(sin_theta * cos(phi) * tangent + sin_theta * sin(phi) * bitangent + cos_theta * n);
if (randf() < 0.5f) {
// Sample a direction based on the Microfacet brdf
// PDF for wh
p = (shininess + 1.0f) * pow(dot(n, wh), shininess) / (2.0f * M_PI);
// Reflect wo around wh to get wi
wi = -(wo - (2.0f * dot(wo, wh) * wh));
// PDF for wi
p = p / (4.0f * dot(wo, wh));
p = p * 0.5f;
return reflection_brdf(wi, wo, n);
} else {
p = 1.0f;
p = p * 0.5f;
float no = 1.5f; // Glass
float ni = 1.0f; // Air
bool outside = dot(wo, n) < 0;
float etaI = outside ? ni : no; // Index of medium we are currently in
float etaT = outside ? no : ni; // Index of medium we are about to enter
float eta = etaI / etaT;
// Compute wi using Snell's law
float cosThetaI = outside ? -dot(n, wo) : dot(n, wo);
float sin2ThetaI = max(0.0f, 1.0f - cosThetaI * cosThetaI);
float sin2ThetaT = eta * eta * sin2ThetaI;
// Total internal reflection -> reflect instead
if (sin2ThetaT >= 1) {
wi = -(wo - (2.0f * dot(wo, wh) * wh));
return reflection_brdf(wi, wo, n);
}
n = outside ? n : -n; // Invert normal when exiting
float cosThetaT = sqrt(1.0f - sin2ThetaT);
wi = normalize(eta * -wo + (eta * cosThetaI - cosThetaT) * n);
vec3 brdf = vec3(transparency) / dot(wo, n);
return brdf;
// We need to attenuate the refracted brdf with (1 - F)
//const float fresnel_term = R0 + (1.0f - R0) * pow(1.0f - abs(dot(wh, wi)), 5.0f);
//return (1 - fresnel_term) * brdf;
}
}
if (dot(n, vec3(0, 0, 1)) > 0.99) print("whatever")
for 1) and you just change tovec3(0, 0, 1)
for a point at the corner. You can place a breakpoint on theprint
and step through the processed values for that points on the sphere. You should be able to compute by hand what should they look like, hopefully they will reveal the error. $\endgroup$