From 02d6d0f3e543aaad7d4e52ecf753a826879b8904 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 4 Feb 2025 14:16:42 +0700 Subject: [PATCH 01/56] initial example --- examples_tests | 2 +- .../hlsl/sampling/box_muller_transform.hlsl | 27 +++++++++++++++++++ src/nbl/builtin/CMakeLists.txt | 1 + 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl diff --git a/examples_tests b/examples_tests index d7f7a87fa0..b171724bb0 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit d7f7a87fa08a56a16cd1bcc7d4d9fd48fc8c278c +Subproject commit b171724bb0db3bf6f144d6eb077e95ddea806cbd diff --git a/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl b/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl new file mode 100644 index 0000000000..efa8d66e2b --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl @@ -0,0 +1,27 @@ +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_BOX_MULLER_TRANSFORM_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BOX_MULLER_TRANSFORM_INCLUDED_ + +#include "nbl/builtin/hlsl/math/functions.hlsl" +#include "nbl/builtin/hlsl/numbers.hlsl" + +namespace nbl +{ +namespace hlsl +{ + +template +vector boxMullerTransform(vector xi, T stddev) +{ + T sinPhi, cosPhi; + nbl::hlsl::sincos(2.0 * numbers::pi * xi.y - numbers::pi, sinPhi, cosPhi); + return vector(cosPhi, sinPhi) * nbl::hlsl::sqrt(-2.0 * nbl::hlsl::log(xi.x)) * stddev; +} + +} +} + +#endif \ No newline at end of file diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index 35f22d6ba1..abab705d13 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -313,6 +313,7 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/line.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/beziers.hlsl") #sampling LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/concentric_mapping.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/box_muller_transform.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/cos_weighted.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/uniform.hlsl") # From 129b50e530f2a3d5b673df8ddaab004b9404fd80 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 6 Feb 2025 16:03:31 +0700 Subject: [PATCH 02/56] use bxdf creation params struct --- examples_tests | 2 +- include/nbl/builtin/hlsl/bxdf/common.hlsl | 15 +++++++++ include/nbl/builtin/hlsl/bxdf/reflection.hlsl | 26 ++++++++++++++++ .../nbl/builtin/hlsl/bxdf/transmission.hlsl | 31 +++++++++++++++++++ 4 files changed, 73 insertions(+), 1 deletion(-) diff --git a/examples_tests b/examples_tests index b171724bb0..5a5fbfe55a 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit b171724bb0db3bf6f144d6eb077e95ddea806cbd +Subproject commit 5a5fbfe55aa4cf062c562f19507ba30de085b7a6 diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index e518f0dcba..050366c6dc 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -844,6 +844,7 @@ enum BxDFClampMode : uint16_t BCM_ABS }; +// unified param struct for calls to BxDF::eval, BxDF::pdf, BxDF::quotient_and_pdf template) struct SBxDFParams { @@ -976,6 +977,20 @@ struct SBxDFParams Scalar uNdotV; }; +// unified param struct for calls to BxDF::create +template) +struct SBxDFCreationParams +{ + bool is_aniso; + Scalar A; + vector Axy; + Spectrum ior0; + Spectrum ior1; + Scalar eta; + Spectrum eta2; + Spectrum luminosityContributionHint; +}; + // fresnel stuff namespace impl { diff --git a/include/nbl/builtin/hlsl/bxdf/reflection.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection.hlsl index bf534e2f8d..b074bcaddb 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection.hlsl @@ -59,6 +59,11 @@ struct SLambertianBxDF return retval; } + static this_t create(SBxDFCreationParams params) + { + return create(); + } + scalar_type __eval_pi_factored_out(scalar_type maxNdotL) { return maxNdotL; @@ -117,6 +122,11 @@ struct SOrenNayarBxDF return retval; } + static this_t create(SBxDFCreationParams params) + { + return create(params.A); + } + scalar_type __rec_pi_factored_out_wo_clamps(scalar_type VdotL, scalar_type maxNdotL, scalar_type maxNdotV) { scalar_type A2 = A * 0.5; @@ -342,6 +352,14 @@ struct SBeckmannBxDF return retval; } + static this_t create(SBxDFCreationParams params) + { + if (params.is_aniso) + return create(params.Axy.x, params.Axy.y, params.ior0, params.ior1); + else + return create(params.A, params.ior0, params.ior1); + } + scalar_type __eval_DG_wo_clamps(params_t params) { if (params.is_aniso) @@ -570,6 +588,14 @@ struct SGGXBxDF return retval; } + static this_t create(SBxDFCreationParams params) + { + if (params.is_aniso) + return create(params.Axy.x, params.Axy.y, params.ior0, params.ior1); + else + return create(params.A, params.ior0, params.ior1); + } + scalar_type __eval_DG_wo_clamps(params_t params) { if (params.is_aniso) diff --git a/include/nbl/builtin/hlsl/bxdf/transmission.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission.hlsl index 17682a7384..18d80e93aa 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission.hlsl @@ -63,6 +63,11 @@ struct SLambertianBxDF return retval; } + static this_t create(SBxDFCreationParams params) + { + return create(); + } + scalar_type __eval_pi_factored_out(scalar_type absNdotL) { return absNdotL; @@ -124,6 +129,11 @@ struct SSmoothDielectricBxDF return retval; } + static this_t create(SBxDFCreationParams params) + { + return create(params.eta); + } + spectral_type eval(params_t params) { return (spectral_type)0; @@ -205,6 +215,11 @@ struct SSmoothDielectricBxDF return retval; } + static this_t create(SBxDFCreationParams params) + { + return create(params.eta2, params.luminosityContributionHint); + } + spectral_type eval(params_t params) { return (spectral_type)0; @@ -299,6 +314,14 @@ struct SBeckmannDielectricBxDF return retval; } + static this_t create(SBxDFCreationParams params) + { + if (params.is_aniso) + return create(params.eta, params.Axy.x, params.Axy.y); + else + return create(params.eta, params.A); + } + spectral_type eval(params_t params) { scalar_type orientedEta, dummy; @@ -466,6 +489,14 @@ struct SGGXDielectricBxDF return retval; } + static this_t create(SBxDFCreationParams params) + { + if (params.is_aniso) + return create(params.eta, params.Axy.x, params.Axy.y); + else + return create(params.eta, params.A); + } + spectral_type eval(params_t params) { scalar_type orientedEta, dummy; From 74261dc22e7f375e12abf4de232e13ea5ebcbee5 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 7 Feb 2025 14:37:25 +0700 Subject: [PATCH 03/56] triangle and rectangle shapes --- examples_tests | 2 +- .../nbl/builtin/hlsl/shapes/rectangle.hlsl | 51 ++++++++++ include/nbl/builtin/hlsl/shapes/triangle.hlsl | 99 +++++++++++++++++++ src/nbl/builtin/CMakeLists.txt | 2 + 4 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 include/nbl/builtin/hlsl/shapes/rectangle.hlsl create mode 100644 include/nbl/builtin/hlsl/shapes/triangle.hlsl diff --git a/examples_tests b/examples_tests index 5a5fbfe55a..85e67ad0c4 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 5a5fbfe55aa4cf062c562f19507ba30de085b7a6 +Subproject commit 85e67ad0c4012d7d8d2014489327036d89b0bf57 diff --git a/include/nbl/builtin/hlsl/shapes/rectangle.hlsl b/include/nbl/builtin/hlsl/shapes/rectangle.hlsl new file mode 100644 index 0000000000..854a326aaf --- /dev/null +++ b/include/nbl/builtin/hlsl/shapes/rectangle.hlsl @@ -0,0 +1,51 @@ +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_SHAPES_RECTANGLE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SHAPES_RECTANGLE_INCLUDED_ + +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace shapes +{ + +template +struct SphericalRectangle +{ + using scalar_type = T; + using vector3_type = vector; + using vector4_type = vector; + using matrix3x3_type = matrix; + + static SphericalRectangle create(NBL_CONST_REF_ARG(vector3_type) observer, NBL_CONST_REF_ARG(vector3_type) rectangleOrigin, NBL_CONST_REF_ARG(vector3_type) T, NBL_CONST_REF_ARG(vector3_type) B, NBL_CONST_REF_ARG(vector3_type) N) + { + matrix3x3_type TBN = nbl::hlsl::transpose(matrix3x3_type(T, B, isotropic_type::N)); + return nbl::hlsl::mul(TBN, rectangleOrigin - observer); + } + + scalar_type solidAngleOfRectangle(NBL_CONST_REF_ARG(vector3_type) r0, NBL_CONST_REF_ARG(vector) rectangleExtents) + { + const vector4_type denorm_n_z = vector4_type(-r0.y, r0.x + rectangleExtents.x, r0.y + rectangleExtents.y, -r0.x); + const vector4_type n_z = denorm_n_z / nbl::hlsl::sqrt((vector4_type)(r0.z * r0.z) + denorm_n_z * denorm_n_z); + const vector4_type cosGamma = vec4( + -n_z[0] * n_z[1], + -n_z[1] * n_z[2], + -n_z[2] * n_z[3], + -n_z[3] * n_z[0] + ); + return math::getSumofArccosABCD(cosGamma[0], cosGamma[1], cosGamma[2], cosGamma[3]) - 2 * numbers::pi; + } +} + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/shapes/triangle.hlsl b/include/nbl/builtin/hlsl/shapes/triangle.hlsl new file mode 100644 index 0000000000..f7ce67a1c9 --- /dev/null +++ b/include/nbl/builtin/hlsl/shapes/triangle.hlsl @@ -0,0 +1,99 @@ +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_SHAPES_TRIANGLE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SHAPES_TRIANGLE_INCLUDED_ + +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace shapes +{ + +template +struct SphericalTriangle +{ + using scalar_type = T; + using vector3_type = vector; + + static SphericalTriangle create(NBL_CONST_REF_ARG(vector3_type) vertex0, NBL_CONST_REF_ARG(vector3_type) vertex1, NBL_CONST_REF_ARG(vector3_type) vertex2, NBL_CONST_REF_ARG(vector3_type) origin) + { + SphericalTriangle retval; + retval.vertex0 = nbl::hlsl::normalize(vertex0 - origin); + retval.vertex1 = nbl::hlsl::normalize(vertex1 - origin); + retval.vertex2 = nbl::hlsl::normalize(vertex2 - origin); + return retval; + } + + bool pyramidAngles(NBL_REF_ARG(vector3_type) cos_sides, NBL_REF_ARG(vector3_type) csc_sides) + { + cos_sides = vector3_type(nbl::hlsl::dot(vertex1, vertex2), nbl::hlsl::dot(vertex2, vertex0), nbl::hlsl::dot(vertex0, vertex1)); + csc_sides = 1.0 / nbl::hlsl::sqrt((vector3_type)(1.f) - cos_sides * cos_sides); + return nbl::hlsl::any(csc_sides >= (vector3_type)(numeric_limits::max)); + } + + scalar_type solidAngleOfTriangle(NBL_REF_ARG(vector3_type) cos_vertices, NBL_REF_ARG(vector3_type) sin_vertices, NBL_REF_ARG(scalar_type) cos_a, NBL_REF_ARG(scalar_type) cos_c, NBL_REF_ARG(scalar_type) csc_b, NBL_REF_ARG(scalar_type) csc_c) + { + vector3_type cos_sides,csc_sides; + if (pyramidAngles(cos_sides, csc_sides)) + return 0.f; + + // these variables might eventually get optimized out + cos_a = cos_sides[0]; + cos_c = cos_sides[2]; + csc_b = csc_sides[1]; + csc_c = csc_sides[2]; + + // Both vertices and angles at the vertices are denoted by the same upper case letters A, B, and C. The angles A, B, C of the triangle are equal to the angles between the planes that intersect the surface of the sphere or, equivalently, the angles between the tangent vectors of the great circle arcs where they meet at the vertices. Angles are in radians. The angles of proper spherical triangles are (by convention) less than PI + cos_vertices = clamp((cos_sides - cos_sides.yzx * cos_sides.zxy) * csc_sides.yzx * csc_sides.zxy, (vector3_type)(-1.f), (vector3_type)1.f); // using Spherical Law of Cosines (TODO: do we need to clamp anymore? since the pyramid angles method introduction?) + sin_vertices = sqrt((vector3_type)1.f - cos_vertices * cos_vertices); + + return math::getArccosSumofABC_minus_PI(cos_vertices[0], cos_vertices[1], cos_vertices[2], sin_vertices[0], sin_vertices[1], sin_vertices[2]); + } + + scalar_type solidAngleOfTriangle() + { + vector3_type dummy0,dummy1; + scalar_type dummy2,dummy3,dummy4,dummy5; + return solidAngleOfTriangle(dummy0,dummy1,dummy2,dummy3,dummy4,dummy5); + } + + scalar_type projectedSolidAngleOfTriangle(NBL_CONST_REF_ARG(vector3_type) receiverNormal, NBL_REF_ARG(vector3_type) cos_sides, NBL_REF_ARG(vector3_type) csc_sides, NBL_REF_ARG(vector3_type) cos_vertices) + { + if (pyramidAngles(cos_sides, csc_sides)) + return 0.f; + + vector3_type awayFromEdgePlane0 = nbl::hlsl::cross(vertex1, vertex2) * csc_sides[0]; + vector3_type awayFromEdgePlane1 = nbl::hlsl::cross(vertex2, vertex0) * csc_sides[1]; + vector3_type awayFromEdgePlane2 = nbl::hlsl::cross(vertex0, vertex1) * csc_sides[2]; + + // useless here but could be useful somewhere else + cos_vertices[0] = nbl::hlsl::dot(awayFromEdgePlane1, awayFromEdgePlane2); + cos_vertices[1] = nbl::hlsl::dot(awayFromEdgePlane2, awayFromEdgePlane0); + cos_vertices[2] = nbl::hlsl::dot(awayFromEdgePlane0, awayFromEdgePlane1); + // TODO: above dot products are in the wrong order, either work out which is which, or try all 6 permutations till it works + cos_vertices = nbl::hlsl::clamp((cos_sides - cos_sides.yzx * cos_sides.zxy) * csc_sides.yzx * csc_sides.zxy, (vector3_type)(-1.f), (vector3_type)1.f); + + matrix mat = + const vector3_type externalProducts = nbl::hlsl::abs(nbl::hlsl::transpose(awayFromEdgePlane) * receiverNormal); + + const vector3_type pyramidAngles = acos(cos_sides); + return nbl::hlsl::dot(pyramidAngles, externalProducts) / (2.f * numbers::pi); + } + + vector3_type vertex0; + vector3_type vertex1; + vector3_type vertex2; +}; + +} +} +} + +#endif diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index abab705d13..a79f9ac31a 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -311,6 +311,8 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/circle.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/ellipse.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/line.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/beziers.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/triangle.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/rectangle.hlsl") #sampling LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/concentric_mapping.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/box_muller_transform.hlsl") From ab3ae20171ed7ea0d78338114eb420e9f179b459 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 10 Feb 2025 16:58:22 +0700 Subject: [PATCH 04/56] more sampling methods --- examples_tests | 2 +- .../nbl/builtin/hlsl/sampling/bilinear.hlsl | 61 ++++++++ include/nbl/builtin/hlsl/sampling/linear.hlsl | 45 ++++++ .../projected_spherical_triangle.hlsl | 94 +++++++++++++ .../hlsl/sampling/spherical_triangle.hlsl | 132 ++++++++++++++++++ src/nbl/builtin/CMakeLists.txt | 4 + 6 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 include/nbl/builtin/hlsl/sampling/bilinear.hlsl create mode 100644 include/nbl/builtin/hlsl/sampling/linear.hlsl create mode 100644 include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl create mode 100644 include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl diff --git a/examples_tests b/examples_tests index 85e67ad0c4..2c500b1e06 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 85e67ad0c4012d7d8d2014489327036d89b0bf57 +Subproject commit 2c500b1e06e3e83b2a427bf0aa1ef27878467e0b diff --git a/include/nbl/builtin/hlsl/sampling/bilinear.hlsl b/include/nbl/builtin/hlsl/sampling/bilinear.hlsl new file mode 100644 index 0000000000..1d5f9a91e8 --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/bilinear.hlsl @@ -0,0 +1,61 @@ +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_SAMPLING_BILINEAR_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_BILINEAR_INCLUDED_ + +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template +struct Bilinear +{ + using scalar_type = T; + using vector2_type = vector; + using vector3_type = vector; + using vector4_type = vector; + + static Bilinear create(NBL_CONST_REF_ARG(vector4_type) bilinearCoeffs) + { + Bilinear retval; + retval.bilinearCoeffs = bilinearCoeffs; + return retval; + } + + vector2_type generate(NBL_REG_ARG(scalar_type) rcpPdf, NBL_CONST_REF_ARG(vector2_type) u) + { + const vector2_type twiceAreasUnderXCurve = vector2_type(bilinearCoeffs[0] + bilinearCoeffs[1], bilinearCoeffs[2] + bilinearCoeffs[3]); + Linear lineary = Linear::create(twiceAreasUnderXCurve); + u.y = lineary.generate(u.y); + + const vector2_type ySliceEndPoints = vector2_type(nbl::hlsl::mix(bilinearCoeffs[0], bilinearCoeffs[2], u.y), nbl::hlsl::mix(bilinearCoeffs[1], bilinearCoeffs[3], u.y)); + Linear linearx = Linear::create(ySliceEndPoints); + u.x = linearx.generate(u.x); + + rcpPdf = (twiceAreasUnderXCurve[0] + twiceAreasUnderXCurve[1]) / (4.0 * nbl::hlsl::mix(ySliceEndPoints[0], ySliceEndPoints[1], u.x)); + + return u; + } + + scalar_type pdf(NBL_CONST_REF_ARG(vector2_type) u) + { + return 4.0 * nbl::hlsl::mix(nbl::hlsl::mix(bilinearCoeffs[0], bilinearCoeffs[1], u.x), nbl::hlsl::mix(bilinearCoeffs[2], bilinearCoeffs[3], u.x), u.y) / (bilinearCoeffs[0] + bilinearCoeffs[1] + bilinearCoeffs[2] + bilinearCoeffs[3]); + } + + vector4_type bilinearCoeffs; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/sampling/linear.hlsl b/include/nbl/builtin/hlsl/sampling/linear.hlsl new file mode 100644 index 0000000000..8b9b3fb058 --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/linear.hlsl @@ -0,0 +1,45 @@ +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_SAMPLING_BILINEAR_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_BILINEAR_INCLUDED_ + +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template +struct Linear +{ + using scalar_type = T; + using vector2_type = vector; + + static Linear create(NBL_CONST_REF_ARG(vector2_type) linearCoeffs) + { + Linear retval; + retval.linearCoeffs = linearCoeffs; + return retval; + } + + scalar_type generate(scalar_type u) + { + const scalar_type rcpDiff = 1.0 / (linearCoeffs[0] - linearCoeffs[1]); + const vector2_type squaredCoeffs = linearCoeffs * linearCoeffs; + return nbl::hlsl::abs(rcpDiff) < numeric_limits::max ? (linearCoeffs[0] - nbl::hlsl::sqrt(nbl::hlsl::mix(squaredCoeffs[0], squaredCoeffs[1], u))) * rcpDiff : u; + } + + vector2_type linearCoeffs; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl b/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl new file mode 100644 index 0000000000..5832e9aab2 --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl @@ -0,0 +1,94 @@ +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_SAMPLING_PROJECTED_SPHERICAL_TRIANGLE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_PROJECTED_SPHERICAL_TRIANGLE_INCLUDED_ + +#include +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template +struct ProjectedSphericalTriangle +{ + using scalar_type = T; + using vector2_type = vector; + using vector3_type = vector; + using vector4_type = vector; + + static ProjectedSphericalTriangle create(NBL_CONST_REG_ARG(shapes::SphericalTriangle) tri) + { + ProjectedSphericalTriangle retval; + retval.tri = tri; + return retval; + } + + vector4_type computeBilinearPatch(NBL_CONST_REG_ARG(vector3_type) receiverNormal, bool isBSDF) + { + const scalar_type minimumProjSolidAngle = 0.0; + + matrix m = matrix(tri.vertex0, tri.vertex1, tri.vertex2); + const vector3_type bxdfPdfAtVertex = math::conditionalAbsOrMax(isBSDF, nbl::hlsl::mul(m, receiverNormal), (vector3_type)minimumProjSolidAngle); + + return bxdfPdfAtVertex.yyxz; + } + + vector3_type generate(NBL_REG_ARG(scalar_type) rcpPdf, scalar_type solidAngle, NBL_CONST_REG_ARG(vector3_type) cos_vertices, NBL_CONST_REG_ARG(vector3_type) sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, NBL_CONST_REG_ARG(vector3_type) receiverNormal, bool isBSDF, NBL_CONST_REG_ARG(vector2_type) u) + { + // pre-warp according to proj solid angle approximation + vector4_type patch = computeBilinearPatch(receiverNormal, isBSDF); + Bilinear bilinear = Bilinear::create(patch); + u = bilinear.generate(rcpPdf, u); + + // now warp the points onto a spherical triangle + const vector3_type L = tri.generate(solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, u); + rcpPdf *= solidAngle; + + return L; + } + + vector3_type generate(NBL_REG_ARG(scalar_type) rcpPdf, NBL_CONST_REG_ARG(vector3_type) receiverNormal, bool isBSDF, NBL_CONST_REG_ARG(vector2_type) u) + { + scalar_type cos_a, cos_c, csc_b, csc_c; + vector3_type cos_vertices, sin_vertices; + const scalar_type solidAngle = tri.solidAngleOfTriangle(cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c); + return generate(rcpPdf, solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, receiverNormal, isBSDF, u); + } + + scalar_type pdf(scalar_type solidAngle, NBL_CONST_REG_ARG(vector3_type) cos_vertices, NBL_CONST_REG_ARG(vector3_type) sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, NBL_CONST_REG_ARG(vector3_type) receiverNormal, bool receiverWasBSDF, NBL_CONST_REG_ARG(vector3_type) L) + { + scalar_type pdf; + const vector2_type u = tri.generateInverse(pdf, solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, L); + + vector4_type patch = computeBilinearPatch(receiverNormal, receiverWasBSDF); + Bilinear bilinear = Bilinear::create(patch); + return pdf * bilinear.pdf(u); + } + + scalar_type pdf(NBL_CONST_REG_ARG(vector3_type) receiverNormal, bool receiverWasBSDF, NBL_CONST_REG_ARG(vector3_type) L) + { + scalar_type pdf; + const vector2_type u = tri.generateInverse(pdf, L); + + vector4_type patch = computeBilinearPatch(receiverNormal, receiverWasBSDF); + Bilinear bilinear = Bilinear::create(patch); + return pdf * bilinear.pdf(u); + } + + shapes::SphericalTriangle tri; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl b/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl new file mode 100644 index 0000000000..9501cdc3d1 --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl @@ -0,0 +1,132 @@ +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_SAMPLING_SPHERICAL_TRIANGLE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_SPHERICAL_TRIANGLE_INCLUDED_ + +#include +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template +struct SphericalTriangle +{ + using scalar_type = T; + using vector2_type = vector; + using vector3_type = vector; + + static SphericalTriangle create(NBL_CONST_REG_ARG(shapes::SphericalTriangle) tri) + { + SphericalTriangle retval; + retval.tri = tri; + return retval; + } + + vector3_type slerp_delta(NBL_CONST_REF_ARG(vector3_type) start, NBL_CONST_REF_ARG(vector3_type) preScaledWaypoint, scalar_type cosAngleFromStart) + { + vector3_type planeNormal = nbl::hlsl::cross(start,preScaledWaypoint); + + cosAngleFromStart *= 0.5; + const scalar_type sinAngle = nbl::hlsl::sqrt(0.5 - cosAngleFromStart); + const scalar_type cosAngle = nbl::hlsl::sqrt(0.5 + cosAngleFromStart); + + planeNormal *= sinAngle; + const vector3_type precompPart = nbl::hlsl::cross(planeNormal, start) * 2.0; + + return precompPart * cosAngle + nbl::hlsl::cross(planeNormal, precompPart); + } + + // WARNING: can and will return NAN if one or three of the triangle edges are near zero length + vector3_type generate(scalar_type solidAngle, NBL_CONST_REF_ARG(vector3_type) cos_vertices, NBL_CONST_REF_ARG(vector3_type) sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, NBL_CONST_REF_ARG(vector2_type) u) + { + scalar_type negSinSubSolidAngle,negCosSubSolidAngle; + math::sincos(solidAngle * u.x - numbers::pi, negSinSubSolidAngle, negCosSubSolidAngle); + + const scalar_type p = negCosSubSolidAngle * sin_vertices[0] - negSinSubSolidAngle * cos_vertices[0]; + const scalar_type q = -negSinSubSolidAngle * sin_vertices[0] - negCosSubSolidAngle * cos_vertices[0]; + + // TODO: we could optimize everything up and including to the first slerp, because precision here is just godawful + scalar_type u_ = q - cos_vertices[0]; + scalar_type v_ = p + sin_vertices[0] * cos_c; + + // the slerps could probably be optimized by sidestepping `normalize` calls and accumulating scaling factors + vector3_type C_s = tri.vertex0; + if (csc_b < numeric_limits::max) + { + const scalar_type cosAngleAlongAC = ((v_ * q - u_ * p) * cos_vertices[0] - v_) / ((v_ * p + u_ * q) * sin_vertices[0]); + if (nbl::hlsl::abs(cosAngleAlongAC) < 1.f) + C_s += slerp_delta(tri.vertex0, tri.vertex2 * csc_b, cosAngleAlongAC); + } + + vector3_type retval = tri.vertex1; + const scalar_type cosBC_s = nbl::hlsl::dot(C_s, tri.vertex1); + const scalar_type csc_b_s = 1.0 / nbl::hlsl::sqrt(1.0 - cosBC_s * cosBC_s); + if (csc_b_s < numeric_limits::max) + { + const scalar_type cosAngleAlongBC_s = nbl::hlsl::clamp(1.0 + cosBC_s * u.y - u.y, -1.f, 1.f); + if (nbl::hlsl::abs(cosAngleAlongBC_s) < 1.f) + retval += slerp_delta(tri.vertex1, C_s * csc_b_s, cosAngleAlongBC_s); + } + return retval; + } + + vector3_type generate(NBL_REF_ARG(scalar_type) rcpPdf, NBL_CONST_REF_ARG(vector2_type) u) + { + scalar_type cos_a, cos_c, csc_b, csc_c; + vector3_type cos_vertices, sin_vertices; + + rcpPdf = tri.solidAngleOfTriangle(cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c); + + return generate(rcpPdf, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, u); + } + + vector2_type generateInverse(NBL_REF_ARG(scalar_type) pdf, scalar_type solidAngle, NBL_CONST_REF_ARG(vector3_type) cos_vertices, NBL_CONST_REF_ARG(vector3_type) sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, NBL_CONST_REF_ARG(vector3_type) L) + { + pdf = 1.0 / solidAngle; + + const scalar_type cosAngleAlongBC_s = nbl::hlsl::dot(L, tri.vertex1); + const scalar_type csc_a_ = 1.0 / nbl::hlsl::sqrt(1.0 - cosAngleAlongBC_s * cosAngleAlongBC_s); + const scalar_type cos_b_ = nbl::hlsl::dot(L, tri.vertex0); + + const scalar_type cosB_ = (cos_b_ - cosAngleAlongBC_s * cos_c) * csc_a_ * csc_c; + const scalar_type sinB_ = nbl::hlsl::sqrt(1.0 - cosB_ * cosB_); + + const scalar_type cosC_ = sin_vertices[0] * sinB_* cos_c - cos_vertices[0] * cosB_; + const scalar_type sinC_ = nbl::hlsl::sqrt(1.0 - cosC_ * cosC_); + + const scalar_type subTriSolidAngleRatio = math::getArccosSumofABC_minus_PI(cos_vertices[0], cosB_, cosC_, sin_vertices[0], sinB_, sinC_) * pdf; + const scalar_type u = subTriSolidAngleRatio > numeric_limits::min ? subTriSolidAngleRatio : 0.0; + + const scalar_type cosBC_s = (cos_vertices[0] + cosB_ * cosC_) / (sinB_ * sinC_); + const scalar_type v = (1.0 - cosAngleAlongBC_s) / (1.0 - (cosBC_s < asfloat(0x3f7fffff) ? cosBC_s : cos_c)); + + return vector2_type(u,v); + } + + vector2_type generateInverse(NBL_REF_ARG(scalar_type) pdf, NBL_CONST_REF_ARG(vector3_type) L) + { + scalar_type cos_a, cos_c, csc_b, csc_c; + vector3_type cos_vertices, sin_vertices; + + const scalar_type solidAngle = tri.solidAngleOfTriangle(cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c); + + return generateInverse(pdf, solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, L); + } + + shapes::SphericalTriangle tri; +}; + +} +} +} + +#endif diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index a79f9ac31a..5cc0108874 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -314,9 +314,13 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/beziers.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/triangle.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/rectangle.hlsl") #sampling +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/linear.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/bilinear.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/concentric_mapping.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/box_muller_transform.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/cos_weighted.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/spherical_triangle.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/projected_spherical_triangle.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/uniform.hlsl") # LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/ndarray_addressing.hlsl") From 355cfecdcd508151435f01b49c7f9a1e02975a23 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 11 Feb 2025 14:03:51 +0700 Subject: [PATCH 05/56] spherical rectangle --- examples_tests | 2 +- .../hlsl/sampling/spherical_rectangle.hlsl | 86 +++++++++++++++++++ .../nbl/builtin/hlsl/shapes/rectangle.hlsl | 15 +++- include/nbl/builtin/hlsl/shapes/triangle.hlsl | 2 +- src/nbl/builtin/CMakeLists.txt | 1 + 5 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl diff --git a/examples_tests b/examples_tests index 2c500b1e06..e6a99165c1 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 2c500b1e06e3e83b2a427bf0aa1ef27878467e0b +Subproject commit e6a99165c1b153977192f9722381fc24f566c9ca diff --git a/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl b/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl new file mode 100644 index 0000000000..83224bfabd --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl @@ -0,0 +1,86 @@ +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_SAMPLING_SPHERICAL_RECTANGLE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_SPHERICAL_RECTANGLE_INCLUDED_ + +#include +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template +struct SphericalRectangle +{ + using scalar_type = T; + using vector2_type = vector; + using vector3_type = vector; + using vector4_type = vector; + + static SphericalRectangle create(NBL_CONST_REG_ARG(shapes::SphericalRectangle) rect) + { + SphericalRectangle retval; + retval.rect = rect; + return retval; + } + + vector2_type generate(NBL_CONST_REF_ARG(vector2_type) rectangleExtents, NBL_CONST_REF_ARG(vector2_type) uv, NBL_REF_ARG(scalar_type) S) + { + const vector4_type denorm_n_z = vector4_type(-rect.r0.y, rect.r0.x + rectangleExtents.x, rect.r0.y + rectangleExtents.y, -rect.r0.x); + const vector4_type n_z = denorm_n_z / nbl::hlsl::sqrt(vector4_type(rect.r0.z * rect.r0.z) + denorm_n_z * denorm_n_z); + const vector4_type cosGamma = vector4_type( + -n_z[0] * n_z[1], + -n_z[1] * n_z[2], + -n_z[2] * n_z[3], + -n_z[3] * n_z[0] + ); + + scalar_type p = math::getSumofArccosAB(cosGamma[0], cosGamma[1]); + scalar_type q = math::getSumofArccosAB(cosGamma[2], cosGamma[3]); + + const scalar_type k = 2 * numbers::pi - q; + const scalar_type b0 = n_z[0]; + const scalar_type b1 = n_z[2]; + S = p + q - 2 * numbers::pi; + + const scalar_type CLAMP_EPS = 1e-5f; + + // flip z axsis if rect.r0.z > 0 + const uint32_t zFlipMask = (asuint(rect.r0.z) ^ 0x80000000u) & 0x80000000u; + rect.r0.z = asfloat(asuint(rect.r0.z) ^ zFlipMask); + vector3_type r1 = rect.r0 + vector3_type(rectangleExtents.x, rectangleExtents.y, 0); + + const scalar_type au = uv.x * S + k; + const scalar_type fu = (nbl::hlsl::cos(au) * b0 - b1) / nbl::hlsl::sin(au); + const scalar_type cu_2 = nbl::hlsl::max(fu * fu + b0 * b0, 1.f); // forces `cu` to be in [-1,1] + const scalar_type cu = asfloat(asuint(1.0 / nbl::hlsl::sqrt(cu_2)) ^ (asuint(fu) & 0x80000000u)); + + scalar_type xu = -(cu * rect.r0.z) * 1.0 / nbl::hlsl::sqrt(1 - cu * cu); + xu = nbl::hlsl::clamp(xu, rect.r0.x, r1.x); // avoid Infs + const scalar_type d_2 = xu * xu + rect.r0.z * rect.r0.z; + const scalar_type d = nbl::hlsl::sqrt(d_2); + + const scalar_type h0 = rect.r0.y / nbl::hlsl::sqrt(d_2 + rect.r0.y * rect.r0.y); + const scalar_type h1 = r1.y / nbl::hlsl::sqrt(d_2 + r1.y * r1.y); + const scalar_type hv = h0 + uv.y * (h1 - h0), hv2 = hv * hv; + const scalar_type yv = (hv2 < 1 - CLAMP_EPS) ? (hv * d) / nbl::hlsl::sqrt(1 - hv2) : r1.y; + + return vector2_type((xu - rect.r0.x) / rectangleExtents.x, (yv - rect.r0.y) / rectangleExtents.y); + } + + shapes::SphericalRectangle rect; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/shapes/rectangle.hlsl b/include/nbl/builtin/hlsl/shapes/rectangle.hlsl index 854a326aaf..a61f23cafa 100644 --- a/include/nbl/builtin/hlsl/shapes/rectangle.hlsl +++ b/include/nbl/builtin/hlsl/shapes/rectangle.hlsl @@ -24,13 +24,22 @@ struct SphericalRectangle using vector4_type = vector; using matrix3x3_type = matrix; + static SphericalRectangle create(NBL_CONST_REF_ARG(vector3_type) observer, NBL_CONST_REF_ARG(vector3_type) rectangleOrigin, NBL_CONST_REF_ARG(matrix3x3_type) basis) + { + SphericalRectangle retval; + retval.r0 = nbl::hlsl::mul(basis, rectangleOrigin - observer); + return retval; + } + static SphericalRectangle create(NBL_CONST_REF_ARG(vector3_type) observer, NBL_CONST_REF_ARG(vector3_type) rectangleOrigin, NBL_CONST_REF_ARG(vector3_type) T, NBL_CONST_REF_ARG(vector3_type) B, NBL_CONST_REF_ARG(vector3_type) N) { + SphericalRectangle retval; matrix3x3_type TBN = nbl::hlsl::transpose(matrix3x3_type(T, B, isotropic_type::N)); - return nbl::hlsl::mul(TBN, rectangleOrigin - observer); + retval.r0 = nbl::hlsl::mul(TBN, rectangleOrigin - observer); + return retval; } - scalar_type solidAngleOfRectangle(NBL_CONST_REF_ARG(vector3_type) r0, NBL_CONST_REF_ARG(vector) rectangleExtents) + scalar_type solidAngleOfRectangle(NBL_CONST_REF_ARG(vector) rectangleExtents) { const vector4_type denorm_n_z = vector4_type(-r0.y, r0.x + rectangleExtents.x, r0.y + rectangleExtents.y, -r0.x); const vector4_type n_z = denorm_n_z / nbl::hlsl::sqrt((vector4_type)(r0.z * r0.z) + denorm_n_z * denorm_n_z); @@ -42,6 +51,8 @@ struct SphericalRectangle ); return math::getSumofArccosABCD(cosGamma[0], cosGamma[1], cosGamma[2], cosGamma[3]) - 2 * numbers::pi; } + + vector3_type r0; } } diff --git a/include/nbl/builtin/hlsl/shapes/triangle.hlsl b/include/nbl/builtin/hlsl/shapes/triangle.hlsl index f7ce67a1c9..59ba508596 100644 --- a/include/nbl/builtin/hlsl/shapes/triangle.hlsl +++ b/include/nbl/builtin/hlsl/shapes/triangle.hlsl @@ -49,7 +49,7 @@ struct SphericalTriangle cos_c = cos_sides[2]; csc_b = csc_sides[1]; csc_c = csc_sides[2]; - + // Both vertices and angles at the vertices are denoted by the same upper case letters A, B, and C. The angles A, B, C of the triangle are equal to the angles between the planes that intersect the surface of the sphere or, equivalently, the angles between the tangent vectors of the great circle arcs where they meet at the vertices. Angles are in radians. The angles of proper spherical triangles are (by convention) less than PI cos_vertices = clamp((cos_sides - cos_sides.yzx * cos_sides.zxy) * csc_sides.yzx * csc_sides.zxy, (vector3_type)(-1.f), (vector3_type)1.f); // using Spherical Law of Cosines (TODO: do we need to clamp anymore? since the pyramid angles method introduction?) sin_vertices = sqrt((vector3_type)1.f - cos_vertices * cos_vertices); diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index 5cc0108874..82d081138a 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -321,6 +321,7 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/box_muller_transform LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/cos_weighted.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/spherical_triangle.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/projected_spherical_triangle.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/spherical_rectangle.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/uniform.hlsl") # LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/ndarray_addressing.hlsl") From 11180f45db44a5599bbaa08f342686cce315713e Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 17 Feb 2025 16:58:42 +0700 Subject: [PATCH 06/56] fix aniso cache bug --- examples_tests | 2 +- include/nbl/builtin/hlsl/bxdf/common.hlsl | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/examples_tests b/examples_tests index 83d8a92584..159d1533e8 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 83d8a92584c28d9ad31c9853f6abb59922bc2249 +Subproject commit 159d1533e8d82e3c5e82165e8b79ea67c0f23111 diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index 050366c6dc..71bde312fe 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -610,6 +610,7 @@ struct SAnisotropicMicrofacetCache : SIsotropicMicrofacetCache using ray_dir_info_type = ray_dir_info::SBasic; using anisotropic_type = surface_interactions::SAnisotropic; + using isocache_type = SIsotropicMicrofacetCache; using sample_type = SLightSample; // always valid by construction @@ -671,7 +672,9 @@ struct SAnisotropicMicrofacetCache : SIsotropicMicrofacetCache const scalar_type orientedEta, const scalar_type rcpOrientedEta, NBL_REF_ARG(vector3_type) H ) { - const bool valid = this_t::compute(retval,transmitted,V,L,N,NdotL,VdotL,orientedEta,rcpOrientedEta,H); + isocache_type iso = (isocache_type)retval; + const bool valid = isocache_type::compute(iso,transmitted,V,L,N,NdotL,VdotL,orientedEta,rcpOrientedEta,H); + retval = (this_t)iso; if (valid) { retval.TdotH = nbl::hlsl::dot(T,H); @@ -687,8 +690,10 @@ struct SAnisotropicMicrofacetCache : SIsotropicMicrofacetCache const scalar_type eta ) { + isocache_type iso = (isocache_type)retval; vector3_type H; - const bool valid = this_t::compute(retval,interaction,_sample,eta,H); + const bool valid = isocache_type::compute(iso,interaction,_sample,eta,H); + retval = (this_t)iso; if (valid) { retval.TdotH = nbl::hlsl::dot(interaction.T,H); From 451605197b486b8da5970d10274f63825c407256 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 18 Feb 2025 15:24:42 +0700 Subject: [PATCH 07/56] init func to modify bxdf params directly --- examples_tests | 2 +- include/nbl/builtin/hlsl/bxdf/common.hlsl | 9 +++-- include/nbl/builtin/hlsl/bxdf/reflection.hlsl | 34 +++++++++++++++--- .../nbl/builtin/hlsl/bxdf/transmission.hlsl | 36 ++++++++++++++++--- 4 files changed, 66 insertions(+), 15 deletions(-) diff --git a/examples_tests b/examples_tests index 159d1533e8..a7350db7d7 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 159d1533e8d82e3c5e82165e8b79ea67c0f23111 +Subproject commit a7350db7d7e422fa5086982b3327103c06cfbe44 diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index 71bde312fe..f4ae69aafc 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -987,11 +987,10 @@ template Axy; - Spectrum ior0; - Spectrum ior1; - Scalar eta; + vector A; // roughness + Spectrum ior0; // source ior + Spectrum ior1; // destination ior + Scalar eta; // in most cases, eta will be calculated from ior0 and ior1; see monochromeEta in pathtracer.hlsl Spectrum eta2; Spectrum luminosityContributionHint; }; diff --git a/include/nbl/builtin/hlsl/bxdf/reflection.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection.hlsl index b074bcaddb..76d00c268c 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection.hlsl @@ -64,6 +64,11 @@ struct SLambertianBxDF return create(); } + void init(SBxDFCreationParams params) + { + // do nothing + } + scalar_type __eval_pi_factored_out(scalar_type maxNdotL) { return maxNdotL; @@ -124,7 +129,12 @@ struct SOrenNayarBxDF static this_t create(SBxDFCreationParams params) { - return create(params.A); + return create(params.A.x); + } + + void init(SBxDFCreationParams params) + { + A = params.A.x; } scalar_type __rec_pi_factored_out_wo_clamps(scalar_type VdotL, scalar_type maxNdotL, scalar_type maxNdotV) @@ -355,9 +365,16 @@ struct SBeckmannBxDF static this_t create(SBxDFCreationParams params) { if (params.is_aniso) - return create(params.Axy.x, params.Axy.y, params.ior0, params.ior1); + return create(params.A.x, params.A.y, params.ior0, params.ior1); else - return create(params.A, params.ior0, params.ior1); + return create(params.A.x, params.ior0, params.ior1); + } + + void init(SBxDFCreationParams params) + { + A = params.A; + ior0 = params.ior0; + ior1 = params.ior1; } scalar_type __eval_DG_wo_clamps(params_t params) @@ -591,9 +608,16 @@ struct SGGXBxDF static this_t create(SBxDFCreationParams params) { if (params.is_aniso) - return create(params.Axy.x, params.Axy.y, params.ior0, params.ior1); + return create(params.A.x, params.A.y, params.ior0, params.ior1); else - return create(params.A, params.ior0, params.ior1); + return create(params.A.x, params.ior0, params.ior1); + } + + void init(SBxDFCreationParams params) + { + A = params.A; + ior0 = params.ior0; + ior1 = params.ior1; } scalar_type __eval_DG_wo_clamps(params_t params) diff --git a/include/nbl/builtin/hlsl/bxdf/transmission.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission.hlsl index 18d80e93aa..99e7e5f6a8 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission.hlsl @@ -68,6 +68,11 @@ struct SLambertianBxDF return create(); } + void init(SBxDFCreationParams params) + { + // do nothing + } + scalar_type __eval_pi_factored_out(scalar_type absNdotL) { return absNdotL; @@ -134,6 +139,11 @@ struct SSmoothDielectricBxDF return create(params.eta); } + void init(SBxDFCreationParams params) + { + eta = params.eta; + } + spectral_type eval(params_t params) { return (spectral_type)0; @@ -220,6 +230,12 @@ struct SSmoothDielectricBxDF return create(params.eta2, params.luminosityContributionHint); } + void init(SBxDFCreationParams params) + { + eta2 = params.eta2; + luminosityContributionHint = params.luminosityContributionHint; + } + spectral_type eval(params_t params) { return (spectral_type)0; @@ -317,9 +333,15 @@ struct SBeckmannDielectricBxDF static this_t create(SBxDFCreationParams params) { if (params.is_aniso) - return create(params.eta, params.Axy.x, params.Axy.y); + return create(params.eta, params.A.x, params.A.y); else - return create(params.eta, params.A); + return create(params.eta, params.A.x); + } + + void init(SBxDFCreationParams params) + { + A = params.A; + eta = params.eta; } spectral_type eval(params_t params) @@ -492,9 +514,15 @@ struct SGGXDielectricBxDF static this_t create(SBxDFCreationParams params) { if (params.is_aniso) - return create(params.eta, params.Axy.x, params.Axy.y); + return create(params.eta, params.A.x, params.A.y); else - return create(params.eta, params.A); + return create(params.eta, params.A.x); + } + + void init(SBxDFCreationParams params) + { + A = params.A; + eta = params.eta; } spectral_type eval(params_t params) From d9a00c9998e9ec33a1cdcbf132bcbf5c1dfc7652 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 20 Feb 2025 16:55:35 +0700 Subject: [PATCH 08/56] bug fixes --- examples_tests | 2 +- .../nbl/builtin/hlsl/bxdf/bxdf_traits.hlsl | 2 +- include/nbl/builtin/hlsl/bxdf/common.hlsl | 2 +- include/nbl/builtin/hlsl/bxdf/geom_smith.hlsl | 4 ++-- include/nbl/builtin/hlsl/bxdf/reflection.hlsl | 8 +++---- .../nbl/builtin/hlsl/bxdf/transmission.hlsl | 9 +++++--- .../hlsl/sampling/box_muller_transform.hlsl | 2 +- .../hlsl/sampling/concentric_mapping.hlsl | 2 +- .../projected_spherical_triangle.hlsl | 12 +++++----- .../hlsl/sampling/spherical_triangle.hlsl | 2 +- .../nbl/builtin/hlsl/shapes/rectangle.hlsl | 22 +++++++++---------- include/nbl/builtin/hlsl/shapes/triangle.hlsl | 4 ++-- 12 files changed, 37 insertions(+), 34 deletions(-) diff --git a/examples_tests b/examples_tests index a7350db7d7..2f77555ce4 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit a7350db7d7e422fa5086982b3327103c06cfbe44 +Subproject commit 2f77555ce484c2f8ecb390e68fc3f4c830b23ef7 diff --git a/include/nbl/builtin/hlsl/bxdf/bxdf_traits.hlsl b/include/nbl/builtin/hlsl/bxdf/bxdf_traits.hlsl index e63cf0113e..78b23830bc 100644 --- a/include/nbl/builtin/hlsl/bxdf/bxdf_traits.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/bxdf_traits.hlsl @@ -101,4 +101,4 @@ struct bxdf_traits > } } -#endif \ No newline at end of file +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index f4ae69aafc..6acdbab74b 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -874,7 +874,7 @@ struct SBxDFParams return retval; } - template && surface_interactions::Anisotropic) + template && surface_interactions::Anisotropic) static SBxDFParams create(LightSample _sample, Aniso interaction, BxDFClampMode clamp = BCM_NONE) { this_t retval; diff --git a/include/nbl/builtin/hlsl/bxdf/geom_smith.hlsl b/include/nbl/builtin/hlsl/bxdf/geom_smith.hlsl index 61aa10399c..5a6f6cdf26 100644 --- a/include/nbl/builtin/hlsl/bxdf/geom_smith.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/geom_smith.hlsl @@ -236,12 +236,12 @@ struct GGX scalar_type G1_wo_numerator(scalar_type NdotX, scalar_type NdotX2, scalar_type a2, scalar_type one_minus_a2) { - return 1.0 / (NdotX + ggx_devsh_part(NdotX2,a2,one_minus_a2)); + return 1.0 / (NdotX + devsh_part(NdotX2,a2,one_minus_a2)); } scalar_type G1_wo_numerator(scalar_type NdotX, scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2, scalar_type ax2, scalar_type ay2) { - return 1.0 / (NdotX + ggx_devsh_part(TdotX2, BdotX2, NdotX2, ax2, ay2)); + return 1.0 / (NdotX + devsh_part(TdotX2, BdotX2, NdotX2, ax2, ay2)); } scalar_type G1_wo_numerator(scalar_type NdotX, scalar_type devsh_part) diff --git a/include/nbl/builtin/hlsl/bxdf/reflection.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection.hlsl index 76d00c268c..8f4da11b05 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection.hlsl @@ -232,7 +232,7 @@ struct SBlinnPhongBxDF ndf::SAnisotropicParams ndfparams = ndf::SAnisotropicParams::create(params.NdotH, 1.0 / (1.0 - params.NdotH2), params.TdotH2, params.BdotH2, n.x, n.y); ndf::BlinnPhong blinn_phong; scalar_type DG = blinn_phong(ndfparams); - if (any>(a2 > (vector2_type)numeric_limits::min)) + if (any >(a2 > (vector2_type)numeric_limits::min)) { smith::SAnisotropicParams smithparams = smith::SAnisotropicParams::create(a2.x, a2.y, params.TdotV2, params.BdotV2, params.NdotV2, params.TdotL2, params.BdotL2, params.NdotL2, 0); smith::Beckmann beckmann; @@ -245,7 +245,7 @@ struct SBlinnPhongBxDF ndf::SIsotropicParams ndfparams = ndf::SIsotropicParams::create(n, params.NdotH, params.NdotH2); ndf::BlinnPhong blinn_phong; scalar_type NG = blinn_phong(ndfparams); - if (any>(a2 > (vector2_type)numeric_limits::min)) + if (any >(a2 > (vector2_type)numeric_limits::min)) { smith::SIsotropicParams smithparams = smith::SIsotropicParams::create(a2.x, params.NdotV2, params.NdotL2, 0); smith::Beckmann beckmann; @@ -386,7 +386,7 @@ struct SBeckmannBxDF ndf::SAnisotropicParams ndfparams = ndf::SAnisotropicParams::create(A.x, A.y, ax2, ay2, params.TdotH2, params.BdotH2, params.NdotH2); ndf::Beckmann beckmann_ndf; scalar_type NG = beckmann_ndf(ndfparams); - if (any>(A > (vector2_type)numeric_limits::min)) + if (any >(A > (vector2_type)numeric_limits::min)) { smith::SAnisotropicParams smithparams = smith::SAnisotropicParams::create(ax2, ay2, params.TdotV2, params.BdotV2, params.NdotV2, params.TdotL2, params.BdotL2, params.NdotL2, 0); smith::Beckmann beckmann_smith; @@ -629,7 +629,7 @@ struct SGGXBxDF ndf::SAnisotropicParams ndfparams = ndf::SAnisotropicParams::create(A.x, A.y, ax2, ay2, params.TdotH2, params.BdotH2, params.NdotH2); ndf::GGX ggx_ndf; scalar_type NG = ggx_ndf(ndfparams); - if (any>(A > (vector2_type)numeric_limits::min)) + if (any >(A > (vector2_type)numeric_limits::min)) { smith::SAnisotropicParams smithparams = smith::SAnisotropicParams::create(ax2, ay2, params.NdotV, params.TdotV2, params.BdotV2, params.NdotV2, params.NdotL, params.TdotL2, params.BdotL2, params.NdotL2); smith::GGX ggx_smith; diff --git a/include/nbl/builtin/hlsl/bxdf/transmission.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission.hlsl index 99e7e5f6a8..1d6bf8ead6 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission.hlsl @@ -110,8 +110,11 @@ struct SLambertianBxDF // microfacet bxdfs -template && IsotropicMicrofacetCache && AnisotropicMicrofacetCache) -struct SSmoothDielectricBxDF +template // NBL_FUNC_REQUIRES(Sample && IsotropicMicrofacetCache && AnisotropicMicrofacetCache) // dxc won't let me put this in +struct SSmoothDielectricBxDF; + +template +struct SSmoothDielectricBxDF { using this_t = SSmoothDielectricBxDF; using scalar_type = typename LightSample::scalar_type; @@ -200,7 +203,7 @@ struct SSmoothDielectricBxDF scalar_type eta; }; -template && IsotropicMicrofacetCache && AnisotropicMicrofacetCache) +template struct SSmoothDielectricBxDF { using this_t = SSmoothDielectricBxDF; diff --git a/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl b/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl index efa8d66e2b..57a18589fd 100644 --- a/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl +++ b/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl @@ -24,4 +24,4 @@ vector boxMullerTransform(vector xi, T stddev) } } -#endif \ No newline at end of file +#endif diff --git a/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl b/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl index 2b06581740..60865e7c8e 100644 --- a/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl +++ b/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl @@ -39,4 +39,4 @@ vector concentricMapping(vector _u) } } -#endif \ No newline at end of file +#endif diff --git a/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl b/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl index 5832e9aab2..945ca053b8 100644 --- a/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl +++ b/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl @@ -25,14 +25,14 @@ struct ProjectedSphericalTriangle using vector3_type = vector; using vector4_type = vector; - static ProjectedSphericalTriangle create(NBL_CONST_REG_ARG(shapes::SphericalTriangle) tri) + static ProjectedSphericalTriangle create(NBL_CONST_REF_ARG(shapes::SphericalTriangle) tri) { ProjectedSphericalTriangle retval; retval.tri = tri; return retval; } - vector4_type computeBilinearPatch(NBL_CONST_REG_ARG(vector3_type) receiverNormal, bool isBSDF) + vector4_type computeBilinearPatch(NBL_CONST_REF_ARG(vector3_type) receiverNormal, bool isBSDF) { const scalar_type minimumProjSolidAngle = 0.0; @@ -42,7 +42,7 @@ struct ProjectedSphericalTriangle return bxdfPdfAtVertex.yyxz; } - vector3_type generate(NBL_REG_ARG(scalar_type) rcpPdf, scalar_type solidAngle, NBL_CONST_REG_ARG(vector3_type) cos_vertices, NBL_CONST_REG_ARG(vector3_type) sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, NBL_CONST_REG_ARG(vector3_type) receiverNormal, bool isBSDF, NBL_CONST_REG_ARG(vector2_type) u) + vector3_type generate(NBL_REF_ARG(scalar_type) rcpPdf, scalar_type solidAngle, NBL_CONST_REF_ARG(vector3_type) cos_vertices, NBL_CONST_REF_ARG(vector3_type) sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, NBL_CONST_REF_ARG(vector3_type) receiverNormal, bool isBSDF, NBL_CONST_REF_ARG(vector2_type) u) { // pre-warp according to proj solid angle approximation vector4_type patch = computeBilinearPatch(receiverNormal, isBSDF); @@ -56,7 +56,7 @@ struct ProjectedSphericalTriangle return L; } - vector3_type generate(NBL_REG_ARG(scalar_type) rcpPdf, NBL_CONST_REG_ARG(vector3_type) receiverNormal, bool isBSDF, NBL_CONST_REG_ARG(vector2_type) u) + vector3_type generate(NBL_REF_ARG(scalar_type) rcpPdf, NBL_CONST_REF_ARG(vector3_type) receiverNormal, bool isBSDF, NBL_CONST_REF_ARG(vector2_type) u) { scalar_type cos_a, cos_c, csc_b, csc_c; vector3_type cos_vertices, sin_vertices; @@ -64,7 +64,7 @@ struct ProjectedSphericalTriangle return generate(rcpPdf, solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, receiverNormal, isBSDF, u); } - scalar_type pdf(scalar_type solidAngle, NBL_CONST_REG_ARG(vector3_type) cos_vertices, NBL_CONST_REG_ARG(vector3_type) sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, NBL_CONST_REG_ARG(vector3_type) receiverNormal, bool receiverWasBSDF, NBL_CONST_REG_ARG(vector3_type) L) + scalar_type pdf(scalar_type solidAngle, NBL_CONST_REF_ARG(vector3_type) cos_vertices, NBL_CONST_REF_ARG(vector3_type) sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, NBL_CONST_REF_ARG(vector3_type) receiverNormal, bool receiverWasBSDF, NBL_CONST_REF_ARG(vector3_type) L) { scalar_type pdf; const vector2_type u = tri.generateInverse(pdf, solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, L); @@ -74,7 +74,7 @@ struct ProjectedSphericalTriangle return pdf * bilinear.pdf(u); } - scalar_type pdf(NBL_CONST_REG_ARG(vector3_type) receiverNormal, bool receiverWasBSDF, NBL_CONST_REG_ARG(vector3_type) L) + scalar_type pdf(NBL_CONST_REF_ARG(vector3_type) receiverNormal, bool receiverWasBSDF, NBL_CONST_REF_ARG(vector3_type) L) { scalar_type pdf; const vector2_type u = tri.generateInverse(pdf, L); diff --git a/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl b/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl index 9501cdc3d1..1d4fda454d 100644 --- a/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl +++ b/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl @@ -24,7 +24,7 @@ struct SphericalTriangle using vector2_type = vector; using vector3_type = vector; - static SphericalTriangle create(NBL_CONST_REG_ARG(shapes::SphericalTriangle) tri) + static SphericalTriangle create(NBL_CONST_REF_ARG(shapes::SphericalTriangle) tri) { SphericalTriangle retval; retval.tri = tri; diff --git a/include/nbl/builtin/hlsl/shapes/rectangle.hlsl b/include/nbl/builtin/hlsl/shapes/rectangle.hlsl index a61f23cafa..47d3927f31 100644 --- a/include/nbl/builtin/hlsl/shapes/rectangle.hlsl +++ b/include/nbl/builtin/hlsl/shapes/rectangle.hlsl @@ -16,25 +16,25 @@ namespace hlsl namespace shapes { -template +template struct SphericalRectangle { - using scalar_type = T; - using vector3_type = vector; - using vector4_type = vector; - using matrix3x3_type = matrix; + using scalar_type = Scalar; + using vector3_type = vector; + using vector4_type = vector; + using matrix3x3_type = matrix; - static SphericalRectangle create(NBL_CONST_REF_ARG(vector3_type) observer, NBL_CONST_REF_ARG(vector3_type) rectangleOrigin, NBL_CONST_REF_ARG(matrix3x3_type) basis) + static SphericalRectangle create(NBL_CONST_REF_ARG(vector3_type) observer, NBL_CONST_REF_ARG(vector3_type) rectangleOrigin, NBL_CONST_REF_ARG(matrix3x3_type) basis) { - SphericalRectangle retval; + SphericalRectangle retval; retval.r0 = nbl::hlsl::mul(basis, rectangleOrigin - observer); return retval; } - static SphericalRectangle create(NBL_CONST_REF_ARG(vector3_type) observer, NBL_CONST_REF_ARG(vector3_type) rectangleOrigin, NBL_CONST_REF_ARG(vector3_type) T, NBL_CONST_REF_ARG(vector3_type) B, NBL_CONST_REF_ARG(vector3_type) N) + static SphericalRectangle create(NBL_CONST_REF_ARG(vector3_type) observer, NBL_CONST_REF_ARG(vector3_type) rectangleOrigin, NBL_CONST_REF_ARG(vector3_type) T, NBL_CONST_REF_ARG(vector3_type) B, NBL_CONST_REF_ARG(vector3_type) N) { - SphericalRectangle retval; - matrix3x3_type TBN = nbl::hlsl::transpose(matrix3x3_type(T, B, isotropic_type::N)); + SphericalRectangle retval; + matrix3x3_type TBN = nbl::hlsl::transpose(matrix3x3_type(T, B, N)); retval.r0 = nbl::hlsl::mul(TBN, rectangleOrigin - observer); return retval; } @@ -53,7 +53,7 @@ struct SphericalRectangle } vector3_type r0; -} +}; } } diff --git a/include/nbl/builtin/hlsl/shapes/triangle.hlsl b/include/nbl/builtin/hlsl/shapes/triangle.hlsl index 59ba508596..d904ed7246 100644 --- a/include/nbl/builtin/hlsl/shapes/triangle.hlsl +++ b/include/nbl/builtin/hlsl/shapes/triangle.hlsl @@ -80,8 +80,8 @@ struct SphericalTriangle // TODO: above dot products are in the wrong order, either work out which is which, or try all 6 permutations till it works cos_vertices = nbl::hlsl::clamp((cos_sides - cos_sides.yzx * cos_sides.zxy) * csc_sides.yzx * csc_sides.zxy, (vector3_type)(-1.f), (vector3_type)1.f); - matrix mat = - const vector3_type externalProducts = nbl::hlsl::abs(nbl::hlsl::transpose(awayFromEdgePlane) * receiverNormal); + matrix awayFromEdgePlane = matrix(awayFromEdgePlane0, awayFromEdgePlane1, awayFromEdgePlane2); + const vector3_type externalProducts = nbl::hlsl::abs(/* transposed already */awayFromEdgePlane * receiverNormal); const vector3_type pyramidAngles = acos(cos_sides); return nbl::hlsl::dot(pyramidAngles, externalProducts) / (2.f * numbers::pi); From ffe9029c78af7b2d1bf4d77cbf900d632960edd9 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 21 Feb 2025 14:16:59 +0700 Subject: [PATCH 09/56] fix sampling bugs #2 --- examples_tests | 2 +- include/nbl/builtin/hlsl/sampling/bilinear.hlsl | 2 +- include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl | 2 +- include/nbl/builtin/hlsl/sampling/linear.hlsl | 4 ++-- .../builtin/hlsl/sampling/projected_spherical_triangle.hlsl | 1 + include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/examples_tests b/examples_tests index 2f77555ce4..99aed4777c 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 2f77555ce484c2f8ecb390e68fc3f4c830b23ef7 +Subproject commit 99aed4777c208c5acc4e66bb7ea8dc48f814c8d0 diff --git a/include/nbl/builtin/hlsl/sampling/bilinear.hlsl b/include/nbl/builtin/hlsl/sampling/bilinear.hlsl index 1d5f9a91e8..3542e2dfef 100644 --- a/include/nbl/builtin/hlsl/sampling/bilinear.hlsl +++ b/include/nbl/builtin/hlsl/sampling/bilinear.hlsl @@ -31,7 +31,7 @@ struct Bilinear return retval; } - vector2_type generate(NBL_REG_ARG(scalar_type) rcpPdf, NBL_CONST_REF_ARG(vector2_type) u) + vector2_type generate(NBL_REF_ARG(scalar_type) rcpPdf, NBL_CONST_REF_ARG(vector2_type) u) { const vector2_type twiceAreasUnderXCurve = vector2_type(bilinearCoeffs[0] + bilinearCoeffs[1], bilinearCoeffs[2] + bilinearCoeffs[3]); Linear lineary = Linear::create(twiceAreasUnderXCurve); diff --git a/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl b/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl index 57a18589fd..dcac2279be 100644 --- a/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl +++ b/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl @@ -17,7 +17,7 @@ template vector boxMullerTransform(vector xi, T stddev) { T sinPhi, cosPhi; - nbl::hlsl::sincos(2.0 * numbers::pi * xi.y - numbers::pi, sinPhi, cosPhi); + math::sincos(2.0 * numbers::pi * xi.y - numbers::pi, sinPhi, cosPhi); return vector(cosPhi, sinPhi) * nbl::hlsl::sqrt(-2.0 * nbl::hlsl::log(xi.x)) * stddev; } diff --git a/include/nbl/builtin/hlsl/sampling/linear.hlsl b/include/nbl/builtin/hlsl/sampling/linear.hlsl index 8b9b3fb058..12d445eefe 100644 --- a/include/nbl/builtin/hlsl/sampling/linear.hlsl +++ b/include/nbl/builtin/hlsl/sampling/linear.hlsl @@ -2,8 +2,8 @@ // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_BUILTIN_HLSL_SAMPLING_BILINEAR_INCLUDED_ -#define _NBL_BUILTIN_HLSL_SAMPLING_BILINEAR_INCLUDED_ +#ifndef _NBL_BUILTIN_HLSL_SAMPLING_LINEAR_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_LINEAR_INCLUDED_ #include #include diff --git a/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl b/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl index 945ca053b8..cfc96dc9cb 100644 --- a/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl +++ b/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl @@ -8,6 +8,7 @@ #include #include #include +#include #include namespace nbl diff --git a/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl b/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl index 83224bfabd..c42bf8e464 100644 --- a/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl +++ b/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl @@ -25,7 +25,7 @@ struct SphericalRectangle using vector3_type = vector; using vector4_type = vector; - static SphericalRectangle create(NBL_CONST_REG_ARG(shapes::SphericalRectangle) rect) + static SphericalRectangle create(NBL_CONST_REF_ARG(shapes::SphericalRectangle) rect) { SphericalRectangle retval; retval.rect = rect; From b2073128e066d3c068362c9b505a2fe14e1f43fb Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 21 Feb 2025 16:57:49 +0700 Subject: [PATCH 10/56] fix rank type trait for matrix/vector --- examples_tests | 2 +- include/nbl/builtin/hlsl/type_traits.hlsl | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/examples_tests b/examples_tests index a1a8ec03bc..a802a97943 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit a1a8ec03bcc90c0f46ffba6d7c50e05a633834b4 +Subproject commit a802a97943bd9e17187a306f8058c21d2774678b diff --git a/include/nbl/builtin/hlsl/type_traits.hlsl b/include/nbl/builtin/hlsl/type_traits.hlsl index 3443a9d9b7..17d0ed827e 100644 --- a/include/nbl/builtin/hlsl/type_traits.hlsl +++ b/include/nbl/builtin/hlsl/type_traits.hlsl @@ -612,7 +612,19 @@ NBL_CONSTEXPR bool is_matrix_v = is_matrix::value; #ifdef __HLSL_VERSION template -struct rank : integral_constant::value ? 2 : (is_vector::value ? 1 : 0)> { }; +struct rank : integral_constant, + uint64_t, + 2ull, + conditional_value< + is_vector_v, + uint64_t, + 1ull, + 0ull + >::value + >::value +> { }; template struct rank : integral_constant::value> { }; From 69a257d878df518902acd56b3be52e8a28c35337 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 25 Feb 2025 16:57:01 +0700 Subject: [PATCH 11/56] temporary fix for dxc bug issue 7154 --- examples_tests | 2 +- include/nbl/builtin/hlsl/bxdf/common.hlsl | 38 +++++++++++------------ include/nbl/builtin/hlsl/limits.hlsl | 4 +-- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/examples_tests b/examples_tests index 3827fd3c33..f97757bffc 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 3827fd3c330eaac4c11478330cfb0f4a362f99c6 +Subproject commit f97757bffcc28ad208a10dfb485214b8d9e1fdd1 diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index 6acdbab74b..6e5174f73c 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -312,8 +312,8 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createFromTangentSpace(pV,rdirinfo,frame)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(rdirinfo,pVdotL,pV)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(rdirinfo,pVdotL,pV,pV,pV)), ::nbl::hlsl::is_same_v, T)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create(pV,iso)), ::nbl::hlsl::is_same_v, T)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create(pV,aniso)), ::nbl::hlsl::is_same_v, T)) + //((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create(pV,iso)), ::nbl::hlsl::is_same_v, T)) // NOTE: temporarily commented out due to dxc bug https://github.com/microsoft/DirectXShaderCompiler/issues/7154 + //((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create(pV,aniso)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getTangentSpaceL()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) ) && surface_interactions::Anisotropic && surface_interactions::Isotropic && ray_dir_info::Basic; @@ -380,21 +380,21 @@ struct SLightSample return retval; } - // overloads for surface_interactions - template - static this_t create(NBL_CONST_REF_ARG(vector3_type) L, NBL_CONST_REF_ARG(surface_interactions::SIsotropic) interaction) - { - const vector3_type V = interaction.V.getDirection(); - const scalar_type VdotL = nbl::hlsl::dot(V,L); - return create(L, VdotL, interaction.N); - } - template - static this_t create(NBL_CONST_REF_ARG(vector3_type) L, NBL_CONST_REF_ARG(surface_interactions::SAnisotropic) interaction) - { - const vector3_type V = interaction.V.getDirection(); - const scalar_type VdotL = nbl::hlsl::dot(V,L); - return create(L,VdotL,interaction.T,interaction.B,interaction.N); - } + // overloads for surface_interactions, NOTE: temporarily commented out due to dxc bug https://github.com/microsoft/DirectXShaderCompiler/issues/7154 + // template + // static this_t create(NBL_CONST_REF_ARG(vector3_type) L, NBL_CONST_REF_ARG(surface_interactions::SIsotropic) interaction) + // { + // const vector3_type V = interaction.V.getDirection(); + // const scalar_type VdotL = nbl::hlsl::dot(V,L); + // return create(L, VdotL, interaction.N); + // } + // template + // static this_t create(NBL_CONST_REF_ARG(vector3_type) L, NBL_CONST_REF_ARG(surface_interactions::SAnisotropic) interaction) + // { + // const vector3_type V = interaction.V.getDirection(); + // const scalar_type VdotL = nbl::hlsl::dot(V,L); + // return create(L,VdotL,interaction.T,interaction.B,interaction.N); + // } // vector3_type getTangentSpaceL() NBL_CONST_MEMBER_FUNC { @@ -875,7 +875,7 @@ struct SBxDFParams } template && surface_interactions::Anisotropic) - static SBxDFParams create(LightSample _sample, Aniso interaction, BxDFClampMode clamp = BCM_NONE) + static this_t create(LightSample _sample, Aniso interaction, BxDFClampMode clamp = BCM_NONE) { this_t retval; retval.NdotV = clamp == BCM_ABS ? abs(interaction.NdotV) : @@ -922,7 +922,7 @@ struct SBxDFParams } template && surface_interactions::Anisotropic && AnisotropicMicrofacetCache) - static SBxDFParams create(LightSample _sample, Aniso interaction, Cache cache, BxDFClampMode clamp = BCM_NONE) + static this_t create(LightSample _sample, Aniso interaction, Cache cache, BxDFClampMode clamp = BCM_NONE) { this_t retval; retval.NdotH = cache.NdotH; diff --git a/include/nbl/builtin/hlsl/limits.hlsl b/include/nbl/builtin/hlsl/limits.hlsl index 146957dc3e..5fe682c9e3 100644 --- a/include/nbl/builtin/hlsl/limits.hlsl +++ b/include/nbl/builtin/hlsl/limits.hlsl @@ -129,7 +129,7 @@ struct num_base : type_identity NBL_CONSTEXPR_STATIC_INLINE int32_t float_max_decimal_exponent = 4*S16 + 30*S32 + 232*S64; NBL_CONSTEXPR_STATIC_INLINE int32_t float_exponent_bits = 8 * size - 1 - (float_digits-1); - NBL_CONSTEXPR_STATIC_INLINE int32_t float_max_exponent = 1 << (float_exponent_bits-1); + NBL_CONSTEXPR_STATIC_INLINE int32_t float_max_exponent = int32_t(1) << (float_exponent_bits-1); NBL_CONSTEXPR_STATIC_INLINE int32_t float_min_exponent = 3 - float_max_exponent; NBL_CONSTEXPR_STATIC_INLINE bool is_bool = is_same::value; @@ -146,7 +146,7 @@ struct num_base : type_identity // (TODO) think about what this means for HLSL // identifies floating-point types that can represent the special value "quiet not-a-number" (NaN) - NBL_CONSTEXPR_STATIC_INLINE bool has_quiet_NaN = !is_integer; + NBL_CONSTEXPR_STATIC_INLINE bool has_quiet_NaN = !is_integer; // identifies floating-point types that can represent the special value "signaling not-a-number" (NaN) NBL_CONSTEXPR_STATIC_INLINE bool has_signaling_NaN = !is_integer; // identifies the denormalization style used by the floating-point type From 866e6d7f15ce25e0573341a5d8631354f5b081a0 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 26 Feb 2025 16:55:23 +0700 Subject: [PATCH 12/56] some bug fixes again --- examples_tests | 2 +- include/nbl/builtin/hlsl/bxdf/reflection.hlsl | 20 ++++++------ .../nbl/builtin/hlsl/bxdf/transmission.hlsl | 32 +++++++++---------- .../nbl/builtin/hlsl/sampling/bilinear.hlsl | 3 +- .../hlsl/sampling/concentric_mapping.hlsl | 2 +- .../projected_spherical_triangle.hlsl | 12 ++++--- .../hlsl/sampling/spherical_rectangle.hlsl | 2 +- .../nbl/builtin/hlsl/shapes/rectangle.hlsl | 2 +- include/nbl/builtin/hlsl/shapes/triangle.hlsl | 2 +- 9 files changed, 40 insertions(+), 37 deletions(-) diff --git a/examples_tests b/examples_tests index f97757bffc..8e759f24d5 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit f97757bffcc28ad208a10dfb485214b8d9e1fdd1 +Subproject commit 8e759f24d5b386291660f50af1c04efbff3eff08 diff --git a/include/nbl/builtin/hlsl/bxdf/reflection.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection.hlsl index 8f4da11b05..4609216144 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection.hlsl @@ -18,13 +18,13 @@ namespace reflection { // still need these? -template && surface_interactions::Isotropic && surface_interactions::Anisotropic && ray_dir_info::Basic && is_scalar_v) LightSample cos_generate(NBL_CONST_REF_ARG(Iso) interaction) { return LightSample(interaction.V.reflect(interaction.N,interaction.NdotV),interaction.NdotV,interaction.N); } -template && surface_interactions::Isotropic && surface_interactions::Anisotropic && ray_dir_info::Basic && is_scalar_v) LightSample cos_generate(NBL_CONST_REF_ARG(Aniso) interaction) { @@ -441,14 +441,14 @@ struct SBeckmannBxDF scalar_type sinTheta = sqrt(1.0 - cosTheta * cosTheta); scalar_type tanTheta = sinTheta / cosTheta; scalar_type cotTheta = 1.0 / tanTheta; - + scalar_type a = -1.0; scalar_type c = erf(cosTheta); scalar_type sample_x = max(u.x, 1.0e-6); scalar_type theta = acos(cosTheta); scalar_type fit = 1.0 + theta * (-0.876 + theta * (0.4265 - 0.0594*theta)); scalar_type b = c - (1.0 + c) * pow(1.0-sample_x, fit); - + scalar_type normalization = 1.0 / (1.0 + c + numbers::inv_sqrtpi * tanTheta * exp(-cosTheta*cosTheta)); const int ITER_THRESHOLD = 10; @@ -475,7 +475,7 @@ struct SBeckmannBxDF slope.x = erfInv(b); slope.y = erfInv(2.0 * max(u.y, 1.0e-6) - 1.0); } - + scalar_type sinTheta = sqrt(1.0 - V.z*V.z); scalar_type cosPhi = sinTheta==0.0 ? 1.0 : clamp(V.x/sinTheta, -1.0, 1.0); scalar_type sinPhi = sinTheta==0.0 ? 0.0 : clamp(V.y/sinTheta, -1.0, 1.0); @@ -494,7 +494,7 @@ struct SBeckmannBxDF { const vector3_type localV = interaction.getTangentSpaceV(); const vector3_type H = __generate(localV, u); - + cache = anisocache_type::create(localV, H); ray_dir_info_type localL; localL.direction = math::reflect(localV, H, cache.VdotH); @@ -558,7 +558,7 @@ struct SBeckmannBxDF const spectral_type reflectance = fresnelConductor(ior0, ior1, params.VdotH); quo = reflectance * G2_over_G1; } - + return quotient_pdf_type::create(quo, _pdf); } @@ -679,7 +679,7 @@ struct SGGXBxDF scalar_type t2 = r * sin(phi); scalar_type s = 0.5 * (1.0 + V.z); t2 = (1.0 - s)*sqrt(1.0 - t1*t1) + s*t2; - + //reprojection onto hemisphere //TODO try it wothout the max(), not sure if -t1*t1-t2*t2>-1.0 vector3_type H = t1*T1 + t2*T2 + sqrt(max(0.0, 1.0-t1*t1-t2*t2))*V; @@ -691,7 +691,7 @@ struct SGGXBxDF { const vector3_type localV = interaction.getTangentSpaceV(); const vector3_type H = __generate(localV, u); - + cache = anisocache_type::create(localV, H); ray_dir_info_type localL; localL.direction = math::reflect(localV, H, cache.VdotH); @@ -753,7 +753,7 @@ struct SGGXBxDF const spectral_type reflectance = fresnelConductor(ior0, ior1, params.VdotH); quo = reflectance * G2_over_G1; } - + return quotient_pdf_type::create(quo, _pdf); } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission.hlsl index 1d6bf8ead6..38de552f00 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission.hlsl @@ -17,13 +17,13 @@ namespace bxdf namespace transmission { -template && surface_interactions::Isotropic && surface_interactions::Anisotropic && ray_dir_info::Basic && is_scalar_v) LightSample cos_generate(NBL_CONST_REF_ARG(Iso) interaction) { return LightSample(interaction.V.transmit(),-1.f,interaction.N); } -template && surface_interactions::Isotropic && surface_interactions::Anisotropic && ray_dir_info::Basic && is_scalar_v) LightSample cos_generate(NBL_CONST_REF_ARG(Aniso) interaction) { @@ -169,7 +169,7 @@ struct SSmoothDielectricBxDF scalar_type orientedEta, rcpOrientedEta; const bool backside = math::getOrientedEtas(orientedEta, rcpOrientedEta, interaction.NdotV, eta); bool dummy; - return __generate_wo_clamps(interaction.V.direction, interaction.T, interaction.B, interaction.N, backside, interaction.NdotV, + return __generate_wo_clamps(interaction.V.direction, interaction.T, interaction.B, interaction.N, backside, interaction.NdotV, interaction.NdotV, interaction.NdotV*interaction.NdotV, u, rcpOrientedEta, orientedEta*orientedEta, rcpOrientedEta*rcpOrientedEta, dummy); } @@ -178,7 +178,7 @@ struct SSmoothDielectricBxDF scalar_type orientedEta, rcpOrientedEta; const bool backside = math::getOrientedEtas(orientedEta, rcpOrientedEta, interaction.NdotV, eta); bool dummy; - return __generate_wo_clamps(interaction.V.direction, interaction.T, interaction.B, interaction.N, backside, interaction.NdotV, + return __generate_wo_clamps(interaction.V.direction, interaction.T, interaction.B, interaction.N, backside, interaction.NdotV, nbl::hlsl::abs(interaction.NdotV), interaction.NdotV*interaction.NdotV, u, rcpOrientedEta, orientedEta*orientedEta, rcpOrientedEta*rcpOrientedEta, dummy); } @@ -191,7 +191,7 @@ struct SSmoothDielectricBxDF quotient_pdf_type quotient_and_pdf(params_t params) { const bool transmitted = isTransmissionPath(params.uNdotV, params.uNdotL); - + scalar_type dummy, rcpOrientedEta; const bool backside = math::getOrientedEtas(dummy, rcpOrientedEta, params.NdotV, eta); @@ -245,7 +245,7 @@ struct SSmoothDielectricBxDF } // usually `luminosityContributionHint` would be the Rec.709 luma coefficients (the Y row of the RGB to CIE XYZ matrix) - // its basically a set of weights that determine + // its basically a set of weights that determine // assert(1.0==luminosityContributionHint.r+luminosityContributionHint.g+luminosityContributionHint.b); // `remainderMetadata` is a variable which the generator function returns byproducts of sample generation that would otherwise have to be redundantly calculated `quotient_and_pdf` sample_type __generate_wo_clamps(vector3_type V, vector3_type T, vector3_type B, vector3_type N, scalar_type NdotV, scalar_type absNdotV, NBL_REF_ARG(vector3_type) u, spectral_type eta2, spectral_type luminosityContributionHint, NBL_REF_ARG(spectral_type) remainderMetadata) @@ -259,7 +259,7 @@ struct SSmoothDielectricBxDF scalar_type rcpChoiceProb; const bool transmitted = math::partitionRandVariable(reflectionProb, u.z, rcpChoiceProb); remainderMetadata = (transmitted ? ((spectral_type)(1.0) - reflectance) : reflectance) * rcpChoiceProb; - + ray_dir_info_type L; L.direction = (transmitted ? (vector3_type)(0.0) : N * 2.0f * NdotV) - V; return sample_type::create(L, nbl::hlsl::dot(V, L.direction), T, B, N); @@ -352,7 +352,7 @@ struct SBeckmannDielectricBxDF scalar_type orientedEta, dummy; const bool backside = math::getOrientedEtas(orientedEta, dummy, params.VdotH, eta); const scalar_type orientedEta2 = orientedEta * orientedEta; - + const scalar_type VdotHLdotH = params.VdotH * params.LdotH; const bool transmitted = VdotHLdotH < 0.0; @@ -373,10 +373,10 @@ struct SBeckmannDielectricBxDF { const scalar_type localVdotH = nbl::hlsl::dot(localV,H); const scalar_type reflectance = fresnelDielectric_common(orientedEta2,nbl::hlsl::abs(localVdotH)); - + scalar_type rcpChoiceProb; bool transmitted = math::partitionRandVariable(reflectance, u.z, rcpChoiceProb); - + cache = anisocache_type::create(localV, H); const scalar_type VdotH = cache.VdotH; @@ -419,7 +419,7 @@ struct SBeckmannDielectricBxDF const bool transmitted = VdotHLdotH < 0.0; const scalar_type reflectance = fresnelDielectric_common(orientedEta2, nbl::hlsl::abs(params.VdotH)); - + scalar_type ndf, lambda; if (params.is_aniso) { @@ -443,7 +443,7 @@ struct SBeckmannDielectricBxDF smith::Beckmann beckmann_smith; lambda = beckmann_smith.Lambda(params.NdotV2, a2); } - + return smith::VNDF_pdf_wo_clamps >(ndf,lambda,params.NdotV,transmitted,params.VdotH,params.LdotH,VdotHLdotH,orientedEta,reflectance,onePlusLambda_V); } @@ -533,7 +533,7 @@ struct SGGXDielectricBxDF scalar_type orientedEta, dummy; const bool backside = math::getOrientedEtas(orientedEta, dummy, params.VdotH, eta); const scalar_type orientedEta2 = orientedEta * orientedEta; - + const scalar_type VdotHLdotH = params.VdotH * params.LdotH; const bool transmitted = VdotHLdotH < 0.0; @@ -560,10 +560,10 @@ struct SGGXDielectricBxDF { const scalar_type localVdotH = nbl::hlsl::dot(localV,H); const scalar_type reflectance = fresnelDielectric_common(orientedEta2,nbl::hlsl::abs(localVdotH)); - + scalar_type rcpChoiceProb; bool transmitted = math::partitionRandVariable(reflectance, u.z, rcpChoiceProb); - + cache = anisocache_type::create(localV, H); const scalar_type VdotH = cache.VdotH; @@ -640,7 +640,7 @@ struct SGGXDielectricBxDF { const scalar_type ax2 = A.x*A.x; const scalar_type ay2 = A.y*A.y; - + scalar_type _pdf = pdf(params); smith::GGX ggx_smith; diff --git a/include/nbl/builtin/hlsl/sampling/bilinear.hlsl b/include/nbl/builtin/hlsl/sampling/bilinear.hlsl index 3542e2dfef..42a923f650 100644 --- a/include/nbl/builtin/hlsl/sampling/bilinear.hlsl +++ b/include/nbl/builtin/hlsl/sampling/bilinear.hlsl @@ -31,8 +31,9 @@ struct Bilinear return retval; } - vector2_type generate(NBL_REF_ARG(scalar_type) rcpPdf, NBL_CONST_REF_ARG(vector2_type) u) + vector2_type generate(NBL_REF_ARG(scalar_type) rcpPdf, NBL_CONST_REF_ARG(vector2_type) _u) { + vector2_type u = _u; const vector2_type twiceAreasUnderXCurve = vector2_type(bilinearCoeffs[0] + bilinearCoeffs[1], bilinearCoeffs[2] + bilinearCoeffs[3]); Linear lineary = Linear::create(twiceAreasUnderXCurve); u.y = lineary.generate(u.y); diff --git a/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl b/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl index 60865e7c8e..dfc7dd6bcb 100644 --- a/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl +++ b/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl @@ -16,7 +16,7 @@ vector concentricMapping(vector _u) vector u = 2.0f * _u - 1.0f; vector p; - if (u == (vector)(0.0)) + if (nbl::hlsl::all >(u == (vector)(0.0))) p = (vector)(0.0); else { diff --git a/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl b/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl index cfc96dc9cb..f2f29ed12b 100644 --- a/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl +++ b/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl @@ -36,22 +36,23 @@ struct ProjectedSphericalTriangle vector4_type computeBilinearPatch(NBL_CONST_REF_ARG(vector3_type) receiverNormal, bool isBSDF) { const scalar_type minimumProjSolidAngle = 0.0; - + matrix m = matrix(tri.vertex0, tri.vertex1, tri.vertex2); const vector3_type bxdfPdfAtVertex = math::conditionalAbsOrMax(isBSDF, nbl::hlsl::mul(m, receiverNormal), (vector3_type)minimumProjSolidAngle); return bxdfPdfAtVertex.yyxz; } - vector3_type generate(NBL_REF_ARG(scalar_type) rcpPdf, scalar_type solidAngle, NBL_CONST_REF_ARG(vector3_type) cos_vertices, NBL_CONST_REF_ARG(vector3_type) sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, NBL_CONST_REF_ARG(vector3_type) receiverNormal, bool isBSDF, NBL_CONST_REF_ARG(vector2_type) u) + vector3_type generate(NBL_REF_ARG(scalar_type) rcpPdf, scalar_type solidAngle, NBL_CONST_REF_ARG(vector3_type) cos_vertices, NBL_CONST_REF_ARG(vector3_type) sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, NBL_CONST_REF_ARG(vector3_type) receiverNormal, bool isBSDF, NBL_CONST_REF_ARG(vector2_type) _u) { + vector2_type u; // pre-warp according to proj solid angle approximation vector4_type patch = computeBilinearPatch(receiverNormal, isBSDF); Bilinear bilinear = Bilinear::create(patch); u = bilinear.generate(rcpPdf, u); // now warp the points onto a spherical triangle - const vector3_type L = tri.generate(solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, u); + const vector3_type L = sphtri.generate(solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, u); rcpPdf *= solidAngle; return L; @@ -68,7 +69,7 @@ struct ProjectedSphericalTriangle scalar_type pdf(scalar_type solidAngle, NBL_CONST_REF_ARG(vector3_type) cos_vertices, NBL_CONST_REF_ARG(vector3_type) sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, NBL_CONST_REF_ARG(vector3_type) receiverNormal, bool receiverWasBSDF, NBL_CONST_REF_ARG(vector3_type) L) { scalar_type pdf; - const vector2_type u = tri.generateInverse(pdf, solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, L); + const vector2_type u = sphtri.generateInverse(pdf, solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, L); vector4_type patch = computeBilinearPatch(receiverNormal, receiverWasBSDF); Bilinear bilinear = Bilinear::create(patch); @@ -78,7 +79,7 @@ struct ProjectedSphericalTriangle scalar_type pdf(NBL_CONST_REF_ARG(vector3_type) receiverNormal, bool receiverWasBSDF, NBL_CONST_REF_ARG(vector3_type) L) { scalar_type pdf; - const vector2_type u = tri.generateInverse(pdf, L); + const vector2_type u = sphtri.generateInverse(pdf, L); vector4_type patch = computeBilinearPatch(receiverNormal, receiverWasBSDF); Bilinear bilinear = Bilinear::create(patch); @@ -86,6 +87,7 @@ struct ProjectedSphericalTriangle } shapes::SphericalTriangle tri; + sampling::SphericalTriangle sphtri; }; } diff --git a/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl b/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl index c42bf8e464..cca3f21dd9 100644 --- a/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl +++ b/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl @@ -35,7 +35,7 @@ struct SphericalRectangle vector2_type generate(NBL_CONST_REF_ARG(vector2_type) rectangleExtents, NBL_CONST_REF_ARG(vector2_type) uv, NBL_REF_ARG(scalar_type) S) { const vector4_type denorm_n_z = vector4_type(-rect.r0.y, rect.r0.x + rectangleExtents.x, rect.r0.y + rectangleExtents.y, -rect.r0.x); - const vector4_type n_z = denorm_n_z / nbl::hlsl::sqrt(vector4_type(rect.r0.z * rect.r0.z) + denorm_n_z * denorm_n_z); + const vector4_type n_z = denorm_n_z / nbl::hlsl::sqrt((vector4_type)(rect.r0.z * rect.r0.z) + denorm_n_z * denorm_n_z); const vector4_type cosGamma = vector4_type( -n_z[0] * n_z[1], -n_z[1] * n_z[2], diff --git a/include/nbl/builtin/hlsl/shapes/rectangle.hlsl b/include/nbl/builtin/hlsl/shapes/rectangle.hlsl index 47d3927f31..f1a1e37575 100644 --- a/include/nbl/builtin/hlsl/shapes/rectangle.hlsl +++ b/include/nbl/builtin/hlsl/shapes/rectangle.hlsl @@ -43,7 +43,7 @@ struct SphericalRectangle { const vector4_type denorm_n_z = vector4_type(-r0.y, r0.x + rectangleExtents.x, r0.y + rectangleExtents.y, -r0.x); const vector4_type n_z = denorm_n_z / nbl::hlsl::sqrt((vector4_type)(r0.z * r0.z) + denorm_n_z * denorm_n_z); - const vector4_type cosGamma = vec4( + const vector4_type cosGamma = vector4_type( -n_z[0] * n_z[1], -n_z[1] * n_z[2], -n_z[2] * n_z[3], diff --git a/include/nbl/builtin/hlsl/shapes/triangle.hlsl b/include/nbl/builtin/hlsl/shapes/triangle.hlsl index d904ed7246..67fdfa0476 100644 --- a/include/nbl/builtin/hlsl/shapes/triangle.hlsl +++ b/include/nbl/builtin/hlsl/shapes/triangle.hlsl @@ -81,7 +81,7 @@ struct SphericalTriangle cos_vertices = nbl::hlsl::clamp((cos_sides - cos_sides.yzx * cos_sides.zxy) * csc_sides.yzx * csc_sides.zxy, (vector3_type)(-1.f), (vector3_type)1.f); matrix awayFromEdgePlane = matrix(awayFromEdgePlane0, awayFromEdgePlane1, awayFromEdgePlane2); - const vector3_type externalProducts = nbl::hlsl::abs(/* transposed already */awayFromEdgePlane * receiverNormal); + const vector3_type externalProducts = nbl::hlsl::abs(nbl::hlsl::mul(/* transposed already */awayFromEdgePlane, receiverNormal)); const vector3_type pyramidAngles = acos(cos_sides); return nbl::hlsl::dot(pyramidAngles, externalProducts) / (2.f * numbers::pi); From 85e955f3ceb7bc7a74647c3c06eee283603028aa Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 27 Feb 2025 10:38:37 +0700 Subject: [PATCH 13/56] fix wrong template usage --- include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl b/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl index dfc7dd6bcb..437f9fe963 100644 --- a/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl +++ b/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl @@ -16,7 +16,7 @@ vector concentricMapping(vector _u) vector u = 2.0f * _u - 1.0f; vector p; - if (nbl::hlsl::all >(u == (vector)(0.0))) + if (nbl::hlsl::all >(u == (vector)(0.0))) p = (vector)(0.0); else { From 1c773d9bb78b3124ff0ba727c15b0818794f0502 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 4 Mar 2025 17:03:09 +0700 Subject: [PATCH 14/56] fix typo --- include/nbl/builtin/hlsl/bxdf/common.hlsl | 1 - 1 file changed, 1 deletion(-) diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index 66592d6319..1ddff69d72 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -694,7 +694,6 @@ struct SAnisotropicMicrofacetCache const scalar_type eta ) { - isocache_type iso = (isocache_type)retval; vector3_type H; const bool valid = isocache_type::compute(retval.iso_cache,interaction,_sample,eta,H); if (valid) From e8d2ed8cc0ebf0e76ee560aabecf8fe44eb3fbd4 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 14 Mar 2025 17:02:10 +0700 Subject: [PATCH 15/56] fixed some func usage to nbl ver --- .../hlsl/sampling/spherical_triangle.hlsl | 2 +- include/nbl/builtin/hlsl/shapes/triangle.hlsl | 35 +++++++++++-------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl b/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl index 1d4fda454d..7828fc14ea 100644 --- a/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl +++ b/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl @@ -107,7 +107,7 @@ struct SphericalTriangle const scalar_type u = subTriSolidAngleRatio > numeric_limits::min ? subTriSolidAngleRatio : 0.0; const scalar_type cosBC_s = (cos_vertices[0] + cosB_ * cosC_) / (sinB_ * sinC_); - const scalar_type v = (1.0 - cosAngleAlongBC_s) / (1.0 - (cosBC_s < asfloat(0x3f7fffff) ? cosBC_s : cos_c)); + const scalar_type v = (1.0 - cosAngleAlongBC_s) / (1.0 - (cosBC_s < bit_cast(0x3f7fffff) ? cosBC_s : cos_c)); return vector2_type(u,v); } diff --git a/include/nbl/builtin/hlsl/shapes/triangle.hlsl b/include/nbl/builtin/hlsl/shapes/triangle.hlsl index 67fdfa0476..d3f5a90215 100644 --- a/include/nbl/builtin/hlsl/shapes/triangle.hlsl +++ b/include/nbl/builtin/hlsl/shapes/triangle.hlsl @@ -5,6 +5,7 @@ #ifndef _NBL_BUILTIN_HLSL_SHAPES_TRIANGLE_INCLUDED_ #define _NBL_BUILTIN_HLSL_SHAPES_TRIANGLE_INCLUDED_ +#include #include #include #include @@ -33,9 +34,13 @@ struct SphericalTriangle bool pyramidAngles(NBL_REF_ARG(vector3_type) cos_sides, NBL_REF_ARG(vector3_type) csc_sides) { - cos_sides = vector3_type(nbl::hlsl::dot(vertex1, vertex2), nbl::hlsl::dot(vertex2, vertex0), nbl::hlsl::dot(vertex0, vertex1)); - csc_sides = 1.0 / nbl::hlsl::sqrt((vector3_type)(1.f) - cos_sides * cos_sides); - return nbl::hlsl::any(csc_sides >= (vector3_type)(numeric_limits::max)); + cos_sides = vector3_type(hlsl::dot(vertex1, vertex2), hlsl::dot(vertex2, vertex0), hlsl::dot(vertex0, vertex1)); + csc_sides = (vector3_type)(1.f) - cos_sides * cos_sides; + csc_sides.x = hlsl::rsqrt(csc_sides.x); + csc_sides.y = hlsl::rsqrt(csc_sides.y); + csc_sides.z = hlsl::rsqrt(csc_sides.z); + + return hlsl::any >(csc_sides >= (vector3_type)(numeric_limits::max)); } scalar_type solidAngleOfTriangle(NBL_REF_ARG(vector3_type) cos_vertices, NBL_REF_ARG(vector3_type) sin_vertices, NBL_REF_ARG(scalar_type) cos_a, NBL_REF_ARG(scalar_type) cos_c, NBL_REF_ARG(scalar_type) csc_b, NBL_REF_ARG(scalar_type) csc_c) @@ -51,8 +56,8 @@ struct SphericalTriangle csc_c = csc_sides[2]; // Both vertices and angles at the vertices are denoted by the same upper case letters A, B, and C. The angles A, B, C of the triangle are equal to the angles between the planes that intersect the surface of the sphere or, equivalently, the angles between the tangent vectors of the great circle arcs where they meet at the vertices. Angles are in radians. The angles of proper spherical triangles are (by convention) less than PI - cos_vertices = clamp((cos_sides - cos_sides.yzx * cos_sides.zxy) * csc_sides.yzx * csc_sides.zxy, (vector3_type)(-1.f), (vector3_type)1.f); // using Spherical Law of Cosines (TODO: do we need to clamp anymore? since the pyramid angles method introduction?) - sin_vertices = sqrt((vector3_type)1.f - cos_vertices * cos_vertices); + cos_vertices = hlsl::clamp((cos_sides - cos_sides.yzx * cos_sides.zxy) * csc_sides.yzx * csc_sides.zxy, (vector3_type)(-1.f), (vector3_type)1.f); // using Spherical Law of Cosines (TODO: do we need to clamp anymore? since the pyramid angles method introduction?) + sin_vertices = hlsl::sqrt((vector3_type)1.f - cos_vertices * cos_vertices); return math::getArccosSumofABC_minus_PI(cos_vertices[0], cos_vertices[1], cos_vertices[2], sin_vertices[0], sin_vertices[1], sin_vertices[2]); } @@ -69,22 +74,22 @@ struct SphericalTriangle if (pyramidAngles(cos_sides, csc_sides)) return 0.f; - vector3_type awayFromEdgePlane0 = nbl::hlsl::cross(vertex1, vertex2) * csc_sides[0]; - vector3_type awayFromEdgePlane1 = nbl::hlsl::cross(vertex2, vertex0) * csc_sides[1]; - vector3_type awayFromEdgePlane2 = nbl::hlsl::cross(vertex0, vertex1) * csc_sides[2]; + vector3_type awayFromEdgePlane0 = hlsl::cross(vertex1, vertex2) * csc_sides[0]; + vector3_type awayFromEdgePlane1 = hlsl::cross(vertex2, vertex0) * csc_sides[1]; + vector3_type awayFromEdgePlane2 = hlsl::cross(vertex0, vertex1) * csc_sides[2]; // useless here but could be useful somewhere else - cos_vertices[0] = nbl::hlsl::dot(awayFromEdgePlane1, awayFromEdgePlane2); - cos_vertices[1] = nbl::hlsl::dot(awayFromEdgePlane2, awayFromEdgePlane0); - cos_vertices[2] = nbl::hlsl::dot(awayFromEdgePlane0, awayFromEdgePlane1); + cos_vertices[0] = hlsl::dot(awayFromEdgePlane1, awayFromEdgePlane2); + cos_vertices[1] = hlsl::dot(awayFromEdgePlane2, awayFromEdgePlane0); + cos_vertices[2] = hlsl::dot(awayFromEdgePlane0, awayFromEdgePlane1); // TODO: above dot products are in the wrong order, either work out which is which, or try all 6 permutations till it works - cos_vertices = nbl::hlsl::clamp((cos_sides - cos_sides.yzx * cos_sides.zxy) * csc_sides.yzx * csc_sides.zxy, (vector3_type)(-1.f), (vector3_type)1.f); + cos_vertices = hlsl::clamp((cos_sides - cos_sides.yzx * cos_sides.zxy) * csc_sides.yzx * csc_sides.zxy, (vector3_type)(-1.f), (vector3_type)1.f); matrix awayFromEdgePlane = matrix(awayFromEdgePlane0, awayFromEdgePlane1, awayFromEdgePlane2); - const vector3_type externalProducts = nbl::hlsl::abs(nbl::hlsl::mul(/* transposed already */awayFromEdgePlane, receiverNormal)); + const vector3_type externalProducts = hlsl::abs(hlsl::mul(/* transposed already */awayFromEdgePlane, receiverNormal)); - const vector3_type pyramidAngles = acos(cos_sides); - return nbl::hlsl::dot(pyramidAngles, externalProducts) / (2.f * numbers::pi); + const vector3_type pyramidAngles = acos(cos_sides); + return hlsl::dot(pyramidAngles, externalProducts) / (2.f * numbers::pi); } vector3_type vertex0; From c47f4469cbf529b1f2b370817c50416aa194ead3 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 17 Mar 2025 13:57:25 +0700 Subject: [PATCH 16/56] specify template args --- .../hlsl/sampling/spherical_rectangle.hlsl | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl b/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl index cca3f21dd9..663cd5e3d1 100644 --- a/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl +++ b/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl @@ -35,7 +35,7 @@ struct SphericalRectangle vector2_type generate(NBL_CONST_REF_ARG(vector2_type) rectangleExtents, NBL_CONST_REF_ARG(vector2_type) uv, NBL_REF_ARG(scalar_type) S) { const vector4_type denorm_n_z = vector4_type(-rect.r0.y, rect.r0.x + rectangleExtents.x, rect.r0.y + rectangleExtents.y, -rect.r0.x); - const vector4_type n_z = denorm_n_z / nbl::hlsl::sqrt((vector4_type)(rect.r0.z * rect.r0.z) + denorm_n_z * denorm_n_z); + const vector4_type n_z = denorm_n_z / hlsl::sqrt((vector4_type)(rect.r0.z * rect.r0.z) + denorm_n_z * denorm_n_z); const vector4_type cosGamma = vector4_type( -n_z[0] * n_z[1], -n_z[1] * n_z[2], @@ -54,24 +54,24 @@ struct SphericalRectangle const scalar_type CLAMP_EPS = 1e-5f; // flip z axsis if rect.r0.z > 0 - const uint32_t zFlipMask = (asuint(rect.r0.z) ^ 0x80000000u) & 0x80000000u; - rect.r0.z = asfloat(asuint(rect.r0.z) ^ zFlipMask); + const uint32_t zFlipMask = (bit_cast(rect.r0.z) ^ 0x80000000u) & 0x80000000u; + rect.r0.z = bit_cast(bit_cast(rect.r0.z) ^ zFlipMask); vector3_type r1 = rect.r0 + vector3_type(rectangleExtents.x, rectangleExtents.y, 0); const scalar_type au = uv.x * S + k; - const scalar_type fu = (nbl::hlsl::cos(au) * b0 - b1) / nbl::hlsl::sin(au); - const scalar_type cu_2 = nbl::hlsl::max(fu * fu + b0 * b0, 1.f); // forces `cu` to be in [-1,1] - const scalar_type cu = asfloat(asuint(1.0 / nbl::hlsl::sqrt(cu_2)) ^ (asuint(fu) & 0x80000000u)); + const scalar_type fu = (hlsl::cos(au) * b0 - b1) / hlsl::sin(au); + const scalar_type cu_2 = hlsl::max(fu * fu + b0 * b0, 1.f); // forces `cu` to be in [-1,1] + const scalar_type cu = bit_cast(bit_cast(1.0 / hlsl::sqrt(cu_2)) ^ (bit_cast(fu) & 0x80000000u)); - scalar_type xu = -(cu * rect.r0.z) * 1.0 / nbl::hlsl::sqrt(1 - cu * cu); - xu = nbl::hlsl::clamp(xu, rect.r0.x, r1.x); // avoid Infs + scalar_type xu = -(cu * rect.r0.z) * 1.0 / hlsl::sqrt(1 - cu * cu); + xu = hlsl::clamp(xu, rect.r0.x, r1.x); // avoid Infs const scalar_type d_2 = xu * xu + rect.r0.z * rect.r0.z; - const scalar_type d = nbl::hlsl::sqrt(d_2); + const scalar_type d = hlsl::sqrt(d_2); - const scalar_type h0 = rect.r0.y / nbl::hlsl::sqrt(d_2 + rect.r0.y * rect.r0.y); - const scalar_type h1 = r1.y / nbl::hlsl::sqrt(d_2 + r1.y * r1.y); + const scalar_type h0 = rect.r0.y / hlsl::sqrt(d_2 + rect.r0.y * rect.r0.y); + const scalar_type h1 = r1.y / hlsl::sqrt(d_2 + r1.y * r1.y); const scalar_type hv = h0 + uv.y * (h1 - h0), hv2 = hv * hv; - const scalar_type yv = (hv2 < 1 - CLAMP_EPS) ? (hv * d) / nbl::hlsl::sqrt(1 - hv2) : r1.y; + const scalar_type yv = (hv2 < 1 - CLAMP_EPS) ? (hv * d) / hlsl::sqrt(1 - hv2) : r1.y; return vector2_type((xu - rect.r0.x) / rectangleExtents.x, (yv - rect.r0.y) / rectangleExtents.y); } From 1137b6b767142ef44e2bad80032e0053515a0051 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 17 Mar 2025 14:31:39 +0700 Subject: [PATCH 17/56] update to latest example --- examples_tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples_tests b/examples_tests index a2c84c2513..b5194ef176 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit a2c84c2513510f42cd245a015c6ba7ddd0d6eeaa +Subproject commit b5194ef1768587aef72be4fdc044b5b881160609 From 7e8dd8143795536d2c82e01d1d24db5bcf2a4aeb Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 19 Mar 2025 16:16:31 +0700 Subject: [PATCH 18/56] latest example --- examples_tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples_tests b/examples_tests index a2c84c2513..ca8f2ec8fa 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit a2c84c2513510f42cd245a015c6ba7ddd0d6eeaa +Subproject commit ca8f2ec8fa84a2bd1bfeb4348263f82d14026bca From b1546383f2f44e4bcb84456aee990a278672e74b Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 21 Mar 2025 16:49:40 +0700 Subject: [PATCH 19/56] fix use of static const in func --- examples_tests | 2 +- include/nbl/builtin/hlsl/math/functions.hlsl | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/examples_tests b/examples_tests index ca8f2ec8fa..e95f09d5d2 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit ca8f2ec8fa84a2bd1bfeb4348263f82d14026bca +Subproject commit e95f09d5d20181c4107064cec08bddc689a7f399 diff --git a/include/nbl/builtin/hlsl/math/functions.hlsl b/include/nbl/builtin/hlsl/math/functions.hlsl index f7a84005e8..f47efff877 100644 --- a/include/nbl/builtin/hlsl/math/functions.hlsl +++ b/include/nbl/builtin/hlsl/math/functions.hlsl @@ -122,11 +122,7 @@ void frisvad(NBL_CONST_REF_ARG(T) normal, NBL_REF_ARG(T) tangent, NBL_REF_ARG(T) bool partitionRandVariable(float leftProb, NBL_REF_ARG(float) xi, NBL_REF_ARG(float) rcpChoiceProb) { -#ifdef __HLSL_VERSION - NBL_CONSTEXPR float NEXT_ULP_AFTER_UNITY = asfloat(0x3f800001u); -#else - NBL_CONSTEXPR float32_t NEXT_ULP_AFTER_UNITY = bit_cast(0x3f800001u); -#endif + const float32_t NEXT_ULP_AFTER_UNITY = bit_cast(0x3f800001u); const bool pickRight = xi >= leftProb * NEXT_ULP_AFTER_UNITY; // This is all 100% correct taking into account the above NEXT_ULP_AFTER_UNITY From 7892563055f9bc95c14b9704bd4b24c5000eda96 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 24 Mar 2025 14:43:43 +0700 Subject: [PATCH 20/56] added more morton order stuff --- examples_tests | 2 +- include/nbl/builtin/glsl/utils/morton.glsl | 17 ++++++ include/nbl/builtin/hlsl/math/morton.hlsl | 68 ++++++++++++++++++++++ 3 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 include/nbl/builtin/hlsl/math/morton.hlsl diff --git a/examples_tests b/examples_tests index e95f09d5d2..3cdfb4baf2 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit e95f09d5d20181c4107064cec08bddc689a7f399 +Subproject commit 3cdfb4baf2df319643620a8189c277dec20cb163 diff --git a/include/nbl/builtin/glsl/utils/morton.glsl b/include/nbl/builtin/glsl/utils/morton.glsl index de3be8b9c7..fd07a9cad8 100644 --- a/include/nbl/builtin/glsl/utils/morton.glsl +++ b/include/nbl/builtin/glsl/utils/morton.glsl @@ -22,6 +22,18 @@ uint nbl_glsl_morton_decode2d8bComponent(in uint x) return x; } +uint nbl_glsl_morton_decode2d32bComponent(in uint x) +{ + x &= 0x55555555u; + x = (x ^ (x >> 1u)) & 0x33333333u; + x = (x ^ (x >> 2u)) & 0x0f0f0f0fu; + x = (x ^ (x >> 4u)) & 0x00ff00ffu; + x = (x ^ (x >> 8u)) & 0x0000ffffu; + x = (x ^ (x >> 16u)); + return x; +} + + uvec2 nbl_glsl_morton_decode2d4b(in uint x) { return uvec2(nbl_glsl_morton_decode2d4bComponent(x), nbl_glsl_morton_decode2d4bComponent(x >> 1u)); @@ -32,4 +44,9 @@ uvec2 nbl_glsl_morton_decode2d8b(in uint x) return uvec2(nbl_glsl_morton_decode2d8bComponent(x), nbl_glsl_morton_decode2d8bComponent(x >> 1u)); } +uvec2 nbl_glsl_morton_decode2d32b(in uint x) +{ + return uvec2(nbl_glsl_morton_decode2d32bComponent(x), nbl_glsl_morton_decode2d32bComponent(x >> 1u)); +} + #endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/math/morton.hlsl b/include/nbl/builtin/hlsl/math/morton.hlsl new file mode 100644 index 0000000000..4a6cb5dfd3 --- /dev/null +++ b/include/nbl/builtin/hlsl/math/morton.hlsl @@ -0,0 +1,68 @@ +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_MATH_MORTON_INCLUDED_ +#define _NBL_BUILTIN_HLSL_MATH_MORTON_INCLUDED_ + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace math +{ + +namespace impl +{ + +template +struct MortonComponent; + +template +struct MortonComponent +{ + static T decode2d(T x) + { + x &= 0x55555555u; + x = (x ^ (x >> 1u)) & 0x33333333u; + x = (x ^ (x >> 2u)) & 0x0f0f0f0fu; + x = (x ^ (x >> 4u)) & 0x00ff00ffu; + return x; + } +}; + +template +struct MortonComponent +{ + static T decode2d(T x) + { + x &= 0x55555555u; + x = (x ^ (x >> 1u)) & 0x33333333u; + x = (x ^ (x >> 2u)) & 0x0f0f0f0fu; + x = (x ^ (x >> 4u)) & 0x00ff00ffu; + x = (x ^ (x >> 8u)) & 0x0000ffffu; + x = (x ^ (x >> 16u)); + return x; + } +}; + +} + +template +struct Morton +{ + using vector2_type = vector; + using component_type = impl::MortonComponent; + + static vector2_type decode2d(T x) + { + return vector2_type(component_type::decode2d(x), component_type::decode2d(x >> 1u)); + } +}; + +} +} +} + +#endif From b21b789991cb544c9d95ce4b28dc22e28c68778b Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 28 Mar 2025 14:40:47 +0700 Subject: [PATCH 21/56] latest example --- examples_tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples_tests b/examples_tests index 3cdfb4baf2..1535561525 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 3cdfb4baf2df319643620a8189c277dec20cb163 +Subproject commit 1535561525c1df59d227969692ae7405b507962b From 26adf951ee2e83ca23eeb8700543bdf37f8261cd Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 27 Jun 2025 10:19:00 +0700 Subject: [PATCH 22/56] latest example --- examples_tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples_tests b/examples_tests index a6de5908a2..52c1aa54cf 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit a6de5908a269d0f6853e0c1e94dec8fcdbe6540e +Subproject commit 52c1aa54cf859b63a8ff6df648f743003e5e13fe From 36910c676e1bc7a26802a5b8d7f84227b208a803 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 30 Jun 2025 09:58:25 +0700 Subject: [PATCH 23/56] latest example fixes --- examples_tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples_tests b/examples_tests index 52c1aa54cf..8b31859520 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 52c1aa54cf859b63a8ff6df648f743003e5e13fe +Subproject commit 8b31859520069831b246d13270b43b97aea83141 From 4b3224acd723dacea570a1f94642f2b61d110f42 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 26 Aug 2025 11:50:15 +0700 Subject: [PATCH 24/56] initial iridescent fresnel, only single channel ior --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 165 ++++++++++++++++++++- 1 file changed, 157 insertions(+), 8 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 8284f46e6e..25c5e64d2b 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -10,6 +10,7 @@ #include "nbl/builtin/hlsl/numbers.hlsl" #include "nbl/builtin/hlsl/complex.hlsl" #include "nbl/builtin/hlsl/tgmath.hlsl" +#include "nbl/builtin/hlsl/colorspace/decodeCIEXYZ.hlsl" #include "nbl/builtin/hlsl/vector_utils/vector_traits.hlsl" namespace nbl @@ -366,22 +367,54 @@ struct Conductor return retval; } + // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) + static vector __polarized_w_phase_shift(NBL_CONST_REF_ARG(T) orientedEta, scalar_type orientedEtak, scalar_type cosTheta, NBL_REF_ARG(vector) phi) + { + scalar_type cosTheta_2 = cosTheta * cosTheta; + scalar_type sinTheta2 = scalar_type(1.0) - cosTheta_2; + const scalar_type eta = orientedEta[0]; + const scalar_type eta2 = eta*eta; + const scalar_type etak = orientedEtak; + const scalar_type etak2 = etak*etak; + + scalar_type z = eta2 - etak2 - sinTheta2; + scalar_type w = hlsl::sqrt(z * z + scalar_type(4.0) * eta2 * eta2 * etak2); + scalar_type a2 = (z + w) / scalar_type(2.0); + scalar_type b2 = (w - z) / scalar_type(2.0); + scalar_type b = hlsl::sqrt(b2); + + const scalar_type etaLen2 = eta2 + etak2; + assert(etaLen2 > hlsl::exp2(-numeric_limits::digits)); + scalar_type t1 = etaLen2 * cosTheta_2; + const scalar_type etaCosTwice = eta * clampedCosTheta * scalar_type(2.0); + + phi.y = hlsl::atan(scalar_type(2.0) * b * cosTheta, a2 + b2 - cosTheta_2) + numbers::pi; + phi.x = hlsl::atan(scalar_type(2.0) * eta2 * cosTheta * (scalar_type(2.0) * etak * hlsl::sqrt(a2) - etak2 * b), t1 - a2 + b2); + + vector R; // (rp, rs) + const T rs_common = etaLen2 + cosTheta_2; + R.y = (rs_common - etaCosTwice) / (rs_common + etaCosTwice); + const T rp_common = t1 + scalar_type(1.0); + R.x = (rp_common - etaCosTwice) / (rp_common + etaCosTwice); + return R; + } + T operator()() { - const scalar_type cosTheta2 = clampedCosTheta * clampedCosTheta; - //const float sinTheta2 = 1.0 - cosTheta2; + const scalar_type cosTheta_2 = clampedCosTheta * clampedCosTheta; + //const float sinTheta2 = 1.0 - cosTheta_2; const T etaLen2 = eta * eta + etak2; assert(hlsl::all(etaLen2 > hlsl::promote(hlsl::exp2(-numeric_limits::digits)))); - const T etaCosTwice = eta * clampedCosTheta * 2.0f; + const T etaCosTwice = eta * clampedCosTheta * hlsl::promote(2.0); - const T rs_common = etaLen2 + (T)(cosTheta2); + const T rs_common = etaLen2 + hlsl::promote(cosTheta_2); const T rs2 = (rs_common - etaCosTwice) / (rs_common + etaCosTwice); - const T rp_common = etaLen2 * cosTheta2 + (T)(1.0); + const T rp_common = etaLen2 * cosTheta_2 + hlsl::promote(1.0); const T rp2 = (rp_common - etaCosTwice) / (rp_common + etaCosTwice); - return (rs2 + rp2) * 0.5f; + return (rs2 + rp2) * hlsl::promote(0.5); } T eta; @@ -405,9 +438,35 @@ struct Dielectric return retval; } + // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) + static vector __polarized_w_phase_shift(NBL_CONST_REF_ARG(T) orientedEta, scalar_type cosTheta, NBL_REF_ARG(vector) phi) + { + scalar_type sinTheta2 = scalar_type(1.0) - cosTheta * cosTheta; + const scalar_type eta = orientedEta[0]; + const scalar_type eta2 = eta * eta; + + // TIR + if (eta2 * sinTheta2 > scalar_type(1.0)) + { + const scalar_type phase_advance_s = hlsl::sqrt(sinTheta2 - scalar_type(1.0) / eta2) / cosTheta; + phi = scalar_type(2.0) * hlsl::atan(vector(-eta2 * phase_advance_s, + phase_advance_s)); + return hlsl::promote >(1.0); + } + + scalar_type t0 = hlsl::sqrt(eta2 - sinTheta2); + scalar_type t2 = eta * cosTheta; + + vector R; // (rp, rs) + R.x = (t0 - t2) / (t0 + t2); + R.y = (cosTheta - t0) / (cosTheta + t0); + phi = hlsl::mix(hlsl::promote >(0.0), hlsl::promote >(numbers::pi), R < vector(0.0)); + return R; + } + static T __call(NBL_CONST_REF_ARG(T) orientedEta2, scalar_type absCosTheta) { - const scalar_type sinTheta2 = 1.0 - absCosTheta * absCosTheta; + const scalar_type sinTheta2 = scalar_type(1.0) - absCosTheta * absCosTheta; // the max() clamping can handle TIR when orientedEta2<1.0 const T t0 = hlsl::sqrt(hlsl::max(orientedEta2 - sinTheta2, hlsl::promote(0.0))); @@ -416,7 +475,7 @@ struct Dielectric const T t2 = orientedEta2 * absCosTheta; const T rp = (t0 - t2) / (t0 + t2); - return (rs * rs + rp * rp) * 0.5f; + return (rs * rs + rp * rp) * scalar_type(0.5); } T operator()() @@ -451,6 +510,96 @@ struct DielectricFrontFaceOnly scalar_type absCosTheta; }; +// adapted from https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html +template) +struct Iridescent +{ + using scalar_type = typename vector_traits::scalar_type; + using vector2_type = vector; + using vector_type = T; + + // Depolarization functions for natural light + static scalar_type depolarize(vector2_type polV) + { + return scalar_type(0.5) * (polV.x + polV.y); + } + static vector_type depolarizeColor(vector_type colS, vector_type colP) + { + return scalar_type(0.5) * (colS + colP); + } + + // Evaluation XYZ sensitivity curves in Fourier space + static vector_type evalSensitivity(scalar_type opd, scalar_type shift) + { + // Use Gaussian fits, given by 3 parameters: val, pos and var + scalar_type phase = scalar_type(2.0) * numbers::pi * opd * scalar_type(1.0e-6); + vector_type val = vector_type(5.4856e-13, 4.4201e-13, 5.2481e-13); + vector_type pos = vector_type(1.6810e+06, 1.7953e+06, 2.2084e+06); + vector_type var = vector_type(4.3278e+09, 9.3046e+09, 6.6121e+09); + vector_type xyz = val * hlsl::sqrt(scalar_type(2.0) * numbers::pi * var) * hlsl::cos(pos * phase + shift) * hlsl::exp(- var * phase * phase); + xyz.x += scalar_type(9.7470e-14) * hlsl::sqrt(scalar_type(2.0) * numbers::pi * scalar_type(4.5282e+09)) * hlsl::cos(scalar_type(2.2399e+06) * phase + shift) * hlsl::exp(scalar_type(-4.5282e+09) * phase * phase); + return xyz / scalar_type(1.0685e-7); + } + + T operator()() + { + // Force ior_1 -> 1.0 when Dinc -> 0.0 + scalar_type ior_1 = hlsl::mix(1.0, ior1, hlsl::smoothstep(0.0, 0.03, Dinc)); + + scalar_type eta1 = ior_1; // ior/1.0 (air) + scalar_type rcp_eta1 = 1.0/eta1; + scalar_type cosTheta_1 = cosTheta; + scalar_type cosTheta_2 = hlsl::sqrt(1.0 - rcp_eta1*rcp_eta1*(1.0-cosTheta_1*cosTheta_1) ); + + // First interface + vector2_type phi12; + vector2_type R12 = Dielectric >::__polarized_w_phase_shift(hlsl::promote >(eta1), cosTheta_1, phi12); + vector2_type R21 = R12; + vector2_type T121 = hlsl::promote(1.0) - R12; + vector2_type phi21 = hlsl::promote(numbers::pi) - phi12; + + // Second interface + scalar_type eta2 = ior2/ior_1; + scalar_type etak2 = iork2/ior_1; + vector2_type R23, phi23; + Conductor >::__polarized_w_phase_shift(hlsl::promote >(eta2), etak2, cosTheta_2, phi23); + + // Phase shift + scalar_type OPD = Dinc*cosTheta_2; + vector2_type phi2 = phi21 + phi23; + + // Compound terms + vector_type I = hlsl::promote(0.0); + vector2_type R123 = R12*R23; + vector2_type r123 = hlsl::sqrt(R123); + vector2_type Rs = T121*T121*R23 / (hlsl::promote(1.0) - R123); + + // Reflectance term for m=0 (DC term amplitude) + vector2_type C0 = R12 + Rs; + vector_type S0 = evalSensitivity(0.0, 0.0); + I += depol(C0) * S0; + + // Reflectance term for m>0 (pairs of diracs) + vector2_type Cm = Rs - T121; + NBL_UNROLL for (uint32_t m = 1; m <= 3; m++) + { + Cm *= r123; + vector_type SmS = scalar_type(2.0) * evalSensitivity(scalar_type(m) * OPD, scalar_type(m) * phi2.x); + vector_type SmP = scalar_type(2.0) * evalSensitivity(scalar_type(m) * OPD, scalar_type(m) * phi2.y); + I += depolColor(Cm.x*SmS, Cm.y*SmP); + } + + // Convert back to RGB reflectance + return hlsl::clamp(hlsl::mul(colorspace::decode::XYZtoscRGB, I), vector_type(0.0), vector_type(1.0)); + } + + scalar_type cosTheta; // LdotH + scalar_type Dinc; // thickness of thin film in micrometers, max 25 + scalar_type ior1; // thin-film index + scalar_type ior2; // complex conductor index, k==0 makes dielectric + scalar_type iork2; +}; + // gets the sum of all R, T R T, T R^3 T, T R^5 T, ... paths template || concepts::FloatingPointLikeVectorial) From 9c7190654df1d664b6dc01aea6a98baae1abcb75 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 26 Aug 2025 16:29:11 +0700 Subject: [PATCH 25/56] iridescent fresnel does rgb IOR, use in brdf --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 210 +++++++++++------- .../hlsl/bxdf/reflection/iridescent.hlsl | 202 +++++++++++++++++ src/nbl/builtin/CMakeLists.txt | 1 + 3 files changed, 328 insertions(+), 85 deletions(-) create mode 100644 include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 25c5e64d2b..62c5f17d51 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -368,35 +368,24 @@ struct Conductor } // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) - static vector __polarized_w_phase_shift(NBL_CONST_REF_ARG(T) orientedEta, scalar_type orientedEtak, scalar_type cosTheta, NBL_REF_ARG(vector) phi) + static void __polarized(NBL_CONST_REF_ARG(T) orientedEta, NBL_CONST_REF_ARG(T) orientedEtak, scalar_type cosTheta, NBL_REF_ARG(scalar_type) Rp, NBL_REF_ARG(scalar_type) Rs) { scalar_type cosTheta_2 = cosTheta * cosTheta; scalar_type sinTheta2 = scalar_type(1.0) - cosTheta_2; const scalar_type eta = orientedEta[0]; const scalar_type eta2 = eta*eta; - const scalar_type etak = orientedEtak; + const scalar_type etak = orientedEtak[0]; const scalar_type etak2 = etak*etak; - scalar_type z = eta2 - etak2 - sinTheta2; - scalar_type w = hlsl::sqrt(z * z + scalar_type(4.0) * eta2 * eta2 * etak2); - scalar_type a2 = (z + w) / scalar_type(2.0); - scalar_type b2 = (w - z) / scalar_type(2.0); - scalar_type b = hlsl::sqrt(b2); - const scalar_type etaLen2 = eta2 + etak2; assert(etaLen2 > hlsl::exp2(-numeric_limits::digits)); scalar_type t1 = etaLen2 * cosTheta_2; const scalar_type etaCosTwice = eta * clampedCosTheta * scalar_type(2.0); - phi.y = hlsl::atan(scalar_type(2.0) * b * cosTheta, a2 + b2 - cosTheta_2) + numbers::pi; - phi.x = hlsl::atan(scalar_type(2.0) * eta2 * cosTheta * (scalar_type(2.0) * etak * hlsl::sqrt(a2) - etak2 * b), t1 - a2 + b2); - - vector R; // (rp, rs) const T rs_common = etaLen2 + cosTheta_2; - R.y = (rs_common - etaCosTwice) / (rs_common + etaCosTwice); + Rs = (rs_common - etaCosTwice) / (rs_common + etaCosTwice); const T rp_common = t1 + scalar_type(1.0); - R.x = (rp_common - etaCosTwice) / (rp_common + etaCosTwice); - return R; + Rp = (rp_common - etaCosTwice) / (rp_common + etaCosTwice); } T operator()() @@ -438,30 +427,20 @@ struct Dielectric return retval; } - // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) - static vector __polarized_w_phase_shift(NBL_CONST_REF_ARG(T) orientedEta, scalar_type cosTheta, NBL_REF_ARG(vector) phi) + // returns reflectance R = (rp, rs) + static void __polarized(NBL_CONST_REF_ARG(T) orientedEta, scalar_type cosTheta, NBL_REF_ARG(scalar_type) Rp, NBL_REF_ARG(scalar_type) Rs) { scalar_type sinTheta2 = scalar_type(1.0) - cosTheta * cosTheta; const scalar_type eta = orientedEta[0]; const scalar_type eta2 = eta * eta; - // TIR - if (eta2 * sinTheta2 > scalar_type(1.0)) - { - const scalar_type phase_advance_s = hlsl::sqrt(sinTheta2 - scalar_type(1.0) / eta2) / cosTheta; - phi = scalar_type(2.0) * hlsl::atan(vector(-eta2 * phase_advance_s, - phase_advance_s)); - return hlsl::promote >(1.0); - } - scalar_type t0 = hlsl::sqrt(eta2 - sinTheta2); scalar_type t2 = eta * cosTheta; - vector R; // (rp, rs) - R.x = (t0 - t2) / (t0 + t2); - R.y = (cosTheta - t0) / (cosTheta + t0); - phi = hlsl::mix(hlsl::promote >(0.0), hlsl::promote >(numbers::pi), R < vector(0.0)); - return R; + scalar_type rp = (t0 - t2) / (t0 + t2); + Rp = rp * rp; + scalar_type rs = (cosTheta - t0) / (cosTheta + t0); + Rs = rs * rs; } static T __call(NBL_CONST_REF_ARG(T) orientedEta2, scalar_type absCosTheta) @@ -515,89 +494,150 @@ template struct Iridescent { using scalar_type = typename vector_traits::scalar_type; - using vector2_type = vector; + using monochrome_type = vector; using vector_type = T; - // Depolarization functions for natural light - static scalar_type depolarize(vector2_type polV) - { - return scalar_type(0.5) * (polV.x + polV.y); - } - static vector_type depolarizeColor(vector_type colS, vector_type colP) + // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) + static void phase_shift(const vector_type orientedEta, const vector_type orientedEtak, scalar_type cosTheta, NBL_REF_ARG(vector_type) phiS, NBL_REF_ARG(vector_type) phiP) { - return scalar_type(0.5) * (colS + colP); + scalar_type cosTheta_2 = cosTheta * cosTheta; + scalar_type sinTheta2 = scalar_type(1.0) - cosTheta_2; + const vector_type eta2 = orientedEta*orientedEta; + const vector_type etak2 = orientedEtak*orientedEtak; + + vector_type z = eta2 - etak2 - hlsl::promote(sinTheta2); + vector_type w = hlsl::sqrt(z * z + scalar_type(4.0) * eta2 * eta2 * etak2); + vector_type a2 = (z + w) * hlsl::promote(0.5); + vector_type b2 = (w - z) * hlsl::promote(0.5); + vector_type b = hlsl::sqrt(b2); + + const vector_type t0 = eta2 + etak2; + const vector_type t1 = t0 * cosTheta_2; + + phiS = hlsl::atan(scalar_type(2.0) * b * cosTheta, a2 + b2 - hlsl::promote(cosTheta_2)); + phiP = hlsl::atan(scalar_type(2.0) * eta2 * cosTheta * (scalar_type(2.0) * orientedEtak * hlsl::sqrt(a2) - etak2 * b), t1 - a2 + b2); } // Evaluation XYZ sensitivity curves in Fourier space - static vector_type evalSensitivity(scalar_type opd, scalar_type shift) + static vector_type evalSensitivity(vector_type opd, vector_type shift) { // Use Gaussian fits, given by 3 parameters: val, pos and var - scalar_type phase = scalar_type(2.0) * numbers::pi * opd * scalar_type(1.0e-6); + vector_type phase = scalar_type(2.0) * numbers::pi * opd * scalar_type(1.0e-9); + vector_type phase2 = phase * phase; vector_type val = vector_type(5.4856e-13, 4.4201e-13, 5.2481e-13); vector_type pos = vector_type(1.6810e+06, 1.7953e+06, 2.2084e+06); vector_type var = vector_type(4.3278e+09, 9.3046e+09, 6.6121e+09); - vector_type xyz = val * hlsl::sqrt(scalar_type(2.0) * numbers::pi * var) * hlsl::cos(pos * phase + shift) * hlsl::exp(- var * phase * phase); - xyz.x += scalar_type(9.7470e-14) * hlsl::sqrt(scalar_type(2.0) * numbers::pi * scalar_type(4.5282e+09)) * hlsl::cos(scalar_type(2.2399e+06) * phase + shift) * hlsl::exp(scalar_type(-4.5282e+09) * phase * phase); + vector_type xyz = val * hlsl::sqrt(scalar_type(2.0) * numbers::pi * var) * hlsl::cos(pos * phase + shift) * hlsl::exp(-var * phase2); + xyz.x += scalar_type(9.7470e-14) * hlsl::sqrt(scalar_type(2.0) * numbers::pi * scalar_type(4.5282e+09)) * hlsl::cos(hlsl::promote(2.2399e+06) * phase + shift) * hlsl::exp(hlsl::promote(-4.5282e+09) * phase2); return xyz / scalar_type(1.0685e-7); } T operator()() { - // Force ior_1 -> 1.0 when Dinc -> 0.0 - scalar_type ior_1 = hlsl::mix(1.0, ior1, hlsl::smoothstep(0.0, 0.03, Dinc)); + // Force ior_2 -> 1.0 when Dinc -> 0.0 + scalar_type ior_2 = hlsl::mix(1.0, ior2, hlsl::smoothstep(0.0, 0.03, Dinc)); + const vector_type wavelengths = vector_type(580.0, 550.0, 450.0); - scalar_type eta1 = ior_1; // ior/1.0 (air) - scalar_type rcp_eta1 = 1.0/eta1; + vector_type eta12 = ior_2/ior1; + vector_type eta23 = ior3/ior_2; + vector_type etak23 = iork3/ior_2; scalar_type cosTheta_1 = cosTheta; - scalar_type cosTheta_2 = hlsl::sqrt(1.0 - rcp_eta1*rcp_eta1*(1.0-cosTheta_1*cosTheta_1) ); - - // First interface - vector2_type phi12; - vector2_type R12 = Dielectric >::__polarized_w_phase_shift(hlsl::promote >(eta1), cosTheta_1, phi12); - vector2_type R21 = R12; - vector2_type T121 = hlsl::promote(1.0) - R12; - vector2_type phi21 = hlsl::promote(numbers::pi) - phi12; - - // Second interface - scalar_type eta2 = ior2/ior_1; - scalar_type etak2 = iork2/ior_1; - vector2_type R23, phi23; - Conductor >::__polarized_w_phase_shift(hlsl::promote >(eta2), etak2, cosTheta_2, phi23); - - // Phase shift - scalar_type OPD = Dinc*cosTheta_2; - vector2_type phi2 = phi21 + phi23; - - // Compound terms + vector_type cosTheta_2; + + vector_type R12p, T121p, R23p, R12s, T121s, R23s, ct2; + NBL_UNROLL for(uint32_t i = 0; i < vector_traits::Dimension; i++) // TODO: could probably do calcs on vectors instead of loop + { + const scalar_type scale = ior1[i]/ior_2[i]; //(cosTheta_1 > 0) ? ior1[i]/ior_2[i] : ior_2[i]/ior1[i]; + const scalar_type cosThetaTSqr = 1 - (1-cosTheta_1*cosTheta_1) * scale * scale; + + /* Check for total internal reflection */ + if (cosThetaTSqr <= 0.0f) + { + R12s[i] = 1.0; + R12p[i] = 1.0; + + // Compute the transmission coefficients + T121p[i] = 0.0; + T121s[i] = 0.0; + } + else + { + cosTheta_2[i] = hlsl::sqrt(cosThetaTSqr); + Dielectric::__polarized(hlsl::promote(eta12[i]), cosTheta_1, R12p[i], R12s[i]); + + // Reflected part by the base + // if kappa==0, base material is dielectric + if (hlsl::all::Dimension> >(iork3 < hlsl::promote(hlsl::numeric_limits::min))) + Dielectric::__polarized(hlsl::promote(eta23[i]), cosTheta_2[i], R23p[i], R23s[i]); + else + Conductor::__polarized(hlsl::promote(eta23[i]), hlsl::promote(etak23[i]), cosTheta_2[i], R23p[i], R23s[i]); + + // Compute the transmission coefficients + T121p[i] = 1.0 - R12p[i]; + T121s[i] = 1.0 - R12s[i]; + } + } + + /* Optical Path Difference */ + const vector_type D = 2.0 * ior_2 * Dinc * cosTheta_2; + const vector_type Dphi = 2.0 * numbers::pi * D / wavelengths; + + vector_type phi21p, phi21s, phi23p, phi23s, r123s, r123p, Rs, cosP, irid; vector_type I = hlsl::promote(0.0); - vector2_type R123 = R12*R23; - vector2_type r123 = hlsl::sqrt(R123); - vector2_type Rs = T121*T121*R23 / (hlsl::promote(1.0) - R123); + /* Evaluate the phase shift */ + phase_shift(eta12, hlsl::promote(0.0), hlsl::promote(cosTheta_1), phi21p, phi21s); + phase_shift(eta23, etak23, cosTheta_2, phi23p, phi23s); + phi21p = hlsl::promote(numbers::pi) - phi21p; + phi21s = hlsl::promote(numbers::pi) - phi21s; + + r123p = hlsl::sqrt(R12p*R23p); + r123s = hlsl::sqrt(R12s*R23s); + + vector_type C0, Cm, Sm; + const vector_type S0 = hlsl::promote(1.0); + + /* Iridescence term using spectral antialiasing for Parallel polarization */ + // Reflectance term for m=0 (DC term amplitude) + Rs = (T121p*T121p*R23p) / (hlsl::promote(1.0) - R12p*R23p); + C0 = R12p + Rs; + I += C0 * S0; + + // Reflectance term for m>0 (pairs of diracs) + Cm = Rs - T121p; + NBL_UNROLL for (int m=1; m<=2; ++m) + { + Cm *= r123p; + Sm = 2.0 * evalSensitivity(m*D, m*(phi23p+phi21p)); + I += Cm*Sm; + } + + /* Iridescence term using spectral antialiasing for Perpendicular polarization */ // Reflectance term for m=0 (DC term amplitude) - vector2_type C0 = R12 + Rs; - vector_type S0 = evalSensitivity(0.0, 0.0); - I += depol(C0) * S0; + Rs = (T121s*T121s*R23s) / (hlsl::promote(1.0) - R12s*R23s); + C0 = R12s + Rs; + I += C0 * S0; // Reflectance term for m>0 (pairs of diracs) - vector2_type Cm = Rs - T121; - NBL_UNROLL for (uint32_t m = 1; m <= 3; m++) + Cm = Rs - T121s; + NBL_UNROLL for (int m=1; m<=2; ++m) { - Cm *= r123; - vector_type SmS = scalar_type(2.0) * evalSensitivity(scalar_type(m) * OPD, scalar_type(m) * phi2.x); - vector_type SmP = scalar_type(2.0) * evalSensitivity(scalar_type(m) * OPD, scalar_type(m) * phi2.y); - I += depolColor(Cm.x*SmS, Cm.y*SmP); + Cm *= r123s; + Sm = 2.0 * evalSensitivity(m*D, m*(phi23s+phi21s)); + I += Cm*Sm; } - // Convert back to RGB reflectance - return hlsl::clamp(hlsl::mul(colorspace::decode::XYZtoscRGB, I), vector_type(0.0), vector_type(1.0)); + // note: original paper used the CIE XYZ 1931 to RGB conversion + I = hlsl::mul(colorspace::decode::XYZtoscRGB, I); + return hlsl::max(I, hlsl::promote(0.0)); } scalar_type cosTheta; // LdotH - scalar_type Dinc; // thickness of thin film in micrometers, max 25 - scalar_type ior1; // thin-film index - scalar_type ior2; // complex conductor index, k==0 makes dielectric - scalar_type iork2; + scalar_type Dinc; // thickness of thin film in nanometers, max 25000nm + vector_type ior1; // usually air (1.0) + vector_type ior2; // thin-film index + vector_type ior3; // complex conductor index, k==0 makes dielectric + vector_type iork3; }; diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl new file mode 100644 index 0000000000..6d34d3d7ba --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl @@ -0,0 +1,202 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_REFLECTION_IRIDESCENT_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_REFLECTION_IRIDESCENT_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace reflection +{ + +template) +struct SIridescent +{ + using this_t = SIridescent; + NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); + NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); + + NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(sample_type, Config); + NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); + NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); + NBL_BXDF_CONFIG_ALIAS(isocache_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisocache_type, Config); + + using ndf_type = ndf::GGX; + using fresnel_type = fresnel::Iridescent; + using measure_transform_type = ndf::SDualMeasureQuant; + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; + + struct SCreationParams + { + scalar_type A; + scalar_type thickness; // thin-film thickness in nm + spectral_type ior0 + spectral_type ior1 + spectral_type ior2 + spectral_type iork2; + }; + using creation_type = SCreationParams; + + struct SIridQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + + scalar_type devsh_v; + scalar_type devsh_l; + }; + using query_type = SIridQuery; + + static this_t create(scalar_type A, scalar_type thickness, NBL_CONST_REF_ARG(spectral_type) ior0, NBL_CONST_REF_ARG(spectral_type) ior1, NBL_CONST_REF_ARG(spectral_type) ior2, NBL_CONST_REF_ARG(spectral_type) iork2) + { + this_t retval; + retval.__base.ndf.A = vector2_type(A, A); + retval.__base.ndf.a2 = A*A; + retval.__base.ndf.one_minus_a2 = scalar_type(1.0) - A*A; + retval.__base.fresnel.Dinc = thickness; + retval.__base.fresnel.ior1 = ior0; + retval.__base.fresnel.ior2 = ior1; + retval.__base.fresnel.ior3 = ior2; + retval.__base.fresnel.iork3 = iork2; + return retval; + } + static this_t create(NBL_CONST_REF_ARG(creation_type) params) + { + return create(params.A, params.thickness, params.ior0, params.ior1, params.ior2, params.iork2); + } + + query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) + { + query_type query; + ndf_type ggx_ndf = __base.getNDF(); + query.devsh_v = ggx_ndf.devsh_part(interaction.getNdotV2()); + query.devsh_l = ggx_ndf.devsh_part(_sample.getNdotL2()); + return query; + } + + spectral_type eval(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + if (_sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min) + { + struct SGGXG2XQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } + + scalar_type devsh_v; + scalar_type devsh_l; + BxDFClampMode _clamp; + }; + + SGGXG2XQuery g2_query; + g2_query.devsh_v = query.getDevshV(); + g2_query.devsh_l = query.getDevshL(); + g2_query._clamp = _clamp; + + measure_transform_type dualMeasure = __base.template __DG(g2_query, _sample, interaction, cache); + dualMeasure.maxNdotL = _sample.getNdotL(_clamp); + scalar_type DG = dualMeasure.getProjectedLightMeasure(); + fresnel_type f = __base.getFresnel(); + f.cosTheta = cache.getLdotH(); + return f() * DG; + } + else + return hlsl::promote(0.0); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(isocache_type) cache) + { + SGGXAnisotropic ggx_aniso = SGGXAnisotropic::create(__base.ndf.A.x, __base.ndf.A.y, __base.fresnel.ior3/__base.fresnel.ior2, __base.fresnel.iork3/__base.fresnel.ior2); + anisocache_type anisocache; + sample_type s = ggx_aniso.generate(anisotropic_interaction_type::create(interaction), u, anisocache); + cache = anisocache.iso_cache; + return s; + } + + scalar_type pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + struct SGGXDG1Query + { + using scalar_type = scalar_type; + + scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } + scalar_type getG1over2NdotV() NBL_CONST_MEMBER_FUNC { return G1_over_2NdotV; } + + scalar_type ndf; + scalar_type G1_over_2NdotV; + }; + + SGGXDG1Query dg1_query; + ndf_type ggx_ndf = __base.getNDF(); + dg1_query.ndf = __base.__D(cache); + + const scalar_type devsh_v = query.getDevshV(); + dg1_query.G1_over_2NdotV = ggx_ndf.G1_wo_numerator_devsh_part(interaction.getNdotV(_clamp), devsh_v); + + measure_transform_type dualMeasure = __base.template __DG1(dg1_query); + return dualMeasure.getMicrofacetMeasure(); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + scalar_type _pdf = pdf(query, interaction, cache); + + spectral_type quo = hlsl::promote(0.0); + if (_sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min) + { + struct SGGXG2XQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } + + scalar_type devsh_v; + scalar_type devsh_l; + BxDFClampMode _clamp; + }; + + ndf_type ggx_ndf = __base.getNDF(); + + SGGXG2XQuery g2_query; + g2_query.devsh_v = query.getDevshV(); + g2_query.devsh_l = query.getDevshL(); + g2_query._clamp = _clamp; + const scalar_type G2_over_G1 = ggx_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); + + fresnel_type f = __base.getFresnel(); + f.cosTheta = cache.getLdotH(); + const spectral_type reflectance = f(); + quo = reflectance * G2_over_G1; + } + + return quotient_pdf_type::create(quo, _pdf); + } + + SCookTorrance __base; +}; + +} +} +} +} + +#endif diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index 17e07257b4..fcc79d3714 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -347,6 +347,7 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/reflection/beckmann.hlsl LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/reflection/ggx.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/reflection/lambertian.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/reflection/oren_nayar.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/reflection/iridescent.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/beckmann.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/ggx.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/lambertian.hlsl") From 0f5130684242ec75d051a8d94edba8d1e30c565f Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 27 Aug 2025 11:29:57 +0700 Subject: [PATCH 26/56] some bug fixes --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 51 ++++++++++--------- include/nbl/builtin/hlsl/bxdf/reflection.hlsl | 1 + .../hlsl/bxdf/reflection/iridescent.hlsl | 15 ++++-- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 62c5f17d51..26d80d4316 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -367,7 +367,7 @@ struct Conductor return retval; } - // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) + // TODO: will probably merge with __call at some point static void __polarized(NBL_CONST_REF_ARG(T) orientedEta, NBL_CONST_REF_ARG(T) orientedEtak, scalar_type cosTheta, NBL_REF_ARG(scalar_type) Rp, NBL_REF_ARG(scalar_type) Rs) { scalar_type cosTheta_2 = cosTheta * cosTheta; @@ -380,11 +380,11 @@ struct Conductor const scalar_type etaLen2 = eta2 + etak2; assert(etaLen2 > hlsl::exp2(-numeric_limits::digits)); scalar_type t1 = etaLen2 * cosTheta_2; - const scalar_type etaCosTwice = eta * clampedCosTheta * scalar_type(2.0); + const scalar_type etaCosTwice = eta * cosTheta * scalar_type(2.0); - const T rs_common = etaLen2 + cosTheta_2; + const scalar_type rs_common = etaLen2 + cosTheta_2; Rs = (rs_common - etaCosTwice) / (rs_common + etaCosTwice); - const T rp_common = t1 + scalar_type(1.0); + const scalar_type rp_common = t1 + scalar_type(1.0); Rp = (rp_common - etaCosTwice) / (rp_common + etaCosTwice); } @@ -427,7 +427,7 @@ struct Dielectric return retval; } - // returns reflectance R = (rp, rs) + // TODO: will probably merge with __call at some point static void __polarized(NBL_CONST_REF_ARG(T) orientedEta, scalar_type cosTheta, NBL_REF_ARG(scalar_type) Rp, NBL_REF_ARG(scalar_type) Rs) { scalar_type sinTheta2 = scalar_type(1.0) - cosTheta * cosTheta; @@ -435,7 +435,7 @@ struct Dielectric const scalar_type eta2 = eta * eta; scalar_type t0 = hlsl::sqrt(eta2 - sinTheta2); - scalar_type t2 = eta * cosTheta; + scalar_type t2 = eta2 * cosTheta; scalar_type rp = (t0 - t2) / (t0 + t2); Rp = rp * rp; @@ -451,6 +451,7 @@ struct Dielectric const T t0 = hlsl::sqrt(hlsl::max(orientedEta2 - sinTheta2, hlsl::promote(0.0))); const T rs = (hlsl::promote(absCosTheta) - t0) / (hlsl::promote(absCosTheta) + t0); + // one additional orientedEta multiplied to remove the 1/orientedEta and make it the same as t0 for rs const T t2 = orientedEta2 * absCosTheta; const T rp = (t0 - t2) / (t0 + t2); @@ -498,10 +499,10 @@ struct Iridescent using vector_type = T; // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) - static void phase_shift(const vector_type orientedEta, const vector_type orientedEtak, scalar_type cosTheta, NBL_REF_ARG(vector_type) phiS, NBL_REF_ARG(vector_type) phiP) + static void phase_shift(const vector_type orientedEta, const vector_type orientedEtak, const vector_type cosTheta, NBL_REF_ARG(vector_type) phiS, NBL_REF_ARG(vector_type) phiP) { - scalar_type cosTheta_2 = cosTheta * cosTheta; - scalar_type sinTheta2 = scalar_type(1.0) - cosTheta_2; + vector_type cosTheta_2 = cosTheta * cosTheta; + vector_type sinTheta2 = hlsl::promote(1.0) - cosTheta_2; const vector_type eta2 = orientedEta*orientedEta; const vector_type etak2 = orientedEtak*orientedEtak; @@ -514,28 +515,28 @@ struct Iridescent const vector_type t0 = eta2 + etak2; const vector_type t1 = t0 * cosTheta_2; - phiS = hlsl::atan(scalar_type(2.0) * b * cosTheta, a2 + b2 - hlsl::promote(cosTheta_2)); - phiP = hlsl::atan(scalar_type(2.0) * eta2 * cosTheta * (scalar_type(2.0) * orientedEtak * hlsl::sqrt(a2) - etak2 * b), t1 - a2 + b2); + phiS = hlsl::atan2(scalar_type(2.0) * b * cosTheta, a2 + b2 - cosTheta_2); + phiP = hlsl::atan2(scalar_type(2.0) * eta2 * cosTheta * (scalar_type(2.0) * orientedEtak * hlsl::sqrt(a2) - etak2 * b), t1 - a2 + b2); } // Evaluation XYZ sensitivity curves in Fourier space static vector_type evalSensitivity(vector_type opd, vector_type shift) { // Use Gaussian fits, given by 3 parameters: val, pos and var - vector_type phase = scalar_type(2.0) * numbers::pi * opd * scalar_type(1.0e-9); + vector_type phase = scalar_type(2.0) * numbers::pi * opd * scalar_type(1.0e-9); vector_type phase2 = phase * phase; vector_type val = vector_type(5.4856e-13, 4.4201e-13, 5.2481e-13); vector_type pos = vector_type(1.6810e+06, 1.7953e+06, 2.2084e+06); vector_type var = vector_type(4.3278e+09, 9.3046e+09, 6.6121e+09); - vector_type xyz = val * hlsl::sqrt(scalar_type(2.0) * numbers::pi * var) * hlsl::cos(pos * phase + shift) * hlsl::exp(-var * phase2); - xyz.x += scalar_type(9.7470e-14) * hlsl::sqrt(scalar_type(2.0) * numbers::pi * scalar_type(4.5282e+09)) * hlsl::cos(hlsl::promote(2.2399e+06) * phase + shift) * hlsl::exp(hlsl::promote(-4.5282e+09) * phase2); + vector_type xyz = val * hlsl::sqrt(scalar_type(2.0) * numbers::pi * var) * hlsl::cos(pos * phase + shift) * hlsl::exp(-var * phase2); + xyz.x = xyz.x + scalar_type(9.7470e-14) * hlsl::sqrt(scalar_type(2.0) * numbers::pi * scalar_type(4.5282e+09)) * hlsl::cos(scalar_type(2.2399e+06) * phase[0] + shift[0]) * hlsl::exp(scalar_type(-4.5282e+09) * phase2[0]); return xyz / scalar_type(1.0685e-7); } T operator()() { // Force ior_2 -> 1.0 when Dinc -> 0.0 - scalar_type ior_2 = hlsl::mix(1.0, ior2, hlsl::smoothstep(0.0, 0.03, Dinc)); + vector_type ior_2 = ior2;//hlsl::mix(1.0, ior2, hlsl::smoothStep(0.0, 0.03, Dinc)); const vector_type wavelengths = vector_type(580.0, 550.0, 450.0); vector_type eta12 = ior_2/ior1; @@ -579,8 +580,8 @@ struct Iridescent } /* Optical Path Difference */ - const vector_type D = 2.0 * ior_2 * Dinc * cosTheta_2; - const vector_type Dphi = 2.0 * numbers::pi * D / wavelengths; + const vector_type D = hlsl::promote(2.0 * Dinc) * ior_2 * cosTheta_2; + const vector_type Dphi = hlsl::promote(2.0 * numbers::pi) * D / wavelengths; vector_type phi21p, phi21s, phi23p, phi23s, r123s, r123p, Rs, cosP, irid; vector_type I = hlsl::promote(0.0); @@ -588,8 +589,8 @@ struct Iridescent /* Evaluate the phase shift */ phase_shift(eta12, hlsl::promote(0.0), hlsl::promote(cosTheta_1), phi21p, phi21s); phase_shift(eta23, etak23, cosTheta_2, phi23p, phi23s); - phi21p = hlsl::promote(numbers::pi) - phi21p; - phi21s = hlsl::promote(numbers::pi) - phi21s; + phi21p = hlsl::promote(numbers::pi) - phi21p; + phi21s = hlsl::promote(numbers::pi) - phi21s; r123p = hlsl::sqrt(R12p*R23p); r123s = hlsl::sqrt(R12s*R23s); @@ -608,7 +609,7 @@ struct Iridescent NBL_UNROLL for (int m=1; m<=2; ++m) { Cm *= r123p; - Sm = 2.0 * evalSensitivity(m*D, m*(phi23p+phi21p)); + Sm = hlsl::promote(2.0) * evalSensitivity(hlsl::promote(m)*D, hlsl::promote(m)*(phi23p+phi21p)); I += Cm*Sm; } @@ -623,12 +624,16 @@ struct Iridescent NBL_UNROLL for (int m=1; m<=2; ++m) { Cm *= r123s; - Sm = 2.0 * evalSensitivity(m*D, m*(phi23s+phi21s)); + Sm = hlsl::promote(2.0) * evalSensitivity(hlsl::promote(m)*D, hlsl::promote(m) *(phi23s+phi21s)); I += Cm*Sm; } - // note: original paper used the CIE XYZ 1931 to RGB conversion - I = hlsl::mul(colorspace::decode::XYZtoscRGB, I); + const scalar_type r = 2.3646381 * I[0] - 0.8965361 * I[1] - 0.4680737 * I[2]; + const scalar_type g = -0.5151664 * I[0] + 1.4264000 * I[1] + 0.0887608 * I[2]; + const scalar_type b = 0.0052037 * I[0] - 0.0144081 * I[1] + 1.0092106 * I[2]; + I[0] = r; + I[1] = g; + I[2] = b; return hlsl::max(I, hlsl::promote(0.0)); } diff --git a/include/nbl/builtin/hlsl/bxdf/reflection.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection.hlsl index ed049d3d1a..774f011021 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection.hlsl @@ -8,6 +8,7 @@ #include "nbl/builtin/hlsl/bxdf/reflection/oren_nayar.hlsl" #include "nbl/builtin/hlsl/bxdf/reflection/beckmann.hlsl" #include "nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl" namespace nbl { diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl index 6d34d3d7ba..8103edb647 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl @@ -42,9 +42,9 @@ struct SIridescent { scalar_type A; scalar_type thickness; // thin-film thickness in nm - spectral_type ior0 - spectral_type ior1 - spectral_type ior2 + spectral_type ior0; + spectral_type ior1; + spectral_type ior2; spectral_type iork2; }; using creation_type = SCreationParams; @@ -195,6 +195,15 @@ struct SIridescent }; } + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + } } } From f205d4e92e98252aaea3aa029bffa935b3bc14c9 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 27 Aug 2025 15:32:55 +0700 Subject: [PATCH 27/56] replace loop for vector operations in fresnel --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 129 +++++++++------------ 1 file changed, 57 insertions(+), 72 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 26d80d4316..4c83d33ad5 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -368,23 +368,23 @@ struct Conductor } // TODO: will probably merge with __call at some point - static void __polarized(NBL_CONST_REF_ARG(T) orientedEta, NBL_CONST_REF_ARG(T) orientedEtak, scalar_type cosTheta, NBL_REF_ARG(scalar_type) Rp, NBL_REF_ARG(scalar_type) Rs) + static void __polarized(const T orientedEta, const T orientedEtak, const T cosTheta, NBL_REF_ARG(T) Rp, NBL_REF_ARG(T) Rs) { - scalar_type cosTheta_2 = cosTheta * cosTheta; - scalar_type sinTheta2 = scalar_type(1.0) - cosTheta_2; - const scalar_type eta = orientedEta[0]; - const scalar_type eta2 = eta*eta; - const scalar_type etak = orientedEtak[0]; - const scalar_type etak2 = etak*etak; + T cosTheta_2 = cosTheta * cosTheta; + T sinTheta2 = hlsl::promote(1.0) - cosTheta_2; + const T eta = orientedEta; + const T eta2 = eta*eta; + const T etak = orientedEtak; + const T etak2 = etak*etak; - const scalar_type etaLen2 = eta2 + etak2; - assert(etaLen2 > hlsl::exp2(-numeric_limits::digits)); - scalar_type t1 = etaLen2 * cosTheta_2; - const scalar_type etaCosTwice = eta * cosTheta * scalar_type(2.0); + const T etaLen2 = eta2 + etak2; + assert(hlsl::all(etaLen2 > hlsl::promote(hlsl::exp2(-numeric_limits::digits)))); + T t1 = etaLen2 * cosTheta_2; + const T etaCosTwice = eta * cosTheta * scalar_type(2.0); - const scalar_type rs_common = etaLen2 + cosTheta_2; + const T rs_common = etaLen2 + cosTheta_2; Rs = (rs_common - etaCosTwice) / (rs_common + etaCosTwice); - const scalar_type rp_common = t1 + scalar_type(1.0); + const T rp_common = t1 + hlsl::promote(1.0); Rp = (rp_common - etaCosTwice) / (rp_common + etaCosTwice); } @@ -428,22 +428,22 @@ struct Dielectric } // TODO: will probably merge with __call at some point - static void __polarized(NBL_CONST_REF_ARG(T) orientedEta, scalar_type cosTheta, NBL_REF_ARG(scalar_type) Rp, NBL_REF_ARG(scalar_type) Rs) + static void __polarized(const T orientedEta, const T cosTheta, NBL_REF_ARG(T) Rp, NBL_REF_ARG(T) Rs) { - scalar_type sinTheta2 = scalar_type(1.0) - cosTheta * cosTheta; - const scalar_type eta = orientedEta[0]; - const scalar_type eta2 = eta * eta; + T sinTheta2 = hlsl::promote(1.0) - cosTheta * cosTheta; + const T eta = orientedEta; + const T eta2 = eta * eta; - scalar_type t0 = hlsl::sqrt(eta2 - sinTheta2); - scalar_type t2 = eta2 * cosTheta; + T t0 = hlsl::sqrt(eta2 - sinTheta2); + T t2 = eta2 * cosTheta; - scalar_type rp = (t0 - t2) / (t0 + t2); + T rp = (t0 - t2) / (t0 + t2); Rp = rp * rp; - scalar_type rs = (cosTheta - t0) / (cosTheta + t0); + T rs = (cosTheta - t0) / (cosTheta + t0); Rs = rs * rs; } - static T __call(NBL_CONST_REF_ARG(T) orientedEta2, scalar_type absCosTheta) + static T __call(const T orientedEta2, scalar_type absCosTheta) { const scalar_type sinTheta2 = scalar_type(1.0) - absCosTheta * absCosTheta; @@ -496,7 +496,7 @@ struct Iridescent { using scalar_type = typename vector_traits::scalar_type; using monochrome_type = vector; - using vector_type = T; + using vector_type = T; // assert dim==3? // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) static void phase_shift(const vector_type orientedEta, const vector_type orientedEtak, const vector_type cosTheta, NBL_REF_ARG(vector_type) phiS, NBL_REF_ARG(vector_type) phiP) @@ -506,7 +506,7 @@ struct Iridescent const vector_type eta2 = orientedEta*orientedEta; const vector_type etak2 = orientedEtak*orientedEtak; - vector_type z = eta2 - etak2 - hlsl::promote(sinTheta2); + vector_type z = eta2 - etak2 - sinTheta2; vector_type w = hlsl::sqrt(z * z + scalar_type(4.0) * eta2 * eta2 * etak2); vector_type a2 = (z + w) * hlsl::promote(0.5); vector_type b2 = (w - z) * hlsl::promote(0.5); @@ -515,8 +515,8 @@ struct Iridescent const vector_type t0 = eta2 + etak2; const vector_type t1 = t0 * cosTheta_2; - phiS = hlsl::atan2(scalar_type(2.0) * b * cosTheta, a2 + b2 - cosTheta_2); - phiP = hlsl::atan2(scalar_type(2.0) * eta2 * cosTheta * (scalar_type(2.0) * orientedEtak * hlsl::sqrt(a2) - etak2 * b), t1 - a2 + b2); + phiS = hlsl::atan2(hlsl::promote(2.0) * b * cosTheta, a2 + b2 - cosTheta_2); + phiP = hlsl::atan2(hlsl::promote(2.0) * eta2 * cosTheta * (hlsl::promote(2.0) * orientedEtak * hlsl::sqrt(a2) - etak2 * b), t1 - a2 + b2); } // Evaluation XYZ sensitivity curves in Fourier space @@ -535,58 +535,44 @@ struct Iridescent T operator()() { - // Force ior_2 -> 1.0 when Dinc -> 0.0 - vector_type ior_2 = ior2;//hlsl::mix(1.0, ior2, hlsl::smoothStep(0.0, 0.03, Dinc)); const vector_type wavelengths = vector_type(580.0, 550.0, 450.0); - vector_type eta12 = ior_2/ior1; - vector_type eta23 = ior3/ior_2; - vector_type etak23 = iork3/ior_2; + vector_type eta12 = ior2/ior1; + vector_type eta23 = ior3/ior2; + vector_type etak23 = iork3/ior2; scalar_type cosTheta_1 = cosTheta; vector_type cosTheta_2; - vector_type R12p, T121p, R23p, R12s, T121s, R23s, ct2; - NBL_UNROLL for(uint32_t i = 0; i < vector_traits::Dimension; i++) // TODO: could probably do calcs on vectors instead of loop - { - const scalar_type scale = ior1[i]/ior_2[i]; //(cosTheta_1 > 0) ? ior1[i]/ior_2[i] : ior_2[i]/ior1[i]; - const scalar_type cosThetaTSqr = 1 - (1-cosTheta_1*cosTheta_1) * scale * scale; - - /* Check for total internal reflection */ - if (cosThetaTSqr <= 0.0f) - { - R12s[i] = 1.0; - R12p[i] = 1.0; - - // Compute the transmission coefficients - T121p[i] = 0.0; - T121s[i] = 0.0; - } - else - { - cosTheta_2[i] = hlsl::sqrt(cosThetaTSqr); - Dielectric::__polarized(hlsl::promote(eta12[i]), cosTheta_1, R12p[i], R12s[i]); - - // Reflected part by the base - // if kappa==0, base material is dielectric - if (hlsl::all::Dimension> >(iork3 < hlsl::promote(hlsl::numeric_limits::min))) - Dielectric::__polarized(hlsl::promote(eta23[i]), cosTheta_2[i], R23p[i], R23s[i]); - else - Conductor::__polarized(hlsl::promote(eta23[i]), hlsl::promote(etak23[i]), cosTheta_2[i], R23p[i], R23s[i]); - - // Compute the transmission coefficients - T121p[i] = 1.0 - R12p[i]; - T121s[i] = 1.0 - R12s[i]; - } - } + vector_type R12p, R23p, R12s, R23s; + const vector_type scale = ior1/ior2; + const vector_type cosTheta2_2 = hlsl::promote(1.0) - hlsl::promote(1-cosTheta_1*cosTheta_1) * scale * scale; + + cosTheta_2 = hlsl::sqrt(cosTheta2_2); + Dielectric::__polarized(eta12, hlsl::promote(cosTheta_1), R12p, R12s); + + // Reflected part by the base + // if kappa==0, base material is dielectric + if (hlsl::all::Dimension> >(iork3 < hlsl::promote(hlsl::numeric_limits::min))) + Dielectric::__polarized(eta23, cosTheta_2, R23p, R23s); + else + Conductor::__polarized(eta23, etak23, cosTheta_2, R23p, R23s); + + // Check for total internal reflection + R12s = hlsl::mix(R12s, hlsl::promote(1.0), cosTheta2_2 <= hlsl::promote(0.0)); + R12p = hlsl::mix(R12p, hlsl::promote(1.0), cosTheta2_2 <= hlsl::promote(0.0)); + + // Compute the transmission coefficients + vector_type T121p = hlsl::promote(1.0) - R12p; + vector_type T121s = hlsl::promote(1.0) - R12s; - /* Optical Path Difference */ - const vector_type D = hlsl::promote(2.0 * Dinc) * ior_2 * cosTheta_2; + // Optical Path Difference + const vector_type D = hlsl::promote(2.0 * Dinc) * ior2 * cosTheta_2; const vector_type Dphi = hlsl::promote(2.0 * numbers::pi) * D / wavelengths; - vector_type phi21p, phi21s, phi23p, phi23s, r123s, r123p, Rs, cosP, irid; + vector_type phi21p, phi21s, phi23p, phi23s, r123s, r123p, Rs; vector_type I = hlsl::promote(0.0); - /* Evaluate the phase shift */ + // Evaluate the phase shift phase_shift(eta12, hlsl::promote(0.0), hlsl::promote(cosTheta_1), phi21p, phi21s); phase_shift(eta23, etak23, cosTheta_2, phi23p, phi23s); phi21p = hlsl::promote(numbers::pi) - phi21p; @@ -598,7 +584,7 @@ struct Iridescent vector_type C0, Cm, Sm; const vector_type S0 = hlsl::promote(1.0); - /* Iridescence term using spectral antialiasing for Parallel polarization */ + // Iridescence term using spectral antialiasing // Reflectance term for m=0 (DC term amplitude) Rs = (T121p*T121p*R23p) / (hlsl::promote(1.0) - R12p*R23p); C0 = R12p + Rs; @@ -613,7 +599,6 @@ struct Iridescent I += Cm*Sm; } - /* Iridescence term using spectral antialiasing for Perpendicular polarization */ // Reflectance term for m=0 (DC term amplitude) Rs = (T121s*T121s*R23s) / (hlsl::promote(1.0) - R12s*R23s); C0 = R12s + Rs; @@ -634,11 +619,11 @@ struct Iridescent I[0] = r; I[1] = g; I[2] = b; - return hlsl::max(I, hlsl::promote(0.0)); + return hlsl::max(I, hlsl::promote(0.0)) * hlsl::promote(0.5); } scalar_type cosTheta; // LdotH - scalar_type Dinc; // thickness of thin film in nanometers, max 25000nm + scalar_type Dinc; // thickness of thin film in nanometers, rec. 100-25000nm vector_type ior1; // usually air (1.0) vector_type ior2; // thin-film index vector_type ior3; // complex conductor index, k==0 makes dielectric From f6daa6fd50fdc41d0b85a1c3686f4b1eee2ab4ea Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 27 Aug 2025 16:27:07 +0700 Subject: [PATCH 28/56] added iridescent btdf --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 4 +- .../hlsl/bxdf/reflection/iridescent.hlsl | 4 +- .../nbl/builtin/hlsl/bxdf/transmission.hlsl | 1 + .../hlsl/bxdf/transmission/iridescent.hlsl | 212 ++++++++++++++++++ src/nbl/builtin/CMakeLists.txt | 1 + 5 files changed, 218 insertions(+), 4 deletions(-) create mode 100644 include/nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 4c83d33ad5..fe9bd890ef 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -540,7 +540,7 @@ struct Iridescent vector_type eta12 = ior2/ior1; vector_type eta23 = ior3/ior2; vector_type etak23 = iork3/ior2; - scalar_type cosTheta_1 = cosTheta; + scalar_type cosTheta_1 = absCosTheta; vector_type cosTheta_2; vector_type R12p, R23p, R12s, R23s; @@ -622,7 +622,7 @@ struct Iridescent return hlsl::max(I, hlsl::promote(0.0)) * hlsl::promote(0.5); } - scalar_type cosTheta; // LdotH + scalar_type absCosTheta;// LdotH scalar_type Dinc; // thickness of thin film in nanometers, rec. 100-25000nm vector_type ior1; // usually air (1.0) vector_type ior2; // thin-film index diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl index 8103edb647..c8f0631a03 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl @@ -114,7 +114,7 @@ struct SIridescent dualMeasure.maxNdotL = _sample.getNdotL(_clamp); scalar_type DG = dualMeasure.getProjectedLightMeasure(); fresnel_type f = __base.getFresnel(); - f.cosTheta = cache.getLdotH(); + f.absCosTheta = cache.getLdotH(); return f() * DG; } else @@ -183,7 +183,7 @@ struct SIridescent const scalar_type G2_over_G1 = ggx_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); fresnel_type f = __base.getFresnel(); - f.cosTheta = cache.getLdotH(); + f.absCosTheta = cache.getLdotH(); const spectral_type reflectance = f(); quo = reflectance * G2_over_G1; } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission.hlsl index 81f531e1a6..8fe66cefa1 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission.hlsl @@ -8,6 +8,7 @@ #include "nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl" #include "nbl/builtin/hlsl/bxdf/transmission/beckmann.hlsl" #include "nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl" +#include "nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl" namespace nbl { diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl new file mode 100644 index 0000000000..aa2fde373e --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl @@ -0,0 +1,212 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_IRIDESCENT_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_IRIDESCENT_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace transmission +{ + +template) +struct SIridescent +{ + using this_t = SIridescent; + NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); + NBL_BXDF_CONFIG_ALIAS(matrix3x3_type, Config); + NBL_BXDF_CONFIG_ALIAS(monochrome_type, Config); + NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); + + NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(sample_type, Config); + NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); + NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); + NBL_BXDF_CONFIG_ALIAS(isocache_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisocache_type, Config); + using brdf_type = reflection::SGGXIsotropic; + + using ndf_type = ndf::GGX; + using fresnel_type = fresnel::Iridescent; + using measure_transform_type = ndf::SDualMeasureQuant; + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; + + struct SCreationParams + { + scalar_type A; + scalar_type thickness; // thin-film thickness in nm + spectral_type ior0; + spectral_type ior1; + spectral_type ior2; + }; + using creation_type = SCreationParams; + + struct SIridQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + + scalar_type devsh_v; + scalar_type devsh_l; + }; + using query_type = SIridQuery; + + static this_t create(scalar_type A, scalar_type thickness, NBL_CONST_REF_ARG(spectral_type) ior0, NBL_CONST_REF_ARG(spectral_type) ior1, NBL_CONST_REF_ARG(spectral_type) ior2) + { + this_t retval; + retval.__base.ndf.A = vector2_type(A, A); + retval.__base.ndf.a2 = A*A; + retval.__base.ndf.one_minus_a2 = scalar_type(1.0) - A*A; + retval.__base.fresnel.Dinc = thickness; + retval.__base.fresnel.ior1 = ior0; + retval.__base.fresnel.ior2 = ior1; + retval.__base.fresnel.ior3 = ior2; + retval.__base.fresnel.iork3 = hlsl::promote(0.0); // always 0.0 for dielectric base layer + return retval; + } + static this_t create(NBL_CONST_REF_ARG(creation_type) params) + { + return create(params.A, params.thickness, params.ior0, params.ior1, params.ior2); + } + + query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) + { + query_type query; + ndf_type ggx_ndf = __base.getNDF(); + query.devsh_v = ggx_ndf.devsh_part(interaction.getNdotV2()); + query.devsh_l = ggx_ndf.devsh_part(_sample.getNdotL2()); + return query; + } + + spectral_type eval(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + struct SGGXG2XQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } + + scalar_type devsh_v; + scalar_type devsh_l; + BxDFClampMode _clamp; + }; + + SGGXG2XQuery g2_query; + g2_query.devsh_v = query.getDevshV(); + g2_query.devsh_l = query.getDevshL(); + g2_query._clamp = _clamp; + + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(1.0, hlsl::promote(__base.fresnel.ior3/__base.fresnel.ior2)); + measure_transform_type dualMeasure = __base.template __DG(g2_query, _sample, interaction, cache); + dualMeasure.absNdotL = _sample.getNdotL(_clamp); + dualMeasure.orientedEta = orientedEta.value[0]; + scalar_type DG = dualMeasure.getProjectedLightMeasure(); + + fresnel_type f = __base.getFresnel(); + f.absCosTheta = hlsl::abs(cache.getLdotH()); + return hlsl::promote(f()[0]) * DG; + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u, NBL_REF_ARG(isocache_type) cache) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(1.0, hlsl::promote(__base.fresnel.ior3/__base.fresnel.ior2)); + SGGXDielectricAnisotropic ggx_aniso = SGGXDielectricAnisotropic::create(orientedEta, __base.ndf.A.x, __base.ndf.A.y); + anisocache_type anisocache; + sample_type s = ggx_aniso.generate(anisotropic_interaction_type::create(interaction), u, anisocache); + cache = anisocache.iso_cache; + return s; + } + + scalar_type pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + struct SGGXDG1Query + { + using scalar_type = scalar_type; + + scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } + scalar_type getG1over2NdotV() NBL_CONST_MEMBER_FUNC { return G1_over_2NdotV; } + scalar_type getOrientedEta() NBL_CONST_MEMBER_FUNC { return orientedEta; } + + scalar_type ndf; + scalar_type G1_over_2NdotV; + scalar_type orientedEta; + }; + + SGGXDG1Query dg1_query; + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(1.0, hlsl::promote(__base.fresnel.ior3/__base.fresnel.ior2)); + dg1_query.orientedEta = orientedEta.value[0]; + + fresnel_type f = __base.getFresnel(); + f.absCosTheta = hlsl::abs(cache.getLdotH()); + const scalar_type reflectance = f()[0]; + + ndf_type ggx_ndf = __base.getNDF(); + dg1_query.ndf = __base.__D(cache); + dg1_query.G1_over_2NdotV = ggx_ndf.G1_wo_numerator_devsh_part(interaction.getNdotV(_clamp), query.getDevshV()); + + measure_transform_type dualMeasure = __base.template __DG1(dg1_query, cache); + return hlsl::mix(reflectance, scalar_type(1.0) - reflectance, cache.isTransmission()) * dualMeasure.getMicrofacetMeasure(); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + scalar_type _pdf = pdf(query, interaction, cache); + const bool transmitted = cache.isTransmission(); + + struct SGGXG2XQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } + + scalar_type devsh_v; + scalar_type devsh_l; + BxDFClampMode _clamp; + }; + + ndf_type ggx_ndf = __base.getNDF(); + SGGXG2XQuery g2_query; + g2_query.devsh_v = query.getDevshV(); + g2_query.devsh_l = query.getDevshL(); + g2_query._clamp = _clamp; + + scalar_type quo; + quo = ggx_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); + + return quotient_pdf_type::create(quo, _pdf); + } + + SCookTorrance __base; +}; + +} + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + +} +} +} + +#endif diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index fcc79d3714..91b0b411e8 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -352,6 +352,7 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/beckmann.hl LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/ggx.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/lambertian.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/smooth_dielectric.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/iridescent.hlsl") #subgroup LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/subgroup/ballot.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/subgroup/basic.hlsl") From 09402ea10d5947f34f713f24786bc22ac093205d Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 27 Aug 2025 16:29:52 +0700 Subject: [PATCH 29/56] added unit tests --- examples_tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples_tests b/examples_tests index fe3ca26794..ea439349dc 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit fe3ca267946d7f4d0fbe4cff9c437b2e62637da9 +Subproject commit ea439349dc9461f4d18eab143fe705e4cbf10c81 From aa0f6e85a16f66585ecc8c0d42228c83b2a9251a Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 28 Aug 2025 10:43:47 +0700 Subject: [PATCH 30/56] latest example --- examples_tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples_tests b/examples_tests index ceb40d10fa..91658e3f07 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit ceb40d10fa5ee578b9a10fa1111eeae54c670db7 +Subproject commit 91658e3f0737d192cafba9e073c5d67cc6eaaac6 From d2cb193b2cc25aa5abe82943b36648892eb7e724 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 28 Aug 2025 15:50:22 +0700 Subject: [PATCH 31/56] moved colorspace transform mats into struct functions --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 12 +- include/nbl/builtin/hlsl/colorspace.hlsl | 198 ++++++++++++++++++ .../builtin/hlsl/colorspace/decodeCIEXYZ.hlsl | 79 ------- .../builtin/hlsl/colorspace/encodeCIEXYZ.hlsl | 83 -------- src/nbl/builtin/CMakeLists.txt | 3 +- 5 files changed, 202 insertions(+), 173 deletions(-) create mode 100644 include/nbl/builtin/hlsl/colorspace.hlsl delete mode 100644 include/nbl/builtin/hlsl/colorspace/decodeCIEXYZ.hlsl delete mode 100644 include/nbl/builtin/hlsl/colorspace/encodeCIEXYZ.hlsl diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index fe9bd890ef..7fde50ab95 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -10,7 +10,7 @@ #include "nbl/builtin/hlsl/numbers.hlsl" #include "nbl/builtin/hlsl/complex.hlsl" #include "nbl/builtin/hlsl/tgmath.hlsl" -#include "nbl/builtin/hlsl/colorspace/decodeCIEXYZ.hlsl" +#include "nbl/builtin/hlsl/colorspace.hlsl" #include "nbl/builtin/hlsl/vector_utils/vector_traits.hlsl" namespace nbl @@ -535,7 +535,7 @@ struct Iridescent T operator()() { - const vector_type wavelengths = vector_type(580.0, 550.0, 450.0); + const vector_type wavelengths = vector_type(colorspace::scRGB::wavelength_R, colorspace::scRGB::wavelength_G, colorspace::scRGB::wavelength_B); vector_type eta12 = ior2/ior1; vector_type eta23 = ior3/ior2; @@ -613,13 +613,7 @@ struct Iridescent I += Cm*Sm; } - const scalar_type r = 2.3646381 * I[0] - 0.8965361 * I[1] - 0.4680737 * I[2]; - const scalar_type g = -0.5151664 * I[0] + 1.4264000 * I[1] + 0.0887608 * I[2]; - const scalar_type b = 0.0052037 * I[0] - 0.0144081 * I[1] + 1.0092106 * I[2]; - I[0] = r; - I[1] = g; - I[2] = b; - return hlsl::max(I, hlsl::promote(0.0)) * hlsl::promote(0.5); + return hlsl::max(colorspace::scRGB::FromXYZ(I), hlsl::promote(0.0)) * hlsl::promote(0.5); } scalar_type absCosTheta;// LdotH diff --git a/include/nbl/builtin/hlsl/colorspace.hlsl b/include/nbl/builtin/hlsl/colorspace.hlsl new file mode 100644 index 0000000000..cadc29fa67 --- /dev/null +++ b/include/nbl/builtin/hlsl/colorspace.hlsl @@ -0,0 +1,198 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_COLOR_SPACE_COLORSPACE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_COLOR_SPACE_COLORSPACE_INCLUDED_ + +#include + +namespace nbl +{ +namespace hlsl +{ +namespace colorspace +{ + +struct colorspace_base +{ + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 580.0f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 550.0f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 450.0f; +}; + +struct scRGB : colorspace_base +{ + static float32_t3 FromXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3( 3.240970f, -1.537383f, -0.498611f), + float32_t3(-0.969244f, 1.875968f, 0.041555f), + float32_t3( 0.055630f, -0.203977f, 1.056972f) + ); + return hlsl::mul(mat, val); + } + + static float32_t3 ToXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3(0.412391f, 0.357584f, 0.180481f), + float32_t3(0.212639f, 0.715169f, 0.072192f), + float32_t3(0.019331f, 0.119195f, 0.950532f) + ); + return hlsl::mul(mat, val); + } +}; + +struct sRGB : scRGB {}; +struct BT709 : scRGB {}; + +struct Display_P3 : colorspace_base +{ + static float32_t3 FromXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3( 2.4934969119f, -0.9313836179f, -0.4027107845f), + float32_t3(-0.8294889696f, 1.7626640603f, 0.0236246858f), + float32_t3( 0.0358458302f, -0.0761723893f, 0.9568845240f) + ); + return hlsl::mul(mat, val); + } + + static float32_t3 ToXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3(0.4865709486f, 0.2656676932f, 0.1982172852f), + float32_t3(0.2289745641f, 0.6917385218f, 0.0792869141f), + float32_t3(0.0000000000f, 0.0451133819f, 1.0439443689f) + ); + return hlsl::mul(mat, val); + } +}; + +struct DCI_P3 : colorspace_base +{ + static float32_t3 FromXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3(1.0f, 0.0f, 0.0f), + float32_t3(0.0f, 1.0f, 0.0f), + float32_t3(0.0f, 0.0f, 1.0f) + ); + return hlsl::mul(mat, val); + } + + static float32_t3 ToXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3(1.0f, 0.0f, 0.0f), + float32_t3(0.0f, 1.0f, 0.0f), + float32_t3(0.0f, 0.0f, 1.0f) + ); + return hlsl::mul(mat, val); + } +}; + +struct BT2020 : colorspace_base +{ + static float32_t3 FromXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3( 1.716651f, -0.355671f, -0.253366f), + float32_t3(-0.666684f, 1.616481f, 0.015769f), + float32_t3( 0.017640f, -0.042771f, 0.942103f) + ); + return hlsl::mul(mat, val); + } + + static float32_t3 ToXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3(0.636958f, 0.144617f, 0.168881f), + float32_t3(0.262700f, 0.677998f, 0.059302f), + float32_t3(0.000000f, 0.028073f, 1.060985f) + ); + return hlsl::mul(mat, val); + } +}; + +struct HDR10_ST2084 : BT2020 {}; +struct DOLBYIVISION : BT2020 {}; +struct HDR10_HLG : BT2020 {}; + +struct AdobeRGB : colorspace_base +{ + static float32_t3 FromXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3( 2.0415879038f, -0.5650069743f, -0.3447313508f), + float32_t3(-0.9692436363f, 1.8759675015f, 0.0415550574f), + float32_t3( 0.0134442806f, -0.1183623922f, 1.0151749944f) + ); + return hlsl::mul(mat, val); + } + + static float32_t3 ToXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3(0.5766690429f, 0.1855582379f, 0.1882286462f), + float32_t3(0.2973449753f, 0.6273635663f, 0.0752914585f), + float32_t3(0.0270313614f, 0.0706888525f, 0.9913375368f) + ); + return hlsl::mul(mat, val); + } +}; + +struct ACES2065_1 : colorspace_base +{ + static float32_t3 FromXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3( 1.0498110175f, 0.0000000000f, -0.0000974845f), + float32_t3(-0.4959030231f, 1.3733130458f, 0.0982400361f), + float32_t3( 0.0000000000f, 0.0000000000f, 0.9912520182f) + ); + return hlsl::mul(mat, val); + } + + static float32_t3 ToXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3(0.9525523959f, 0.0000000000f, 0.0000936786f), + float32_t3(0.3439664498f, 0.7281660966f, -0.0721325464f), + float32_t3(0.0000000000f, 0.0000000000f, 1.0088251844f) + ); + return hlsl::mul(mat, val); + } +}; + +struct ACEScc : colorspace_base +{ + static float32_t3 FromXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3( 1.6410233797f, -0.3248032942f, -0.2364246952f), + float32_t3(-0.6636628587f, 1.6153315917f, 0.0167563477f), + float32_t3( 0.0117218943f, -0.0082844420f, 0.9883948585f) + ); + return hlsl::mul(mat, val); + } + + static float32_t3 ToXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3( 0.6624541811f, 0.1340042065f, 0.1561876870f), + float32_t3( 0.2722287168f, 0.6740817658f, 0.0536895174f), + float32_t3(-0.0055746495f, 0.0040607335f, 1.0103391003f) + ); + return hlsl::mul(mat, val); + } +}; + +struct ACEScct : ACEScc {}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/colorspace/decodeCIEXYZ.hlsl b/include/nbl/builtin/hlsl/colorspace/decodeCIEXYZ.hlsl deleted file mode 100644 index 29a93124d9..0000000000 --- a/include/nbl/builtin/hlsl/colorspace/decodeCIEXYZ.hlsl +++ /dev/null @@ -1,79 +0,0 @@ - -// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef _NBL_BUILTIN_HLSL_COLOR_SPACE_DECODE_CIE_XYZ_INCLUDED_ -#define _NBL_BUILTIN_HLSL_COLOR_SPACE_DECODE_CIE_XYZ_INCLUDED_ - -#include - -namespace nbl -{ -namespace hlsl -{ -namespace colorspace -{ -namespace decode -{ - -NBL_CONSTEXPR float32_t3x3 XYZtoscRGB = float32_t3x3( - float32_t3( 3.240970f, -1.537383f, -0.498611f), - float32_t3(-0.969244f, 1.875968f, 0.041555f), - float32_t3( 0.055630f, -0.203977f, 1.056972f) -); - -NBL_CONSTEXPR float32_t3x3 XYZtosRGB = XYZtoscRGB; -NBL_CONSTEXPR float32_t3x3 XYZtoBT709 = XYZtoscRGB; - - -NBL_CONSTEXPR float32_t3x3 XYZtoDisplay_P3 = float32_t3x3( - float32_t3( 2.4934969119f, -0.9313836179f, -0.4027107845f), - float32_t3(-0.8294889696f, 1.7626640603f, 0.0236246858f), - float32_t3( 0.0358458302f, -0.0761723893f, 0.9568845240f) -); - -NBL_CONSTEXPR float32_t3x3 XYZtoDCI_P3 = float32_t3x3( - float32_t3(1.0f, 0.0f, 0.0f), - float32_t3(0.0f, 1.0f, 0.0f), - float32_t3(0.0f, 0.0f, 1.0f) -); - -NBL_CONSTEXPR float32_t3x3 XYZtoBT2020 = float32_t3x3( - float32_t3( 1.716651f, -0.355671f, -0.253366f), - float32_t3(-0.666684f, 1.616481f, 0.015769f), - float32_t3( 0.017640f, -0.042771f, 0.942103f) -); - -NBL_CONSTEXPR float32_t3x3 XYZtoHDR10_ST2084 = XYZtoBT2020; -NBL_CONSTEXPR float32_t3x3 XYZtoDOLBYIVISION = XYZtoBT2020; -NBL_CONSTEXPR float32_t3x3 XYZtoHDR10_HLG = XYZtoBT2020; - -NBL_CONSTEXPR float32_t3x3 XYZtoAdobeRGB = float32_t3x3( - float32_t3( 2.0415879038f, -0.5650069743f, -0.3447313508f), - float32_t3(-0.9692436363f, 1.8759675015f, 0.0415550574f), - float32_t3( 0.0134442806f, -0.1183623922f, 1.0151749944f) -); - - -NBL_CONSTEXPR float32_t3x3 XYZtoACES2065_1 = float32_t3x3( - float32_t3( 1.0498110175f, 0.0000000000f, -0.0000974845f), - float32_t3(-0.4959030231f, 1.3733130458f, 0.0982400361f), - float32_t3( 0.0000000000f, 0.0000000000f, 0.9912520182f) -); - -NBL_CONSTEXPR float32_t3x3 XYZtoACEScc = float32_t3x3( - float32_t3( 1.6410233797f, -0.3248032942f, -0.2364246952f), - float32_t3(-0.6636628587f, 1.6153315917f, 0.0167563477f), - float32_t3( 0.0117218943f, -0.0082844420f, 0.9883948585f) -); - -NBL_CONSTEXPR float32_t3x3 XYZtoACEScct = XYZtoACEScc; - -} -} -} -} - - -#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/colorspace/encodeCIEXYZ.hlsl b/include/nbl/builtin/hlsl/colorspace/encodeCIEXYZ.hlsl deleted file mode 100644 index cc8ac317b7..0000000000 --- a/include/nbl/builtin/hlsl/colorspace/encodeCIEXYZ.hlsl +++ /dev/null @@ -1,83 +0,0 @@ - - -// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef _NBL_BUILTIN_HLSL_COLOR_SPACE_ENCODE_CIE_XYZ_INCLUDED_ -#define _NBL_BUILTIN_HLSL_COLOR_SPACE_ENCODE_CIE_XYZ_INCLUDED_ - -#include - -namespace nbl -{ -namespace hlsl -{ -namespace colorspace -{ - -NBL_CONSTEXPR float32_t3x3 scRGBtoXYZ = float32_t3x3( - float32_t3(0.412391f, 0.357584f, 0.180481f), - float32_t3(0.212639f, 0.715169f, 0.072192f), - float32_t3(0.019331f, 0.119195f, 0.950532f) -); - -NBL_CONSTEXPR float32_t3x3 sRGBtoXYZ = scRGBtoXYZ; - -NBL_CONSTEXPR float32_t3x3 BT709toXYZ = scRGBtoXYZ; - - -NBL_CONSTEXPR float32_t3x3 Display_P3toXYZ = float32_t3x3( - float32_t3(0.4865709486f, 0.2656676932f, 0.1982172852f), - float32_t3(0.2289745641f, 0.6917385218f, 0.0792869141f), - float32_t3(0.0000000000f, 0.0451133819f, 1.0439443689f) -); - - -NBL_CONSTEXPR float32_t3x3 DCI_P3toXYZ = float32_t3x3( - float32_t3(1.0f, 0.0f, 0.0f), - float32_t3(0.0f, 1.0f, 0.0f), - float32_t3(0.0f, 0.0f, 1.0f) -); - - -NBL_CONSTEXPR float32_t3x3 BT2020toXYZ = float32_t3x3( - float32_t3(0.636958f, 0.144617f, 0.168881f), - float32_t3(0.262700f, 0.677998f, 0.059302f), - float32_t3(0.000000f, 0.028073f, 1.060985f) -); - -NBL_CONSTEXPR float32_t3x3 HDR10_ST2084toXYZ = BT2020toXYZ; - -NBL_CONSTEXPR float32_t3x3 DOLBYIVISIONtoXYZ = BT2020toXYZ; - -NBL_CONSTEXPR float32_t3x3 HDR10_HLGtoXYZ = BT2020toXYZ; - - -NBL_CONSTEXPR float32_t3x3 AdobeRGBtoXYZ = float32_t3x3( - float32_t3(0.5766690429f, 0.1855582379f, 0.1882286462f), - float32_t3(0.2973449753f, 0.6273635663f, 0.0752914585f), - float32_t3(0.0270313614f, 0.0706888525f, 0.9913375368f) -); - - -NBL_CONSTEXPR float32_t3x3 ACES2065_1toXYZ = float32_t3x3( - float32_t3(0.9525523959f, 0.0000000000f, 0.0000936786f), - float32_t3(0.3439664498f, 0.7281660966f, -0.0721325464f), - float32_t3(0.0000000000f, 0.0000000000f, 1.0088251844f) -); - - -NBL_CONSTEXPR float32_t3x3 ACEScctoXYZ = float32_t3x3( - float32_t3( 0.6624541811f, 0.1340042065f, 0.1561876870f), - float32_t3( 0.2722287168f, 0.6740817658f, 0.0536895174f), - float32_t3(-0.0055746495f, 0.0040607335f, 1.0103391003f) -); - -NBL_CONSTEXPR float32_t3x3 ACESccttoXYZ = ACEScctoXYZ; - -} -} -} - -#endif \ No newline at end of file diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index 91b0b411e8..8f0f90cf48 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -307,8 +307,7 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/acceleration_structures.hlsl" #colorspace LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/colorspace/EOTF.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/colorspace/OETF.hlsl") -LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/colorspace/decodeCIEXYZ.hlsl") -LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/colorspace/encodeCIEXYZ.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/colorspace.hlsl") #barycentrics LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/barycentric/utils.hlsl") #scanning append From 33fc955d716b3322985abe39f269634cae651674 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 28 Aug 2025 16:22:22 +0700 Subject: [PATCH 32/56] some more colorspace utility --- examples_tests | 2 +- include/nbl/builtin/hlsl/colorspace.hlsl | 84 ++++++++++++------------ 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/examples_tests b/examples_tests index 91658e3f07..99206836d6 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 91658e3f0737d192cafba9e073c5d67cc6eaaac6 +Subproject commit 99206836d6afd6fe55735b88f4a576a0b52e10a2 diff --git a/include/nbl/builtin/hlsl/colorspace.hlsl b/include/nbl/builtin/hlsl/colorspace.hlsl index cadc29fa67..f6b5d76ae8 100644 --- a/include/nbl/builtin/hlsl/colorspace.hlsl +++ b/include/nbl/builtin/hlsl/colorspace.hlsl @@ -23,25 +23,25 @@ struct colorspace_base struct scRGB : colorspace_base { - static float32_t3 FromXYZ(float32_t3 val) + static float32_t3x3 FromXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3( 3.240970f, -1.537383f, -0.498611f), float32_t3(-0.969244f, 1.875968f, 0.041555f), float32_t3( 0.055630f, -0.203977f, 1.056972f) ); - return hlsl::mul(mat, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } - static float32_t3 ToXYZ(float32_t3 val) + static float32_t3x3 ToXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3(0.412391f, 0.357584f, 0.180481f), float32_t3(0.212639f, 0.715169f, 0.072192f), float32_t3(0.019331f, 0.119195f, 0.950532f) ); - return hlsl::mul(mat, val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } }; struct sRGB : scRGB {}; @@ -49,71 +49,71 @@ struct BT709 : scRGB {}; struct Display_P3 : colorspace_base { - static float32_t3 FromXYZ(float32_t3 val) + static float32_t3x3 FromXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3( 2.4934969119f, -0.9313836179f, -0.4027107845f), float32_t3(-0.8294889696f, 1.7626640603f, 0.0236246858f), float32_t3( 0.0358458302f, -0.0761723893f, 0.9568845240f) ); - return hlsl::mul(mat, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } - static float32_t3 ToXYZ(float32_t3 val) + static float32_t3x3 ToXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3(0.4865709486f, 0.2656676932f, 0.1982172852f), float32_t3(0.2289745641f, 0.6917385218f, 0.0792869141f), float32_t3(0.0000000000f, 0.0451133819f, 1.0439443689f) ); - return hlsl::mul(mat, val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } }; struct DCI_P3 : colorspace_base { - static float32_t3 FromXYZ(float32_t3 val) + static float32_t3x3 FromXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3(1.0f, 0.0f, 0.0f), float32_t3(0.0f, 1.0f, 0.0f), float32_t3(0.0f, 0.0f, 1.0f) ); - return hlsl::mul(mat, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } - static float32_t3 ToXYZ(float32_t3 val) + static float32_t3x3 ToXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3(1.0f, 0.0f, 0.0f), float32_t3(0.0f, 1.0f, 0.0f), float32_t3(0.0f, 0.0f, 1.0f) ); - return hlsl::mul(mat, val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } }; struct BT2020 : colorspace_base { - static float32_t3 FromXYZ(float32_t3 val) + static float32_t3x3 FromXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3( 1.716651f, -0.355671f, -0.253366f), float32_t3(-0.666684f, 1.616481f, 0.015769f), float32_t3( 0.017640f, -0.042771f, 0.942103f) ); - return hlsl::mul(mat, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } - static float32_t3 ToXYZ(float32_t3 val) + static float32_t3x3 ToXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3(0.636958f, 0.144617f, 0.168881f), float32_t3(0.262700f, 0.677998f, 0.059302f), float32_t3(0.000000f, 0.028073f, 1.060985f) ); - return hlsl::mul(mat, val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } }; struct HDR10_ST2084 : BT2020 {}; @@ -122,71 +122,71 @@ struct HDR10_HLG : BT2020 {}; struct AdobeRGB : colorspace_base { - static float32_t3 FromXYZ(float32_t3 val) + static float32_t3x3 FromXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3( 2.0415879038f, -0.5650069743f, -0.3447313508f), float32_t3(-0.9692436363f, 1.8759675015f, 0.0415550574f), float32_t3( 0.0134442806f, -0.1183623922f, 1.0151749944f) ); - return hlsl::mul(mat, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } - static float32_t3 ToXYZ(float32_t3 val) + static float32_t3x3 ToXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3(0.5766690429f, 0.1855582379f, 0.1882286462f), float32_t3(0.2973449753f, 0.6273635663f, 0.0752914585f), float32_t3(0.0270313614f, 0.0706888525f, 0.9913375368f) ); - return hlsl::mul(mat, val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } }; struct ACES2065_1 : colorspace_base { - static float32_t3 FromXYZ(float32_t3 val) + static float32_t3x3 FromXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3( 1.0498110175f, 0.0000000000f, -0.0000974845f), float32_t3(-0.4959030231f, 1.3733130458f, 0.0982400361f), float32_t3( 0.0000000000f, 0.0000000000f, 0.9912520182f) ); - return hlsl::mul(mat, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } - static float32_t3 ToXYZ(float32_t3 val) + static float32_t3x3 ToXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3(0.9525523959f, 0.0000000000f, 0.0000936786f), float32_t3(0.3439664498f, 0.7281660966f, -0.0721325464f), float32_t3(0.0000000000f, 0.0000000000f, 1.0088251844f) ); - return hlsl::mul(mat, val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } }; struct ACEScc : colorspace_base { - static float32_t3 FromXYZ(float32_t3 val) + static float32_t3x3 FromXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3( 1.6410233797f, -0.3248032942f, -0.2364246952f), float32_t3(-0.6636628587f, 1.6153315917f, 0.0167563477f), float32_t3( 0.0117218943f, -0.0082844420f, 0.9883948585f) ); - return hlsl::mul(mat, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } - static float32_t3 ToXYZ(float32_t3 val) + static float32_t3x3 ToXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3( 0.6624541811f, 0.1340042065f, 0.1561876870f), float32_t3( 0.2722287168f, 0.6740817658f, 0.0536895174f), float32_t3(-0.0055746495f, 0.0040607335f, 1.0103391003f) ); - return hlsl::mul(mat, val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } }; struct ACEScct : ACEScc {}; From d8b67737a16b332539fb91a26a1eb21e49a6a445 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 3 Nov 2025 11:13:49 +0700 Subject: [PATCH 33/56] restore encode/decode matrices --- .../builtin/hlsl/colorspace/decodeCIEXYZ.hlsl | 79 ++++++++++++++++++ .../builtin/hlsl/colorspace/encodeCIEXYZ.hlsl | 83 +++++++++++++++++++ src/nbl/builtin/CMakeLists.txt | 2 + 3 files changed, 164 insertions(+) create mode 100644 include/nbl/builtin/hlsl/colorspace/decodeCIEXYZ.hlsl create mode 100644 include/nbl/builtin/hlsl/colorspace/encodeCIEXYZ.hlsl diff --git a/include/nbl/builtin/hlsl/colorspace/decodeCIEXYZ.hlsl b/include/nbl/builtin/hlsl/colorspace/decodeCIEXYZ.hlsl new file mode 100644 index 0000000000..29a93124d9 --- /dev/null +++ b/include/nbl/builtin/hlsl/colorspace/decodeCIEXYZ.hlsl @@ -0,0 +1,79 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_COLOR_SPACE_DECODE_CIE_XYZ_INCLUDED_ +#define _NBL_BUILTIN_HLSL_COLOR_SPACE_DECODE_CIE_XYZ_INCLUDED_ + +#include + +namespace nbl +{ +namespace hlsl +{ +namespace colorspace +{ +namespace decode +{ + +NBL_CONSTEXPR float32_t3x3 XYZtoscRGB = float32_t3x3( + float32_t3( 3.240970f, -1.537383f, -0.498611f), + float32_t3(-0.969244f, 1.875968f, 0.041555f), + float32_t3( 0.055630f, -0.203977f, 1.056972f) +); + +NBL_CONSTEXPR float32_t3x3 XYZtosRGB = XYZtoscRGB; +NBL_CONSTEXPR float32_t3x3 XYZtoBT709 = XYZtoscRGB; + + +NBL_CONSTEXPR float32_t3x3 XYZtoDisplay_P3 = float32_t3x3( + float32_t3( 2.4934969119f, -0.9313836179f, -0.4027107845f), + float32_t3(-0.8294889696f, 1.7626640603f, 0.0236246858f), + float32_t3( 0.0358458302f, -0.0761723893f, 0.9568845240f) +); + +NBL_CONSTEXPR float32_t3x3 XYZtoDCI_P3 = float32_t3x3( + float32_t3(1.0f, 0.0f, 0.0f), + float32_t3(0.0f, 1.0f, 0.0f), + float32_t3(0.0f, 0.0f, 1.0f) +); + +NBL_CONSTEXPR float32_t3x3 XYZtoBT2020 = float32_t3x3( + float32_t3( 1.716651f, -0.355671f, -0.253366f), + float32_t3(-0.666684f, 1.616481f, 0.015769f), + float32_t3( 0.017640f, -0.042771f, 0.942103f) +); + +NBL_CONSTEXPR float32_t3x3 XYZtoHDR10_ST2084 = XYZtoBT2020; +NBL_CONSTEXPR float32_t3x3 XYZtoDOLBYIVISION = XYZtoBT2020; +NBL_CONSTEXPR float32_t3x3 XYZtoHDR10_HLG = XYZtoBT2020; + +NBL_CONSTEXPR float32_t3x3 XYZtoAdobeRGB = float32_t3x3( + float32_t3( 2.0415879038f, -0.5650069743f, -0.3447313508f), + float32_t3(-0.9692436363f, 1.8759675015f, 0.0415550574f), + float32_t3( 0.0134442806f, -0.1183623922f, 1.0151749944f) +); + + +NBL_CONSTEXPR float32_t3x3 XYZtoACES2065_1 = float32_t3x3( + float32_t3( 1.0498110175f, 0.0000000000f, -0.0000974845f), + float32_t3(-0.4959030231f, 1.3733130458f, 0.0982400361f), + float32_t3( 0.0000000000f, 0.0000000000f, 0.9912520182f) +); + +NBL_CONSTEXPR float32_t3x3 XYZtoACEScc = float32_t3x3( + float32_t3( 1.6410233797f, -0.3248032942f, -0.2364246952f), + float32_t3(-0.6636628587f, 1.6153315917f, 0.0167563477f), + float32_t3( 0.0117218943f, -0.0082844420f, 0.9883948585f) +); + +NBL_CONSTEXPR float32_t3x3 XYZtoACEScct = XYZtoACEScc; + +} +} +} +} + + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/colorspace/encodeCIEXYZ.hlsl b/include/nbl/builtin/hlsl/colorspace/encodeCIEXYZ.hlsl new file mode 100644 index 0000000000..cc8ac317b7 --- /dev/null +++ b/include/nbl/builtin/hlsl/colorspace/encodeCIEXYZ.hlsl @@ -0,0 +1,83 @@ + + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_COLOR_SPACE_ENCODE_CIE_XYZ_INCLUDED_ +#define _NBL_BUILTIN_HLSL_COLOR_SPACE_ENCODE_CIE_XYZ_INCLUDED_ + +#include + +namespace nbl +{ +namespace hlsl +{ +namespace colorspace +{ + +NBL_CONSTEXPR float32_t3x3 scRGBtoXYZ = float32_t3x3( + float32_t3(0.412391f, 0.357584f, 0.180481f), + float32_t3(0.212639f, 0.715169f, 0.072192f), + float32_t3(0.019331f, 0.119195f, 0.950532f) +); + +NBL_CONSTEXPR float32_t3x3 sRGBtoXYZ = scRGBtoXYZ; + +NBL_CONSTEXPR float32_t3x3 BT709toXYZ = scRGBtoXYZ; + + +NBL_CONSTEXPR float32_t3x3 Display_P3toXYZ = float32_t3x3( + float32_t3(0.4865709486f, 0.2656676932f, 0.1982172852f), + float32_t3(0.2289745641f, 0.6917385218f, 0.0792869141f), + float32_t3(0.0000000000f, 0.0451133819f, 1.0439443689f) +); + + +NBL_CONSTEXPR float32_t3x3 DCI_P3toXYZ = float32_t3x3( + float32_t3(1.0f, 0.0f, 0.0f), + float32_t3(0.0f, 1.0f, 0.0f), + float32_t3(0.0f, 0.0f, 1.0f) +); + + +NBL_CONSTEXPR float32_t3x3 BT2020toXYZ = float32_t3x3( + float32_t3(0.636958f, 0.144617f, 0.168881f), + float32_t3(0.262700f, 0.677998f, 0.059302f), + float32_t3(0.000000f, 0.028073f, 1.060985f) +); + +NBL_CONSTEXPR float32_t3x3 HDR10_ST2084toXYZ = BT2020toXYZ; + +NBL_CONSTEXPR float32_t3x3 DOLBYIVISIONtoXYZ = BT2020toXYZ; + +NBL_CONSTEXPR float32_t3x3 HDR10_HLGtoXYZ = BT2020toXYZ; + + +NBL_CONSTEXPR float32_t3x3 AdobeRGBtoXYZ = float32_t3x3( + float32_t3(0.5766690429f, 0.1855582379f, 0.1882286462f), + float32_t3(0.2973449753f, 0.6273635663f, 0.0752914585f), + float32_t3(0.0270313614f, 0.0706888525f, 0.9913375368f) +); + + +NBL_CONSTEXPR float32_t3x3 ACES2065_1toXYZ = float32_t3x3( + float32_t3(0.9525523959f, 0.0000000000f, 0.0000936786f), + float32_t3(0.3439664498f, 0.7281660966f, -0.0721325464f), + float32_t3(0.0000000000f, 0.0000000000f, 1.0088251844f) +); + + +NBL_CONSTEXPR float32_t3x3 ACEScctoXYZ = float32_t3x3( + float32_t3( 0.6624541811f, 0.1340042065f, 0.1561876870f), + float32_t3( 0.2722287168f, 0.6740817658f, 0.0536895174f), + float32_t3(-0.0055746495f, 0.0040607335f, 1.0103391003f) +); + +NBL_CONSTEXPR float32_t3x3 ACESccttoXYZ = ACEScctoXYZ; + +} +} +} + +#endif \ No newline at end of file diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index 6e4c6f5a56..83aef9f3dd 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -236,6 +236,8 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/acceleration_structures.hlsl" #colorspace LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/colorspace/EOTF.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/colorspace/OETF.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/colorspace/decodeCIEXYZ.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/colorspace/encodeCIEXYZ.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/colorspace.hlsl") #barycentrics LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/barycentric/utils.hlsl") From 65239d923961a9d3ec0bcd3cbc91609c761bb9b2 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 3 Nov 2025 14:50:28 +0700 Subject: [PATCH 34/56] colorspace specific dominant wavelengths (still missing ACES), use global conversion matrices --- include/nbl/builtin/hlsl/colorspace.hlsl | 141 +++++++++-------------- 1 file changed, 54 insertions(+), 87 deletions(-) diff --git a/include/nbl/builtin/hlsl/colorspace.hlsl b/include/nbl/builtin/hlsl/colorspace.hlsl index f6b5d76ae8..78186066df 100644 --- a/include/nbl/builtin/hlsl/colorspace.hlsl +++ b/include/nbl/builtin/hlsl/colorspace.hlsl @@ -6,6 +6,8 @@ #define _NBL_BUILTIN_HLSL_COLOR_SPACE_COLORSPACE_INCLUDED_ #include +#include +#include namespace nbl { @@ -16,32 +18,29 @@ namespace colorspace struct colorspace_base { - NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 580.0f; - NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 550.0f; - NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 450.0f; + // default CIE RGB primaries wavelengths + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 700.0f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 546.1f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 435.8f; }; struct scRGB : colorspace_base { + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 611.4f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 549.1f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 464.2f; + static float32_t3x3 FromXYZ() { - return float32_t3x3( - float32_t3( 3.240970f, -1.537383f, -0.498611f), - float32_t3(-0.969244f, 1.875968f, 0.041555f), - float32_t3( 0.055630f, -0.203977f, 1.056972f) - ); + return XYZtoscRGB; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoscRGB, val); } static float32_t3x3 ToXYZ() { - return float32_t3x3( - float32_t3(0.412391f, 0.357584f, 0.180481f), - float32_t3(0.212639f, 0.715169f, 0.072192f), - float32_t3(0.019331f, 0.119195f, 0.950532f) - ); + return scRGBtoXYZ; } - static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(scRGBtoXYZ, val); } }; struct sRGB : scRGB {}; @@ -49,71 +48,59 @@ struct BT709 : scRGB {}; struct Display_P3 : colorspace_base { + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 614.9f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 544.2f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 464.2f; + static float32_t3x3 FromXYZ() { - return float32_t3x3( - float32_t3( 2.4934969119f, -0.9313836179f, -0.4027107845f), - float32_t3(-0.8294889696f, 1.7626640603f, 0.0236246858f), - float32_t3( 0.0358458302f, -0.0761723893f, 0.9568845240f) - ); + return XYZtoDisplay_P3; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoDisplay_P3, val); } static float32_t3x3 ToXYZ() { - return float32_t3x3( - float32_t3(0.4865709486f, 0.2656676932f, 0.1982172852f), - float32_t3(0.2289745641f, 0.6917385218f, 0.0792869141f), - float32_t3(0.0000000000f, 0.0451133819f, 1.0439443689f) - ); + return Display_P3toXYZ; } - static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(Display_P3toXYZ, val); } }; struct DCI_P3 : colorspace_base { + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 614.9f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 544.2f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 464.2f; + static float32_t3x3 FromXYZ() { - return float32_t3x3( - float32_t3(1.0f, 0.0f, 0.0f), - float32_t3(0.0f, 1.0f, 0.0f), - float32_t3(0.0f, 0.0f, 1.0f) - ); + return XYZtoDCI_P3; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoDCI_P3, val); } static float32_t3x3 ToXYZ() { - return float32_t3x3( - float32_t3(1.0f, 0.0f, 0.0f), - float32_t3(0.0f, 1.0f, 0.0f), - float32_t3(0.0f, 0.0f, 1.0f) - ); + return DCI_P3toXYZ; } - static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(DCI_P3toXYZ, val); } }; struct BT2020 : colorspace_base { + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 630.0f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 532.0f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 467.0f; + static float32_t3x3 FromXYZ() { - return float32_t3x3( - float32_t3( 1.716651f, -0.355671f, -0.253366f), - float32_t3(-0.666684f, 1.616481f, 0.015769f), - float32_t3( 0.017640f, -0.042771f, 0.942103f) - ); + return XYZtoBT2020; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoBT2020, val); } static float32_t3x3 ToXYZ() { - return float32_t3x3( - float32_t3(0.636958f, 0.144617f, 0.168881f), - float32_t3(0.262700f, 0.677998f, 0.059302f), - float32_t3(0.000000f, 0.028073f, 1.060985f) - ); + return BT2020toXYZ; } - static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(BT2020toXYZ, val); } }; struct HDR10_ST2084 : BT2020 {}; @@ -122,71 +109,51 @@ struct HDR10_HLG : BT2020 {}; struct AdobeRGB : colorspace_base { + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 611.4f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 534.7f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 464.2f; + static float32_t3x3 FromXYZ() { - return float32_t3x3( - float32_t3( 2.0415879038f, -0.5650069743f, -0.3447313508f), - float32_t3(-0.9692436363f, 1.8759675015f, 0.0415550574f), - float32_t3( 0.0134442806f, -0.1183623922f, 1.0151749944f) - ); + return XYZtoAdobeRGB; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoAdobeRGB, val); } static float32_t3x3 ToXYZ() { - return float32_t3x3( - float32_t3(0.5766690429f, 0.1855582379f, 0.1882286462f), - float32_t3(0.2973449753f, 0.6273635663f, 0.0752914585f), - float32_t3(0.0270313614f, 0.0706888525f, 0.9913375368f) - ); + return AdobeRGBtoXYZ; } - static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(AdobeRGBtoXYZ, val); } }; struct ACES2065_1 : colorspace_base { static float32_t3x3 FromXYZ() { - return float32_t3x3( - float32_t3( 1.0498110175f, 0.0000000000f, -0.0000974845f), - float32_t3(-0.4959030231f, 1.3733130458f, 0.0982400361f), - float32_t3( 0.0000000000f, 0.0000000000f, 0.9912520182f) - ); + return XYZtoACES2065_1; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoACES2065_1, val); } static float32_t3x3 ToXYZ() { - return float32_t3x3( - float32_t3(0.9525523959f, 0.0000000000f, 0.0000936786f), - float32_t3(0.3439664498f, 0.7281660966f, -0.0721325464f), - float32_t3(0.0000000000f, 0.0000000000f, 1.0088251844f) - ); + return ACES2065_1toXYZ; } - static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ACES2065_1toXYZ, val); } }; struct ACEScc : colorspace_base { static float32_t3x3 FromXYZ() { - return float32_t3x3( - float32_t3( 1.6410233797f, -0.3248032942f, -0.2364246952f), - float32_t3(-0.6636628587f, 1.6153315917f, 0.0167563477f), - float32_t3( 0.0117218943f, -0.0082844420f, 0.9883948585f) - ); + return XYZtoACEScc; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoACEScc, val); } static float32_t3x3 ToXYZ() { - return float32_t3x3( - float32_t3( 0.6624541811f, 0.1340042065f, 0.1561876870f), - float32_t3( 0.2722287168f, 0.6740817658f, 0.0536895174f), - float32_t3(-0.0055746495f, 0.0040607335f, 1.0103391003f) - ); + return ACEScctoXYZ; } - static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ACEScctoXYZ, val); } }; struct ACEScct : ACEScc {}; From 9a8b77e30e79b51370a8d4603a85aeec11c3a208 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 3 Nov 2025 16:57:06 +0700 Subject: [PATCH 35/56] update iridescent fresnel to match fresnel concept --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 56 +++++++++++++++++----- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index a40a6496c9..824cc6d6df 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -510,13 +510,26 @@ struct Dielectric }; // adapted from https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html -template) +template) struct Iridescent { + using this_t = Iridescent; using scalar_type = typename vector_traits::scalar_type; using monochrome_type = vector; using vector_type = T; // assert dim==3? + static this_t create(scalar_type Dinc, vector_type ior1, vector_type ior2, vector_type ior3, vector_type iork3) + { + this_t retval; + retval.Dinc = Dinc; + retval.eta12 = ior2/ior1; + retval.eta23 = ior3/ior2; + retval.etak23 = scalar_type(0.0); + NBL_IF_CONSTEXPR(SupportsTransmission) + retval.etak23 = iork3/ior2; + return retval; + } + // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) static void phase_shift(const vector_type orientedEta, const vector_type orientedEtak, const vector_type cosTheta, NBL_REF_ARG(vector_type) phiS, NBL_REF_ARG(vector_type) phiP) { @@ -552,18 +565,15 @@ struct Iridescent return xyz / scalar_type(1.0685e-7); } - T operator()() + T operator()(const scalar_type clampedCosTheta /* LdotH */) { const vector_type wavelengths = vector_type(colorspace::scRGB::wavelength_R, colorspace::scRGB::wavelength_G, colorspace::scRGB::wavelength_B); - vector_type eta12 = ior2/ior1; - vector_type eta23 = ior3/ior2; - vector_type etak23 = iork3/ior2; - scalar_type cosTheta_1 = absCosTheta; + scalar_type cosTheta_1 = clampedCosTheta; vector_type cosTheta_2; vector_type R12p, R23p, R12s, R23s; - const vector_type scale = ior1/ior2; + const vector_type scale = scalar_type(1.0)/eta12; const vector_type cosTheta2_2 = hlsl::promote(1.0) - hlsl::promote(1-cosTheta_1*cosTheta_1) * scale * scale; cosTheta_2 = hlsl::sqrt(cosTheta2_2); @@ -571,7 +581,7 @@ struct Iridescent // Reflected part by the base // if kappa==0, base material is dielectric - if (hlsl::all::Dimension> >(iork3 < hlsl::promote(hlsl::numeric_limits::min))) + NBL_IF_CONSTEXPR(SupportsTransmission) Dielectric::__polarized(eta23, cosTheta_2, R23p, R23s); else Conductor::__polarized(eta23, etak23, cosTheta_2, R23p, R23s); @@ -635,12 +645,32 @@ struct Iridescent return hlsl::max(colorspace::scRGB::FromXYZ(I), hlsl::promote(0.0)) * hlsl::promote(0.5); } - scalar_type absCosTheta;// LdotH + scalar_type getRefractionOrientedEta() NBL_CONST_MEMBER_FUNC { return eta23[0]; } + OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC + { + OrientedEtaRcps rcpEta; + rcpEta.value = hlsl::promote(1.0) / eta23[0]; + rcpEta.value2 = rcpEta.value * rcpEta.value; + return rcpEta; + } + + this_t getReorientedFresnel(const scalar_type NdotI) NBL_CONST_MEMBER_FUNC + { + const bool flip = NdotI < scalar_type(0.0); + this_t orientedFresnel; + orientedFresnel.Dinc = Dinc; + orientedFresnel.eta12 = hlsl::mix(eta12, hlsl::promote(1.0)/eta12, flip); + orientedFresnel.eta23 = hlsl::mix(eta23, hlsl::promote(1.0)/eta23, flip); + orientedFresnel.etak23 = hlsl::promote(0.0); + NBL_IF_CONSTEXPR(SupportsTransmission) + orientedFresnel.etak23 = hlsl::mix(etak23, hlsl::promote(1.0)/etak23, flip); + return orientedFresnel; + } + scalar_type Dinc; // thickness of thin film in nanometers, rec. 100-25000nm - vector_type ior1; // usually air (1.0) - vector_type ior2; // thin-film index - vector_type ior3; // complex conductor index, k==0 makes dielectric - vector_type iork3; + vector_type eta12; // outside (usually air 1.0) -> thin-film IOR + vector_type eta23; // thin-film -> base material IOR + vector_type etak23; // thin-film -> complex component, k==0 makes dielectric }; From 76ad0ed10fb39d65fee94621e339b3b58b5a8f29 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 4 Nov 2025 11:16:00 +0700 Subject: [PATCH 36/56] minor typo fixes --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 5 +++- include/nbl/builtin/hlsl/colorspace.hlsl | 28 +++++++++++----------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 824cc6d6df..67a2dea9d0 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -522,6 +522,7 @@ struct Iridescent { this_t retval; retval.Dinc = Dinc; + retval.thinFilmIor = ior2; retval.eta12 = ior2/ior1; retval.eta23 = ior3/ior2; retval.etak23 = scalar_type(0.0); @@ -595,7 +596,7 @@ struct Iridescent vector_type T121s = hlsl::promote(1.0) - R12s; // Optical Path Difference - const vector_type D = hlsl::promote(2.0 * Dinc) * ior2 * cosTheta_2; + const vector_type D = hlsl::promote(2.0 * Dinc) * thinFilmIor * cosTheta_2; const vector_type Dphi = hlsl::promote(2.0 * numbers::pi) * D / wavelengths; vector_type phi21p, phi21s, phi23p, phi23s, r123s, r123p, Rs; @@ -659,6 +660,7 @@ struct Iridescent const bool flip = NdotI < scalar_type(0.0); this_t orientedFresnel; orientedFresnel.Dinc = Dinc; + orientedFresnel.thinFilmIor = thinFilmIor; orientedFresnel.eta12 = hlsl::mix(eta12, hlsl::promote(1.0)/eta12, flip); orientedFresnel.eta23 = hlsl::mix(eta23, hlsl::promote(1.0)/eta23, flip); orientedFresnel.etak23 = hlsl::promote(0.0); @@ -668,6 +670,7 @@ struct Iridescent } scalar_type Dinc; // thickness of thin film in nanometers, rec. 100-25000nm + vector_type thinFilmIor; vector_type eta12; // outside (usually air 1.0) -> thin-film IOR vector_type eta23; // thin-film -> base material IOR vector_type etak23; // thin-film -> complex component, k==0 makes dielectric diff --git a/include/nbl/builtin/hlsl/colorspace.hlsl b/include/nbl/builtin/hlsl/colorspace.hlsl index 78186066df..9e6da57344 100644 --- a/include/nbl/builtin/hlsl/colorspace.hlsl +++ b/include/nbl/builtin/hlsl/colorspace.hlsl @@ -32,9 +32,9 @@ struct scRGB : colorspace_base static float32_t3x3 FromXYZ() { - return XYZtoscRGB; + return decode::XYZtoscRGB; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoscRGB, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(decode::XYZtoscRGB, val); } static float32_t3x3 ToXYZ() { @@ -54,9 +54,9 @@ struct Display_P3 : colorspace_base static float32_t3x3 FromXYZ() { - return XYZtoDisplay_P3; + return decode::XYZtoDisplay_P3; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoDisplay_P3, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(decode::XYZtoDisplay_P3, val); } static float32_t3x3 ToXYZ() { @@ -73,9 +73,9 @@ struct DCI_P3 : colorspace_base static float32_t3x3 FromXYZ() { - return XYZtoDCI_P3; + return decode::XYZtoDCI_P3; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoDCI_P3, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(decode::XYZtoDCI_P3, val); } static float32_t3x3 ToXYZ() { @@ -92,9 +92,9 @@ struct BT2020 : colorspace_base static float32_t3x3 FromXYZ() { - return XYZtoBT2020; + return decode::XYZtoBT2020; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoBT2020, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(decode::XYZtoBT2020, val); } static float32_t3x3 ToXYZ() { @@ -115,9 +115,9 @@ struct AdobeRGB : colorspace_base static float32_t3x3 FromXYZ() { - return XYZtoAdobeRGB; + return decode::XYZtoAdobeRGB; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoAdobeRGB, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(decode::XYZtoAdobeRGB, val); } static float32_t3x3 ToXYZ() { @@ -130,9 +130,9 @@ struct ACES2065_1 : colorspace_base { static float32_t3x3 FromXYZ() { - return XYZtoACES2065_1; + return decode::XYZtoACES2065_1; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoACES2065_1, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(decode::XYZtoACES2065_1, val); } static float32_t3x3 ToXYZ() { @@ -145,9 +145,9 @@ struct ACEScc : colorspace_base { static float32_t3x3 FromXYZ() { - return XYZtoACEScc; + return decode::XYZtoACEScc; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoACEScc, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(decode::XYZtoACEScc, val); } static float32_t3x3 ToXYZ() { From 53c930f5cbc52b9d82819a5f47d214e1f95046e8 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 4 Nov 2025 15:10:59 +0700 Subject: [PATCH 37/56] minor fixes to cook torrance, ndf infinity, move util functions into class --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 100 +++++++----------- .../nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl | 25 +++-- include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl | 22 ++-- .../ndf/microfacet_to_light_transform.hlsl | 73 +++++++------ 4 files changed, 106 insertions(+), 114 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl index fdce140f68..1ce00ba310 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -67,60 +67,6 @@ struct quant_query_helper return ndf.template createQuantQuery(interaction, cache, dummy); } }; - -template -struct checkValid; - -template -struct checkValid -{ - using scalar_type = typename F::scalar_type; - - template - static bool __call(NBL_CONST_REF_ARG(F) orientedFresnel, NBL_CONST_REF_ARG(Sample) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) - { - return _sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min; - } -}; - -template -struct checkValid -{ - using scalar_type = typename F::scalar_type; - using vector_type = typename F::vector_type; // expect monochrome - - template - static bool __call(NBL_CONST_REF_ARG(F) orientedFresnel, NBL_CONST_REF_ARG(Sample) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) - { - fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(scalar_type(1.0), hlsl::promote(orientedFresnel.getRefractionOrientedEta())); - return cache.isValid(orientedEta); - } -}; - -template -struct getOrientedFresnel; - -template -struct getOrientedFresnel -{ - static F __call(NBL_CONST_REF_ARG(F) fresnel, typename F::scalar_type NdotV) - { - // expect conductor fresnel - return fresnel; - } -}; - -template -NBL_PARTIAL_REQ_TOP(fresnel::TwoSidedFresnel) -struct getOrientedFresnel) > -{ - using scalar_type = typename F::scalar_type; - - static F __call(NBL_CONST_REF_ARG(F) fresnel, scalar_type NdotV) - { - return fresnel.getReorientedFresnel(NdotV); - } -}; } // N (NDF), F (fresnel) @@ -138,6 +84,7 @@ struct SCookTorrance NBL_CONSTEXPR_STATIC_INLINE bool IsBSDF = ndf_type::SupportedPaths != ndf::MTT_REFLECT; NBL_HLSL_BXDF_ANISOTROPIC_COND_DECLS(IsAnisotropic); + // utility functions template, class MicrofacetCache=conditional_t NBL_FUNC_REQUIRES(!ndf::NDF_CanOverwriteDG && RequiredInteraction && RequiredMicrofacetCache) @@ -155,13 +102,40 @@ struct SCookTorrance DG = dg.projectedLightMeasure; } + template) + static fresnel_type getOrientedFresnel(NBL_CONST_REF_ARG(fresnel_type) fresnel, scalar_type NdotV) + { + // expect conductor fresnel + return fresnel; + } + template) + static fresnel_type getOrientedFresnel(NBL_CONST_REF_ARG(fresnel_type) fresnel, scalar_type NdotV) + { + return fresnel.getReorientedFresnel(NdotV); + } + + template, + class MicrofacetCache=conditional_t, typename C=bool_constant > + static enable_if_t checkValid(NBL_CONST_REF_ARG(fresnel_type) orientedFresnel, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + return _sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min; + } + template, + class MicrofacetCache=conditional_t, typename C=bool_constant > + static enable_if_t checkValid(NBL_CONST_REF_ARG(fresnel_type) orientedFresnel, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(scalar_type(1.0), hlsl::promote(orientedFresnel.getRefractionOrientedEta())); + return cache.isValid(orientedEta); + } + + // bxdf stuff template, class MicrofacetCache=conditional_t NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) { - fresnel_type _f = impl::getOrientedFresnel::__call(fresnel, interaction.getNdotV()); - if (!impl::checkValid::template __call(_f, _sample, interaction, cache)) + fresnel_type _f = getOrientedFresnel(fresnel, interaction.getNdotV()); + if (!checkValid(_f, _sample, interaction, cache)) return hlsl::promote(0.0); using quant_query_type = typename ndf_type::quant_query_type; @@ -178,7 +152,7 @@ struct SCookTorrance overwriteDG(DG, ndf, gq, qq, _sample, interaction, cache, isInfinity); - if (isInfinity) + if (isInfinity) // after all calls setting DG, allows compiler to throw away calls to ndf.D if using overwriteDG return hlsl::promote(0.0); scalar_type clampedVdotH = cache.getVdotH(); @@ -245,7 +219,7 @@ struct SCookTorrance const vector3_type localV = interaction.getTangentSpaceV(); const scalar_type NdotV = localV.z; - fresnel_type _f = impl::getOrientedFresnel::__call(fresnel, NdotV); + fresnel_type _f = getOrientedFresnel(fresnel, NdotV); fresnel::OrientedEtaRcps rcpEta = _f.getOrientedEtaRcps(); const vector3_type upperHemisphereV = ieee754::flipSignIfRHSNegative(localV, hlsl::promote(NdotV)); @@ -325,7 +299,7 @@ struct SCookTorrance dg1_query_type dq = ndf.template createDG1Query(interaction, cache); - fresnel_type _f = impl::getOrientedFresnel::__call(fresnel, interaction.getNdotV()); + fresnel_type _f = getOrientedFresnel(fresnel, interaction.getNdotV()); quant_query_type qq = impl::quant_query_helper::template __call(ndf, _f, interaction, cache); quant_type DG1 = ndf.template DG1(dq, qq, _sample, interaction, isInfinity); @@ -344,8 +318,8 @@ struct SCookTorrance NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) { - fresnel_type _f = impl::getOrientedFresnel::__call(fresnel, interaction.getNdotV()); - if (!impl::checkValid::template __call(_f, _sample, interaction, cache)) + fresnel_type _f = getOrientedFresnel(fresnel, interaction.getNdotV()); + if (!checkValid(_f, _sample, interaction, cache)) return scalar_type(0.0); bool isInfinity; @@ -363,9 +337,9 @@ struct SCookTorrance bool isInfinity; scalar_type _pdf = __pdf(_sample, interaction, cache, isInfinity); - fresnel_type _f = impl::getOrientedFresnel::__call(fresnel, interaction.getNdotV()); + fresnel_type _f = getOrientedFresnel(fresnel, interaction.getNdotV()); - const bool valid = impl::checkValid::template __call(_f, _sample, interaction, cache); + const bool valid = checkValid(_f, _sample, interaction, cache); assert(valid); // expect the generated sample to always be valid, different checks for brdf and btdf scalar_type G2_over_G1 = scalar_type(1.0); diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl index f7d9f7c2f7..70f58a0dc7 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl @@ -107,11 +107,12 @@ struct BeckmannCommon(numeric_limits::infinity); } - isInfinity = false; scalar_type NdotH2 = cache.getNdotH2(); scalar_type nom = exp2((NdotH2 - scalar_type(1.0)) / (log(2.0) * a2 * NdotH2)); scalar_type denom = a2 * NdotH2 * NdotH2; - return numbers::inv_pi * nom / denom; + scalar_type ndf = numbers::inv_pi * nom / denom; + isInfinity = hlsl::isinf(ndf); + return ndf; } scalar_type C2(scalar_type NdotX2) @@ -141,7 +142,9 @@ struct BeckmannCommon(-(cache.getTdotH2() / ax2 + cache.getBdotH2() / ay2) / NdotH2); scalar_type denom = a2 * NdotH2 * NdotH2; - return numbers::inv_pi * nom / denom; + scalar_type ndf = numbers::inv_pi * nom / denom; + isInfinity = hlsl::isinf(ndf); + return ndf; } scalar_type C2(scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2) @@ -277,12 +280,7 @@ struct Beckmann quant_query_type quant_query; // only has members for refraction NBL_IF_CONSTEXPR(SupportsTransmission) { - quant_query.VdotHLdotH = cache.getVdotHLdotH(); - const scalar_type VdotH = cache.getVdotH(); - const scalar_type VdotH_etaLdotH = hlsl::mix(VdotH + orientedEta * cache.getLdotH(), - VdotH / orientedEta + cache.getLdotH(), - interaction.getPathOrigin() == PathOrigin::PO_SENSOR); - quant_query.neg_rcp2_refractionDenom = scalar_type(-1.0) / (VdotH_etaLdotH * VdotH_etaLdotH); + quant_query = quant_query_type::template create(interaction, cache, orientedEta); } return quant_query; } @@ -337,8 +335,13 @@ struct Beckmann quant_type DG1(NBL_CONST_REF_ARG(dg1_query_type) query, NBL_CONST_REF_ARG(quant_query_type) quant_query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_REF_ARG(bool) isInfinity) { scalar_type D = query.getNdf(); - scalar_type dg1 = query.getNdf() / (scalar_type(1.0) + query.getLambdaV()); - isInfinity = D == bit_cast(numeric_limits::infinity); + isInfinity = hlsl::isinf(D); + if (isInfinity) + { + quant_type dmq; + return dmq; + } + scalar_type dg1 = D / (scalar_type(1.0) + query.getLambdaV()); return createDualMeasureQuantity(dg1, interaction.getNdotV(BxDFClampMode::BCM_ABS), _sample.getNdotL(BxDFClampMode::BCM_ABS), quant_query); } diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl index b27c892abe..1174e1ba93 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl @@ -97,7 +97,9 @@ struct GGXCommon / (denom * denom); + scalar_type ndf = a2 * numbers::inv_pi / (denom * denom); + isInfinity = hlsl::isinf(ndf); + return ndf; } scalar_type devsh_part(scalar_type NdotX2) @@ -128,7 +130,9 @@ struct GGXCommon / (a2 * denom * denom); + scalar_type ndf = numbers::inv_pi / (a2 * denom * denom); + isInfinity = hlsl::isinf(ndf); + return ndf; } // TODO: potential idea for making GGX spin using covariance matrix of sorts: https://www.desmos.com/3d/weq2ginq9o @@ -220,12 +224,7 @@ struct GGX quant_query_type quant_query; // only has members for refraction NBL_IF_CONSTEXPR(SupportsTransmission) { - quant_query.VdotHLdotH = cache.getVdotHLdotH(); - const scalar_type VdotH = cache.getVdotH(); - const scalar_type VdotH_etaLdotH = hlsl::mix(VdotH + orientedEta * cache.getLdotH(), - VdotH / orientedEta + cache.getLdotH(), - interaction.getPathOrigin() == PathOrigin::PO_SENSOR); - quant_query.neg_rcp2_refractionDenom = scalar_type(-1.0) / (VdotH_etaLdotH * VdotH_etaLdotH); + quant_query = quant_query_type::template create(interaction, cache, orientedEta); } return quant_query; } @@ -282,9 +281,12 @@ struct GGX quant_type DG1(NBL_CONST_REF_ARG(dg1_query_type) query, NBL_CONST_REF_ARG(quant_query_type) quant_query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_REF_ARG(bool) isInfinity) { scalar_type D = query.getNdfwoNumerator(); - scalar_type dg1_over_2NdotV = query.getNdfwoNumerator() * query.getG1over2NdotV(); - isInfinity = D == bit_cast(numeric_limits::infinity); + isInfinity = hlsl::isinf(D); quant_type dmq; + if (isInfinity) + return dmq; + + scalar_type dg1_over_2NdotV = D * query.getG1over2NdotV(); dmq.microfacetMeasure = scalar_type(2.0) * interaction.getNdotV(_clamp) * dg1_over_2NdotV; NBL_IF_CONSTEXPR(SupportsTransmission) diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl index 58b8892ade..bfcb375ac8 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl @@ -18,9 +18,9 @@ namespace ndf enum MicrofacetTransformTypes : uint16_t { - MTT_REFLECT = 0b01, - MTT_REFRACT = 0b10, - MTT_REFLECT_REFRACT = 0b11 + MTT_REFLECT = 0b01, + MTT_REFRACT = 0b10, + MTT_REFLECT_REFRACT = 0b11 }; namespace microfacet_transform_concepts @@ -43,25 +43,38 @@ NBL_CONCEPT_END( template struct DualMeasureQuantQuery { - using scalar_type = T; + using scalar_type = T; - // note in pbrt it's `abs(VdotH)*abs(LdotH)` - // we leverage the fact that under transmission the sign must always be negative and rest of the code already accounts for that - scalar_type getVdotHLdotH() NBL_CONST_MEMBER_FUNC { return VdotHLdotH; } - scalar_type getNeg_rcp2_refractionDenom() NBL_CONST_MEMBER_FUNC { return neg_rcp2_refractionDenom ; } + template + static DualMeasureQuantQuery create(NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache, scalar_type orientedEta) + { + DualMeasureQuantQuery retval; + retval.VdotHLdotH = cache.getVdotHLdotH(); + const scalar_type VdotH = cache.getVdotH(); + const scalar_type VdotH_etaLdotH = hlsl::mix(VdotH + orientedEta * cache.getLdotH(), + VdotH / orientedEta + cache.getLdotH(), + interaction.getPathOrigin() == PathOrigin::PO_SENSOR); + retval.neg_rcp2_refractionDenom = scalar_type(-1.0) / (VdotH_etaLdotH * VdotH_etaLdotH); + return retval; + } - scalar_type VdotHLdotH; - scalar_type neg_rcp2_refractionDenom; + // note in pbrt it's `abs(VdotH)*abs(LdotH)` + // we leverage the fact that under transmission the sign must always be negative and rest of the code already accounts for that + scalar_type getVdotHLdotH() NBL_CONST_MEMBER_FUNC { return VdotHLdotH; } + scalar_type getNeg_rcp2_refractionDenom() NBL_CONST_MEMBER_FUNC { return neg_rcp2_refractionDenom ; } + + scalar_type VdotHLdotH; + scalar_type neg_rcp2_refractionDenom; }; template struct SDualMeasureQuant { - using value_type = T; - - T microfacetMeasure; - T projectedLightMeasure; + using value_type = T; + + T microfacetMeasure; + T projectedLightMeasure; }; namespace impl @@ -69,19 +82,19 @@ namespace impl template struct createDualMeasureQuantity_helper { - using scalar_type = typename vector_traits::scalar_type; + using scalar_type = typename vector_traits::scalar_type; - static SDualMeasureQuant __call(const T microfacetMeasure, scalar_type clampedNdotV, scalar_type clampedNdotL, scalar_type VdotHLdotH, scalar_type neg_rcp2_refractionDenom) - { - assert(clampedNdotV >= scalar_type(0.0) && clampedNdotL >= scalar_type(0.0)); - SDualMeasureQuant retval; - retval.microfacetMeasure = microfacetMeasure; - // do constexpr booleans first so optimizer picks up this and short circuits - const bool transmitted = reflect_refract==MTT_REFRACT || (reflect_refract!=MTT_REFLECT && VdotHLdotH < scalar_type(0.0)); - retval.projectedLightMeasure = microfacetMeasure * hlsl::mix(scalar_type(0.25),VdotHLdotH*neg_rcp2_refractionDenom,transmitted)/clampedNdotV; - // VdotHLdotH is negative under transmission, so thats denominator is negative - return retval; - } + static SDualMeasureQuant __call(const T microfacetMeasure, scalar_type clampedNdotV, scalar_type clampedNdotL, scalar_type VdotHLdotH, scalar_type neg_rcp2_refractionDenom) + { + assert(clampedNdotV >= scalar_type(0.0) && clampedNdotL >= scalar_type(0.0)); + SDualMeasureQuant retval; + retval.microfacetMeasure = microfacetMeasure; + // do constexpr booleans first so optimizer picks up this and short circuits + const bool transmitted = reflect_refract==MTT_REFRACT || (reflect_refract!=MTT_REFLECT && VdotHLdotH < scalar_type(0.0)); + retval.projectedLightMeasure = microfacetMeasure * hlsl::mix(scalar_type(0.25),VdotHLdotH*neg_rcp2_refractionDenom,transmitted)/clampedNdotV; + // VdotHLdotH is negative under transmission, so thats denominator is negative + return retval; + } }; } @@ -89,18 +102,18 @@ struct createDualMeasureQuantity_helper template SDualMeasureQuant createDualMeasureQuantity(const T specialMeasure, typename vector_traits::scalar_type clampedNdotV, typename vector_traits::scalar_type clampedNdotL) { - typename vector_traits::scalar_type dummy; - return impl::createDualMeasureQuantity_helper::__call(specialMeasure,clampedNdotV,clampedNdotL,dummy,dummy); + typename vector_traits::scalar_type dummy; + return impl::createDualMeasureQuantity_helper::__call(specialMeasure,clampedNdotV,clampedNdotL,dummy,dummy); } template SDualMeasureQuant createDualMeasureQuantity(const T specialMeasure, typename vector_traits::scalar_type clampedNdotV, typename vector_traits::scalar_type clampedNdotL, typename vector_traits::scalar_type VdotHLdotH, typename vector_traits::scalar_type neg_rcp2_refractionDenom) { - return impl::createDualMeasureQuantity_helper::__call(specialMeasure,clampedNdotV,clampedNdotL,VdotHLdotH,neg_rcp2_refractionDenom); + return impl::createDualMeasureQuantity_helper::__call(specialMeasure,clampedNdotV,clampedNdotL,VdotHLdotH,neg_rcp2_refractionDenom); } template SDualMeasureQuant createDualMeasureQuantity(const T specialMeasure, typename vector_traits::scalar_type clampedNdotV, typename vector_traits::scalar_type clampedNdotL, NBL_CONST_REF_ARG(Query) query) { - return impl::createDualMeasureQuantity_helper::__call(specialMeasure,clampedNdotV,clampedNdotL,query.getVdotHLdotH(),query.getNeg_rcp2_refractionDenom()); + return impl::createDualMeasureQuantity_helper::__call(specialMeasure,clampedNdotV,clampedNdotL,query.getVdotHLdotH(),query.getNeg_rcp2_refractionDenom()); } } From 23de8ed07c43740ed934e0a24dafb9b6b20d35be Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 4 Nov 2025 16:09:15 +0700 Subject: [PATCH 38/56] changes to angle adding usage --- .../nbl/builtin/hlsl/math/angle_adding.hlsl | 38 ++++++++----------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/include/nbl/builtin/hlsl/math/angle_adding.hlsl b/include/nbl/builtin/hlsl/math/angle_adding.hlsl index 8a89840573..5d99b60ef2 100644 --- a/include/nbl/builtin/hlsl/math/angle_adding.hlsl +++ b/include/nbl/builtin/hlsl/math/angle_adding.hlsl @@ -21,53 +21,45 @@ struct sincos_accumulator { using this_t = sincos_accumulator; - static this_t create() + static this_t create(T cosA) { - this_t retval; - retval.runningSum = complex_t::create(T(1.0), T(0.0)); - return retval; + return create(cosA, sqrt(T(1.0) - cosA * cosA)); } - static this_t create(T cosA) + static this_t create(T cosA, T sinA) { this_t retval; - retval.runningSum = complex_t::create(cosA, T(0)); + retval.runningSum.real(cosA); + retval.runningSum.imag(sinA); + retval.wraparound = 0u; return retval; } - void addCosine(T cosA, T biasA) + void addCosine(T cosA, T sinA) { - const T bias = biasA + runningSum.imag(); const T a = cosA; const T b = runningSum.real(); const bool reverse = abs(min(a, b)) > max(a, b); - const T c = a * b - sqrt((T(1.0) - a * a) * (T(1.0) - b * b)); + const T c = a * b - sinA * runningSum.imag(); + const T d = sinA * b + a * runningSum.imag(); runningSum.real(ieee754::flipSign(c, reverse)); - runningSum.imag(hlsl::mix(bias, bias + numbers::pi, reverse)); + runningSum.imag(ieee754::flipSign(d, reverse)); + + wraparound += hlsl::mix(0u, 1u, reverse); } void addCosine(T cosA) { - addCosine(cosA, T(0.0)); + addCosine(cosA, sqrt(T(1.0) - cosA * cosA)); } T getSumofArccos() { - return acos(runningSum.real()) + runningSum.imag(); - } - - static T getArccosSumofABC_minus_PI(T cosA, T cosB, T cosC, T sinA, T sinB, T sinC) - { - const bool AltminusB = cosA < (-cosB); - const T cosSumAB = cosA * cosB - sinA * sinB; - const bool ABltminusC = cosSumAB < (-cosC); - const bool ABltC = cosSumAB < cosC; - // apply triple angle formula - const T absArccosSumABC = acos(clamp(cosSumAB * cosC - (cosA * sinB + sinA * cosB) * sinC, T(-1.0), T(1.0))); - return ((AltminusB ? ABltC : ABltminusC) ? (-absArccosSumABC) : absArccosSumABC) + ((AltminusB || ABltminusC) ? numbers::pi : (-numbers::pi)); + return acos(runningSum.real()) + wraparound * numbers::pi; } complex_t runningSum; + uint16_t wraparound; }; } From ff9e03fb07d959ecacaf247122f3c1e64c63d9d3 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 4 Nov 2025 16:20:43 +0700 Subject: [PATCH 39/56] better orthonormality for H --- .../builtin/hlsl/bxdf/base/cook_torrance_base.hlsl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl index 1ce00ba310..9d9f2ceca5 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -128,6 +128,12 @@ struct SCookTorrance return cache.isValid(orientedEta); } + bool dotIsUnity(const vector3_type a, const vector3_type b) + { + const scalar_type ab = hlsl::dot(a, b); + return hlsl::max(ab, scalar_type(1.0) / ab) <= scalar_type(1.0 + 1e-3); + } + // bxdf stuff template, class MicrofacetCache=conditional_t @@ -256,8 +262,14 @@ struct SCookTorrance assert(cache.isValid(fresnel::OrientedEtas::create(scalar_type(1.0), hlsl::promote(_f.getRefractionOrientedEta())))); ray_dir_info_type V = interaction.getV(); + const matrix3x3_type fromTangent = interaction.getFromTangentSpace(); + // tangent frame orthonormality + assert(dotIsUnity(fromTangent[0],fromTangent[1])); + assert(dotIsUnity(fromTangent[1],fromTangent[2])); + assert(dotIsUnity(fromTangent[2],fromTangent[0])); + // NDF sampling produced a unit length direction + assert(dotIsUnity(localH,localH)); const vector3_type H = hlsl::mul(interaction.getFromTangentSpace(), localH); - assert(hlsl::abs(hlsl::length(H) - scalar_type(1.0)) < scalar_type(1e-4)); Refract r = Refract::create(V.getDirection(), H); struct reflect_refract_wrapper // so we don't recalculate LdotH From 18b39222c70769fbb8b20d5184845a085f7f83b2 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 4 Nov 2025 17:11:28 +0700 Subject: [PATCH 40/56] minor change to ggx clamp --- include/nbl/builtin/hlsl/bxdf/ndf.hlsl | 1 - include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl | 20 ++++++++------------ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/ndf.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf.hlsl index da1dc5e630..6fab8790a4 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf.hlsl @@ -103,7 +103,6 @@ NBL_CONSTEXPR_STATIC_INLINE bool RequiredMicrofacetCache = IS_ANISO ? Anisotropi #define NBL_HLSL_NDF_CONSTEXPR_DECLS(ANISO,REFLECT_REFRACT) NBL_CONSTEXPR_STATIC_INLINE bool IsAnisotropic = ANISO;\ NBL_CONSTEXPR_STATIC_INLINE MicrofacetTransformTypes SupportedPaths = REFLECT_REFRACT;\ NBL_CONSTEXPR_STATIC_INLINE bool SupportsTransmission = REFLECT_REFRACT != MTT_REFLECT;\ -NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = SupportsTransmission ? BxDFClampMode::BCM_ABS : BxDFClampMode::BCM_NONE;\ NBL_HLSL_BXDF_ANISOTROPIC_COND_DECLS(IsAnisotropic);\ #define NBL_HLSL_NDF_TYPE_ALIASES(...) using this_t = BOOST_PP_REMOVE_PARENS(BOOST_PP_SEQ_ELEM(0, __VA_ARGS__));\ diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl index 1174e1ba93..558100548e 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl @@ -84,8 +84,6 @@ struct GGXCommon) scalar_type D(NBL_CONST_REF_ARG(MicrofacetCache) cache, NBL_REF_ARG(bool) isInfinity) @@ -118,8 +116,6 @@ struct GGXCommon) scalar_type D(NBL_CONST_REF_ARG(MicrofacetCache) cache, NBL_REF_ARG(bool) isInfinity) { @@ -234,7 +230,7 @@ struct GGX dg1_query_type dg1_query; bool dummy; dg1_query.ndf = __ndf_base.template D(cache, dummy); - scalar_type clampedNdotV = interaction.getNdotV(_clamp); + scalar_type clampedNdotV = interaction.getNdotV(BxDFClampMode::BCM_ABS); dg1_query.G1_over_2NdotV = G1_wo_numerator(clampedNdotV, __ndf_base.devsh_part(interaction.getNdotV2())); return dg1_query; } @@ -252,7 +248,7 @@ struct GGX dg1_query_type dg1_query; bool dummy; dg1_query.ndf = __ndf_base.template D(cache, dummy); - scalar_type clampedNdotV = interaction.getNdotV(_clamp); + scalar_type clampedNdotV = interaction.getNdotV(BxDFClampMode::BCM_ABS); dg1_query.G1_over_2NdotV = G1_wo_numerator(clampedNdotV, __ndf_base.devsh_part(interaction.getTdotV2(), interaction.getBdotV2(), interaction.getNdotV2())); return dg1_query; } @@ -287,7 +283,7 @@ struct GGX return dmq; scalar_type dg1_over_2NdotV = D * query.getG1over2NdotV(); - dmq.microfacetMeasure = scalar_type(2.0) * interaction.getNdotV(_clamp) * dg1_over_2NdotV; + dmq.microfacetMeasure = scalar_type(2.0) * interaction.getNdotV(BxDFClampMode::BCM_ABS) * dg1_over_2NdotV; NBL_IF_CONSTEXPR(SupportsTransmission) { @@ -305,8 +301,8 @@ struct GGX template && RequiredInteraction && RequiredMicrofacetCache) scalar_type correlated_wo_numerator(NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) { - scalar_type NdotV = interaction.getNdotV(_clamp); - scalar_type NdotL = _sample.getNdotL(_clamp); + scalar_type NdotV = interaction.getNdotV(BxDFClampMode::BCM_ABS); + scalar_type NdotL = _sample.getNdotL(BxDFClampMode::BCM_ABS); scalar_type devsh_v = query.getDevshV(); scalar_type devsh_l = query.getDevshL(); // without numerator, numerator is 2 * NdotV * NdotL, we factor out 4 * NdotV * NdotL, hence 0.5 @@ -329,7 +325,7 @@ struct GGX template && RequiredInteraction && RequiredMicrofacetCache) scalar_type correlated(NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) { - return scalar_type(4.0) * interaction.getNdotV(_clamp) * _sample.getNdotL(_clamp) * correlated_wo_numerator(query, _sample, interaction, cache); + return scalar_type(4.0) * interaction.getNdotV(BxDFClampMode::BCM_ABS) * _sample.getNdotL(BxDFClampMode::BCM_ABS) * correlated_wo_numerator(query, _sample, interaction, cache); } template && RequiredInteraction && RequiredMicrofacetCache) @@ -366,8 +362,8 @@ struct GGX scalar_type G2_over_G1(NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) { scalar_type G2_over_G1; - scalar_type NdotV = interaction.getNdotV(_clamp); - scalar_type NdotL = _sample.getNdotL(_clamp); + scalar_type NdotV = interaction.getNdotV(BxDFClampMode::BCM_ABS); + scalar_type NdotL = _sample.getNdotL(BxDFClampMode::BCM_ABS); scalar_type devsh_v = query.getDevshV(); scalar_type devsh_l = query.getDevshL(); if (cache.isTransmission()) From f3dd05c1b5efae9f62112b0f611a74dd9d8f6839 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 5 Nov 2025 10:53:26 +0700 Subject: [PATCH 41/56] minor changes to create complex_t in angle_adding --- include/nbl/builtin/hlsl/complex.hlsl | 2 +- include/nbl/builtin/hlsl/math/angle_adding.hlsl | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/include/nbl/builtin/hlsl/complex.hlsl b/include/nbl/builtin/hlsl/complex.hlsl index f61b707865..da04c49b51 100644 --- a/include/nbl/builtin/hlsl/complex.hlsl +++ b/include/nbl/builtin/hlsl/complex.hlsl @@ -24,7 +24,7 @@ template struct complex_t : public std::complex { using base_t = std::complex; - complex_t(const Scalar real, const Scalar imag) : base_t(real, imag) {} + complex_t(const Scalar real = Scalar(), const Scalar imag = Scalar()) : base_t(real, imag) {} static complex_t create(const Scalar real, const Scalar imag) { complex_t retVal(real, imag); diff --git a/include/nbl/builtin/hlsl/math/angle_adding.hlsl b/include/nbl/builtin/hlsl/math/angle_adding.hlsl index 5d99b60ef2..6918050542 100644 --- a/include/nbl/builtin/hlsl/math/angle_adding.hlsl +++ b/include/nbl/builtin/hlsl/math/angle_adding.hlsl @@ -29,8 +29,9 @@ struct sincos_accumulator static this_t create(T cosA, T sinA) { this_t retval; - retval.runningSum.real(cosA); - retval.runningSum.imag(sinA); + retval.runningSum = complex_t::create(cosA, sinA); + // retval.runningSum.real(cosA); + // retval.runningSum.imag(sinA); retval.wraparound = 0u; return retval; } @@ -38,10 +39,11 @@ struct sincos_accumulator void addCosine(T cosA, T sinA) { const T a = cosA; - const T b = runningSum.real(); - const bool reverse = abs(min(a, b)) > max(a, b); - const T c = a * b - sinA * runningSum.imag(); - const T d = sinA * b + a * runningSum.imag(); + const T cosB = runningSum.real(); + const T sinB = runningSum.imag(); + const bool reverse = abs(min(a, cosB)) > max(a, cosB); + const T c = a * cosB - sinA * sinB; + const T d = sinA * cosB + a * sinB; runningSum.real(ieee754::flipSign(c, reverse)); runningSum.imag(ieee754::flipSign(d, reverse)); From 68d3614043c25efcd1f72f64095a2ab56371066d Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 5 Nov 2025 12:02:52 +0700 Subject: [PATCH 42/56] refactor out common section of cook torrance generate --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 145 +++++++++--------- 1 file changed, 70 insertions(+), 75 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl index 9d9f2ceca5..8eeecc3456 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -174,80 +174,11 @@ struct SCookTorrance return impl::__implicit_promote::__call(_f(clampedVdotH)) * DG; } - template > - enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(anisocache_type) cache) - { - { - const scalar_type NdotV = interaction.getNdotV(); - if (NdotV < numeric_limits::min) - return sample_type::createInvalid(); - assert(!hlsl::isnan(NdotV)); - } - - const vector3_type localV = interaction.getTangentSpaceV(); - const vector3_type localH = ndf.generateH(localV, u); - const scalar_type VdotH = hlsl::dot(localV, localH); - assert(VdotH >= scalar_type(0.0)); // VNDF sampling guarantees VdotH has same sign as NdotV (should be positive for BRDF) - - const scalar_type NdotL = scalar_type(2.0) * VdotH * localH.z - localV.z; - ray_dir_info_type L; - if (NdotL > 0) // compiler's Common Subexpression Elimination pass should re-use 2*VdotH later - { - ray_dir_info_type V = interaction.getV(); - const vector3_type H = hlsl::mul(interaction.getFromTangentSpace(), localH); - struct reflect_wrapper // so we don't recalculate VdotH - { - vector3_type operator()() NBL_CONST_MEMBER_FUNC - { - return r(VdotH); - } - bxdf::Reflect r; - scalar_type VdotH; - }; - reflect_wrapper rw; - rw.r = bxdf::Reflect::create(V.getDirection(), H); - rw.VdotH = VdotH; - L = V.template reflect(rw); - - cache = anisocache_type::createForReflection(localV, localH); - - const vector3_type T = interaction.getT(); - const vector3_type B = interaction.getB(); - - return sample_type::create(L, T, B, NdotL); - } - else // fail if samples have invalid paths - return sample_type::createInvalid(); // should check if sample direction is invalid - } - template > - enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u, NBL_REF_ARG(anisocache_type) cache) + sample_type __generate_common(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type localH, + const scalar_type NdotV, const scalar_type VdotH, bool transmitted, + NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps) rcpEta, + NBL_REF_ARG(anisocache_type) cache, NBL_REF_ARG(bool) valid) { - const vector3_type localV = interaction.getTangentSpaceV(); - const scalar_type NdotV = localV.z; - - fresnel_type _f = getOrientedFresnel(fresnel, NdotV); - fresnel::OrientedEtaRcps rcpEta = _f.getOrientedEtaRcps(); - - const vector3_type upperHemisphereV = ieee754::flipSignIfRHSNegative(localV, hlsl::promote(NdotV)); - const vector3_type localH = ndf.generateH(upperHemisphereV, u.xy); - const scalar_type VdotH = hlsl::dot(localV, localH); - - NBL_IF_CONSTEXPR(!ndf_type::GuaranteedVNDF) - { - // allow for rejection sampling, theoretically NdotV=0 or VdotH=0 is valid, but leads to 0 value contribution anyway - if ((IsBSDF ? (NdotV*VdotH) : VdotH) <= scalar_type(0.0)) - return sample_type::createInvalid(); - assert(!hlsl::isnan(NdotV*VdotH)); - } - else - { - assert(NdotV*VdotH >= scalar_type(0.0)); - } - const scalar_type reflectance = _f(hlsl::abs(VdotH))[0]; - - scalar_type rcpChoiceProb; - scalar_type z = u.z; - bool transmitted = math::partitionRandVariable(reflectance, z, rcpChoiceProb); const scalar_type LdotH = hlsl::mix(VdotH, ieee754::copySign(hlsl::sqrt(rcpEta.value2[0]*VdotH*VdotH + scalar_type(1.0) - rcpEta.value2[0]), -VdotH), transmitted); // fail if samples have invalid paths @@ -256,10 +187,12 @@ struct SCookTorrance // VNDF sampling guarantees that `VdotH` has same sign as `NdotV` // and `transmitted` controls the sign of `LdotH` relative to `VdotH` by construction (reflect -> same sign, or refract -> opposite sign) if (ComputeMicrofacetNormal::isTransmissionPath(NdotV, NdotL) != transmitted) + { + valid = false; return sample_type::createInvalid(); // should check if sample direction is invalid + } cache = anisocache_type::createPartial(VdotH, LdotH, localH.z, transmitted, rcpEta); - assert(cache.isValid(fresnel::OrientedEtas::create(scalar_type(1.0), hlsl::promote(_f.getRefractionOrientedEta())))); ray_dir_info_type V = interaction.getV(); const matrix3x3_type fromTangent = interaction.getFromTangentSpace(); @@ -290,10 +223,72 @@ struct SCookTorrance const vector3_type T = interaction.getT(); const vector3_type B = interaction.getB(); - cache.fillTangents(T, B, H); + valid = true; return sample_type::create(L, T, B, NdotL); } + template > + enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(anisocache_type) cache) + { + const scalar_type NdotV = interaction.getNdotV(); + if (NdotV < numeric_limits::min) + return sample_type::createInvalid(); + assert(!hlsl::isnan(NdotV)); + + const vector3_type localV = interaction.getTangentSpaceV(); + const vector3_type localH = ndf.generateH(localV, u); + const scalar_type VdotH = hlsl::dot(localV, localH); + assert(VdotH >= scalar_type(0.0)); // VNDF sampling guarantees VdotH has same sign as NdotV (should be positive for BRDF) + + fresnel::OrientedEtaRcps dummy; + bool valid; + sample_type s = __generate_common(interaction, localH, NdotV, VdotH, false, dummy, cache, valid); + if (valid) + cache = anisocache_type::createForReflection(localV, localH); + return s; + } + template > + enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u, NBL_REF_ARG(anisocache_type) cache) + { + const vector3_type localV = interaction.getTangentSpaceV(); + const scalar_type NdotV = localV.z; + + fresnel_type _f = getOrientedFresnel(fresnel, NdotV); + fresnel::OrientedEtaRcps rcpEta = _f.getOrientedEtaRcps(); + + const vector3_type upperHemisphereV = ieee754::flipSignIfRHSNegative(localV, hlsl::promote(NdotV)); + const vector3_type localH = ndf.generateH(upperHemisphereV, u.xy); + const scalar_type VdotH = hlsl::dot(localV, localH); + + NBL_IF_CONSTEXPR(!ndf_type::GuaranteedVNDF) + { + // allow for rejection sampling, theoretically NdotV=0 or VdotH=0 is valid, but leads to 0 value contribution anyway + if ((IsBSDF ? (NdotV*VdotH) : VdotH) <= scalar_type(0.0)) + return sample_type::createInvalid(); + assert(!hlsl::isnan(NdotV*VdotH)); + } + else + { + assert(NdotV*VdotH >= scalar_type(0.0)); + } + const scalar_type reflectance = _f(hlsl::abs(VdotH))[0]; + + scalar_type rcpChoiceProb; + scalar_type z = u.z; + bool transmitted = math::partitionRandVariable(reflectance, z, rcpChoiceProb); + + bool valid; + sample_type s = __generate_common(interaction, localH, NdotV, VdotH, transmitted, rcpEta, cache, valid); + if (valid) + { + assert(cache.isValid(fresnel::OrientedEtas::create(scalar_type(1.0), hlsl::promote(_f.getRefractionOrientedEta())))); + const vector3_type T = interaction.getT(); + const vector3_type B = interaction.getB(); + const vector3_type H = hlsl::mul(interaction.getFromTangentSpace(), localH); + cache.fillTangents(T, B, H); + } + return s; + } template > enable_if_t generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const conditional_t u, NBL_REF_ARG(isocache_type) cache) { From aebfecf563037973ff635b16ab622106729bd90f Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 6 Nov 2025 16:38:10 +0700 Subject: [PATCH 43/56] added luma contribution hint, getPrefixThroughputWeights to interaction + takes Spectrum template type --- include/nbl/builtin/hlsl/bxdf/common.hlsl | 41 +++++++++++++++++------ include/nbl/builtin/hlsl/bxdf/ndf.hlsl | 2 +- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index 34d2e3d66c..58597aa43e 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -188,11 +188,14 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::ray_dir_info_type)) ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::spectral_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getV()), ::nbl::hlsl::is_same_v, typename T::ray_dir_info_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getN()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getNdotV(clampMode)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getNdotV2()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getPathOrigin()), ::nbl::hlsl::is_same_v, PathOrigin)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getLuminosityContributionHint()), ::nbl::hlsl::is_same_v, typename T::spectral_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getPrefixThroughputWeights()), ::nbl::hlsl::is_same_v, typename T::spectral_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(normV,normN)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(ray_dir_info::Basic, typename T::ray_dir_info_type)) ); @@ -202,21 +205,25 @@ NBL_CONCEPT_END( #undef iso #include -template) +template && concepts::FloatingPointLikeVectorial) struct SIsotropic { + using this_t = SIsotropic; using ray_dir_info_type = RayDirInfo; using scalar_type = typename RayDirInfo::scalar_type; using vector3_type = typename RayDirInfo::vector3_type; + using spectral_type = vector3_type; // WARNING: Changed since GLSL, now arguments need to be normalized! - static SIsotropic create(NBL_CONST_REF_ARG(RayDirInfo) normalizedV, const vector3_type normalizedN) + static this_t create(NBL_CONST_REF_ARG(RayDirInfo) normalizedV, const vector3_type normalizedN) { - SIsotropic retval; + this_t retval; retval.V = normalizedV; retval.N = normalizedN; retval.NdotV = nbl::hlsl::dot(retval.N, retval.V.getDirection()); retval.NdotV2 = retval.NdotV * retval.NdotV; + retval.luminosityContributionHint = hlsl::promote(1.0); + retval.throughputWeights = hlsl::promote(1.0); return retval; } @@ -230,11 +237,20 @@ struct SIsotropic scalar_type getNdotV2() NBL_CONST_MEMBER_FUNC { return NdotV2; } PathOrigin getPathOrigin() NBL_CONST_MEMBER_FUNC { return PathOrigin::PO_SENSOR; } + spectral_type getLuminosityContributionHint() NBL_CONST_MEMBER_FUNC { return luminosityContributionHint; } + spectral_type getPrefixThroughputWeights() NBL_CONST_MEMBER_FUNC + { + spectral_type prefixThroughputWeights = luminosityContributionHint * throughputWeights; + return prefixThroughputWeights / math::lpNorm(prefixThroughputWeights); + } RayDirInfo V; vector3_type N; scalar_type NdotV; scalar_type NdotV2; + + spectral_type luminosityContributionHint; + spectral_type throughputWeights; // product of all quotients so far }; #define NBL_CONCEPT_NAME Anisotropic @@ -278,6 +294,7 @@ struct SAnisotropic using scalar_type = typename ray_dir_info_type::scalar_type; using vector3_type = typename ray_dir_info_type::vector3_type; using matrix3x3_type = matrix; + using spectral_type = typename isotropic_interaction_type::spectral_type; // WARNING: Changed since GLSL, now arguments need to be normalized! static this_t create( @@ -319,6 +336,8 @@ struct SAnisotropic scalar_type getNdotV(BxDFClampMode _clamp = BxDFClampMode::BCM_NONE) NBL_CONST_MEMBER_FUNC { return isotropic.getNdotV(_clamp); } scalar_type getNdotV2() NBL_CONST_MEMBER_FUNC { return isotropic.getNdotV2(); } PathOrigin getPathOrigin() NBL_CONST_MEMBER_FUNC { return isotropic.getPathOrigin(); } + spectral_type getLuminosityContributionHint() NBL_CONST_MEMBER_FUNC { return isotropic.getLuminosityContributionHint(); } + spectral_type getPrefixThroughputWeights() NBL_CONST_MEMBER_FUNC { return isotropic.getPrefixThroughputWeights(); } vector3_type getT() NBL_CONST_MEMBER_FUNC { return T; } vector3_type getB() NBL_CONST_MEMBER_FUNC { return B; } @@ -345,7 +364,7 @@ struct SAnisotropic #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (_sample, T) -#define NBL_CONCEPT_PARAM_1 (inter, surface_interactions::SIsotropic) +#define NBL_CONCEPT_PARAM_1 (inter, surface_interactions::SIsotropic) #define NBL_CONCEPT_PARAM_2 (rdirinfo, typename T::ray_dir_info_type) #define NBL_CONCEPT_PARAM_3 (pV, typename T::vector3_type) #define NBL_CONCEPT_PARAM_4 (frame, typename T::matrix3x3_type) @@ -376,7 +395,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(rdirinfo,pV)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(rdirinfo,pV,pV,pV)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(rdirinfo,pV,pV,pNdotL)), ::nbl::hlsl::is_same_v, T)) - // ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create >(pV,inter)), ::nbl::hlsl::is_same_v, T)) // NOTE: temporarily commented out due to dxc bug https://github.com/microsoft/DirectXShaderCompiler/issues/7154 + // ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create >(pV,inter)), ::nbl::hlsl::is_same_v, T)) // NOTE: temporarily commented out due to dxc bug https://github.com/microsoft/DirectXShaderCompiler/issues/7154 ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getTangentSpaceL()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createInvalid()), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(ray_dir_info::Basic, typename T::ray_dir_info_type)) @@ -525,7 +544,7 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (cache, T) -#define NBL_CONCEPT_PARAM_1 (iso, surface_interactions::SIsotropic >) +#define NBL_CONCEPT_PARAM_1 (iso, surface_interactions::SIsotropic, typename T::vector3_type >) #define NBL_CONCEPT_PARAM_2 (pNdotV, typename T::scalar_type) #define NBL_CONCEPT_PARAM_3 (_sample, SLightSample >) #define NBL_CONCEPT_PARAM_4 (V, typename T::vector3_type) @@ -540,9 +559,9 @@ NBL_CONCEPT_BEGIN(6) NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createForReflection(pNdotV,pNdotV,pNdotV,pNdotV)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createForReflection(pNdotV,pNdotV,pNdotV)), ::nbl::hlsl::is_same_v, T)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template createForReflection >,SLightSample > >(iso,_sample)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template createForReflection, typename T::vector3_type >,SLightSample > >(iso,_sample)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(V,V,V,eta,V)), ::nbl::hlsl::is_same_v, T)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create >,SLightSample > >(iso,_sample,eta,V)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create, typename T::vector3_type >,SLightSample > >(iso,_sample,eta,V)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(ReadableIsotropicMicrofacetCache, T)) ); #undef eta @@ -698,7 +717,7 @@ struct SIsotropicMicrofacetCache #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (cache, T) -#define NBL_CONCEPT_PARAM_1 (aniso, surface_interactions::SAnisotropic > >) +#define NBL_CONCEPT_PARAM_1 (aniso, surface_interactions::SAnisotropic, typename T::vector3_type > >) #define NBL_CONCEPT_PARAM_2 (pNdotL, typename T::scalar_type) #define NBL_CONCEPT_PARAM_3 (_sample, SLightSample >) #define NBL_CONCEPT_PARAM_4 (V, typename T::vector3_type) @@ -724,9 +743,9 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createForReflection(V,V)), ::nbl::hlsl::is_same_v, T)) // ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(V,V,b0,rcp_eta)), ::nbl::hlsl::is_same_v, T)) // TODO: refuses to compile when arg4 is rcp_eta for some reason, eta is fine ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createForReflection(V,V,pNdotL)), ::nbl::hlsl::is_same_v, T)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template createForReflection > >,SLightSample > >(aniso,_sample)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template createForReflection, typename T::vector3_type > >,SLightSample > >(aniso,_sample)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(V,V,V,V,V,eta,V)), ::nbl::hlsl::is_same_v, T)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create > >,SLightSample > >(aniso,_sample,eta)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create, typename T::vector3_type > >,SLightSample > >(aniso,_sample,eta)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createPartial(pNdotL,pNdotL,pNdotL,b0,rcp_eta)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR)(cache.fillTangents(V,V,V))) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(CreatableIsotropicMicrofacetCache, typename T::isocache_type)) diff --git a/include/nbl/builtin/hlsl/bxdf/ndf.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf.hlsl index 6fab8790a4..f882374f99 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf.hlsl @@ -20,7 +20,7 @@ namespace ndf namespace dummy_impl { using sample_t = SLightSample >; -using interaction_t = surface_interactions::SAnisotropic > >; +using interaction_t = surface_interactions::SAnisotropic, vector > >; using cache_t = SAnisotropicMicrofacetCache >; } From b53fa1bbb5bf8bb1ca49dc1832c78be84de71cf8 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 6 Nov 2025 16:40:21 +0700 Subject: [PATCH 44/56] cook torrance bsdf can use spectral fresnel, uses interaction.getPrefixThroughputWeights --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 19 ++++++++++++++----- include/nbl/builtin/hlsl/math/functions.hlsl | 12 ++++++------ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl index 8eeecc3456..7451318929 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -167,8 +167,8 @@ struct SCookTorrance NBL_IF_CONSTEXPR(IsBSDF) { - const scalar_type reflectance = _f(clampedVdotH)[0]; - return hlsl::promote(hlsl::mix(reflectance, scalar_type(1.0) - reflectance, cache.isTransmission())) * DG; + const spectral_type reflectance = impl::__implicit_promote::__call(_f(clampedVdotH)); + return hlsl::mix(reflectance, hlsl::promote(1.0) - reflectance, cache.isTransmission()) * DG; } else return impl::__implicit_promote::__call(_f(clampedVdotH)) * DG; @@ -271,7 +271,9 @@ struct SCookTorrance { assert(NdotV*VdotH >= scalar_type(0.0)); } - const scalar_type reflectance = _f(hlsl::abs(VdotH))[0]; + + spectral_type prefixThroughputWeights = interaction.getPrefixThroughputWeights(); + const scalar_type reflectance = hlsl::dot(impl::__implicit_promote::__call(_f(hlsl::abs(VdotH))), prefixThroughputWeights); scalar_type rcpChoiceProb; scalar_type z = u.z; @@ -312,7 +314,8 @@ struct SCookTorrance NBL_IF_CONSTEXPR(IsBSDF) { - const scalar_type reflectance = _f(hlsl::abs(cache.getVdotH()))[0]; + spectral_type prefixThroughputWeights = interaction.getPrefixThroughputWeights(); + const scalar_type reflectance = hlsl::dot(impl::__implicit_promote::__call(_f(hlsl::abs(cache.getVdotH()))), prefixThroughputWeights); return hlsl::mix(reflectance, scalar_type(1.0) - reflectance, cache.isTransmission()) * DG1.projectedLightMeasure; } else @@ -359,7 +362,13 @@ struct SCookTorrance spectral_type quo; NBL_IF_CONSTEXPR(IsBSDF) - quo = hlsl::promote(G2_over_G1); + { + spectral_type prefixThroughputWeights = interaction.getPrefixThroughputWeights(); + spectral_type reflectance = impl::__implicit_promote::__call(_f(hlsl::abs(cache.getVdotH()))); + const scalar_type scaled_reflectance = hlsl::dot(reflectance, prefixThroughputWeights); + quo = hlsl::mix(reflectance / scaled_reflectance, + (hlsl::promote(1.0) - reflectance) / (scalar_type(1.0) - scaled_reflectance), cache.isTransmission()) * G2_over_G1; + } else { const scalar_type VdotH = cache.getVdotH(); diff --git a/include/nbl/builtin/hlsl/math/functions.hlsl b/include/nbl/builtin/hlsl/math/functions.hlsl index 20442c467b..6eee1fae6e 100644 --- a/include/nbl/builtin/hlsl/math/functions.hlsl +++ b/include/nbl/builtin/hlsl/math/functions.hlsl @@ -33,9 +33,9 @@ struct lp_norm getter; - scalar_type retval = abs(getter(v, 0)); - for (int i = 1; i < extent::value; i++) - retval = max(abs(getter(v, i)),retval); + scalar_type retval = abs(getter(v, 0)); + for (int i = 1; i < vector_traits::Dimension; i++) + retval = max(abs(getter(v, i)),retval); return retval; } }; @@ -49,9 +49,9 @@ struct lp_norm getter; - scalar_type retval = abs(getter(v, 0)); - for (int i = 1; i < extent::value; i++) - retval += abs(getter(v, i)); + scalar_type retval = abs(getter(v, 0)); + for (int i = 1; i < vector_traits::Dimension; i++) + retval += abs(getter(v, i)); return retval; } From 946b05082c4f5e13fbaeb112ed974b52d6879f36 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 6 Nov 2025 16:54:06 +0700 Subject: [PATCH 45/56] thin smooth dielectric use luma coeff from interaction --- .../builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl index bc95c03c91..17400adfe2 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -121,7 +121,7 @@ struct SThinSmoothDielectric const spectral_type reflectance = fresnel::thinDielectricInfiniteScatter(fresnel(interaction.getNdotV(_clamp))); // we are only allowed one choice for the entire ray, so make the probability a weighted sum - const scalar_type reflectionProb = nbl::hlsl::dot(reflectance, luminosityContributionHint); + const scalar_type reflectionProb = nbl::hlsl::dot(reflectance, interaction.getLuminosityContributionHint()); scalar_type rcpChoiceProb; scalar_type z = u.z; @@ -161,7 +161,7 @@ struct SThinSmoothDielectric const spectral_type reflectance = fresnel::thinDielectricInfiniteScatter(fresnel(interaction.getNdotV(_clamp))); const spectral_type sampleValue = hlsl::mix(reflectance, hlsl::promote(1.0) - reflectance, transmitted); - const scalar_type sampleProb = nbl::hlsl::dot(sampleValue,luminosityContributionHint); + const scalar_type sampleProb = nbl::hlsl::dot(sampleValue,interaction.getLuminosityContributionHint()); const scalar_type _pdf = bit_cast(numeric_limits::infinity); return quotient_pdf_type::create(sampleValue / sampleProb, _pdf); @@ -172,7 +172,6 @@ struct SThinSmoothDielectric } fresnel::Dielectric fresnel; - spectral_type luminosityContributionHint; }; } From 1a2f561b74c548fe0c2142e7e2e8224535cd5e45 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 7 Nov 2025 14:44:05 +0700 Subject: [PATCH 46/56] split iridescent fresnel by SupportsTransmission, adjusted fresnel concept, iridescent bxdf use cook torrance base --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 2 +- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 141 ++++++++++---- include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl | 2 - .../hlsl/bxdf/reflection/iridescent.hlsl | 181 +---------------- .../hlsl/bxdf/transmission/iridescent.hlsl | 182 +----------------- 5 files changed, 106 insertions(+), 402 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl index 7451318929..df75072dbe 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -51,7 +51,7 @@ struct quant_query_helper template static quant_query_type __call(NBL_REF_ARG(N) ndf, NBL_CONST_REF_ARG(F) fresnel, NBL_CONST_REF_ARG(I) interaction, NBL_CONST_REF_ARG(C) cache) { - return ndf.template createQuantQuery(interaction, cache, fresnel.orientedEta.value[0]); + return ndf.template createQuantQuery(interaction, cache, fresnel.getRefractionOrientedEta()); } }; diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 67a2dea9d0..c9e92ad13d 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -313,8 +313,9 @@ NBL_CONCEPT_BEGIN(2) NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) ((NBL_CONCEPT_REQ_TYPE)(T::vector_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::eta_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((fresnel(cosTheta)), ::nbl::hlsl::is_same_v, typename T::vector_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((fresnel.getOrientedEtaRcps()), ::nbl::hlsl::is_same_v, OrientedEtaRcps)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((fresnel.getOrientedEtaRcps()), ::nbl::hlsl::is_same_v, OrientedEtaRcps)) ); #undef cosTheta #undef fresnel @@ -342,6 +343,7 @@ struct Schlick { using scalar_type = typename vector_traits::scalar_type; using vector_type = T; + using eta_type = vector_type; static Schlick create(NBL_CONST_REF_ARG(T) F0) { @@ -358,11 +360,11 @@ struct Schlick return F0 + (1.0 - F0) * x*x*x*x*x; } - OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC + OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC { - const T sqrtF0 = hlsl::sqrt(F0); - OrientedEtaRcps rcpEta; - rcpEta.value = (hlsl::promote(1.0) - sqrtF0) / (hlsl::promote(1.0) + sqrtF0); + const eta_type sqrtF0 = hlsl::sqrt(F0); + OrientedEtaRcps rcpEta; + rcpEta.value = (hlsl::promote(1.0) - sqrtF0) / (hlsl::promote(1.0) + sqrtF0); rcpEta.value2 = rcpEta.value * rcpEta.value; return rcpEta; } @@ -375,6 +377,7 @@ struct Conductor { using scalar_type = typename vector_traits::scalar_type; using vector_type = T; + using eta_type = vector_type; static Conductor create(NBL_CONST_REF_ARG(T) eta, NBL_CONST_REF_ARG(T) etak) { @@ -432,10 +435,10 @@ struct Conductor return (rs2 + rp2) * hlsl::promote(0.5); } - OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC + OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC { - OrientedEtaRcps rcpEta; - rcpEta.value = hlsl::promote(1.0) / eta; + OrientedEtaRcps rcpEta; + rcpEta.value = hlsl::promote(1.0) / eta; rcpEta.value2 = rcpEta.value * rcpEta.value; return rcpEta; } @@ -450,6 +453,7 @@ struct Dielectric { using scalar_type = typename vector_traits::scalar_type; using vector_type = T; + using eta_type = vector_type; static Dielectric create(NBL_CONST_REF_ARG(OrientedEtas) orientedEta) { @@ -510,26 +514,16 @@ struct Dielectric }; // adapted from https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html +template +struct Iridescent; + +namespace impl +{ template) -struct Iridescent +struct iridescent_helper { - using this_t = Iridescent; using scalar_type = typename vector_traits::scalar_type; - using monochrome_type = vector; - using vector_type = T; // assert dim==3? - - static this_t create(scalar_type Dinc, vector_type ior1, vector_type ior2, vector_type ior3, vector_type iork3) - { - this_t retval; - retval.Dinc = Dinc; - retval.thinFilmIor = ior2; - retval.eta12 = ior2/ior1; - retval.eta23 = ior3/ior2; - retval.etak23 = scalar_type(0.0); - NBL_IF_CONSTEXPR(SupportsTransmission) - retval.etak23 = iork3/ior2; - return retval; - } + using vector_type = T; // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) static void phase_shift(const vector_type orientedEta, const vector_type orientedEtak, const vector_type cosTheta, NBL_REF_ARG(vector_type) phiS, NBL_REF_ARG(vector_type) phiP) @@ -566,7 +560,7 @@ struct Iridescent return xyz / scalar_type(1.0685e-7); } - T operator()(const scalar_type clampedCosTheta /* LdotH */) + T __call(const scalar_type clampedCosTheta) { const vector_type wavelengths = vector_type(colorspace::scRGB::wavelength_R, colorspace::scRGB::wavelength_G, colorspace::scRGB::wavelength_B); @@ -646,11 +640,80 @@ struct Iridescent return hlsl::max(colorspace::scRGB::FromXYZ(I), hlsl::promote(0.0)) * hlsl::promote(0.5); } - scalar_type getRefractionOrientedEta() NBL_CONST_MEMBER_FUNC { return eta23[0]; } - OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC + scalar_type Dinc; // thickness of thin film in nanometers, rec. 100-25000nm + vector_type thinFilmIor; + vector_type eta12; // outside (usually air 1.0) -> thin-film IOR + vector_type eta23; // thin-film -> base material IOR + vector_type etak23; // thin-film -> complex component, k==0 makes dielectric +}; +} + +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeVectorial) +struct Iridescent) > +{ + using this_t = Iridescent; + using scalar_type = typename vector_traits::scalar_type; + using vector_type = T; // assert dim==3? + using eta_type = vector_type; + + static this_t create(scalar_type Dinc, vector_type ior1, vector_type ior2, vector_type ior3, vector_type iork3) + { + this_t retval; + retval.helper.Dinc = Dinc; + retval.helper.thinFilmIor = ior2; + retval.helper.eta12 = ior2/ior1; + retval.helper.eta23 = ior3/ior2; + retval.helper.etak23 = iork3/ior2; + return retval; + } + + T operator()(const scalar_type clampedCosTheta) + { + return helper.__call(clampedCosTheta); + } + + OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC + { + OrientedEtaRcps rcpEta; + rcpEta.value = hlsl::promote(1.0) / helper.eta23; + rcpEta.value2 = rcpEta.value * rcpEta.value; + return rcpEta; + } + + impl::iridescent_helper helper; +}; + +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeVectorial) +struct Iridescent) > +{ + using this_t = Iridescent; + using scalar_type = typename vector_traits::scalar_type; + using vector_type = T; // assert dim==3? + using eta_type = vector; + + static this_t create(scalar_type Dinc, vector_type ior1, vector_type ior2, vector_type ior3) { - OrientedEtaRcps rcpEta; - rcpEta.value = hlsl::promote(1.0) / eta23[0]; + this_t retval; + retval.helper.Dinc = Dinc; + retval.helper.thinFilmIor = ior2; + retval.helper.eta12 = ior2/ior1; + retval.helper.eta23 = ior3/ior2; + retval.helper.etak23 = hlsl::promote(0.0); + return retval; + } + + T operator()(const scalar_type clampedCosTheta) + { + return helper.__call(clampedCosTheta); + } + + scalar_type getRefractionOrientedEta() NBL_CONST_MEMBER_FUNC { return helper.eta23[0]; } + OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC + { + OrientedEtaRcps rcpEta; + rcpEta.value = hlsl::promote(1.0) / helper.eta23[0]; rcpEta.value2 = rcpEta.value * rcpEta.value; return rcpEta; } @@ -659,21 +722,15 @@ struct Iridescent { const bool flip = NdotI < scalar_type(0.0); this_t orientedFresnel; - orientedFresnel.Dinc = Dinc; - orientedFresnel.thinFilmIor = thinFilmIor; - orientedFresnel.eta12 = hlsl::mix(eta12, hlsl::promote(1.0)/eta12, flip); - orientedFresnel.eta23 = hlsl::mix(eta23, hlsl::promote(1.0)/eta23, flip); - orientedFresnel.etak23 = hlsl::promote(0.0); - NBL_IF_CONSTEXPR(SupportsTransmission) - orientedFresnel.etak23 = hlsl::mix(etak23, hlsl::promote(1.0)/etak23, flip); + orientedFresnel.helper.Dinc = helper.Dinc; + orientedFresnel.helper.thinFilmIor = helper.thinFilmIor; + orientedFresnel.helper.eta12 = hlsl::mix(helper.eta12, hlsl::promote(1.0)/helper.eta12, flip); + orientedFresnel.helper.eta23 = hlsl::mix(helper.eta23, hlsl::promote(1.0)/helper.eta23, flip); + orientedFresnel.helper.etak23 = hlsl::promote(0.0); return orientedFresnel; } - scalar_type Dinc; // thickness of thin film in nanometers, rec. 100-25000nm - vector_type thinFilmIor; - vector_type eta12; // outside (usually air 1.0) -> thin-film IOR - vector_type eta23; // thin-film -> base material IOR - vector_type etak23; // thin-film -> complex component, k==0 makes dielectric + impl::iridescent_helper helper; }; diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl index 558100548e..c2b9e959e1 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl @@ -93,7 +93,6 @@ struct GGXCommon(numeric_limits::infinity); } - isInfinity = false; scalar_type denom = scalar_type(1.0) - one_minus_a2 * cache.getNdotH2(); scalar_type ndf = a2 * numbers::inv_pi / (denom * denom); isInfinity = hlsl::isinf(ndf); @@ -124,7 +123,6 @@ struct GGXCommon(numeric_limits::infinity); } - isInfinity = false; scalar_type denom = cache.getTdotH2() / ax2 + cache.getBdotH2() / ay2 + cache.getNdotH2(); scalar_type ndf = numbers::inv_pi / (a2 * denom * denom); isInfinity = hlsl::isinf(ndf); diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl index c8f0631a03..07762d1298 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl @@ -15,184 +15,8 @@ namespace bxdf namespace reflection { -template) -struct SIridescent -{ - using this_t = SIridescent; - NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); - NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); - NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); - NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); - - NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); - NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); - NBL_BXDF_CONFIG_ALIAS(sample_type, Config); - NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); - NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); - NBL_BXDF_CONFIG_ALIAS(isocache_type, Config); - NBL_BXDF_CONFIG_ALIAS(anisocache_type, Config); - - using ndf_type = ndf::GGX; - using fresnel_type = fresnel::Iridescent; - using measure_transform_type = ndf::SDualMeasureQuant; - - NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; - - struct SCreationParams - { - scalar_type A; - scalar_type thickness; // thin-film thickness in nm - spectral_type ior0; - spectral_type ior1; - spectral_type ior2; - spectral_type iork2; - }; - using creation_type = SCreationParams; - - struct SIridQuery - { - using scalar_type = scalar_type; - - scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } - scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } - - scalar_type devsh_v; - scalar_type devsh_l; - }; - using query_type = SIridQuery; - - static this_t create(scalar_type A, scalar_type thickness, NBL_CONST_REF_ARG(spectral_type) ior0, NBL_CONST_REF_ARG(spectral_type) ior1, NBL_CONST_REF_ARG(spectral_type) ior2, NBL_CONST_REF_ARG(spectral_type) iork2) - { - this_t retval; - retval.__base.ndf.A = vector2_type(A, A); - retval.__base.ndf.a2 = A*A; - retval.__base.ndf.one_minus_a2 = scalar_type(1.0) - A*A; - retval.__base.fresnel.Dinc = thickness; - retval.__base.fresnel.ior1 = ior0; - retval.__base.fresnel.ior2 = ior1; - retval.__base.fresnel.ior3 = ior2; - retval.__base.fresnel.iork3 = iork2; - return retval; - } - static this_t create(NBL_CONST_REF_ARG(creation_type) params) - { - return create(params.A, params.thickness, params.ior0, params.ior1, params.ior2, params.iork2); - } - - query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) - { - query_type query; - ndf_type ggx_ndf = __base.getNDF(); - query.devsh_v = ggx_ndf.devsh_part(interaction.getNdotV2()); - query.devsh_l = ggx_ndf.devsh_part(_sample.getNdotL2()); - return query; - } - - spectral_type eval(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) - { - if (_sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min) - { - struct SGGXG2XQuery - { - using scalar_type = scalar_type; - - scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } - scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } - BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } - - scalar_type devsh_v; - scalar_type devsh_l; - BxDFClampMode _clamp; - }; - - SGGXG2XQuery g2_query; - g2_query.devsh_v = query.getDevshV(); - g2_query.devsh_l = query.getDevshL(); - g2_query._clamp = _clamp; - - measure_transform_type dualMeasure = __base.template __DG(g2_query, _sample, interaction, cache); - dualMeasure.maxNdotL = _sample.getNdotL(_clamp); - scalar_type DG = dualMeasure.getProjectedLightMeasure(); - fresnel_type f = __base.getFresnel(); - f.absCosTheta = cache.getLdotH(); - return f() * DG; - } - else - return hlsl::promote(0.0); - } - - sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(isocache_type) cache) - { - SGGXAnisotropic ggx_aniso = SGGXAnisotropic::create(__base.ndf.A.x, __base.ndf.A.y, __base.fresnel.ior3/__base.fresnel.ior2, __base.fresnel.iork3/__base.fresnel.ior2); - anisocache_type anisocache; - sample_type s = ggx_aniso.generate(anisotropic_interaction_type::create(interaction), u, anisocache); - cache = anisocache.iso_cache; - return s; - } - - scalar_type pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) - { - struct SGGXDG1Query - { - using scalar_type = scalar_type; - - scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } - scalar_type getG1over2NdotV() NBL_CONST_MEMBER_FUNC { return G1_over_2NdotV; } - - scalar_type ndf; - scalar_type G1_over_2NdotV; - }; - - SGGXDG1Query dg1_query; - ndf_type ggx_ndf = __base.getNDF(); - dg1_query.ndf = __base.__D(cache); - - const scalar_type devsh_v = query.getDevshV(); - dg1_query.G1_over_2NdotV = ggx_ndf.G1_wo_numerator_devsh_part(interaction.getNdotV(_clamp), devsh_v); - - measure_transform_type dualMeasure = __base.template __DG1(dg1_query); - return dualMeasure.getMicrofacetMeasure(); - } - - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) - { - scalar_type _pdf = pdf(query, interaction, cache); - - spectral_type quo = hlsl::promote(0.0); - if (_sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min) - { - struct SGGXG2XQuery - { - using scalar_type = scalar_type; - - scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } - scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } - BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } - - scalar_type devsh_v; - scalar_type devsh_l; - BxDFClampMode _clamp; - }; - - ndf_type ggx_ndf = __base.getNDF(); - - SGGXG2XQuery g2_query; - g2_query.devsh_v = query.getDevshV(); - g2_query.devsh_l = query.getDevshL(); - g2_query._clamp = _clamp; - const scalar_type G2_over_G1 = ggx_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); - - fresnel_type f = __base.getFresnel(); - f.absCosTheta = cache.getLdotH(); - const spectral_type reflectance = f(); - quo = reflectance * G2_over_G1; - } - - return quotient_pdf_type::create(quo, _pdf); - } - - SCookTorrance __base; -}; +template +using SIridescent = SCookTorrance, fresnel::Iridescent >; } @@ -200,6 +24,7 @@ template struct traits > { NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; }; diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl index aa2fde373e..2e7aa0e56e 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl @@ -15,185 +15,8 @@ namespace bxdf namespace transmission { -template) -struct SIridescent -{ - using this_t = SIridescent; - NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); - NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); - NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); - NBL_BXDF_CONFIG_ALIAS(matrix3x3_type, Config); - NBL_BXDF_CONFIG_ALIAS(monochrome_type, Config); - NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); - - NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); - NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); - NBL_BXDF_CONFIG_ALIAS(sample_type, Config); - NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); - NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); - NBL_BXDF_CONFIG_ALIAS(isocache_type, Config); - NBL_BXDF_CONFIG_ALIAS(anisocache_type, Config); - using brdf_type = reflection::SGGXIsotropic; - - using ndf_type = ndf::GGX; - using fresnel_type = fresnel::Iridescent; - using measure_transform_type = ndf::SDualMeasureQuant; - - NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; - - struct SCreationParams - { - scalar_type A; - scalar_type thickness; // thin-film thickness in nm - spectral_type ior0; - spectral_type ior1; - spectral_type ior2; - }; - using creation_type = SCreationParams; - - struct SIridQuery - { - using scalar_type = scalar_type; - - scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } - scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } - - scalar_type devsh_v; - scalar_type devsh_l; - }; - using query_type = SIridQuery; - - static this_t create(scalar_type A, scalar_type thickness, NBL_CONST_REF_ARG(spectral_type) ior0, NBL_CONST_REF_ARG(spectral_type) ior1, NBL_CONST_REF_ARG(spectral_type) ior2) - { - this_t retval; - retval.__base.ndf.A = vector2_type(A, A); - retval.__base.ndf.a2 = A*A; - retval.__base.ndf.one_minus_a2 = scalar_type(1.0) - A*A; - retval.__base.fresnel.Dinc = thickness; - retval.__base.fresnel.ior1 = ior0; - retval.__base.fresnel.ior2 = ior1; - retval.__base.fresnel.ior3 = ior2; - retval.__base.fresnel.iork3 = hlsl::promote(0.0); // always 0.0 for dielectric base layer - return retval; - } - static this_t create(NBL_CONST_REF_ARG(creation_type) params) - { - return create(params.A, params.thickness, params.ior0, params.ior1, params.ior2); - } - - query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) - { - query_type query; - ndf_type ggx_ndf = __base.getNDF(); - query.devsh_v = ggx_ndf.devsh_part(interaction.getNdotV2()); - query.devsh_l = ggx_ndf.devsh_part(_sample.getNdotL2()); - return query; - } - - spectral_type eval(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) - { - struct SGGXG2XQuery - { - using scalar_type = scalar_type; - - scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } - scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } - BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } - - scalar_type devsh_v; - scalar_type devsh_l; - BxDFClampMode _clamp; - }; - - SGGXG2XQuery g2_query; - g2_query.devsh_v = query.getDevshV(); - g2_query.devsh_l = query.getDevshL(); - g2_query._clamp = _clamp; - - fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(1.0, hlsl::promote(__base.fresnel.ior3/__base.fresnel.ior2)); - measure_transform_type dualMeasure = __base.template __DG(g2_query, _sample, interaction, cache); - dualMeasure.absNdotL = _sample.getNdotL(_clamp); - dualMeasure.orientedEta = orientedEta.value[0]; - scalar_type DG = dualMeasure.getProjectedLightMeasure(); - - fresnel_type f = __base.getFresnel(); - f.absCosTheta = hlsl::abs(cache.getLdotH()); - return hlsl::promote(f()[0]) * DG; - } - - sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u, NBL_REF_ARG(isocache_type) cache) - { - fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(1.0, hlsl::promote(__base.fresnel.ior3/__base.fresnel.ior2)); - SGGXDielectricAnisotropic ggx_aniso = SGGXDielectricAnisotropic::create(orientedEta, __base.ndf.A.x, __base.ndf.A.y); - anisocache_type anisocache; - sample_type s = ggx_aniso.generate(anisotropic_interaction_type::create(interaction), u, anisocache); - cache = anisocache.iso_cache; - return s; - } - - scalar_type pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) - { - struct SGGXDG1Query - { - using scalar_type = scalar_type; - - scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } - scalar_type getG1over2NdotV() NBL_CONST_MEMBER_FUNC { return G1_over_2NdotV; } - scalar_type getOrientedEta() NBL_CONST_MEMBER_FUNC { return orientedEta; } - - scalar_type ndf; - scalar_type G1_over_2NdotV; - scalar_type orientedEta; - }; - - SGGXDG1Query dg1_query; - fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(1.0, hlsl::promote(__base.fresnel.ior3/__base.fresnel.ior2)); - dg1_query.orientedEta = orientedEta.value[0]; - - fresnel_type f = __base.getFresnel(); - f.absCosTheta = hlsl::abs(cache.getLdotH()); - const scalar_type reflectance = f()[0]; - - ndf_type ggx_ndf = __base.getNDF(); - dg1_query.ndf = __base.__D(cache); - dg1_query.G1_over_2NdotV = ggx_ndf.G1_wo_numerator_devsh_part(interaction.getNdotV(_clamp), query.getDevshV()); - - measure_transform_type dualMeasure = __base.template __DG1(dg1_query, cache); - return hlsl::mix(reflectance, scalar_type(1.0) - reflectance, cache.isTransmission()) * dualMeasure.getMicrofacetMeasure(); - } - - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) - { - scalar_type _pdf = pdf(query, interaction, cache); - const bool transmitted = cache.isTransmission(); - - struct SGGXG2XQuery - { - using scalar_type = scalar_type; - - scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } - scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } - BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } - - scalar_type devsh_v; - scalar_type devsh_l; - BxDFClampMode _clamp; - }; - - ndf_type ggx_ndf = __base.getNDF(); - SGGXG2XQuery g2_query; - g2_query.devsh_v = query.getDevshV(); - g2_query.devsh_l = query.getDevshL(); - g2_query._clamp = _clamp; - - scalar_type quo; - quo = ggx_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); - - return quotient_pdf_type::create(quo, _pdf); - } - - SCookTorrance __base; -}; +template +using SIridescent = SCookTorrance, fresnel::Iridescent >; } @@ -201,6 +24,7 @@ template struct traits > { NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; }; From 4b8a06b91e3646c524bceef3719581faa0611012 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 7 Nov 2025 16:14:48 +0700 Subject: [PATCH 47/56] fix edge cases for iridescent fresnel --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 28 +++++++++++++------ include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 11 +++++--- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl index df75072dbe..70782969bc 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -128,10 +128,10 @@ struct SCookTorrance return cache.isValid(orientedEta); } - bool dotIsUnity(const vector3_type a, const vector3_type b) + bool dotIsUnity(const vector3_type a, const vector3_type b, const scalar_type value) { const scalar_type ab = hlsl::dot(a, b); - return hlsl::max(ab, scalar_type(1.0) / ab) <= scalar_type(1.0 + 1e-3); + return hlsl::max(ab, value / ab) <= scalar_type(value + 1e-3); } // bxdf stuff @@ -197,11 +197,11 @@ struct SCookTorrance ray_dir_info_type V = interaction.getV(); const matrix3x3_type fromTangent = interaction.getFromTangentSpace(); // tangent frame orthonormality - assert(dotIsUnity(fromTangent[0],fromTangent[1])); - assert(dotIsUnity(fromTangent[1],fromTangent[2])); - assert(dotIsUnity(fromTangent[2],fromTangent[0])); + assert(dotIsUnity(fromTangent[0],fromTangent[1],0.0)); + assert(dotIsUnity(fromTangent[1],fromTangent[2],0.0)); + assert(dotIsUnity(fromTangent[2],fromTangent[0],0.0)); // NDF sampling produced a unit length direction - assert(dotIsUnity(localH,localH)); + assert(dotIsUnity(localH,localH,1.0)); const vector3_type H = hlsl::mul(interaction.getFromTangentSpace(), localH); Refract r = Refract::create(V.getDirection(), H); @@ -238,7 +238,17 @@ struct SCookTorrance const vector3_type localV = interaction.getTangentSpaceV(); const vector3_type localH = ndf.generateH(localV, u); const scalar_type VdotH = hlsl::dot(localV, localH); - assert(VdotH >= scalar_type(0.0)); // VNDF sampling guarantees VdotH has same sign as NdotV (should be positive for BRDF) + NBL_IF_CONSTEXPR(!ndf_type::GuaranteedVNDF) // VNDF sampling guarantees VdotH has same sign as NdotV (should be positive for BRDF) + { + // allow for rejection sampling, theoretically NdotV=0 or VdotH=0 is valid, but leads to 0 value contribution anyway + if (VdotH <= scalar_type(0.0)) + return sample_type::createInvalid(); + assert(!hlsl::isnan(NdotV*VdotH)); + } + else + { + assert(VdotH >= scalar_type(0.0)); + } fresnel::OrientedEtaRcps dummy; bool valid; @@ -263,7 +273,7 @@ struct SCookTorrance NBL_IF_CONSTEXPR(!ndf_type::GuaranteedVNDF) { // allow for rejection sampling, theoretically NdotV=0 or VdotH=0 is valid, but leads to 0 value contribution anyway - if ((IsBSDF ? (NdotV*VdotH) : VdotH) <= scalar_type(0.0)) + if (NdotV*VdotH <= scalar_type(0.0)) return sample_type::createInvalid(); assert(!hlsl::isnan(NdotV*VdotH)); } @@ -283,7 +293,7 @@ struct SCookTorrance sample_type s = __generate_common(interaction, localH, NdotV, VdotH, transmitted, rcpEta, cache, valid); if (valid) { - assert(cache.isValid(fresnel::OrientedEtas::create(scalar_type(1.0), hlsl::promote(_f.getRefractionOrientedEta())))); + assert(cache.isValid(fresnel::OrientedEtas::create(scalar_type(1.0), hlsl::promote(_f.getRefractionOrientedEta())))); const vector3_type T = interaction.getT(); const vector3_type B = interaction.getB(); const vector3_type H = hlsl::mul(interaction.getFromTangentSpace(), localH); diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index c9e92ad13d..c918f1c0fe 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -401,7 +401,7 @@ struct Conductor static void __polarized(const T orientedEta, const T orientedEtak, const T cosTheta, NBL_REF_ARG(T) Rp, NBL_REF_ARG(T) Rs) { T cosTheta_2 = cosTheta * cosTheta; - T sinTheta2 = hlsl::promote(1.0) - cosTheta_2; + // T sinTheta2 = hlsl::promote(1.0) - cosTheta_2; const T eta = orientedEta; const T eta2 = eta*eta; const T etak = orientedEtak; @@ -470,7 +470,7 @@ struct Dielectric const T eta = orientedEta; const T eta2 = eta * eta; - T t0 = hlsl::sqrt(eta2 - sinTheta2); + T t0 = hlsl::sqrt(hlsl::max(eta2 - sinTheta2, hlsl::promote(0.0))); T t2 = eta2 * cosTheta; T rp = (t0 - t2) / (t0 + t2); @@ -569,9 +569,9 @@ struct iridescent_helper vector_type R12p, R23p, R12s, R23s; const vector_type scale = scalar_type(1.0)/eta12; - const vector_type cosTheta2_2 = hlsl::promote(1.0) - hlsl::promote(1-cosTheta_1*cosTheta_1) * scale * scale; + const vector_type cosTheta2_2 = hlsl::promote(1.0) - hlsl::promote(1.0-cosTheta_1*cosTheta_1) * scale * scale; - cosTheta_2 = hlsl::sqrt(cosTheta2_2); + cosTheta_2 = hlsl::sqrt(hlsl::max(cosTheta2_2, hlsl::promote(0.0))); Dielectric::__polarized(eta12, hlsl::promote(cosTheta_1), R12p, R12s); // Reflected part by the base @@ -585,6 +585,9 @@ struct iridescent_helper R12s = hlsl::mix(R12s, hlsl::promote(1.0), cosTheta2_2 <= hlsl::promote(0.0)); R12p = hlsl::mix(R12p, hlsl::promote(1.0), cosTheta2_2 <= hlsl::promote(0.0)); + R23s = hlsl::mix(R23s, hlsl::promote(0.0), cosTheta2_2 <= hlsl::promote(0.0)); + R23p = hlsl::mix(R23p, hlsl::promote(0.0), cosTheta2_2 <= hlsl::promote(0.0)); + // Compute the transmission coefficients vector_type T121p = hlsl::promote(1.0) - R12p; vector_type T121s = hlsl::promote(1.0) - R12s; From 5782a49f31dd1be02a91c5237f6c2bc56566c5a4 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 7 Nov 2025 16:53:11 +0700 Subject: [PATCH 48/56] removed redundant weight in interaction --- .../builtin/hlsl/bxdf/base/cook_torrance_base.hlsl | 12 ++++++------ include/nbl/builtin/hlsl/bxdf/common.hlsl | 9 --------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl index 70782969bc..de4cbcef8e 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -282,8 +282,8 @@ struct SCookTorrance assert(NdotV*VdotH >= scalar_type(0.0)); } - spectral_type prefixThroughputWeights = interaction.getPrefixThroughputWeights(); - const scalar_type reflectance = hlsl::dot(impl::__implicit_promote::__call(_f(hlsl::abs(VdotH))), prefixThroughputWeights); + spectral_type throughputWeights = interaction.getLuminosityContributionHint(); + const scalar_type reflectance = hlsl::dot(impl::__implicit_promote::__call(_f(hlsl::abs(VdotH))), throughputWeights); scalar_type rcpChoiceProb; scalar_type z = u.z; @@ -324,8 +324,8 @@ struct SCookTorrance NBL_IF_CONSTEXPR(IsBSDF) { - spectral_type prefixThroughputWeights = interaction.getPrefixThroughputWeights(); - const scalar_type reflectance = hlsl::dot(impl::__implicit_promote::__call(_f(hlsl::abs(cache.getVdotH()))), prefixThroughputWeights); + spectral_type throughputWeights = interaction.getLuminosityContributionHint(); + const scalar_type reflectance = hlsl::dot(impl::__implicit_promote::__call(_f(hlsl::abs(cache.getVdotH()))), throughputWeights); return hlsl::mix(reflectance, scalar_type(1.0) - reflectance, cache.isTransmission()) * DG1.projectedLightMeasure; } else @@ -373,9 +373,9 @@ struct SCookTorrance spectral_type quo; NBL_IF_CONSTEXPR(IsBSDF) { - spectral_type prefixThroughputWeights = interaction.getPrefixThroughputWeights(); + spectral_type throughputWeights = interaction.getLuminosityContributionHint(); spectral_type reflectance = impl::__implicit_promote::__call(_f(hlsl::abs(cache.getVdotH()))); - const scalar_type scaled_reflectance = hlsl::dot(reflectance, prefixThroughputWeights); + const scalar_type scaled_reflectance = hlsl::dot(reflectance, throughputWeights); quo = hlsl::mix(reflectance / scaled_reflectance, (hlsl::promote(1.0) - reflectance) / (scalar_type(1.0) - scaled_reflectance), cache.isTransmission()) * G2_over_G1; } diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index 58597aa43e..ebad0a925c 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -195,7 +195,6 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getNdotV2()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getPathOrigin()), ::nbl::hlsl::is_same_v, PathOrigin)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getLuminosityContributionHint()), ::nbl::hlsl::is_same_v, typename T::spectral_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getPrefixThroughputWeights()), ::nbl::hlsl::is_same_v, typename T::spectral_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(normV,normN)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(ray_dir_info::Basic, typename T::ray_dir_info_type)) ); @@ -223,7 +222,6 @@ struct SIsotropic retval.NdotV = nbl::hlsl::dot(retval.N, retval.V.getDirection()); retval.NdotV2 = retval.NdotV * retval.NdotV; retval.luminosityContributionHint = hlsl::promote(1.0); - retval.throughputWeights = hlsl::promote(1.0); return retval; } @@ -238,11 +236,6 @@ struct SIsotropic PathOrigin getPathOrigin() NBL_CONST_MEMBER_FUNC { return PathOrigin::PO_SENSOR; } spectral_type getLuminosityContributionHint() NBL_CONST_MEMBER_FUNC { return luminosityContributionHint; } - spectral_type getPrefixThroughputWeights() NBL_CONST_MEMBER_FUNC - { - spectral_type prefixThroughputWeights = luminosityContributionHint * throughputWeights; - return prefixThroughputWeights / math::lpNorm(prefixThroughputWeights); - } RayDirInfo V; vector3_type N; @@ -250,7 +243,6 @@ struct SIsotropic scalar_type NdotV2; spectral_type luminosityContributionHint; - spectral_type throughputWeights; // product of all quotients so far }; #define NBL_CONCEPT_NAME Anisotropic @@ -337,7 +329,6 @@ struct SAnisotropic scalar_type getNdotV2() NBL_CONST_MEMBER_FUNC { return isotropic.getNdotV2(); } PathOrigin getPathOrigin() NBL_CONST_MEMBER_FUNC { return isotropic.getPathOrigin(); } spectral_type getLuminosityContributionHint() NBL_CONST_MEMBER_FUNC { return isotropic.getLuminosityContributionHint(); } - spectral_type getPrefixThroughputWeights() NBL_CONST_MEMBER_FUNC { return isotropic.getPrefixThroughputWeights(); } vector3_type getT() NBL_CONST_MEMBER_FUNC { return T; } vector3_type getB() NBL_CONST_MEMBER_FUNC { return B; } From 55471ca34ee2d42028fca7082128031932e320ed Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 10 Nov 2025 12:13:24 +0700 Subject: [PATCH 49/56] removed duplicate fresnel code, added creation params for iridescent --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 97 +++++++++++----------- 1 file changed, 48 insertions(+), 49 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index c918f1c0fe..52410c8b55 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -397,20 +397,14 @@ struct Conductor return retval; } - // TODO: will probably merge with __call at some point - static void __polarized(const T orientedEta, const T orientedEtak, const T cosTheta, NBL_REF_ARG(T) Rp, NBL_REF_ARG(T) Rs) + static void __polarized(const T orientedEta, const T etaLen2, const T clampedCosTheta, NBL_REF_ARG(T) Rp, NBL_REF_ARG(T) Rs) { - T cosTheta_2 = cosTheta * cosTheta; - // T sinTheta2 = hlsl::promote(1.0) - cosTheta_2; + const T cosTheta_2 = clampedCosTheta * clampedCosTheta; const T eta = orientedEta; - const T eta2 = eta*eta; - const T etak = orientedEtak; - const T etak2 = etak*etak; - const T etaLen2 = eta2 + etak2; assert(hlsl::all(etaLen2 > hlsl::promote(hlsl::exp2(-numeric_limits::digits)))); T t1 = etaLen2 * cosTheta_2; - const T etaCosTwice = eta * cosTheta * scalar_type(2.0); + const T etaCosTwice = eta * clampedCosTheta * hlsl::promote(2.0); const T rs_common = etaLen2 + cosTheta_2; Rs = (rs_common - etaCosTwice) / (rs_common + etaCosTwice); @@ -420,17 +414,8 @@ struct Conductor T operator()(const scalar_type clampedCosTheta) { - const scalar_type cosTheta_2 = clampedCosTheta * clampedCosTheta; - //const float sinTheta2 = 1.0 - cosTheta_2; - - assert(hlsl::all(etaLen2 > hlsl::promote(hlsl::exp2(-numeric_limits::digits)))); - const T etaCosTwice = eta * clampedCosTheta * hlsl::promote(2.0); - - const T rs_common = etaLen2 + hlsl::promote(cosTheta_2); - const T rs2 = (rs_common - etaCosTwice) / (rs_common + etaCosTwice); - - const T rp_common = etaLen2 * cosTheta_2 + hlsl::promote(1.0); - const T rp2 = (rp_common - etaCosTwice) / (rp_common + etaCosTwice); + T rs2, rp2; + __polarized(eta, etaLen2, hlsl::promote(clampedCosTheta), rp2, rs2); return (rs2 + rp2) * hlsl::promote(0.5); } @@ -463,15 +448,13 @@ struct Dielectric return retval; } - // TODO: will probably merge with __call at some point - static void __polarized(const T orientedEta, const T cosTheta, NBL_REF_ARG(T) Rp, NBL_REF_ARG(T) Rs) + static void __polarized(const T orientedEta2, const T cosTheta, NBL_REF_ARG(T) Rp, NBL_REF_ARG(T) Rs) { - T sinTheta2 = hlsl::promote(1.0) - cosTheta * cosTheta; - const T eta = orientedEta; - const T eta2 = eta * eta; + const T sinTheta2 = hlsl::promote(1.0) - cosTheta * cosTheta; - T t0 = hlsl::sqrt(hlsl::max(eta2 - sinTheta2, hlsl::promote(0.0))); - T t2 = eta2 * cosTheta; + // the max() clamping can handle TIR when orientedEta2<1.0 + T t0 = hlsl::sqrt(hlsl::max(orientedEta2 - sinTheta2, hlsl::promote(0.0))); + T t2 = orientedEta2 * cosTheta; T rp = (t0 - t2) / (t0 + t2); Rp = rp * rp; @@ -481,16 +464,10 @@ struct Dielectric static T __call(NBL_CONST_REF_ARG(T) orientedEta2, const scalar_type clampedCosTheta) { - const scalar_type sinTheta2 = scalar_type(1.0) - clampedCosTheta * clampedCosTheta; - - // the max() clamping can handle TIR when orientedEta2<1.0 - const T t0 = hlsl::sqrt(hlsl::max(orientedEta2 - sinTheta2, hlsl::promote(0.0))); - const T rs = (hlsl::promote(clampedCosTheta) - t0) / (hlsl::promote(clampedCosTheta) + t0); - - const T t2 = orientedEta2 * clampedCosTheta; - const T rp = (t0 - t2) / (t0 + t2); + T rs2, rp2; + __polarized(orientedEta2, hlsl::promote(clampedCosTheta), rp2, rs2); - return (rs * rs + rp * rp) * scalar_type(0.5); + return (rs2 + rp2) * hlsl::promote(0.5); } T operator()(const scalar_type clampedCosTheta) @@ -577,9 +554,12 @@ struct iridescent_helper // Reflected part by the base // if kappa==0, base material is dielectric NBL_IF_CONSTEXPR(SupportsTransmission) - Dielectric::__polarized(eta23, cosTheta_2, R23p, R23s); + Dielectric::__polarized(eta23 * eta23, cosTheta_2, R23p, R23s); else - Conductor::__polarized(eta23, etak23, cosTheta_2, R23p, R23s); + { + vector_type etaLen2 = eta23 * eta23 + etak23 * etak23; + Conductor::__polarized(eta23, etaLen2, cosTheta_2, R23p, R23s); + } // Check for total internal reflection R12s = hlsl::mix(R12s, hlsl::promote(1.0), cosTheta2_2 <= hlsl::promote(0.0)); @@ -660,14 +640,24 @@ struct Iridescent; - static this_t create(scalar_type Dinc, vector_type ior1, vector_type ior2, vector_type ior3) + struct CreationParams + { + scalar_type Dinc; + vector_type ior1; + vector_type ior2; + vector_type ior3; + }; + using creation_params_type = CreationParams; + + static this_t create(NBL_CONST_REF_ARG(creation_params_type) params) { this_t retval; - retval.helper.Dinc = Dinc; - retval.helper.thinFilmIor = ior2; - retval.helper.eta12 = ior2/ior1; - retval.helper.eta23 = ior3/ior2; + retval.helper.Dinc = params.Dinc; + retval.helper.thinFilmIor = params.ior2; + retval.helper.eta12 = params.ior2/params.ior1; + retval.helper.eta23 = params.ior3/params.ior2; retval.helper.etak23 = hlsl::promote(0.0); return retval; } From 64be7714107d3ed9d667245002f4f9d64df44968 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 10 Nov 2025 16:28:57 +0700 Subject: [PATCH 50/56] split out iridescent fresnel vars into base struct --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 96 +++++++++------------- 1 file changed, 40 insertions(+), 56 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 52410c8b55..a0f123de2a 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -537,11 +537,15 @@ struct iridescent_helper return xyz / scalar_type(1.0685e-7); } - T __call(const scalar_type clampedCosTheta) + template + static T __call(NBL_CONST_REF_ARG(Params) params, const scalar_type clampedCosTheta) { const vector_type wavelengths = vector_type(colorspace::scRGB::wavelength_R, colorspace::scRGB::wavelength_G, colorspace::scRGB::wavelength_B); - scalar_type cosTheta_1 = clampedCosTheta; + const vector_type eta12 = params.getEta12(); + const vector_type eta23 = params.getEta23(); + const vector_type etak23 = params.getEtak23(); + const scalar_type cosTheta_1 = clampedCosTheta; vector_type cosTheta_2; vector_type R12p, R23p, R12s, R23s; @@ -573,7 +577,7 @@ struct iridescent_helper vector_type T121s = hlsl::promote(1.0) - R12s; // Optical Path Difference - const vector_type D = hlsl::promote(2.0 * Dinc) * thinFilmIor * cosTheta_2; + const vector_type D = hlsl::promote(2.0 * params.getDinc()) * params.getThinFilmIor() * cosTheta_2; const vector_type Dphi = hlsl::promote(2.0 * numbers::pi) * D / wavelengths; vector_type phi21p, phi21s, phi23p, phi23s, r123s, r123p, Rs; @@ -622,6 +626,25 @@ struct iridescent_helper return hlsl::max(colorspace::scRGB::FromXYZ(I), hlsl::promote(0.0)) * hlsl::promote(0.5); } +}; + +template) +struct iridescent_base +{ + using scalar_type = typename vector_traits::scalar_type; + using vector_type = T; + + scalar_type getDinc() NBL_CONST_MEMBER_FUNC { return Dinc; } + vector_type getThinFilmIor() NBL_CONST_MEMBER_FUNC { return thinFilmIor; } + vector_type getEta12() NBL_CONST_MEMBER_FUNC { return eta12; } + vector_type getEta23() NBL_CONST_MEMBER_FUNC { return eta23; } + vector_type getEtak23() NBL_CONST_MEMBER_FUNC + { + NBL_IF_CONSTEXPR(SupportsTransmission) + return hlsl::promote(0.0); + else + return etak23; + } scalar_type Dinc; // thickness of thin film in nanometers, rec. 100-25000nm vector_type thinFilmIor; @@ -639,42 +662,22 @@ struct Iridescent::scalar_type; using vector_type = T; // assert dim==3? using eta_type = vector_type; - - struct CreationParams - { - scalar_type Dinc; - vector_type ior1; - vector_type ior2; - vector_type ior3; - vector_type iork3; - }; - using creation_params_type = CreationParams; - - static this_t create(NBL_CONST_REF_ARG(creation_params_type) params) - { - this_t retval; - retval.helper.Dinc = params.Dinc; - retval.helper.thinFilmIor = params.ior2; - retval.helper.eta12 = params.ior2/params.ior1; - retval.helper.eta23 = params.ior3/params.ior2; - retval.helper.etak23 = params.iork3/params.ior2; - return retval; - } + using base_type = impl::iridescent_base; T operator()(const scalar_type clampedCosTheta) { - return helper.__call(clampedCosTheta); + return impl::iridescent_helper::template __call(__base, clampedCosTheta); } OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC { OrientedEtaRcps rcpEta; - rcpEta.value = hlsl::promote(1.0) / helper.eta23; + rcpEta.value = hlsl::promote(1.0) / __base.eta23; rcpEta.value2 = rcpEta.value * rcpEta.value; return rcpEta; } - impl::iridescent_helper helper; + base_type __base; }; template @@ -685,37 +688,18 @@ struct Iridescent::scalar_type; using vector_type = T; // assert dim==3? using eta_type = vector; - - struct CreationParams - { - scalar_type Dinc; - vector_type ior1; - vector_type ior2; - vector_type ior3; - }; - using creation_params_type = CreationParams; - - static this_t create(NBL_CONST_REF_ARG(creation_params_type) params) - { - this_t retval; - retval.helper.Dinc = params.Dinc; - retval.helper.thinFilmIor = params.ior2; - retval.helper.eta12 = params.ior2/params.ior1; - retval.helper.eta23 = params.ior3/params.ior2; - retval.helper.etak23 = hlsl::promote(0.0); - return retval; - } + using base_type = impl::iridescent_base; T operator()(const scalar_type clampedCosTheta) { - return helper.__call(clampedCosTheta); + return impl::iridescent_helper::template __call(__base, clampedCosTheta); } - scalar_type getRefractionOrientedEta() NBL_CONST_MEMBER_FUNC { return helper.eta23[0]; } + scalar_type getRefractionOrientedEta() NBL_CONST_MEMBER_FUNC { return __base.eta23[0]; } OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC { OrientedEtaRcps rcpEta; - rcpEta.value = hlsl::promote(1.0) / helper.eta23[0]; + rcpEta.value = hlsl::promote(1.0) / __base.eta23[0]; rcpEta.value2 = rcpEta.value * rcpEta.value; return rcpEta; } @@ -724,15 +708,15 @@ struct Iridescent(1.0)/helper.eta12, flip); - orientedFresnel.helper.eta23 = hlsl::mix(helper.eta23, hlsl::promote(1.0)/helper.eta23, flip); - orientedFresnel.helper.etak23 = hlsl::promote(0.0); + orientedFresnel.__base.Dinc = __base.Dinc; + orientedFresnel.__base.thinFilmIor = __base.thinFilmIor; + orientedFresnel.__base.eta12 = hlsl::mix(__base.eta12, hlsl::promote(1.0)/__base.eta12, flip); + orientedFresnel.__base.eta23 = hlsl::mix(__base.eta23, hlsl::promote(1.0)/__base.eta23, flip); + orientedFresnel.__base.etak23 = hlsl::promote(0.0); return orientedFresnel; } - impl::iridescent_helper helper; + base_type __base; }; From 8f9a11447b55fd5fbd0eb8a4f09c372f70f5f979 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 10 Nov 2025 16:30:39 +0700 Subject: [PATCH 51/56] renamed private funcs, replace enable_if usage with NBL_FUNC_REQUIRES --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 60 ++++++++++--------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl index de4cbcef8e..b9416505e6 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -88,14 +88,14 @@ struct SCookTorrance template, class MicrofacetCache=conditional_t NBL_FUNC_REQUIRES(!ndf::NDF_CanOverwriteDG && RequiredInteraction && RequiredMicrofacetCache) - static void overwriteDG(NBL_REF_ARG(scalar_type) DG, ndf_type ndf, NBL_CONST_REF_ARG(typename ndf_type::g2g1_query_type) query, NBL_CONST_REF_ARG(typename ndf_type::quant_query_type) quant_query, + static void __overwriteDG(NBL_REF_ARG(scalar_type) DG, ndf_type ndf, NBL_CONST_REF_ARG(typename ndf_type::g2g1_query_type) query, NBL_CONST_REF_ARG(typename ndf_type::quant_query_type) quant_query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache, NBL_REF_ARG(bool) isInfinity) { } template, class MicrofacetCache=conditional_t NBL_FUNC_REQUIRES(ndf::NDF_CanOverwriteDG && RequiredInteraction && RequiredMicrofacetCache) - static void overwriteDG(NBL_REF_ARG(scalar_type) DG, ndf_type ndf, NBL_CONST_REF_ARG(typename ndf_type::g2g1_query_type) query, NBL_CONST_REF_ARG(typename ndf_type::quant_query_type) quant_query, + static void __overwriteDG(NBL_REF_ARG(scalar_type) DG, ndf_type ndf, NBL_CONST_REF_ARG(typename ndf_type::g2g1_query_type) query, NBL_CONST_REF_ARG(typename ndf_type::quant_query_type) quant_query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache, NBL_REF_ARG(bool) isInfinity) { quant_type dg = ndf.template Dcorrelated(query, quant_query, _sample, interaction, cache, isInfinity); @@ -103,32 +103,32 @@ struct SCookTorrance } template) - static fresnel_type getOrientedFresnel(NBL_CONST_REF_ARG(fresnel_type) fresnel, scalar_type NdotV) + static fresnel_type __getOrientedFresnel(NBL_CONST_REF_ARG(fresnel_type) fresnel, scalar_type NdotV) { // expect conductor fresnel return fresnel; } template) - static fresnel_type getOrientedFresnel(NBL_CONST_REF_ARG(fresnel_type) fresnel, scalar_type NdotV) + static fresnel_type __getOrientedFresnel(NBL_CONST_REF_ARG(fresnel_type) fresnel, scalar_type NdotV) { return fresnel.getReorientedFresnel(NdotV); } template, - class MicrofacetCache=conditional_t, typename C=bool_constant > - static enable_if_t checkValid(NBL_CONST_REF_ARG(fresnel_type) orientedFresnel, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + class MicrofacetCache=conditional_t, typename C=bool_constant NBL_FUNC_REQUIRES(C::value && !IsBSDF) + static bool __checkValid(NBL_CONST_REF_ARG(fresnel_type) orientedFresnel, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) { return _sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min; } template, - class MicrofacetCache=conditional_t, typename C=bool_constant > - static enable_if_t checkValid(NBL_CONST_REF_ARG(fresnel_type) orientedFresnel, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + class MicrofacetCache=conditional_t, typename C=bool_constant NBL_FUNC_REQUIRES(C::value && IsBSDF) + static bool __checkValid(NBL_CONST_REF_ARG(fresnel_type) orientedFresnel, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) { fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(scalar_type(1.0), hlsl::promote(orientedFresnel.getRefractionOrientedEta())); return cache.isValid(orientedEta); } - bool dotIsUnity(const vector3_type a, const vector3_type b, const scalar_type value) + bool __dotIsUnity(const vector3_type a, const vector3_type b, const scalar_type value) { const scalar_type ab = hlsl::dot(a, b); return hlsl::max(ab, value / ab) <= scalar_type(value + 1e-3); @@ -140,8 +140,8 @@ struct SCookTorrance NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) { - fresnel_type _f = getOrientedFresnel(fresnel, interaction.getNdotV()); - if (!checkValid(_f, _sample, interaction, cache)) + fresnel_type _f = __getOrientedFresnel(fresnel, interaction.getNdotV()); + if (!__checkValid(_f, _sample, interaction, cache)) return hlsl::promote(0.0); using quant_query_type = typename ndf_type::quant_query_type; @@ -156,9 +156,11 @@ struct SCookTorrance if (!isInfinity) DG *= ndf.template correlated(gq, _sample, interaction, cache); - overwriteDG(DG, ndf, gq, qq, _sample, interaction, cache, isInfinity); + __overwriteDG(DG, ndf, gq, qq, _sample, interaction, cache, isInfinity); - if (isInfinity) // after all calls setting DG, allows compiler to throw away calls to ndf.D if using overwriteDG + // immediately return only after all calls setting DG + // allows compiler to throw away calls to ndf.D if using __overwriteDG, before that we only avoid computation for G2(correlated) + if (isInfinity) return hlsl::promote(0.0); scalar_type clampedVdotH = cache.getVdotH(); @@ -197,11 +199,11 @@ struct SCookTorrance ray_dir_info_type V = interaction.getV(); const matrix3x3_type fromTangent = interaction.getFromTangentSpace(); // tangent frame orthonormality - assert(dotIsUnity(fromTangent[0],fromTangent[1],0.0)); - assert(dotIsUnity(fromTangent[1],fromTangent[2],0.0)); - assert(dotIsUnity(fromTangent[2],fromTangent[0],0.0)); + assert(__dotIsUnity(fromTangent[0],fromTangent[1],0.0)); + assert(__dotIsUnity(fromTangent[1],fromTangent[2],0.0)); + assert(__dotIsUnity(fromTangent[2],fromTangent[0],0.0)); // NDF sampling produced a unit length direction - assert(dotIsUnity(localH,localH,1.0)); + assert(__dotIsUnity(localH,localH,1.0)); const vector3_type H = hlsl::mul(interaction.getFromTangentSpace(), localH); Refract r = Refract::create(V.getDirection(), H); @@ -227,8 +229,8 @@ struct SCookTorrance valid = true; return sample_type::create(L, T, B, NdotL); } - template > - enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(anisocache_type) cache) + template NBL_FUNC_REQUIRES(C::value && !IsBSDF) + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(anisocache_type) cache) { const scalar_type NdotV = interaction.getNdotV(); if (NdotV < numeric_limits::min) @@ -257,13 +259,13 @@ struct SCookTorrance cache = anisocache_type::createForReflection(localV, localH); return s; } - template > - enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u, NBL_REF_ARG(anisocache_type) cache) + template NBL_FUNC_REQUIRES(C::value && IsBSDF) + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u, NBL_REF_ARG(anisocache_type) cache) { const vector3_type localV = interaction.getTangentSpaceV(); const scalar_type NdotV = localV.z; - fresnel_type _f = getOrientedFresnel(fresnel, NdotV); + fresnel_type _f = __getOrientedFresnel(fresnel, NdotV); fresnel::OrientedEtaRcps rcpEta = _f.getOrientedEtaRcps(); const vector3_type upperHemisphereV = ieee754::flipSignIfRHSNegative(localV, hlsl::promote(NdotV)); @@ -301,8 +303,8 @@ struct SCookTorrance } return s; } - template > - enable_if_t generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const conditional_t u, NBL_REF_ARG(isocache_type) cache) + template NBL_FUNC_REQUIRES(C::value && !IsAnisotropic) + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const conditional_t u, NBL_REF_ARG(isocache_type) cache) { anisocache_type aniso_cache; sample_type s = generate(anisotropic_interaction_type::create(interaction), u, aniso_cache); @@ -318,7 +320,7 @@ struct SCookTorrance dg1_query_type dq = ndf.template createDG1Query(interaction, cache); - fresnel_type _f = getOrientedFresnel(fresnel, interaction.getNdotV()); + fresnel_type _f = __getOrientedFresnel(fresnel, interaction.getNdotV()); quant_query_type qq = impl::quant_query_helper::template __call(ndf, _f, interaction, cache); quant_type DG1 = ndf.template DG1(dq, qq, _sample, interaction, isInfinity); @@ -338,8 +340,8 @@ struct SCookTorrance NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) { - fresnel_type _f = getOrientedFresnel(fresnel, interaction.getNdotV()); - if (!checkValid(_f, _sample, interaction, cache)) + fresnel_type _f = __getOrientedFresnel(fresnel, interaction.getNdotV()); + if (!__checkValid(_f, _sample, interaction, cache)) return scalar_type(0.0); bool isInfinity; @@ -357,9 +359,9 @@ struct SCookTorrance bool isInfinity; scalar_type _pdf = __pdf(_sample, interaction, cache, isInfinity); - fresnel_type _f = getOrientedFresnel(fresnel, interaction.getNdotV()); + fresnel_type _f = __getOrientedFresnel(fresnel, interaction.getNdotV()); - const bool valid = checkValid(_f, _sample, interaction, cache); + const bool valid = __checkValid(_f, _sample, interaction, cache); assert(valid); // expect the generated sample to always be valid, different checks for brdf and btdf scalar_type G2_over_G1 = scalar_type(1.0); From 8a71f406c6e5ed6f3d958e5e34ef4336675674d0 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 10 Nov 2025 17:07:25 +0700 Subject: [PATCH 52/56] avoid promoting 1d fresnel to 3d just to dot product, added private func for that --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 23 ++++++++++++++----- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 20 ++++++++++++---- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl index b9416505e6..6c8f18786c 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -128,6 +128,20 @@ struct SCookTorrance return cache.isValid(orientedEta); } + template, + typename C=bool_constant NBL_FUNC_REQUIRES(C::value && !fresnel_type::ReturnsMonochrome) + static scalar_type __getScaledReflectance(NBL_CONST_REF_ARG(fresnel_type) orientedFresnel, NBL_CONST_REF_ARG(Interaction) interaction, scalar_type clampedVdotH) + { + spectral_type throughputWeights = interaction.getLuminosityContributionHint(); + return hlsl::dot(impl::__implicit_promote::__call(orientedFresnel(clampedVdotH)), throughputWeights); + } + template, + typename C=bool_constant NBL_FUNC_REQUIRES(C::value && fresnel_type::ReturnsMonochrome) + static scalar_type __getScaledReflectance(NBL_CONST_REF_ARG(fresnel_type) orientedFresnel, NBL_CONST_REF_ARG(Interaction) interaction, scalar_type clampedVdotH) + { + return orientedFresnel(clampedVdotH)[0]; + } + bool __dotIsUnity(const vector3_type a, const vector3_type b, const scalar_type value) { const scalar_type ab = hlsl::dot(a, b); @@ -284,8 +298,7 @@ struct SCookTorrance assert(NdotV*VdotH >= scalar_type(0.0)); } - spectral_type throughputWeights = interaction.getLuminosityContributionHint(); - const scalar_type reflectance = hlsl::dot(impl::__implicit_promote::__call(_f(hlsl::abs(VdotH))), throughputWeights); + const scalar_type reflectance = __getScaledReflectance(_f, interaction, hlsl::abs(VdotH)); scalar_type rcpChoiceProb; scalar_type z = u.z; @@ -326,8 +339,7 @@ struct SCookTorrance NBL_IF_CONSTEXPR(IsBSDF) { - spectral_type throughputWeights = interaction.getLuminosityContributionHint(); - const scalar_type reflectance = hlsl::dot(impl::__implicit_promote::__call(_f(hlsl::abs(cache.getVdotH()))), throughputWeights); + const scalar_type reflectance = __getScaledReflectance(_f, interaction, hlsl::abs(cache.getVdotH())); return hlsl::mix(reflectance, scalar_type(1.0) - reflectance, cache.isTransmission()) * DG1.projectedLightMeasure; } else @@ -375,9 +387,8 @@ struct SCookTorrance spectral_type quo; NBL_IF_CONSTEXPR(IsBSDF) { - spectral_type throughputWeights = interaction.getLuminosityContributionHint(); + const scalar_type scaled_reflectance = __getScaledReflectance(_f, interaction, hlsl::abs(cache.getVdotH())); spectral_type reflectance = impl::__implicit_promote::__call(_f(hlsl::abs(cache.getVdotH()))); - const scalar_type scaled_reflectance = hlsl::dot(reflectance, throughputWeights); quo = hlsl::mix(reflectance / scaled_reflectance, (hlsl::promote(1.0) - reflectance) / (scalar_type(1.0) - scaled_reflectance), cache.isTransmission()) * G2_over_G1; } diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index a0f123de2a..56ea88080c 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -345,6 +345,8 @@ struct Schlick using vector_type = T; using eta_type = vector_type; + NBL_CONSTEXPR_STATIC_INLINE bool ReturnsMonochrome = vector_traits::Dimension == 1; + static Schlick create(NBL_CONST_REF_ARG(T) F0) { Schlick retval; @@ -352,7 +354,7 @@ struct Schlick return retval; } - T operator()(const scalar_type clampedCosTheta) + T operator()(const scalar_type clampedCosTheta) NBL_CONST_MEMBER_FUNC { assert(clampedCosTheta >= scalar_type(0.0)); assert(hlsl::all(hlsl::promote(0.02) < F0 && F0 <= hlsl::promote(1.0))); @@ -379,6 +381,8 @@ struct Conductor using vector_type = T; using eta_type = vector_type; + NBL_CONSTEXPR_STATIC_INLINE bool ReturnsMonochrome = vector_traits::Dimension == 1; + static Conductor create(NBL_CONST_REF_ARG(T) eta, NBL_CONST_REF_ARG(T) etak) { Conductor retval; @@ -412,7 +416,7 @@ struct Conductor Rp = (rp_common - etaCosTwice) / (rp_common + etaCosTwice); } - T operator()(const scalar_type clampedCosTheta) + T operator()(const scalar_type clampedCosTheta) NBL_CONST_MEMBER_FUNC { T rs2, rp2; __polarized(eta, etaLen2, hlsl::promote(clampedCosTheta), rp2, rs2); @@ -440,6 +444,8 @@ struct Dielectric using vector_type = T; using eta_type = vector_type; + NBL_CONSTEXPR_STATIC_INLINE bool ReturnsMonochrome = vector_traits::Dimension == 1; + static Dielectric create(NBL_CONST_REF_ARG(OrientedEtas) orientedEta) { Dielectric retval; @@ -470,7 +476,7 @@ struct Dielectric return (rs2 + rp2) * hlsl::promote(0.5); } - T operator()(const scalar_type clampedCosTheta) + T operator()(const scalar_type clampedCosTheta) NBL_CONST_MEMBER_FUNC { return __call(orientedEta2, clampedCosTheta); } @@ -664,7 +670,9 @@ struct Iridescent; - T operator()(const scalar_type clampedCosTheta) + NBL_CONSTEXPR_STATIC_INLINE bool ReturnsMonochrome = vector_traits::Dimension == 1; + + T operator()(const scalar_type clampedCosTheta) NBL_CONST_MEMBER_FUNC { return impl::iridescent_helper::template __call(__base, clampedCosTheta); } @@ -690,7 +698,9 @@ struct Iridescent; using base_type = impl::iridescent_base; - T operator()(const scalar_type clampedCosTheta) + NBL_CONSTEXPR_STATIC_INLINE bool ReturnsMonochrome = vector_traits::Dimension == 1; + + T operator()(const scalar_type clampedCosTheta) NBL_CONST_MEMBER_FUNC { return impl::iridescent_helper::template __call(__base, clampedCosTheta); } From dee1b6106219509857d20580d5b938275236e26d Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 11 Nov 2025 11:23:32 +0700 Subject: [PATCH 53/56] init ndf quants, minor cook torrance fixes --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 27 ++++++++++--------- .../nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl | 2 ++ include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl | 4 +++ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl index 6c8f18786c..a185dc8d98 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -191,12 +191,10 @@ struct SCookTorrance } sample_type __generate_common(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type localH, - const scalar_type NdotV, const scalar_type VdotH, bool transmitted, + const scalar_type NdotV, const scalar_type VdotH, const scalar_type LdotH, bool transmitted, NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps) rcpEta, - NBL_REF_ARG(anisocache_type) cache, NBL_REF_ARG(bool) valid) + NBL_REF_ARG(bool) valid) { - const scalar_type LdotH = hlsl::mix(VdotH, ieee754::copySign(hlsl::sqrt(rcpEta.value2[0]*VdotH*VdotH + scalar_type(1.0) - rcpEta.value2[0]), -VdotH), transmitted); - // fail if samples have invalid paths const scalar_type NdotL = hlsl::mix(scalar_type(2.0) * VdotH * localH.z - NdotV, localH.z * (VdotH * rcpEta.value[0] + LdotH) - NdotV * rcpEta.value[0], transmitted); @@ -208,8 +206,6 @@ struct SCookTorrance return sample_type::createInvalid(); // should check if sample direction is invalid } - cache = anisocache_type::createPartial(VdotH, LdotH, localH.z, transmitted, rcpEta); - ray_dir_info_type V = interaction.getV(); const matrix3x3_type fromTangent = interaction.getFromTangentSpace(); // tangent frame orthonormality @@ -268,7 +264,7 @@ struct SCookTorrance fresnel::OrientedEtaRcps dummy; bool valid; - sample_type s = __generate_common(interaction, localH, NdotV, VdotH, false, dummy, cache, valid); + sample_type s = __generate_common(interaction, localH, NdotV, VdotH, VdotH, false, dummy, valid); if (valid) cache = anisocache_type::createForReflection(localV, localH); return s; @@ -304,10 +300,12 @@ struct SCookTorrance scalar_type z = u.z; bool transmitted = math::partitionRandVariable(reflectance, z, rcpChoiceProb); + const scalar_type LdotH = hlsl::mix(VdotH, ieee754::copySign(hlsl::sqrt(rcpEta.value2[0]*VdotH*VdotH + scalar_type(1.0) - rcpEta.value2[0]), -VdotH), transmitted); bool valid; - sample_type s = __generate_common(interaction, localH, NdotV, VdotH, transmitted, rcpEta, cache, valid); + sample_type s = __generate_common(interaction, localH, NdotV, VdotH, LdotH, transmitted, rcpEta, valid); if (valid) { + cache = anisocache_type::createPartial(VdotH, LdotH, localH.z, transmitted, rcpEta); assert(cache.isValid(fresnel::OrientedEtas::create(scalar_type(1.0), hlsl::promote(_f.getRefractionOrientedEta())))); const vector3_type T = interaction.getT(); const vector3_type B = interaction.getB(); @@ -387,10 +385,15 @@ struct SCookTorrance spectral_type quo; NBL_IF_CONSTEXPR(IsBSDF) { - const scalar_type scaled_reflectance = __getScaledReflectance(_f, interaction, hlsl::abs(cache.getVdotH())); - spectral_type reflectance = impl::__implicit_promote::__call(_f(hlsl::abs(cache.getVdotH()))); - quo = hlsl::mix(reflectance / scaled_reflectance, - (hlsl::promote(1.0) - reflectance) / (scalar_type(1.0) - scaled_reflectance), cache.isTransmission()) * G2_over_G1; + NBL_IF_CONSTEXPR(fresnel_type::ReturnsMonochrome) + quo = hlsl::promote(G2_over_G1); + else + { + const scalar_type scaled_reflectance = __getScaledReflectance(_f, interaction, hlsl::abs(cache.getVdotH())); + spectral_type reflectance = impl::__implicit_promote::__call(_f(hlsl::abs(cache.getVdotH()))); + quo = hlsl::mix(reflectance / scaled_reflectance, + (hlsl::promote(1.0) - reflectance) / (scalar_type(1.0) - scaled_reflectance), cache.isTransmission()) * G2_over_G1; + } } else { diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl index 70f58a0dc7..1406bc8d4f 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl @@ -339,6 +339,8 @@ struct Beckmann if (isInfinity) { quant_type dmq; + dmq.microfacetMeasure = scalar_type(0.0); + dmq.projectedLightMeasure = scalar_type(0.0); return dmq; } scalar_type dg1 = D / (scalar_type(1.0) + query.getLambdaV()); diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl index c2b9e959e1..0e2e9d1291 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl @@ -278,7 +278,11 @@ struct GGX isInfinity = hlsl::isinf(D); quant_type dmq; if (isInfinity) + { + dmq.microfacetMeasure = scalar_type(0.0); + dmq.projectedLightMeasure = scalar_type(0.0); return dmq; + } scalar_type dg1_over_2NdotV = D * query.getG1over2NdotV(); dmq.microfacetMeasure = scalar_type(2.0) * interaction.getNdotV(BxDFClampMode::BCM_ABS) * dg1_over_2NdotV; From cd9b18be9733cf9ba3adde92605c13ad960e1b8e Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 11 Nov 2025 11:43:07 +0700 Subject: [PATCH 54/56] primaries wavelengths for aces colorspaces (not sure they're correct) --- include/nbl/builtin/hlsl/colorspace.hlsl | 32 +++++++++++++----------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/include/nbl/builtin/hlsl/colorspace.hlsl b/include/nbl/builtin/hlsl/colorspace.hlsl index 9e6da57344..4c5bdc093d 100644 --- a/include/nbl/builtin/hlsl/colorspace.hlsl +++ b/include/nbl/builtin/hlsl/colorspace.hlsl @@ -16,15 +16,7 @@ namespace hlsl namespace colorspace { -struct colorspace_base -{ - // default CIE RGB primaries wavelengths - NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 700.0f; - NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 546.1f; - NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 435.8f; -}; - -struct scRGB : colorspace_base +struct scRGB { NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 611.4f; NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 549.1f; @@ -46,7 +38,7 @@ struct scRGB : colorspace_base struct sRGB : scRGB {}; struct BT709 : scRGB {}; -struct Display_P3 : colorspace_base +struct Display_P3 { NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 614.9f; NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 544.2f; @@ -65,7 +57,7 @@ struct Display_P3 : colorspace_base static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(Display_P3toXYZ, val); } }; -struct DCI_P3 : colorspace_base +struct DCI_P3 { NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 614.9f; NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 544.2f; @@ -84,7 +76,7 @@ struct DCI_P3 : colorspace_base static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(DCI_P3toXYZ, val); } }; -struct BT2020 : colorspace_base +struct BT2020 { NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 630.0f; NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 532.0f; @@ -107,7 +99,7 @@ struct HDR10_ST2084 : BT2020 {}; struct DOLBYIVISION : BT2020 {}; struct HDR10_HLG : BT2020 {}; -struct AdobeRGB : colorspace_base +struct AdobeRGB { NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 611.4f; NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 534.7f; @@ -126,8 +118,13 @@ struct AdobeRGB : colorspace_base static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(AdobeRGBtoXYZ, val); } }; -struct ACES2065_1 : colorspace_base +struct ACES2065_1 { + // AP0 primaries (approximate) + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 700.0f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 520.0f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 472.5f; + static float32_t3x3 FromXYZ() { return decode::XYZtoACES2065_1; @@ -141,8 +138,13 @@ struct ACES2065_1 : colorspace_base static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ACES2065_1toXYZ, val); } }; -struct ACEScc : colorspace_base +struct ACEScc { + // AP1 primaries (approximate) + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 630.0f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 533.0f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 467.0f; + static float32_t3x3 FromXYZ() { return decode::XYZtoACEScc; From 0984c7e55289f8e0fe05288a6558d79d6226b4ce Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 11 Nov 2025 15:26:57 +0700 Subject: [PATCH 55/56] fixes to angle adder, handle some edge cases --- include/nbl/builtin/hlsl/colorspace.hlsl | 2 ++ .../nbl/builtin/hlsl/math/angle_adding.hlsl | 24 +++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/include/nbl/builtin/hlsl/colorspace.hlsl b/include/nbl/builtin/hlsl/colorspace.hlsl index 4c5bdc093d..0c56f1e21f 100644 --- a/include/nbl/builtin/hlsl/colorspace.hlsl +++ b/include/nbl/builtin/hlsl/colorspace.hlsl @@ -121,6 +121,7 @@ struct AdobeRGB struct ACES2065_1 { // AP0 primaries (approximate) + // probably not appropriate to use since AP0 space is larger than CIE NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 700.0f; NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 520.0f; NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 472.5f; @@ -141,6 +142,7 @@ struct ACES2065_1 struct ACEScc { // AP1 primaries (approximate) + // probably not appropriate to use since AP1 space is larger than CIE NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 630.0f; NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 533.0f; NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 467.0f; diff --git a/include/nbl/builtin/hlsl/math/angle_adding.hlsl b/include/nbl/builtin/hlsl/math/angle_adding.hlsl index 6918050542..e2064165f3 100644 --- a/include/nbl/builtin/hlsl/math/angle_adding.hlsl +++ b/include/nbl/builtin/hlsl/math/angle_adding.hlsl @@ -28,31 +28,35 @@ struct sincos_accumulator static this_t create(T cosA, T sinA) { + assert(sinA >= T(0.0)); this_t retval; retval.runningSum = complex_t::create(cosA, sinA); - // retval.runningSum.real(cosA); - // retval.runningSum.imag(sinA); - retval.wraparound = 0u; + retval.wraparound = hlsl::mix(T(0.0), T(1.0), cosA == T(-1.0)); return retval; } - void addCosine(T cosA, T sinA) + // This utility expects that all input angles being added are [0,PI], i.e. runningSum.y and sinA are always positive + // We make use of sine and cosine angle sum formulas to accumulate a running sum of angles + // and any "overflow" (when sum goes over PI) is stored in wraparound + // This way, we can accumulate the sines and cosines of a set of angles, and only have to do one acos() call to retrieve the final sum + void addAngle(T cosA, T sinA) { const T a = cosA; const T cosB = runningSum.real(); const T sinB = runningSum.imag(); - const bool reverse = abs(min(a, cosB)) > max(a, cosB); + const bool overflow = abs(min(a, cosB)) > max(a, cosB); const T c = a * cosB - sinA * sinB; const T d = sinA * cosB + a * sinB; - runningSum.real(ieee754::flipSign(c, reverse)); - runningSum.imag(ieee754::flipSign(d, reverse)); + runningSum.real(ieee754::flipSign(c, overflow)); + runningSum.imag(ieee754::flipSign(d, overflow)); - wraparound += hlsl::mix(0u, 1u, reverse); + if (overflow) + wraparound++; } void addCosine(T cosA) { - addCosine(cosA, sqrt(T(1.0) - cosA * cosA)); + addAngle(cosA, sqrt(T(1.0) - cosA * cosA)); } T getSumofArccos() @@ -61,7 +65,7 @@ struct sincos_accumulator } complex_t runningSum; - uint16_t wraparound; + T wraparound; // counts in pi (half revolutions) }; } From 3ae69fa8cd4bc41fe4d675c398362cea5ed7a90a Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 12 Nov 2025 11:00:26 +0700 Subject: [PATCH 56/56] updated example --- examples_tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples_tests b/examples_tests index 2b4db21239..9c60e9905c 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 2b4db2123918f380cc0a35f6889315a02f84ea73 +Subproject commit 9c60e9905cd29393b1b5479276ab64e643714cb9