ustring sample(const Vec3 &Ng, const Vec3 &omega_out, const Vec3 &domega_out_dx, const Vec3 &domega_out_dy, float randu, float randv, Vec3 &omega_in, Vec3 &domega_in_dx, Vec3 &domega_in_dy, float &pdf, Color3 &eval) const { // we are viewing the surface from the right side - send a ray out with cosine // distribution over the hemisphere sample_cos_hemisphere(m_N, omega_out, randu, randv, omega_in, pdf); if (Ng.dot(omega_in) > 0) { // TODO: account for sheen when sampling float cosNO = m_N.dot(omega_out); float sinNO2 = 1 - cosNO * cosNO; float westin = sinNO2 > 0 ? powf(sinNO2, 0.5f * m_edginess) * pdf : 0; eval.setValue(westin, westin, westin); // TODO: find a better approximation for the diffuse bounce domega_in_dx = (2 * m_N.dot(domega_out_dx)) * m_N - domega_out_dx; domega_in_dy = (2 * m_N.dot(domega_out_dy)) * m_N - domega_out_dy; domega_in_dx *= 125; domega_in_dy *= 125; } else { pdf = 0; } return Labels::REFLECT; }
ustring sample (const Vec3 &Ng, const Vec3 &omega_out, const Vec3 &domega_out_dx, const Vec3 &domega_out_dy, float randu, float randv, Vec3 &omega_in, Vec3 &domega_in_dx, Vec3 &domega_in_dy, float &pdf, Color3 &eval) const { Vec3 R, dRdx, dRdy; Vec3 T, dTdx, dTdy; bool inside; fresnel_dielectric(m_eta, m_N, omega_out, domega_out_dx, domega_out_dy, R, dRdx, dRdy, T, dTdx, dTdy, inside); if (!inside) { pdf = 1; eval.setValue(1.0f, 1.0f, 1.0f); omega_in = T; domega_in_dx = dTdx; domega_in_dy = dTdy; } return Labels::TRANSMIT; }
ustring sample (const Vec3 &Ng, const Vec3 &omega_out, const Vec3 &domega_out_dx, const Vec3 &domega_out_dy, float randu, float randv, Vec3 &omega_in, Vec3 &domega_in_dx, Vec3 &domega_in_dy, float &pdf, Color3 &eval) const { // we are viewing the surface from the right side - send a ray out with cosine // distribution over the hemisphere sample_cos_hemisphere (m_N, omega_out, randu, randv, omega_in, pdf); if (Ng.dot(omega_in) > 0) { eval.setValue(pdf, pdf, pdf); // TODO: find a better approximation for the diffuse bounce domega_in_dx = (2 * m_N.dot(domega_out_dx)) * m_N - domega_out_dx; domega_in_dy = (2 * m_N.dot(domega_out_dy)) * m_N - domega_out_dy; domega_in_dx *= 125; domega_in_dy *= 125; } else pdf = 0; return Labels::REFLECT; }
ustring sample(const Vec3 &Ng, const Vec3 &omega_out, const Vec3 &domega_out_dx, const Vec3 &domega_out_dy, float randu, float randv, Vec3 &omega_in, Vec3 &domega_in_dx, Vec3 &domega_in_dy, float &pdf, Color3 &eval) const { float cosNO = m_N.dot(omega_out); if (cosNO > 0) { domega_in_dx = domega_out_dx; domega_in_dy = domega_out_dy; Vec3 T, B; make_orthonormals(omega_out, T, B); float phi = 2 * (float) M_PI * randu; float cosTheta = powf(randv, 1 / (m_invroughness + 1)); float sinTheta2 = 1 - cosTheta * cosTheta; float sinTheta = sinTheta2 > 0 ? sqrtf(sinTheta2) : 0; omega_in = (cosf(phi) * sinTheta) * T + (sinf(phi) * sinTheta) * B + (cosTheta) * omega_out; if (Ng.dot(omega_in) > 0) { // common terms for pdf and eval float cosNI = m_N.dot(omega_in); // make sure the direction we chose is still in the right hemisphere if (cosNI > 0) { pdf = 0.5f * (float) M_1_PI * powf(cosTheta, m_invroughness); pdf = (m_invroughness + 1) * pdf; eval.setValue(pdf, pdf, pdf); // Since there is some blur to this reflection, make the // derivatives a bit bigger. In theory this varies with the // exponent but the exact relationship is complex and // requires more ops than are practical. domega_in_dx *= 10; domega_in_dy *= 10; } } } return Labels::REFLECT; }
ustring sample(const Vec3 &Ng, const Vec3 &omega_out, const Vec3 &domega_out_dx, const Vec3 &domega_out_dy, float randu, float randv, Vec3 &omega_in, Vec3 &domega_in_dx, Vec3 &domega_in_dy, float &pdf, Color3 &eval) const { float cosNO = m_N.dot(omega_out); if (cosNO > 0) { Vec3 X, Y, Z = m_N; make_orthonormals(Z, X, Y); // generate a random microfacet normal m // eq. 35,36: // we take advantage of cos(atan(x)) == 1/sqrt(1+x^2) // and sin(atan(x)) == x/sqrt(1+x^2) float alpha2 = m_ab * m_ab; float tanThetaM = sqrtf(-alpha2 * logf(1 - randu)); float cosThetaM = 1 / sqrtf(1 + tanThetaM * tanThetaM); float sinThetaM = cosThetaM * tanThetaM; float phiM = 2 * float(M_PI) * randv; Vec3 m = (cosf(phiM) * sinThetaM) * X + (sinf(phiM) * sinThetaM) * Y + cosThetaM * Z; if (Refractive == 0) { float cosMO = m.dot(omega_out); if (cosMO > 0) { // eq. 39 - compute actual reflected direction omega_in = 2 * cosMO * m - omega_out; if (Ng.dot(omega_in) > 0) { // microfacet normal is visible to this ray // eq. 25 float cosThetaM2 = cosThetaM * cosThetaM; float tanThetaM2 = tanThetaM * tanThetaM; float cosThetaM4 = cosThetaM2 * cosThetaM2; float D = expf(-tanThetaM2 / alpha2) / (float(M_PI) * alpha2 * cosThetaM4); // eq. 24 float pm = D * cosThetaM; // convert into pdf of the sampled direction // eq. 38 - but see also: // eq. 17 in http://www.graphics.cornell.edu/~bjw/wardnotes.pdf pdf = pm * 0.25f / cosMO; // Eval BRDF*cosNI float cosNI = m_N.dot(omega_in); // eq. 26, 27: now calculate G1(i,m) and G1(o,m) float ao = 1 / (m_ab * sqrtf((1 - cosNO * cosNO) / (cosNO * cosNO))); float ai = 1 / (m_ab * sqrtf((1 - cosNI * cosNI) / (cosNI * cosNI))); float G1o = ao < 1.6f ? (3.535f * ao + 2.181f * ao * ao) / (1 + 2.276f * ao + 2.577f * ao * ao) : 1.0f; float G1i = ai < 1.6f ? (3.535f * ai + 2.181f * ai * ai) / (1 + 2.276f * ai + 2.577f * ai * ai) : 1.0f; float G = G1o * G1i; // eq. 20: (F*G*D)/(4*in*on) float out = (G * D) * 0.25f / cosNO; eval.setValue(out, out, out); domega_in_dx = (2 * m.dot(domega_out_dx)) * m - domega_out_dx; domega_in_dy = (2 * m.dot(domega_out_dy)) * m - domega_out_dy; /* disabled for now - gives texture filtering problems */ #if 0 // Since there is some blur to this reflection, make the // derivatives a bit bigger. In theory this varies with the // roughness but the exact relationship is complex and // requires more ops than are practical. domega_in_dx *= 10; domega_in_dy *= 10; #endif } } } else { // CAUTION: the i and o variables are inverted relative to the paper // eq. 39 - compute actual refractive direction Vec3 R, dRdx, dRdy; Vec3 T, dTdx, dTdy; bool inside; fresnel_dielectric(m_eta, m, omega_out, domega_out_dx, domega_out_dy, R, dRdx, dRdy, T, dTdx, dTdy, inside); if (!inside) { omega_in = T; domega_in_dx = dTdx; domega_in_dy = dTdy; // eq. 33 float cosThetaM2 = cosThetaM * cosThetaM; float tanThetaM2 = tanThetaM * tanThetaM; float cosThetaM4 = cosThetaM2 * cosThetaM2; float D = expf(-tanThetaM2 / alpha2) / (float(M_PI) * alpha2 * cosThetaM4); // eq. 24 float pm = D * cosThetaM; // eval BRDF*cosNI float cosNI = m_N.dot(omega_in); // eq. 26, 27: now calculate G1(i,m) and G1(o,m) float ao = 1 / (m_ab * sqrtf((1 - cosNO * cosNO) / (cosNO * cosNO))); float ai = 1 / (m_ab * sqrtf((1 - cosNI * cosNI) / (cosNI * cosNI))); float G1o = ao < 1.6f ? (3.535f * ao + 2.181f * ao * ao) / (1 + 2.276f * ao + 2.577f * ao * ao) : 1.0f; float G1i = ai < 1.6f ? (3.535f * ai + 2.181f * ai * ai) / (1 + 2.276f * ai + 2.577f * ai * ai) : 1.0f; float G = G1o * G1i; // eq. 21 float cosHI = m.dot(omega_in); float cosHO = m.dot(omega_out); float Ht2 = m_eta * cosHI + cosHO; Ht2 *= Ht2; float out = (fabsf(cosHI * cosHO) * (m_eta * m_eta) * (G * D)) / (cosNO * Ht2); // eq. 38 and eq. 17 pdf = pm * (m_eta * m_eta) * fabsf(cosHI) / Ht2; eval.setValue(out, out, out); /* disabled for now - gives texture filtering problems */ #if 0 // Since there is some blur to this refraction, make the // derivatives a bit bigger. In theory this varies with the // roughness but the exact relationship is complex and // requires more ops than are practical. domega_in_dx *= 10; domega_in_dy *= 10; #endif } } } return Refractive ? Labels::TRANSMIT : Labels::REFLECT; }