diff --git a/internal/CoreRef.cpp b/internal/CoreRef.cpp index 1edbaa6a..daebcb1f 100644 --- a/internal/CoreRef.cpp +++ b/internal/CoreRef.cpp @@ -1164,67 +1164,6 @@ force_inline float angle_between(const fvec4 &v1, const fvec4 &v2) { } } -// "Stratified Sampling of Spherical Triangles" https://www.graphics.cornell.edu/pubs/1995/Arv95c.pdf -// Based on https://www.shadertoy.com/view/4tGGzd -float SampleSphericalTriangle(const fvec4 &P, const fvec4 &p1, const fvec4 &p2, const fvec4 &p3, const fvec2 Xi, - fvec4 *out_dir) { - // Setup spherical triangle - const fvec4 A = normalize(p1 - P), B = normalize(p2 - P), C = normalize(p3 - P); - - // calculate internal angles of spherical triangle: alpha, beta and gamma - const fvec4 BA = orthogonalize(A, B - A); - const fvec4 CA = orthogonalize(A, C - A); - const fvec4 AB = orthogonalize(B, A - B); - const fvec4 CB = orthogonalize(B, C - B); - const fvec4 BC = orthogonalize(C, B - C); - const fvec4 AC = orthogonalize(C, A - C); - - const float alpha = angle_between(BA, CA); - const float beta = angle_between(AB, CB); - const float gamma = angle_between(BC, AC); - - const float area = alpha + beta + gamma - PI; - if (area <= SPHERICAL_AREA_THRESHOLD) { - return 0.0f; - } - - if (out_dir) { - // calculate arc lengths for edges of spherical triangle - // const float a = portable_acosf(clamp(dot(B, C), -1.0f, 1.0f)); - const float b = portable_acosf(clamp(dot(C, A), -1.0f, 1.0f)); - const float c = portable_acosf(clamp(dot(A, B), -1.0f, 1.0f)); - - // Use one random variable to select the new area - const float area_S = Xi.get<0>() * area; - - // Save the sine and cosine of the angle delta - const float p = sinf(area_S - alpha); - const float q = cosf(area_S - alpha); - - // Compute the pair(u; v) that determines sin(beta_s) and cos(beta_s) - const float u = q - cosf(alpha); - const float v = p + sinf(alpha) * cosf(c); - - // Compute the s coordinate as normalized arc length from A to C_s - const float denom = ((v * p + u * q) * sinf(alpha)); - const float s = - (1.0f / b) * portable_acosf(clamp(safe_div(((v * q - u * p) * cosf(alpha) - v), denom), -1.0f, 1.0f)); - - // Compute the third vertex of the sub - triangle - const fvec4 C_s = slerp(A, C, s); - - // Compute the t coordinate using C_s and Xi[1] - const float denom2 = portable_acosf(clamp(dot(C_s, B), -1.0f, 1.0f)); - const float t = safe_div(portable_acosf(clamp(1.0f - Xi.get<1>() * (1.0f - dot(C_s, B)), -1.0f, 1.0f)), denom2); - - // Construct the corresponding point on the sphere. - (*out_dir) = slerp(B, C_s, t); - } - - // return pdf - return (1.0f / area); -} - } // namespace Ref } // namespace Ray @@ -1296,6 +1235,67 @@ float Ray::Ref::SampleSphericalRectangle(const fvec4 &P, const fvec4 &light_pos, return (1.0f / area); } +// "Stratified Sampling of Spherical Triangles" https://www.graphics.cornell.edu/pubs/1995/Arv95c.pdf +// Based on https://www.shadertoy.com/view/4tGGzd +float Ray::Ref::SampleSphericalTriangle(const fvec4 &P, const fvec4 &p1, const fvec4 &p2, const fvec4 &p3, + const fvec2 Xi, fvec4 *out_dir) { + // Setup spherical triangle + const fvec4 A = normalize(p1 - P), B = normalize(p2 - P), C = normalize(p3 - P); + + // calculate internal angles of spherical triangle: alpha, beta and gamma + const fvec4 BA = orthogonalize(A, B - A); + const fvec4 CA = orthogonalize(A, C - A); + const fvec4 AB = orthogonalize(B, A - B); + const fvec4 CB = orthogonalize(B, C - B); + const fvec4 BC = orthogonalize(C, B - C); + const fvec4 AC = orthogonalize(C, A - C); + + const float alpha = angle_between(BA, CA); + const float beta = angle_between(AB, CB); + const float gamma = angle_between(BC, AC); + + const float area = alpha + beta + gamma - PI; + if (area <= SPHERICAL_AREA_THRESHOLD) { + return 0.0f; + } + + if (out_dir) { + // calculate arc lengths for edges of spherical triangle + // const float a = portable_acosf(clamp(dot(B, C), -1.0f, 1.0f)); + const float b = portable_acosf(clamp(dot(C, A), -1.0f, 1.0f)); + const float c = portable_acosf(clamp(dot(A, B), -1.0f, 1.0f)); + + // Use one random variable to select the new area + const float area_S = Xi.get<0>() * area; + + // Save the sine and cosine of the angle delta + const float p = sinf(area_S - alpha); + const float q = cosf(area_S - alpha); + + // Compute the pair(u; v) that determines sin(beta_s) and cos(beta_s) + const float u = q - cosf(alpha); + const float v = p + sinf(alpha) * cosf(c); + + // Compute the s coordinate as normalized arc length from A to C_s + const float denom = ((v * p + u * q) * sinf(alpha)); + const float s = + (1.0f / b) * portable_acosf(clamp(safe_div(((v * q - u * p) * cosf(alpha) - v), denom), -1.0f, 1.0f)); + + // Compute the third vertex of the sub - triangle + const fvec4 C_s = slerp(A, C, s); + + // Compute the t coordinate using C_s and Xi[1] + const float denom2 = portable_acosf(clamp(dot(C_s, B), -1.0f, 1.0f)); + const float t = safe_div(portable_acosf(clamp(1.0f - Xi.get<1>() * (1.0f - dot(C_s, B)), -1.0f, 1.0f)), denom2); + + // Construct the corresponding point on the sphere. + (*out_dir) = slerp(B, C_s, t); + } + + // return pdf + return (1.0f / area); +} + Ray::Ref::fvec2 Ray::Ref::get_scrambled_2d_rand(const uint32_t dim, const uint32_t seed, const int sample, const uint32_t rand_seq[]) { const uint32_t shuffled_dim = nested_uniform_scramble_base2(dim, seed) & (RAND_DIMS_COUNT - 1); diff --git a/internal/CoreRef.h b/internal/CoreRef.h index c70043c5..11ce30e0 100644 --- a/internal/CoreRef.h +++ b/internal/CoreRef.h @@ -306,6 +306,8 @@ fvec2 get_scrambled_2d_rand(const uint32_t dim, const uint32_t seed, const int s float SampleSphericalRectangle(const fvec4 &P, const fvec4 &light_pos, const fvec4 &axis_u, const fvec4 &axis_v, fvec2 Xi, fvec4 *out_p); +float SampleSphericalTriangle(const fvec4 &P, const fvec4 &p1, const fvec4 &p2, const fvec4 &p3, const fvec2 Xi, + fvec4 *out_dir); // Generation of rays void GeneratePrimaryRays(const camera_t &cam, const rect_t &r, int w, int h, const uint32_t rand_seq[], diff --git a/internal/ShadeRef.cpp b/internal/ShadeRef.cpp index 7b5cdb73..8bdec628 100644 --- a/internal/ShadeRef.cpp +++ b/internal/ShadeRef.cpp @@ -1504,13 +1504,12 @@ Ray::color_rgba_t Ray::Ref::ShadeSurface(const pass_settings_t &ps, const float const float cos_theta = fabsf(dot(I, light_forward)); // abs for doublesided light if (cos_theta > 0.0f) { - float light_pdf; -#if USE_SPHERICAL_AREA_LIGHT_SAMPLING - const fvec4 P = TransformPoint(ro, mi->inv_xform); - light_pdf = SampleSphericalTriangle(P, p1, p2, p3, {}, nullptr) / pdf_factor; - if (light_pdf == 0.0f) -#endif - { + float light_pdf = 0.0f; + if (USE_SPHERICAL_AREA_LIGHT_SAMPLING) { + const fvec4 P = TransformPoint(ro, mi->inv_xform); + light_pdf = SampleSphericalTriangle(P, p1, p2, p3, {}, nullptr) / pdf_factor; + } + if (light_pdf == 0.0f) { light_pdf = (inter.t * inter.t) / (tri_area * cos_theta * pdf_factor); }