From 9be65a049ada37fa6156b575ab4ff97285253fe7 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 1 Dec 2025 16:15:12 +0700 Subject: [PATCH 01/69] changed quaternion struct name, brought in quaternion stuff from przemek's remove_core_matrix branch, added create from mat3x3 --- .../nbl/builtin/hlsl/math/quaternions.hlsl | 139 +++++++++++++++++- .../transformation_matrix_utils.hlsl | 21 +-- .../hlsl/sampling/spherical_triangle.hlsl | 4 +- 3 files changed, 147 insertions(+), 17 deletions(-) diff --git a/include/nbl/builtin/hlsl/math/quaternions.hlsl b/include/nbl/builtin/hlsl/math/quaternions.hlsl index aca8d1ff3c..502e56c8e2 100644 --- a/include/nbl/builtin/hlsl/math/quaternions.hlsl +++ b/include/nbl/builtin/hlsl/math/quaternions.hlsl @@ -14,23 +14,152 @@ namespace hlsl namespace math { +template +struct truncated_quaternion +{ + using this_t = truncated_quaternion; + using scalar_type = T; + using data_type = vector; + + static this_t create() + { + this_t q; + q.data = data_type(0.0, 0.0, 0.0); + return q; + } + + data_type data; +}; + template -struct quaternion_t +struct quaternion { - using this_t = quaternion_t; + using this_t = quaternion; using scalar_type = T; using data_type = vector; using vector3_type = vector; using matrix_type = matrix; - static this_t createFromTruncated(const vector3_type first3Components) + static this_t create() + { + this_t q; + q.data = data_type(0.0, 0.0, 0.0, 1.0); + return q; + } + + static this_t create(scalar_type x, scalar_type y, scalar_type z, scalar_type w) + { + this_t q; + q.data = data_type(x, y, z, w); + return q; + } + + static this_t create(NBL_CONST_REF_ARG(this_t) other) + { + return other; + } + + static this_t create(scalar_type pitch, scalar_type yaw, scalar_type roll) + { + const scalar_type rollDiv2 = roll * scalar_type(0.5); + const scalar_type sr = hlsl::sin(rollDiv2); + const scalar_type cr = hlsl::cos(rollDiv2); + + const scalar_type pitchDiv2 = pitch * scalar_type(0.5); + const scalar_type sp = hlsl::sin(pitchDiv2); + const scalar_type cp = hlsl::cos(pitchDiv2); + + const scalar_type yawDiv2 = yaw * scalar_type(0.5); + const scalar_type sy = hlsl::sin(yawDiv2); + const scalar_type cy = hlsl::cos(yawDiv2); + + this_t output; + output.data[0] = cr * sp * cy + sr * cp * sy; // x + output.data[1] = cr * cp * sy - sr * sp * cy; // y + output.data[2] = sr * cp * cy - cr * sp * sy; // z + output.data[3] = cr * cp * cy + sr * sp * sy; // w + + return output; + } + + static this_t create(NBL_CONST_REF_ARG(matrix_type) m) + { + using AsUint = typename unsigned_integer_of_size::type; + const scalar_type m00 = m[0][0], m11 = m[1][1], m22 = m[2][2]; + const scalar_type neg_m00 = bit_cast(bit_cast(m00)^0x80000000u); + const scalar_type neg_m11 = bit_cast(bit_cast(m11)^0x80000000u); + const scalar_type neg_m22 = bit_cast(bit_cast(m22)^0x80000000u); + const data_type Qx = data_type(m00, m00, neg_m00, neg_m00); + const data_type Qy = data_type(m11, neg_m11, m11, neg_m11); + const data_type Qz = data_type(m22, neg_m22, neg_m22, m22); + + const data_type tmp = hlsl::promote(1.0) + Qx + Qy + Qz; + const data_type invscales = hlsl::promote(0.5) / hlsl::sqrt(tmp); + const data_type scales = tmp * invscales * hlsl::promote(0.5); + + // TODO: speed this up + this_t retval; + if (tmp.x > scalar_type(0.0)) + { + retval.data.x = (m[2][1] - m[1][2]) * invscales.x; + retval.data.y = (m[0][2] - m[2][0]) * invscales.x; + retval.data.z = (m[1][0] - m[0][1]) * invscales.x; + retval.data.w = scales.x; + } + else + { + if (tmp.y > scalar_type(0.0)) + { + retval.data.x = scales.y; + retval.data.y = (m[0][1] + m[1][0]) * invscales.y; + retval.data.z = (m[2][0] + m[0][2]) * invscales.y; + retval.data.w = (m[2][1] - m[1][2]) * invscales.y; + } + else if (tmp.z > scalar_type(0.0)) + { + retval.data.x = (m[0][1] + m[1][0]) * invscales.z; + retval.data.y = scales.z; + retval.data.z = (m[0][2] - m[2][0]) * invscales.z; + retval.data.w = (m[1][2] + m[2][1]) * invscales.z; + } + else + { + retval.data.x = (m[0][2] + m[2][0]) * invscales.w; + retval.data.y = (m[1][2] + m[2][1]) * invscales.w; + retval.data.z = scales.w; + retval.data.w = (m[1][0] - m[0][1]) * invscales.w; + } + } + + retval.data = hlsl::normalize(retval.data); + return retval; + } + + static this_t createFromTruncated(NBL_CONST_REF_ARG(truncated_quaternion) first3Components) { this_t retval; - retval.data.xyz = first3Components; - retval.data.w = hlsl::sqrt(scalar_type(1.0) - hlsl::dot(first3Components, first3Components)); + retval.data.xyz = first3Components.data; + retval.data.w = hlsl::sqrt(scalar_type(1.0) - hlsl::dot(first3Components.data, first3Components.data)); return retval; } + this_t operator*(scalar_type scalar) + { + this_t output; + output.data = data * scalar; + return output; + } + + this_t operator*(NBL_CONST_REF_ARG(this_t) other) + { + return this_t::create( + data.w * other.data.w - data.x * other.x - data.y * other.data.y - data.z * other.data.z, + data.w * other.data.x + data.x * other.w + data.y * other.data.z - data.z * other.data.y, + data.w * other.data.y - data.x * other.z + data.y * other.data.w + data.z * other.data.x, + data.w * other.data.z + data.x * other.y - data.y * other.data.x + data.z * other.data.w + ); + } + static this_t lerp(const this_t start, const this_t end, const scalar_type fraction, const scalar_type totalPseudoAngle) { using AsUint = typename unsigned_integer_of_size::type; diff --git a/include/nbl/builtin/hlsl/matrix_utils/transformation_matrix_utils.hlsl b/include/nbl/builtin/hlsl/matrix_utils/transformation_matrix_utils.hlsl index 1ad16dc28d..b1f4ad94dc 100644 --- a/include/nbl/builtin/hlsl/matrix_utils/transformation_matrix_utils.hlsl +++ b/include/nbl/builtin/hlsl/matrix_utils/transformation_matrix_utils.hlsl @@ -2,6 +2,7 @@ #define _NBL_BUILTIN_HLSL_TRANSFORMATION_MATRIX_UTILS_INCLUDED_ #include +#include namespace nbl { @@ -125,28 +126,28 @@ inline matrix buildCameraLookAtMatrixRH( //! Replaces curent rocation and scale by rotation represented by quaternion `quat`, leaves 4th row and 4th colum unchanged template -inline void setRotation(matrix& outMat, NBL_CONST_REF_ARG(core::quaternion) quat) +inline void setRotation(matrix& outMat, NBL_CONST_REF_ARG(math::quaternion) quat) { static_assert(N == 3 || N == 4); outMat[0] = vector( - 1 - 2 * (quat.y * quat.y + quat.z * quat.z), - 2 * (quat.x * quat.y - quat.z * quat.w), - 2 * (quat.x * quat.z + quat.y * quat.w), + 1 - 2 * (quat.data.y * quat.data.y + quat.data.z * quat.data.z), + 2 * (quat.data.x * quat.data.y - quat.data.z * quat.data.w), + 2 * (quat.data.x * quat.data.z + quat.data.y * quat.data.w), outMat[0][3] ); outMat[1] = vector( - 2 * (quat.x * quat.y + quat.z * quat.w), - 1 - 2 * (quat.x * quat.x + quat.z * quat.z), - 2 * (quat.y * quat.z - quat.x * quat.w), + 2 * (quat.data.x * quat.data.y + quat.data.z * quat.data.w), + 1 - 2 * (quat.data.x * quat.data.x + quat.data.z * quat.data.z), + 2 * (quat.data.y * quat.data.z - quat.data.x * quat.data.w), outMat[1][3] ); outMat[2] = vector( - 2 * (quat.x * quat.z - quat.y * quat.w), - 2 * (quat.y * quat.z + quat.x * quat.w), - 1 - 2 * (quat.x * quat.x + quat.y * quat.y), + 2 * (quat.data.x * quat.data.z - quat.data.y * quat.data.w), + 2 * (quat.data.y * quat.data.z + quat.data.x * quat.data.w), + 1 - 2 * (quat.data.x * quat.data.x + quat.data.y * quat.data.y), outMat[2][3] ); } diff --git a/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl b/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl index 0c86b69793..c31e194788 100644 --- a/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl +++ b/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl @@ -51,7 +51,7 @@ struct SphericalTriangle { 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 += math::quaternion_t::slerp_delta(tri.vertex0, tri.vertex2 * csc_b, cosAngleAlongAC); + C_s += math::quaternion::slerp_delta(tri.vertex0, tri.vertex2 * csc_b, cosAngleAlongAC); } vector3_type retval = tri.vertex1; @@ -61,7 +61,7 @@ struct SphericalTriangle { 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 += math::quaternion_t::slerp_delta(tri.vertex1, C_s * csc_b_s, cosAngleAlongBC_s); + retval += math::quaternion::slerp_delta(tri.vertex1, C_s * csc_b_s, cosAngleAlongBC_s); } return retval; } From 3e03467410a4f0b73c169af0067b4488afeecdc2 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 2 Dec 2025 12:05:11 +0700 Subject: [PATCH 02/69] _static_create quaternion to matrix, create quaternion from axis-angle --- .../nbl/builtin/hlsl/math/quaternions.hlsl | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/include/nbl/builtin/hlsl/math/quaternions.hlsl b/include/nbl/builtin/hlsl/math/quaternions.hlsl index 502e56c8e2..823b5bff17 100644 --- a/include/nbl/builtin/hlsl/math/quaternions.hlsl +++ b/include/nbl/builtin/hlsl/math/quaternions.hlsl @@ -59,6 +59,18 @@ struct quaternion return other; } + // angle: Rotation angle expressed in radians. + // axis: Rotation axis, must be normalized. + static this_t create(scalar_type angle, const vector3_type axis) + { + this_t q; + const scalar_type sinTheta = hlsl::sin(angle * 0.5); + const scalar_type cosTheta = hlsl::cos(angle * 0.5); + q.data = data_type(axis.x * sinTheta, axis.y * sinTheta, axis.z * sinTheta, cosTheta); + return q; + } + + static this_t create(scalar_type pitch, scalar_type yaw, scalar_type roll) { const scalar_type rollDiv2 = roll * scalar_type(0.5); @@ -227,6 +239,19 @@ struct quaternion }; } + +namespace impl +{ +template +struct static_cast_helper, math::quaternion > +{ + static inline matrix cast(math::quaternion q) + { + return q.constructMatrix(); + } +}; +} + } } From 34d1385f9baa3463085a494c4077442c5c141945 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 2 Dec 2025 16:46:46 +0700 Subject: [PATCH 03/69] added more quaternion funcs: transformVec, inverse, normalize + static cast to/from truncated quat --- .../nbl/builtin/hlsl/math/quaternions.hlsl | 55 +++++++++++++++++-- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/include/nbl/builtin/hlsl/math/quaternions.hlsl b/include/nbl/builtin/hlsl/math/quaternions.hlsl index 823b5bff17..8070626cc6 100644 --- a/include/nbl/builtin/hlsl/math/quaternions.hlsl +++ b/include/nbl/builtin/hlsl/math/quaternions.hlsl @@ -40,6 +40,8 @@ struct quaternion using vector3_type = vector; using matrix_type = matrix; + using AsUint = typename unsigned_integer_of_size::type; + static this_t create() { this_t q; @@ -66,7 +68,7 @@ struct quaternion this_t q; const scalar_type sinTheta = hlsl::sin(angle * 0.5); const scalar_type cosTheta = hlsl::cos(angle * 0.5); - q.data = data_type(axis.x * sinTheta, axis.y * sinTheta, axis.z * sinTheta, cosTheta); + q.data = data_type(axis * sinTheta, cosTheta); return q; } @@ -96,7 +98,6 @@ struct quaternion static this_t create(NBL_CONST_REF_ARG(matrix_type) m) { - using AsUint = typename unsigned_integer_of_size::type; const scalar_type m00 = m[0][0], m11 = m[1][1], m22 = m[2][2]; const scalar_type neg_m00 = bit_cast(bit_cast(m00)^0x80000000u); const scalar_type neg_m11 = bit_cast(bit_cast(m11)^0x80000000u); @@ -147,7 +148,7 @@ struct quaternion return retval; } - static this_t createFromTruncated(NBL_CONST_REF_ARG(truncated_quaternion) first3Components) + static this_t create(NBL_CONST_REF_ARG(truncated_quaternion) first3Components) { this_t retval; retval.data.xyz = first3Components.data; @@ -174,7 +175,6 @@ struct quaternion static this_t lerp(const this_t start, const this_t end, const scalar_type fraction, const scalar_type totalPseudoAngle) { - using AsUint = typename unsigned_integer_of_size::type; const AsUint negationMask = hlsl::bit_cast(totalPseudoAngle) & AsUint(0x80000000u); const data_type adjEnd = hlsl::bit_cast(hlsl::bit_cast(end.data) ^ negationMask); @@ -208,6 +208,13 @@ struct quaternion return retval; } + vector3_type transformVector(const vector3_type v) + { + scalar_type scale = hlsl::length(data); + vector3_type direction = data.xyz; + return v * scale + hlsl::cross(direction, v * data.w + hlsl::cross(direction, v)) * scalar_type(2.0); + } + matrix_type constructMatrix() { matrix_type mat; @@ -235,6 +242,23 @@ struct quaternion return precompPart * cosAngle + hlsl::cross(planeNormal, precompPart); } + this_t inverse() + { + this_t retval; + retval.data.x = bit_cast(bit_cast(data.x)^0x80000000u); + retval.data.y = bit_cast(bit_cast(data.y)^0x80000000u); + retval.data.z = bit_cast(bit_cast(data.z)^0x80000000u); + retval.data.w = data.w; + return retval; + } + + static this_t normalize(NBL_CONST_REF_ARG(this_t) q) + { + this_t retval; + retval.data = hlsl::normalize(q.data); + return retval; + } + data_type data; }; @@ -242,6 +266,29 @@ struct quaternion namespace impl { + +template +struct static_cast_helper, math::truncated_quaternion > +{ + static inline math::quaternion cast(math::truncated_quaternion q) + { + return math::quaternion::create(q); + } +}; + +template +struct static_cast_helper, math::quaternion > +{ + static inline math::truncated_quaternion cast(math::quaternion q) + { + math::truncated_quaternion t; + t.data.x = t.data.x; + t.data.y = t.data.y; + t.data.z = t.data.z; + return t; + } +}; + template struct static_cast_helper, math::quaternion > { From 61540d64d42c0bf0d546a326698004e58c9f94c2 Mon Sep 17 00:00:00 2001 From: Przemog1 Date: Thu, 11 Dec 2025 17:07:59 +0100 Subject: [PATCH 04/69] Modified `LoadableImage` and `ResolveAccessorBase` concepts --- .../concepts/accessors/loadable_image.hlsl | 6 +-- include/nbl/builtin/hlsl/rwmc/resolve.hlsl | 47 +++++++++---------- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl b/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl index 8c7251214d..924ee240d0 100644 --- a/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl +++ b/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl @@ -25,8 +25,8 @@ namespace accessors // declare concept #define NBL_CONCEPT_NAME LoadableImage -#define NBL_CONCEPT_TPLT_PRM_KINDS (typename)(typename)(int32_t) -#define NBL_CONCEPT_TPLT_PRM_NAMES (U)(T)(Dims) +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename)(typename)(int32_t)(int32_t) +#define NBL_CONCEPT_TPLT_PRM_NAMES (U)(T)(Dims)(Components) // not the greatest syntax but works #define NBL_CONCEPT_PARAM_0 (a,U) #define NBL_CONCEPT_PARAM_1 (uv,vector) @@ -38,7 +38,7 @@ NBL_CONCEPT_BEGIN(3) #define uv NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define layer NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,layer)), ::nbl::hlsl::is_same_v, vector)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,layer)), ::nbl::hlsl::is_same_v, vector)) ); #undef layer #undef uv diff --git a/include/nbl/builtin/hlsl/rwmc/resolve.hlsl b/include/nbl/builtin/hlsl/rwmc/resolve.hlsl index d8f777d277..64cec7dfe3 100644 --- a/include/nbl/builtin/hlsl/rwmc/resolve.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/resolve.hlsl @@ -16,21 +16,21 @@ namespace rwmc { // declare concept #define NBL_CONCEPT_NAME ResolveAccessorBase -#define NBL_CONCEPT_TPLT_PRM_KINDS (typename)(typename)(int32_t) -#define NBL_CONCEPT_TPLT_PRM_NAMES (T)(VectorScalarType)(Dims) +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename)(typename)(int32_t)(int32_t) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T)(VectorScalarType)(Dims)(Components) // not the greatest syntax but works #define NBL_CONCEPT_PARAM_0 (a,T) -#define NBL_CONCEPT_PARAM_1 (scalar,VectorScalarType) +#define NBL_CONCEPT_PARAM_1 (vec,vector) // start concept - NBL_CONCEPT_BEGIN(2) +NBL_CONCEPT_BEGIN(2) // need to be defined AFTER the concept begins #define a NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 -#define scalar NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define vec NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_EXPR)((a.calcLuma(vector(scalar, scalar, scalar))))) + ((NBL_CONCEPT_REQ_EXPR)((a.calcLuma(vec)))) ); #undef a -#undef scalar +#undef vec #include /* ResolveAccessor is required to: @@ -38,14 +38,15 @@ NBL_CONCEPT_END( * - implement function called `calcLuma` which calculates luma from a 3 component pixel value */ -template -NBL_BOOL_CONCEPT ResolveAccessor = ResolveAccessorBase && concepts::accessors::LoadableImage; +template +NBL_BOOL_CONCEPT ResolveAccessor = ResolveAccessorBase && concepts::accessors::LoadableImage; template struct ResolveAccessorAdaptor { using output_scalar_type = OutputScalar; - using output_type = vector; + NBL_CONSTEXPR int32_t Components = 3; + using output_type = vector; NBL_CONSTEXPR int32_t image_dimension = 2; RWTexture2DArray cascade; @@ -63,13 +64,13 @@ struct ResolveAccessorAdaptor int16_t2 cascadeImageDimension = int16_t2(imgWidth, imgHeight); if (any(uv < int16_t2(0, 0)) || any(uv > cascadeImageDimension)) - return vector(0, 0, 0, 0); + return promote(0); return cascade.Load(int32_t3(uv, int32_t(layer))); } }; -template && ResolveAccessor) +template && ResolveAccessor) struct Resolver { using output_type = OutputColorTypeVec; @@ -92,12 +93,10 @@ struct Resolver output_type operator()(NBL_REF_ARG(CascadeAccessor) acc, const int16_t2 coord) { - using scalar_t = typename vector_traits::scalar_type; - scalar_t reciprocalBaseI = 1.f; CascadeSample curr = __sampleCascade(acc, coord, 0u, reciprocalBaseI); - output_type accumulation = output_type(0.0f, 0.0f, 0.0f); + output_type accumulation = promote(0.0f); scalar_t Emin = params.initialEmin; scalar_t prevNormalizedCenterLuma, prevNormalizedNeighbourhoodAverageLuma; @@ -162,15 +161,15 @@ struct Resolver CascadeSample __sampleCascade(NBL_REF_ARG(CascadeAccessor) acc, int16_t2 coord, uint16_t cascadeIndex, scalar_t reciprocalBaseI) { output_type neighbourhood[9]; - neighbourhood[0] = acc.template get(coord + int16_t2(-1, -1), cascadeIndex).xyz; - neighbourhood[1] = acc.template get(coord + int16_t2(0, -1), cascadeIndex).xyz; - neighbourhood[2] = acc.template get(coord + int16_t2(1, -1), cascadeIndex).xyz; - neighbourhood[3] = acc.template get(coord + int16_t2(-1, 0), cascadeIndex).xyz; - neighbourhood[4] = acc.template get(coord + int16_t2(0, 0), cascadeIndex).xyz; - neighbourhood[5] = acc.template get(coord + int16_t2(1, 0), cascadeIndex).xyz; - neighbourhood[6] = acc.template get(coord + int16_t2(-1, 1), cascadeIndex).xyz; - neighbourhood[7] = acc.template get(coord + int16_t2(0, 1), cascadeIndex).xyz; - neighbourhood[8] = acc.template get(coord + int16_t2(1, 1), cascadeIndex).xyz; + neighbourhood[0] = acc.template get(coord + int16_t2(-1, -1), cascadeIndex); + neighbourhood[1] = acc.template get(coord + int16_t2(0, -1), cascadeIndex); + neighbourhood[2] = acc.template get(coord + int16_t2(1, -1), cascadeIndex); + neighbourhood[3] = acc.template get(coord + int16_t2(-1, 0), cascadeIndex); + neighbourhood[4] = acc.template get(coord + int16_t2(0, 0), cascadeIndex); + neighbourhood[5] = acc.template get(coord + int16_t2(1, 0), cascadeIndex); + neighbourhood[6] = acc.template get(coord + int16_t2(-1, 1), cascadeIndex); + neighbourhood[7] = acc.template get(coord + int16_t2(0, 1), cascadeIndex); + neighbourhood[8] = acc.template get(coord + int16_t2(1, 1), cascadeIndex); // numerical robustness float32_t3 excl_hood_sum = ((neighbourhood[0] + neighbourhood[1]) + (neighbourhood[2] + neighbourhood[3])) + From 6953879dade4967e4ca0afd2451cc18b13214d87 Mon Sep 17 00:00:00 2001 From: Przemog1 Date: Mon, 22 Dec 2025 13:14:17 +0100 Subject: [PATCH 05/69] Small RWMC fixes --- .../nbl/builtin/hlsl/concepts/accessors/storable_image.hlsl | 6 +++--- include/nbl/builtin/hlsl/rwmc/resolve.hlsl | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/nbl/builtin/hlsl/concepts/accessors/storable_image.hlsl b/include/nbl/builtin/hlsl/concepts/accessors/storable_image.hlsl index 7eda9b9303..900352d993 100644 --- a/include/nbl/builtin/hlsl/concepts/accessors/storable_image.hlsl +++ b/include/nbl/builtin/hlsl/concepts/accessors/storable_image.hlsl @@ -18,13 +18,13 @@ namespace accessors { // declare concept #define NBL_CONCEPT_NAME StorableImage -#define NBL_CONCEPT_TPLT_PRM_KINDS (typename)(typename)(int32_t) -#define NBL_CONCEPT_TPLT_PRM_NAMES (U)(T)(Dims) +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename)(typename)(int32_t)(int32_t) +#define NBL_CONCEPT_TPLT_PRM_NAMES (U)(T)(Dims)(Components) // not the greatest syntax but works #define NBL_CONCEPT_PARAM_0 (a,U) #define NBL_CONCEPT_PARAM_1 (uv,vector) #define NBL_CONCEPT_PARAM_2 (layer,uint16_t) -#define NBL_CONCEPT_PARAM_3 (data,vector) +#define NBL_CONCEPT_PARAM_3 (data,vector) // start concept NBL_CONCEPT_BEGIN(4) // need to be defined AFTER the cocnept begins diff --git a/include/nbl/builtin/hlsl/rwmc/resolve.hlsl b/include/nbl/builtin/hlsl/rwmc/resolve.hlsl index 64cec7dfe3..34d7c95960 100644 --- a/include/nbl/builtin/hlsl/rwmc/resolve.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/resolve.hlsl @@ -27,7 +27,7 @@ NBL_CONCEPT_BEGIN(2) #define a NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define vec NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_EXPR)((a.calcLuma(vec)))) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.calcLuma(vec)), ::nbl::hlsl::is_same_v, VectorScalarType)) ); #undef a #undef vec From 3d0c45441a8d9fc950e1e88acc31fb928389c650 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 20 Jan 2026 15:56:23 +0700 Subject: [PATCH 06/69] changes to linear, bilinear, box muller for pdf and backward pdf --- .../nbl/builtin/hlsl/sampling/bilinear.hlsl | 21 ++++++++++-------- .../hlsl/sampling/box_muller_transform.hlsl | 6 +++++ include/nbl/builtin/hlsl/sampling/linear.hlsl | 22 ++++++++++--------- .../projected_spherical_triangle.hlsl | 8 +++---- 4 files changed, 34 insertions(+), 23 deletions(-) diff --git a/include/nbl/builtin/hlsl/sampling/bilinear.hlsl b/include/nbl/builtin/hlsl/sampling/bilinear.hlsl index a74869990f..fd3632b994 100644 --- a/include/nbl/builtin/hlsl/sampling/bilinear.hlsl +++ b/include/nbl/builtin/hlsl/sampling/bilinear.hlsl @@ -28,34 +28,37 @@ struct Bilinear { Bilinear retval; retval.bilinearCoeffs = bilinearCoeffs; - retval.twiceAreasUnderXCurve = vector2_type(bilinearCoeffs[0] + bilinearCoeffs[1], bilinearCoeffs[2] + bilinearCoeffs[3]); + retval.bilinearCoeffDiffs = vector2_type(bilinearCoeffs[2]-bilinearCoeffs[0], bilinearCoeffs[3]-bilinearCoeffs[1]); + vector2_type twiceAreasUnderXCurve = vector2_type(bilinearCoeffs[0] + bilinearCoeffs[1], bilinearCoeffs[2] + bilinearCoeffs[3]); + retval.twiceAreasUnderXCurveSumOverFour = scalar_type(4.0) / (twiceAreasUnderXCurve[0] + twiceAreasUnderXCurve[1]); + retval.lineary = Linear::create(twiceAreasUnderXCurve); return retval; } - vector2_type generate(NBL_REF_ARG(scalar_type) rcpPdf, const vector2_type _u) + vector2_type generate(const vector2_type _u) { vector2_type u; - 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)); + const vector2_type ySliceEndPoints = vector2_type(bilinearCoeffs[0] + u.y * bilinearCoeffDiffs[0], bilinearCoeffs[1] + u.y * bilinearCoeffDiffs[1]); 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(const vector2_type u) + scalar_type backwardPdf(const 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]); + const vector2_type ySliceEndPoints = vector2_type(bilinearCoeffs[0] + u.y * bilinearCoeffDiffs[0], bilinearCoeffs[1] + u.y * bilinearCoeffDiffs[1]); + return nbl::hlsl::mix(ySliceEndPoints[0], ySliceEndPoints[1], u.x) * fourOverTwiceAreasUnderXCurveSum; } // unit square: x0y0 x1y0 // x0y1 x1y1 vector4_type bilinearCoeffs; // (x0y0, x0y1, x1y0, x1y1) - vector2_type twiceAreasUnderXCurve; + vector2_type bilinearCoeffDiffs; + vector2_type fourOverTwiceAreasUnderXCurveSum; + Linear lineary; }; } diff --git a/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl b/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl index 9474642f4c..cdd87ee4dc 100644 --- a/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl +++ b/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl @@ -28,6 +28,12 @@ struct BoxMullerTransform return vector2_type(cosPhi, sinPhi) * nbl::hlsl::sqrt(-2.0 * nbl::hlsl::log(xi.x)) * stddev; } + vector2_type backwardPdf(const vector2_type outPos) + { + const vector2_type outPos2 = outPos * outPos; + return vector2_type(nbl::hlsl::exp(scalar_type(-0.5) * (outPos2.x + outPos2.y)), numbers::pi * scalar_type(0.5) * hlsl::atan2(outPos.y, outPos.x)); + } + T stddev; }; diff --git a/include/nbl/builtin/hlsl/sampling/linear.hlsl b/include/nbl/builtin/hlsl/sampling/linear.hlsl index 6c3cf1fad9..7127701117 100644 --- a/include/nbl/builtin/hlsl/sampling/linear.hlsl +++ b/include/nbl/builtin/hlsl/sampling/linear.hlsl @@ -21,26 +21,28 @@ struct Linear using scalar_type = T; using vector2_type = vector; - static Linear create(const vector2_type linearCoeffs) // start and end importance values (start, end) + static Linear create(const vector2_type linearCoeffs) // start and end importance values (start, end), assumed to be at x=0 and x=1 { Linear retval; - retval.linearCoeffStart = linearCoeffs[0]; - retval.rcpDiff = 1.0 / (linearCoeffs[0] - linearCoeffs[1]); + scalar_type rcpDiff = 1.0 / (linearCoeffs[0] - linearCoeffs[1]); + retval.linearCoeffStartOverDiff = linearCoeffs[0] * rcpDiff; vector2_type squaredCoeffs = linearCoeffs * linearCoeffs; - retval.squaredCoeffStart = squaredCoeffs[0]; - retval.squaredCoeffDiff = squaredCoeffs[1] - squaredCoeffs[0]; + scalar_type squaredRcpDiff = rcpDiff * rcpDiff; + retval.squaredCoeffStartOverDiff = squaredCoeffs[0] * squaredRcpDiff; + retval.squaredCoeffDiffOverDiff = (squaredCoeffs[1] - squaredCoeffs[0]) * squaredRcpDiff; return retval; } scalar_type generate(const scalar_type u) { - return hlsl::mix(u, (linearCoeffStart - hlsl::sqrt(squaredCoeffStart + u * squaredCoeffDiff)) * rcpDiff, hlsl::abs(rcpDiff) < numeric_limits::max); + return hlsl::mix(u, (linearCoeffStartOverDiff - hlsl::sqrt(squaredCoeffStartOverDiff + u * squaredCoeffDiffOverDiff)), hlsl::abs(linearCoeffStartOverDiff) < numeric_limits::max); } - scalar_type linearCoeffStart; - scalar_type rcpDiff; - scalar_type squaredCoeffStart; - scalar_type squaredCoeffDiff; + // TODO: add forwardPdf and backwardPdf methods, forward computes from u and backwards from the result of generate + + scalar_type linearCoeffStartOverDiff; + scalar_type squaredCoeffStartOverDiff; + scalar_type squaredCoeffDiffOverDiff; }; } diff --git a/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl b/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl index e60fe28423..b82a10be44 100644 --- a/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl +++ b/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl @@ -49,11 +49,11 @@ struct ProjectedSphericalTriangle // pre-warp according to proj solid angle approximation vector4_type patch = computeBilinearPatch(receiverNormal, isBSDF); Bilinear bilinear = Bilinear::create(patch); - u = bilinear.generate(rcpPdf, _u); + u = bilinear.generate(_u); // now warp the points onto a spherical triangle const vector3_type L = sphtri.generate(solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, u); - rcpPdf *= solidAngle; + rcpPdf = solidAngle / bilinear.backwardPdf(u); return L; } @@ -73,7 +73,7 @@ struct ProjectedSphericalTriangle vector4_type patch = computeBilinearPatch(receiverNormal, receiverWasBSDF); Bilinear bilinear = Bilinear::create(patch); - return pdf * bilinear.pdf(u); + return pdf * bilinear.backwardPdf(u); } scalar_type pdf(const vector3_type receiverNormal, bool receiverWasBSDF, const vector3_type L) @@ -83,7 +83,7 @@ struct ProjectedSphericalTriangle vector4_type patch = computeBilinearPatch(receiverNormal, receiverWasBSDF); Bilinear bilinear = Bilinear::create(patch); - return pdf * bilinear.pdf(u); + return pdf * bilinear.backwardPdf(u); } shapes::SphericalTriangle tri; From bea01f9c4f020ae0b3ff3f1e1d9545d117b4d6d1 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 20 Jan 2026 16:58:42 +0700 Subject: [PATCH 07/69] get polarized fresnel values by component --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 63 +++++++++++----------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 33faa79efc..57e21f04aa 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -431,14 +431,6 @@ struct Conductor return (rs2 + rp2) * hlsl::promote(0.5); } - // OrientedEtaRcps getRefractionOrientedEtaRcps() NBL_CONST_MEMBER_FUNC - // { - // OrientedEtaRcps rcpEta; - // rcpEta.value = hlsl::promote(1.0) / eta; - // rcpEta.value2 = rcpEta.value * rcpEta.value; - // return rcpEta; - // } - T eta; T etak2; T etaLen2; @@ -563,31 +555,44 @@ struct iridescent_helper const vector_type scale = scalar_type(1.0)/eta12; const vector_type cosTheta2_2 = hlsl::promote(1.0) - hlsl::promote(scalar_type(1.0)-cosTheta_1*cosTheta_1) * scale * scale; notTIR = cosTheta2_2 > hlsl::promote(0.0); - cosTheta_2 = hlsl::sqrt(hlsl::max(cosTheta2_2, hlsl::promote(0.0))); + cosTheta_2 = hlsl::mix(hlsl::promote(0.0), hlsl::sqrt(cosTheta2_2), notTIR); } - if (hlsl::any(notTIR)) + NBL_UNROLL for (uint32_t i = 0; i < vector_traits::Dimension; i++) { - Dielectric::__polarized(eta12 * eta12, hlsl::promote(cosTheta_1), R12p, R12s); - - // Reflected part by the base - // if kappa==0, base material is dielectric - NBL_IF_CONSTEXPR(SupportsTransmission) - Dielectric::__polarized(eta23 * eta23, cosTheta_2, R23p, R23s); + // Check for total internal reflection + if (notTIR[i]) + { + using monochrome_type = vector; + monochrome_type p12, s12, p23, s23; + Dielectric::__polarized(hlsl::promote(eta12[i] * eta12[i]), hlsl::promote(cosTheta_1), p12, s12); + + const monochrome_type eta23_2 = hlsl::promote(eta23[i] * eta23[i]); + + // Reflected part by the base + // if kappa==0, base material is dielectric + NBL_IF_CONSTEXPR(SupportsTransmission) + Dielectric::__polarized(eta23_2, hlsl::promote(cosTheta_2[i]), p23, s23); + else + { + const monochrome_type etaLen2 = eta23_2 + hlsl::promote(etak23[i] * etak23[i]); + Conductor::__polarized(hlsl::promote(eta23[i]), etaLen2, hlsl::promote(cosTheta_2[i]), p23, s23); + } + + R12p[i] = p12[0]; + R12s[i] = s12[0]; + R23p[i] = p23[0]; + R23s[i] = s23[0]; + } else { - vector_type etaLen2 = eta23 * eta23 + etak23 * etak23; - Conductor::__polarized(eta23, etaLen2, cosTheta_2, R23p, R23s); + R12s[i] = scalar_type(0.0); + R12p[i] = scalar_type(0.0); + R23s[i] = scalar_type(0.0); + R23p[i] = scalar_type(0.0); } } - // Check for total internal reflection - const vector_type notTIRFactor = vector_type(notTIR); // 0 when TIR, 1 otherwise - R12s = R12s * notTIRFactor; - R12p = R12p * notTIRFactor; - R23s = R23s * notTIRFactor; - R23p = R23p * notTIRFactor; - // Compute the transmission coefficients vector_type T121p = hlsl::promote(1.0) - R12p; vector_type T121s = hlsl::promote(1.0) - R12s; @@ -703,14 +708,6 @@ struct Iridescent getRefractionOrientedEtaRcps() NBL_CONST_MEMBER_FUNC - // { - // OrientedEtaRcps rcpEta; - // rcpEta.value = hlsl::promote(1.0) / base_type::eta13; - // rcpEta.value2 = rcpEta.value * rcpEta.value; - // return rcpEta; - // } - vector_type getEtak23() NBL_CONST_MEMBER_FUNC { return etak23; From 5a36766e04df1dbcb51b1f311550a47874563eef Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 21 Jan 2026 11:44:21 +0700 Subject: [PATCH 08/69] precompute some phase shift constants --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 23 +++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 57e21f04aa..bbb0fd8413 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -508,10 +508,8 @@ struct iridescent_helper using vector_type = T; // returns phi, the phase shift for each plane of polarization (p,s) - static void phase_shift(const vector_type ior1, const vector_type ior2, const vector_type iork2, const vector_type cosTheta, NBL_REF_ARG(vector_type) phiS, NBL_REF_ARG(vector_type) phiP) + static void phase_shift(const vector_type ior1, const vector_type ior2, const vector_type iork2, const vector_type cosTheta, const vector_type cosTheta2, const vector_type sinTheta2, NBL_REF_ARG(vector_type) phiS, NBL_REF_ARG(vector_type) phiP) { - const vector_type cosTheta2 = cosTheta * cosTheta; - const vector_type sinTheta2 = hlsl::promote(1.0) - cosTheta2; const vector_type ior1_2 = ior1*ior1; const vector_type ior2_2 = ior2*ior2; const vector_type iork2_2 = iork2*iork2; @@ -523,6 +521,7 @@ struct iridescent_helper const vector_type a = hlsl::sqrt(a2); const vector_type b = hlsl::sqrt(b2); + // TODO: very optimizable, especially atan2 usage phiS = hlsl::atan2(scalar_type(2.0) * ior1 * b * cosTheta, a2 + b2 - ior1_2*cosTheta2); const vector_type k2_plus_one = hlsl::promote(1.0) + iork2_2; phiP = hlsl::atan2(scalar_type(2.0) * ior1 * ior2_2 * cosTheta * (scalar_type(2.0) * iork2 * a - (hlsl::promote(1.0) - iork2_2) * b), @@ -549,11 +548,11 @@ struct iridescent_helper { const scalar_type cosTheta_1 = clampedCosTheta; vector_type R12p, R23p, R12s, R23s; - vector_type cosTheta_2; + vector_type cosTheta_2, cosTheta2_2; vector::Dimension> notTIR; { const vector_type scale = scalar_type(1.0)/eta12; - const vector_type cosTheta2_2 = hlsl::promote(1.0) - hlsl::promote(scalar_type(1.0)-cosTheta_1*cosTheta_1) * scale * scale; + cosTheta2_2 = hlsl::promote(1.0) - hlsl::promote(scalar_type(1.0)-cosTheta_1*cosTheta_1) * scale * scale; notTIR = cosTheta2_2 > hlsl::promote(0.0); cosTheta_2 = hlsl::mix(hlsl::promote(0.0), hlsl::sqrt(cosTheta2_2), notTIR); } @@ -604,8 +603,18 @@ struct iridescent_helper vector_type I = hlsl::promote(0.0); // Evaluate the phase shift - phase_shift(ior1, ior2, hlsl::promote(0.0), hlsl::promote(cosTheta_1), phi21s, phi21p); - phase_shift(ior2, ior3, iork3, cosTheta_2, phi23s, phi23p); + { + const vector_type ct = hlsl::promote(cosTheta_1); + const vector_type ct2 = ct * ct; + const vector_type st2 = hlsl::promote(1.0) - ct2; + phase_shift(ior1, ior2, hlsl::promote(0.0), ct, ct2, st2, phi21s, phi21p); + } + { + const vector_type ct = hlsl::promote(cosTheta_2); + const vector_type ct2 = cosTheta2_2; + const vector_type st2 = hlsl::promote(1.0) - ct2; + phase_shift(ior2, ior3, iork3, ct, ct2, st2, phi23s, phi23p); + } phi21p = hlsl::promote(numbers::pi) - phi21p; phi21s = hlsl::promote(numbers::pi) - phi21s; From d079f5b5defa09a0325c8a4d14925016d8705a34 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 21 Jan 2026 12:05:35 +0700 Subject: [PATCH 09/69] removed morton struct superseded by new one --- include/nbl/builtin/hlsl/math/morton.hlsl | 68 ----------------------- 1 file changed, 68 deletions(-) delete mode 100644 include/nbl/builtin/hlsl/math/morton.hlsl diff --git a/include/nbl/builtin/hlsl/math/morton.hlsl b/include/nbl/builtin/hlsl/math/morton.hlsl deleted file mode 100644 index 4a6cb5dfd3..0000000000 --- a/include/nbl/builtin/hlsl/math/morton.hlsl +++ /dev/null @@ -1,68 +0,0 @@ -// 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 3f8fd482fe6ea8d6e1f3e1e524478d0d2a999a5f Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 21 Jan 2026 15:08:09 +0700 Subject: [PATCH 10/69] changes to solid angle method name, simplified a lot of code in spherical triangle --- .../projected_spherical_triangle.hlsl | 7 ++- .../hlsl/sampling/spherical_triangle.hlsl | 28 +++++---- .../hlsl/shapes/spherical_rectangle.hlsl | 4 +- .../hlsl/shapes/spherical_triangle.hlsl | 58 +++++++------------ 4 files changed, 45 insertions(+), 52 deletions(-) diff --git a/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl b/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl index b82a10be44..d01712c2f8 100644 --- a/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl +++ b/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl @@ -60,9 +60,12 @@ struct ProjectedSphericalTriangle vector3_type generate(NBL_REF_ARG(scalar_type) rcpPdf, const vector3_type receiverNormal, bool isBSDF, const vector2_type u) { - scalar_type cos_a, cos_c, csc_b, csc_c; + const scalar_type cos_a = tri.cos_sides[0]; + const scalar_type cos_c = tri.cos_sides[2]; + const scalar_type csc_b = tri.csc_sides[1]; + const scalar_type csc_c = tri.csc_sides[2]; vector3_type cos_vertices, sin_vertices; - const scalar_type solidAngle = tri.solidAngleOfTriangle(cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c); + const scalar_type solidAngle = tri.solidAngle(cos_vertices, sin_vertices); return generate(rcpPdf, solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, receiverNormal, isBSDF, u); } diff --git a/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl b/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl index 5770403cd2..4d0a5eafec 100644 --- a/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl +++ b/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl @@ -46,32 +46,35 @@ struct SphericalTriangle 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; + vector3_type C_s = tri.vertices[0]; 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 += math::quaternion::slerp_delta(tri.vertex0, tri.vertex2 * csc_b, cosAngleAlongAC); + C_s += math::quaternion::slerp_delta(tri.vertices[0], tri.vertices[2] * csc_b, cosAngleAlongAC); } - vector3_type retval = tri.vertex1; - const scalar_type cosBC_s = nbl::hlsl::dot(C_s, tri.vertex1); + vector3_type retval = tri.vertices[1]; + const scalar_type cosBC_s = nbl::hlsl::dot(C_s, tri.vertices[1]); 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 += math::quaternion::slerp_delta(tri.vertex1, C_s * csc_b_s, cosAngleAlongBC_s); + retval += math::quaternion::slerp_delta(tri.vertices[1], C_s * csc_b_s, cosAngleAlongBC_s); } return retval; } vector3_type generate(NBL_REF_ARG(scalar_type) rcpPdf, const vector2_type u) { - scalar_type cos_a, cos_c, csc_b, csc_c; + const scalar_type cos_a = tri.cos_sides[0]; + const scalar_type cos_c = tri.cos_sides[2]; + const scalar_type csc_b = tri.csc_sides[1]; + const scalar_type csc_c = tri.csc_sides[2]; vector3_type cos_vertices, sin_vertices; - rcpPdf = tri.solidAngleOfTriangle(cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c); + rcpPdf = tri.solidAngle(cos_vertices, sin_vertices); return generate(rcpPdf, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, u); } @@ -80,9 +83,9 @@ struct SphericalTriangle { pdf = 1.0 / solidAngle; - const scalar_type cosAngleAlongBC_s = nbl::hlsl::dot(L, tri.vertex1); + const scalar_type cosAngleAlongBC_s = nbl::hlsl::dot(L, tri.vertices[1]); 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 cos_b_ = nbl::hlsl::dot(L, tri.vertices[0]); 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_); @@ -104,10 +107,13 @@ struct SphericalTriangle vector2_type generateInverse(NBL_REF_ARG(scalar_type) pdf, const vector3_type L) { - scalar_type cos_a, cos_c, csc_b, csc_c; + const scalar_type cos_a = tri.cos_sides[0]; + const scalar_type cos_c = tri.cos_sides[2]; + const scalar_type csc_b = tri.csc_sides[1]; + const scalar_type csc_c = tri.csc_sides[2]; vector3_type cos_vertices, sin_vertices; - const scalar_type solidAngle = tri.solidAngleOfTriangle(cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c); + const scalar_type solidAngle = tri.solidAngle(cos_vertices, sin_vertices); return generateInverse(pdf, solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, L); } diff --git a/include/nbl/builtin/hlsl/shapes/spherical_rectangle.hlsl b/include/nbl/builtin/hlsl/shapes/spherical_rectangle.hlsl index 11442bef7c..587e221996 100644 --- a/include/nbl/builtin/hlsl/shapes/spherical_rectangle.hlsl +++ b/include/nbl/builtin/hlsl/shapes/spherical_rectangle.hlsl @@ -22,7 +22,6 @@ struct SphericalRectangle { using scalar_type = Scalar; using vector3_type = vector; - using vector4_type = vector; using matrix3x3_type = matrix; static SphericalRectangle create(const vector3_type observer, const vector3_type rectangleOrigin, const matrix3x3_type basis) @@ -40,8 +39,9 @@ struct SphericalRectangle return retval; } - scalar_type solidAngleOfRectangle(const vector rectangleExtents) + scalar_type solidAngle(const vector rectangleExtents) { + using vector4_type = vector; 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 = vector4_type( diff --git a/include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl b/include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl index f574b106ce..028d3e3653 100644 --- a/include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl +++ b/include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl @@ -25,36 +25,29 @@ struct SphericalTriangle using scalar_type = T; using vector3_type = vector; - static SphericalTriangle create(const vector3_type vertex0, const vector3_type vertex1, const vector3_type vertex2, const vector3_type origin) + static SphericalTriangle create(const vector3_type vertices[3], const 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); - retval.cos_sides = vector3_type(hlsl::dot(retval.vertex1, retval.vertex2), hlsl::dot(retval.vertex2, retval.vertex0), hlsl::dot(retval.vertex0, retval.vertex1)); - const vector3_type csc_sides2 = hlsl::promote(1.0) - retval.cos_sides * retval.cos_sides; - retval.csc_sides.x = hlsl::rsqrt(csc_sides2.x); - retval.csc_sides.y = hlsl::rsqrt(csc_sides2.y); - retval.csc_sides.z = hlsl::rsqrt(csc_sides2.z); + retval.vertices[0] = nbl::hlsl::normalize(vertices[0] - origin); + retval.vertices[1] = nbl::hlsl::normalize(vertices[1] - origin); + retval.vertices[2] = nbl::hlsl::normalize(vertices[2] - origin); + retval.cos_sides = vector3_type(hlsl::dot(retval.vertices[1], retval.vertices[2]), hlsl::dot(retval.vertices[2], retval.vertices[0]), hlsl::dot(retval.vertices[0], retval.vertices[1])); + const vector3_type sin_sides2 = hlsl::promote(1.0) - retval.cos_sides * retval.cos_sides; + retval.csc_sides = hlsl::rsqrt(sin_sides2); return retval; } + // checks if any angles are small enough to disregard bool pyramidAngles() { - return hlsl::any >(csc_sides >= (vector3_type)(numeric_limits::max)); + return hlsl::any >(csc_sides >= hlsl::promote(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) + scalar_type solidAngle(NBL_REF_ARG(vector3_type) cos_vertices, NBL_REF_ARG(vector3_type) sin_vertices) { if (pyramidAngles()) 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 = hlsl::clamp((cos_sides - cos_sides.yzx * cos_sides.zxy) * csc_sides.yzx * csc_sides.zxy, hlsl::promote(-1.0), hlsl::promote(1.0)); // using Spherical Law of Cosines (TODO: do we need to clamp anymore? since the pyramid angles method introduction?) sin_vertices = hlsl::sqrt(hlsl::promote(1.0) - cos_vertices * cos_vertices); @@ -65,39 +58,30 @@ struct SphericalTriangle return angle_adder.getSumofArccos() - numbers::pi; } - scalar_type solidAngleOfTriangle() + scalar_type solidAngle() { vector3_type dummy0,dummy1; - scalar_type dummy2,dummy3,dummy4,dummy5; - return solidAngleOfTriangle(dummy0,dummy1,dummy2,dummy3,dummy4,dummy5); + return solidAngle(dummy0,dummy1); } - scalar_type projectedSolidAngleOfTriangle(const vector3_type receiverNormal, NBL_REF_ARG(vector3_type) cos_sides, NBL_REF_ARG(vector3_type) csc_sides, NBL_REF_ARG(vector3_type) cos_vertices) + scalar_type projectedSolidAngle(const 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()) return 0.f; - 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] = 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 = hlsl::clamp((cos_sides - cos_sides.yzx * cos_sides.zxy) * csc_sides.yzx * csc_sides.zxy, hlsl::promote(-1.0), hlsl::promote(1.0)); + cos_vertices = hlsl::clamp((cos_sides - cos_sides.yzx * cos_sides.zxy) * csc_sides.yzx * csc_sides.zxy, hlsl::promote(-1.0), hlsl::promote(1.0)); - matrix awayFromEdgePlane = matrix(awayFromEdgePlane0, awayFromEdgePlane1, awayFromEdgePlane2); + matrix awayFromEdgePlane; + awayFromEdgePlane[0] = hlsl::cross(vertices[1], vertices[2]) * csc_sides[0]; + awayFromEdgePlane[1] = hlsl::cross(vertices[2], vertices[0]) * csc_sides[1]; + awayFromEdgePlane[2] = hlsl::cross(vertices[0], vertices[1]) * csc_sides[2]; const vector3_type externalProducts = hlsl::abs(hlsl::mul(/* transposed already */awayFromEdgePlane, receiverNormal)); - const vector3_type pyramidAngles = acos(cos_sides); - return hlsl::dot(pyramidAngles, externalProducts) / (2.f * numbers::pi); + const vector3_type pyramidAngles = hlsl::acos(cos_sides); + return hlsl::dot(pyramidAngles, externalProducts) / (2.f * numbers::pi); } - vector3_type vertex0; - vector3_type vertex1; - vector3_type vertex2; + vector3_type vertices[3]; vector3_type cos_sides; vector3_type csc_sides; }; From c06f199f144d831c8eac739d393aba0e78104703 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 21 Jan 2026 16:44:34 +0700 Subject: [PATCH 11/69] simplify call to iridescent helper through base struct --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index bbb0fd8413..0865e7f5e1 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -663,6 +663,13 @@ struct iridescent_base using scalar_type = typename vector_traits::scalar_type; using vector_type = T; + template + T __call(const vector_type iork3, const vector_type etak23, const scalar_type clampedCosTheta) NBL_CONST_MEMBER_FUNC + { + return impl::iridescent_helper::template __call(D, ior1, ior2, ior3, iork3, + eta12, eta23, etak23, clampedCosTheta); + } + vector_type D; vector_type ior1; vector_type ior2; @@ -672,6 +679,14 @@ struct iridescent_base vector_type eta23; // thin-film -> base material IOR vector_type eta13; }; + + +// workaround due to DXC bug: github.com/microsoft/DirectXShaderCompiler/issues/5966 +template +T __iridescent_base__call_const(NBL_CONST_REF_ARG(iridescent_base) _this, const T iork3, const T etak23, const typename vector_traits::scalar_type clampedCosTheta) +{ + return _this.template __call(iork3, etak23, clampedCosTheta); +} } template @@ -713,8 +728,7 @@ struct Iridescent::template __call(base_type::D, base_type::ior1, base_type::ior2, base_type::ior3, base_type::iork3, - base_type::eta12, base_type::eta23, getEtak23(), clampedCosTheta); + return impl::__iridescent_base__call_const(this, base_type::iork3, getEtak23(), clampedCosTheta); } vector_type getEtak23() NBL_CONST_MEMBER_FUNC @@ -761,8 +775,7 @@ struct Iridescent::template __call(base_type::D, base_type::ior1, base_type::ior2, base_type::ior3, getEtak23(), - base_type::eta12, base_type::eta23, getEtak23(), clampedCosTheta); + return impl::__iridescent_base__call_const(this, getEtak23(), getEtak23(), clampedCosTheta); } scalar_type getRefractionOrientedEta() NBL_CONST_MEMBER_FUNC { return base_type::eta13[0]; } From 407d0fd6b18f519cc0bc21d8bc730837239afd57 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 22 Jan 2026 11:35:14 +0700 Subject: [PATCH 12/69] removed redundant/unused variables from spherical triangle sample --- .../hlsl/sampling/spherical_triangle.hlsl | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl b/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl index 4d0a5eafec..2f2d12e1df 100644 --- a/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl +++ b/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl @@ -29,27 +29,30 @@ struct SphericalTriangle { SphericalTriangle retval; retval.tri = tri; + vector3_type cos_vertices, sin_vertices; + retval.solidAngle = tri.solidAngle(cos_vertices, sin_vertices); + retval.cosA = cos_vertices[0]; + retval.sinA = sin_vertices[0]; return retval; } - // WARNING: can and will return NAN if one or three of the triangle edges are near zero length - vector3_type generate(scalar_type solidAngle, const vector3_type cos_vertices, const vector3_type sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, const vector2_type u) + vector3_type generate(scalar_type cos_c, scalar_type csc_b, const 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]; + const scalar_type p = negCosSubSolidAngle * sinA - negSinSubSolidAngle * cosA; + const scalar_type q = -negSinSubSolidAngle * sinA - negCosSubSolidAngle * cosA; // 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; + scalar_type u_ = q - cosA; + scalar_type v_ = p + sinA * cos_c; // the slerps could probably be optimized by sidestepping `normalize` calls and accumulating scaling factors vector3_type C_s = tri.vertices[0]; 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]); + const scalar_type cosAngleAlongAC = ((v_ * q - u_ * p) * cosA - v_) / ((v_ * p + u_ * q) * sinA); if (nbl::hlsl::abs(cosAngleAlongAC) < 1.f) C_s += math::quaternion::slerp_delta(tri.vertices[0], tri.vertices[2] * csc_b, cosAngleAlongAC); } @@ -68,18 +71,15 @@ struct SphericalTriangle vector3_type generate(NBL_REF_ARG(scalar_type) rcpPdf, const vector2_type u) { - const scalar_type cos_a = tri.cos_sides[0]; const scalar_type cos_c = tri.cos_sides[2]; const scalar_type csc_b = tri.csc_sides[1]; - const scalar_type csc_c = tri.csc_sides[2]; - vector3_type cos_vertices, sin_vertices; - rcpPdf = tri.solidAngle(cos_vertices, sin_vertices); + rcpPdf = solidAngle; - return generate(rcpPdf, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, u); + return generate(cos_c, csc_b, u); } - vector2_type generateInverse(NBL_REF_ARG(scalar_type) pdf, scalar_type solidAngle, const vector3_type cos_vertices, const vector3_type sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, const vector3_type L) + vector2_type generateInverse(NBL_REF_ARG(scalar_type) pdf, scalar_type cos_c, scalar_type csc_c, const vector3_type L) { pdf = 1.0 / solidAngle; @@ -90,16 +90,16 @@ struct SphericalTriangle 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 cosC_ = sinA * sinB_* cos_c - cosA * cosB_; const scalar_type sinC_ = nbl::hlsl::sqrt(1.0 - cosC_ * cosC_); - math::sincos_accumulator angle_adder = math::sincos_accumulator::create(cos_vertices[0], sin_vertices[0]); + math::sincos_accumulator angle_adder = math::sincos_accumulator::create(cosA, sinA); angle_adder.addAngle(cosB_, sinB_); angle_adder.addAngle(cosC_, sinC_); const scalar_type subTriSolidAngleRatio = (angle_adder.getSumofArccos() - numbers::pi) * 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 cosBC_s = (cosA + cosB_ * cosC_) / (sinB_ * sinC_); const scalar_type v = (1.0 - cosAngleAlongBC_s) / (1.0 - (cosBC_s < bit_cast(0x3f7fffff) ? cosBC_s : cos_c)); return vector2_type(u,v); @@ -107,18 +107,16 @@ struct SphericalTriangle vector2_type generateInverse(NBL_REF_ARG(scalar_type) pdf, const vector3_type L) { - const scalar_type cos_a = tri.cos_sides[0]; const scalar_type cos_c = tri.cos_sides[2]; - const scalar_type csc_b = tri.csc_sides[1]; const scalar_type csc_c = tri.csc_sides[2]; - vector3_type cos_vertices, sin_vertices; - - const scalar_type solidAngle = tri.solidAngle(cos_vertices, sin_vertices); - return generateInverse(pdf, solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, L); + return generateInverse(pdf, cos_c, csc_c, L); } shapes::SphericalTriangle tri; + scalar_type solidAngle; + scalar_type cosA; + scalar_type sinA; }; } From 795b9fa56048133704d9ee65ebf8a4554eb6ac48 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 22 Jan 2026 12:24:57 +0700 Subject: [PATCH 13/69] spherical rectangle stores origin, extent, basis and takes observer instead --- .../hlsl/sampling/spherical_rectangle.hlsl | 23 +++++++++-------- .../hlsl/shapes/spherical_rectangle.hlsl | 25 ++++++++++--------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl b/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl index f9e3d2f7ae..bcd4f19b7a 100644 --- a/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl +++ b/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl @@ -32,10 +32,11 @@ struct SphericalRectangle return retval; } - vector2_type generate(const vector2_type rectangleExtents, const vector2_type uv, NBL_REF_ARG(scalar_type) S) + vector2_type generate(const vector3_type observer, const 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 / hlsl::sqrt(hlsl::promote(rect.r0.z * rect.r0.z) + denorm_n_z * denorm_n_z); + vector3_type r0 = hlsl::mul(rect.basis, rect.origin - observer); + const vector4_type denorm_n_z = vector4_type(-r0.y, r0.x + rect.extents.x, r0.y + rect.extents.y, -r0.x); + const vector4_type n_z = denorm_n_z / hlsl::sqrt(hlsl::promote(r0.z * 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], @@ -57,27 +58,27 @@ struct SphericalRectangle const scalar_type CLAMP_EPS = 1e-5; - // flip z axis if rect.r0.z > 0 - rect.r0.z = ieee754::flipSignIfRHSNegative(rect.r0.z, -rect.r0.z); - vector3_type r1 = rect.r0 + vector3_type(rectangleExtents.x, rectangleExtents.y, 0); + // flip z axis if r0.z > 0 + r0.z = ieee754::flipSignIfRHSNegative(r0.z, -r0.z); + vector3_type r1 = r0 + vector3_type(rect.extents.x, rect.extents.y, 0); const scalar_type au = uv.x * S + k; 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 = ieee754::flipSignIfRHSNegative(scalar_type(1.0) / hlsl::sqrt(cu_2), fu); - scalar_type xu = -(cu * rect.r0.z) / hlsl::sqrt(scalar_type(1.0) - 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; + scalar_type xu = -(cu * r0.z) / hlsl::sqrt(scalar_type(1.0) - cu * cu); + xu = hlsl::clamp(xu, r0.x, r1.x); // avoid Infs + const scalar_type d_2 = xu * xu + r0.z * r0.z; const scalar_type d = hlsl::sqrt(d_2); - const scalar_type h0 = rect.r0.y / hlsl::sqrt(d_2 + rect.r0.y * rect.r0.y); + const scalar_type h0 = r0.y / hlsl::sqrt(d_2 + r0.y * 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); const scalar_type hv2 = hv * hv; const scalar_type yv = hlsl::mix(r1.y, (hv * d) / hlsl::sqrt(scalar_type(1.0) - hv2), hv2 < scalar_type(1.0) - CLAMP_EPS); - return vector2_type((xu - rect.r0.x) / rectangleExtents.x, (yv - rect.r0.y) / rectangleExtents.y); + return vector2_type((xu - r0.x) / rect.extents.x, (yv - r0.y) / rect.extents.y); } shapes::SphericalRectangle rect; diff --git a/include/nbl/builtin/hlsl/shapes/spherical_rectangle.hlsl b/include/nbl/builtin/hlsl/shapes/spherical_rectangle.hlsl index 587e221996..60c2729f21 100644 --- a/include/nbl/builtin/hlsl/shapes/spherical_rectangle.hlsl +++ b/include/nbl/builtin/hlsl/shapes/spherical_rectangle.hlsl @@ -21,28 +21,27 @@ template struct SphericalRectangle { using scalar_type = Scalar; + using vector2_type = vector; using vector3_type = vector; using matrix3x3_type = matrix; - static SphericalRectangle create(const vector3_type observer, const vector3_type rectangleOrigin, const matrix3x3_type basis) + static SphericalRectangle create(const vector3_type rectangleOrigin, const vector3_type right, const vector3_type up) { SphericalRectangle retval; - retval.r0 = nbl::hlsl::mul(basis, rectangleOrigin - observer); + retval.origin = rectangleOrigin; + retval.extents = vector2_type(hlsl::length(right), hlsl::length(up)); + retval.basis[0] = right / retval.extents[0]; + retval.basis[1] = up / retval.extents[1]; + retval.basis[2] = hlsl::normalize(hlsl::cross(retval.basis[0], retval.basis[1])); return retval; } - static SphericalRectangle create(const vector3_type observer, const vector3_type rectangleOrigin, const vector3_type T, vector3_type B, const vector3_type N) + scalar_type solidAngle(const vector3_type observer) { - SphericalRectangle retval; - matrix3x3_type TBN = nbl::hlsl::transpose(matrix3x3_type(T, B, N)); - retval.r0 = nbl::hlsl::mul(TBN, rectangleOrigin - observer); - return retval; - } + const vector3_type r0 = hlsl::mul(basis, origin - observer); - scalar_type solidAngle(const vector rectangleExtents) - { using vector4_type = vector; - const vector4_type denorm_n_z = vector4_type(-r0.y, r0.x + rectangleExtents.x, r0.y + rectangleExtents.y, -r0.x); + const vector4_type denorm_n_z = vector4_type(-r0.y, r0.x + extents.x, r0.y + extents.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 = vector4_type( -n_z[0] * n_z[1], @@ -57,7 +56,9 @@ struct SphericalRectangle return angle_adder.getSumofArccos() - scalar_type(2.0) * numbers::pi; } - vector3_type r0; + vector3_type origin; + vector2_type extents; + matrix3x3_type basis; }; } From f9b949eaf1657d1b6c04adef1fa7bc93ab82561a Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 22 Jan 2026 14:28:42 +0700 Subject: [PATCH 14/69] added compressed spherical rectangle, comments for info of implementation --- .../hlsl/shapes/spherical_rectangle.hlsl | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/include/nbl/builtin/hlsl/shapes/spherical_rectangle.hlsl b/include/nbl/builtin/hlsl/shapes/spherical_rectangle.hlsl index 60c2729f21..5e23774640 100644 --- a/include/nbl/builtin/hlsl/shapes/spherical_rectangle.hlsl +++ b/include/nbl/builtin/hlsl/shapes/spherical_rectangle.hlsl @@ -17,6 +17,40 @@ namespace hlsl namespace shapes { +// What are we likely to do with a Spherical Rectangle? +// 1) Initialize it multiple times from different observers +// 2) Sample it repeatedly + +// How are we likely to get a spherical rect? +// 1) from OBB matrix (with a model space z-axis scale thats irrelevant - but should be forced to 1.f to not mess with distance) +// 2) in a compressed form + +// So, to bring multiple world-space observers into Spherical Rectangle's own space, we need the basis matrix. +// The matrix should be a matrix where the last column is the translation, a 3x3 matrix with a pre-transform translation (worldSpace rectangle origin to be subtracted). + +// You can compute it from an OBB matrix (as given by/to imguizmo to position a [0,1]^2 rectangle mesh where Z+ is the front face. + +// Now, can apply translation: +// 1) post-rotation so a it automatically gets added during a affine pseudo-mul of a 3x4, so pseudo_mul(basis,observer) +// 2) pre-rotation so you keep a worldspace rectangle origin and subtract it before, e.g. mul(basis,worldSpaceOrigin-observer) - this one is possibly better due to next point + +// So we need to store: +// 1) first two COLUMNS of the original OBB matrix (rows of 3x3 basis matrix with the scale still in there), thats kinda your right and up vectors +// 2) pre-rotation translation / the world-space translation of the rectangle +// Theoretically you could get away with not storing one of the up vector components but its not always the same component you can reconstruct (plane orthogonal to up isn't always the XY plane). +// Could compress up vector as a rotation of the default vector orthogonal to right as given by the frisvad-basis function around the right vector plus a scale +// but that becomes a very expensive decompression step involving a quaternion with uniform scale. + +template +struct CompressedSphericalRectangle +{ + using vector3_type = vector; + + vector3_type origin; + vector3_type right; + vector3_type up; +}; + template struct SphericalRectangle { @@ -36,6 +70,11 @@ struct SphericalRectangle return retval; } + static SphericalRectangle create(NBL_CONST_REF_ARG(CompressedSphericalRectangle) compressed) + { + return create(compressed.origin, compressed.right, compressed.up); + } + scalar_type solidAngle(const vector3_type observer) { const vector3_type r0 = hlsl::mul(basis, origin - observer); From 140c5c65943def635fce4871bf96c4f389fc72a9 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 22 Jan 2026 16:22:39 +0700 Subject: [PATCH 15/69] removed unused code --- .../transformation_matrix_utils.hlsl | 235 ------------------ src/nbl/builtin/CMakeLists.txt | 1 - 2 files changed, 236 deletions(-) delete mode 100644 include/nbl/builtin/hlsl/matrix_utils/transformation_matrix_utils.hlsl diff --git a/include/nbl/builtin/hlsl/matrix_utils/transformation_matrix_utils.hlsl b/include/nbl/builtin/hlsl/matrix_utils/transformation_matrix_utils.hlsl deleted file mode 100644 index 1ad16dc28d..0000000000 --- a/include/nbl/builtin/hlsl/matrix_utils/transformation_matrix_utils.hlsl +++ /dev/null @@ -1,235 +0,0 @@ -#ifndef _NBL_BUILTIN_HLSL_TRANSFORMATION_MATRIX_UTILS_INCLUDED_ -#define _NBL_BUILTIN_HLSL_TRANSFORMATION_MATRIX_UTILS_INCLUDED_ - -#include - -namespace nbl -{ -namespace hlsl -{ -//TODO: stolen from cameraz branch, don't have epsilonEqual here, maybe uncomment when merging from imguizmo-lights branch -//// TODO: -> move somewhere else and nbl:: to implement it -//template -//bool isOrthoBase(const T& x, const T& y, const T& z, const E epsilon = 1e-6) -//{ -// auto isNormalized = [](const auto& v, const auto& epsilon) -> bool -// { -// return glm::epsilonEqual(glm::length(v), 1.0, epsilon); -// }; -// -// auto isOrthogonal = [](const auto& a, const auto& b, const auto& epsilon) -> bool -// { -// return glm::epsilonEqual(glm::dot(a, b), 0.0, epsilon); -// }; -// -// return isNormalized(x, epsilon) && isNormalized(y, epsilon) && isNormalized(z, epsilon) && -// isOrthogonal(x, y, epsilon) && isOrthogonal(x, z, epsilon) && isOrthogonal(y, z, epsilon); -//} -//// <- - -template -matrix getMatrix3x4As4x4(const matrix& mat) -{ - matrix output; - for (int i = 0; i < 3; ++i) - output[i] = mat[i]; - output[3] = float32_t4(0.0f, 0.0f, 0.0f, 1.0f); - - return output; -} - -template -matrix getMatrix3x3As4x4(const matrix& mat) -{ - matrix output; - for (int i = 0; i < 3; ++i) - output[i] = float32_t4(mat[i], 1.0f); - output[3] = float32_t4(0.0f, 0.0f, 0.0f, 1.0f); - - return output; -} - -template -inline vector getCastedVector(const vector& in) -{ - vector out; - - for (int i = 0; i < N; ++i) - out[i] = (Tout)(in[i]); - - return out; -} - -template -inline matrix getCastedMatrix(const matrix& in) -{ - matrix out; - - for (int i = 0; i < N; ++i) - out[i] = getCastedVector(in[i]); - - return out; -} - -// TODO: use portable_float when merged -//! multiplies matrices a and b, 3x4 matrices are treated as 4x4 matrices with 4th row set to (0, 0, 0 ,1) -template -inline matrix concatenateBFollowedByA(const matrix& a, const matrix& b) -{ - const auto a4x4 = getMatrix3x4As4x4(a); - const auto b4x4 = getMatrix3x4As4x4(b); - return matrix(mul(a4x4, b4x4)); -} - -// /Arek: glm:: for normalize till dot product is fixed (ambiguity with glm namespace + linker issues) - -template -inline matrix buildCameraLookAtMatrixLH( - const vector& position, - const vector& target, - const vector& upVector) -{ - const vector zaxis = glm::normalize(target - position); - const vector xaxis = glm::normalize(hlsl::cross(upVector, zaxis)); - const vector yaxis = hlsl::cross(zaxis, xaxis); - - matrix r; - r[0] = vector(xaxis, -hlsl::dot(xaxis, position)); - r[1] = vector(yaxis, -hlsl::dot(yaxis, position)); - r[2] = vector(zaxis, -hlsl::dot(zaxis, position)); - - return r; -} - -template -inline matrix buildCameraLookAtMatrixRH( - const vector& position, - const vector& target, - const vector& upVector) -{ - const vector zaxis = glm::normalize(position - target); - const vector xaxis = glm::normalize(hlsl::cross(upVector, zaxis)); - const vector yaxis = hlsl::cross(zaxis, xaxis); - - matrix r; - r[0] = vector(xaxis, -hlsl::dot(xaxis, position)); - r[1] = vector(yaxis, -hlsl::dot(yaxis, position)); - r[2] = vector(zaxis, -hlsl::dot(zaxis, position)); - - return r; -} - -// TODO: test, check if there is better implementation -// TODO: move quaternion to nbl::hlsl -// TODO: why NBL_REF_ARG(MatType) doesn't work????? - -//! Replaces curent rocation and scale by rotation represented by quaternion `quat`, leaves 4th row and 4th colum unchanged -template -inline void setRotation(matrix& outMat, NBL_CONST_REF_ARG(core::quaternion) quat) -{ - static_assert(N == 3 || N == 4); - - outMat[0] = vector( - 1 - 2 * (quat.y * quat.y + quat.z * quat.z), - 2 * (quat.x * quat.y - quat.z * quat.w), - 2 * (quat.x * quat.z + quat.y * quat.w), - outMat[0][3] - ); - - outMat[1] = vector( - 2 * (quat.x * quat.y + quat.z * quat.w), - 1 - 2 * (quat.x * quat.x + quat.z * quat.z), - 2 * (quat.y * quat.z - quat.x * quat.w), - outMat[1][3] - ); - - outMat[2] = vector( - 2 * (quat.x * quat.z - quat.y * quat.w), - 2 * (quat.y * quat.z + quat.x * quat.w), - 1 - 2 * (quat.x * quat.x + quat.y * quat.y), - outMat[2][3] - ); -} - -template -inline void setTranslation(matrix& outMat, NBL_CONST_REF_ARG(vector) translation) -{ - static_assert(N == 3 || N == 4); - - outMat[0].w = translation.x; - outMat[1].w = translation.y; - outMat[2].w = translation.z; -} - - -template -inline matrix buildProjectionMatrixPerspectiveFovRH(float fieldOfViewRadians, float aspectRatio, float zNear, float zFar) -{ - const float h = core::reciprocal(tanf(fieldOfViewRadians * 0.5f)); - _NBL_DEBUG_BREAK_IF(aspectRatio == 0.f); //division by zero - const float w = h / aspectRatio; - - _NBL_DEBUG_BREAK_IF(zNear == zFar); //division by zero - - matrix m; - m[0] = vector(w, 0.f, 0.f, 0.f); - m[1] = vector(0.f, -h, 0.f, 0.f); - m[2] = vector(0.f, 0.f, -zFar / (zFar - zNear), -zNear * zFar / (zFar - zNear)); - m[3] = vector(0.f, 0.f, -1.f, 0.f); - - return m; -} -template -inline matrix buildProjectionMatrixPerspectiveFovLH(float fieldOfViewRadians, float aspectRatio, float zNear, float zFar) -{ - const float h = core::reciprocal(tanf(fieldOfViewRadians * 0.5f)); - _NBL_DEBUG_BREAK_IF(aspectRatio == 0.f); //division by zero - const float w = h / aspectRatio; - - _NBL_DEBUG_BREAK_IF(zNear == zFar); //division by zero - - matrix m; - m[0] = vector(w, 0.f, 0.f, 0.f); - m[1] = vector(0.f, -h, 0.f, 0.f); - m[2] = vector(0.f, 0.f, zFar / (zFar - zNear), -zNear * zFar / (zFar - zNear)); - m[3] = vector(0.f, 0.f, 1.f, 0.f); - - return m; -} - -template -inline matrix buildProjectionMatrixOrthoRH(float widthOfViewVolume, float heightOfViewVolume, float zNear, float zFar) -{ - _NBL_DEBUG_BREAK_IF(widthOfViewVolume == 0.f); //division by zero - _NBL_DEBUG_BREAK_IF(heightOfViewVolume == 0.f); //division by zero - _NBL_DEBUG_BREAK_IF(zNear == zFar); //division by zero - - matrix m; - m[0] = vector(2.f / widthOfViewVolume, 0.f, 0.f, 0.f); - m[1] = vector(0.f, -2.f / heightOfViewVolume, 0.f, 0.f); - m[2] = vector(0.f, 0.f, -1.f / (zFar - zNear), -zNear / (zFar - zNear)); - m[3] = vector(0.f, 0.f, 0.f, 1.f); - - return m; -} - -template -inline matrix buildProjectionMatrixOrthoLH(float widthOfViewVolume, float heightOfViewVolume, float zNear, float zFar) -{ - _NBL_DEBUG_BREAK_IF(widthOfViewVolume == 0.f); //division by zero - _NBL_DEBUG_BREAK_IF(heightOfViewVolume == 0.f); //division by zero - _NBL_DEBUG_BREAK_IF(zNear == zFar); //division by zero - - matrix m; - m[0] = vector(2.f / widthOfViewVolume, 0.f, 0.f, 0.f); - m[1] = vector(0.f, -2.f / heightOfViewVolume, 0.f, 0.f); - m[2] = vector(0.f, 0.f, 1.f / (zFar - zNear), -zNear / (zFar - zNear)); - m[3] = vector(0.f, 0.f, 0.f, 1.f); - - return m; -} - -} -} - -#endif \ No newline at end of file diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index 9a439987b5..ac0903589a 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -222,7 +222,6 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/format.hlsl") #linear algebra LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/linalg/fast_affine.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/linalg/transform.hlsl") -LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/linalg/matrix_utils/transformation_matrix_utils.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/linalg/matrix_runtime_traits.hlsl") # TODO: rename `equations` to `polynomials` probably LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/functions.hlsl") From 2ce08d92d186c83f5c92e57dc25300f2be1dbe60 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 23 Jan 2026 14:31:28 +0700 Subject: [PATCH 16/69] path tracer (unidirectional) and concepts moved from example --- .../builtin/hlsl/path_tracing/concepts.hlsl | 205 +++++++++++++ .../path_tracing/default_accumulator.hlsl | 43 +++ .../hlsl/path_tracing/unidirectional.hlsl | 290 ++++++++++++++++++ src/nbl/builtin/CMakeLists.txt | 4 + 4 files changed, 542 insertions(+) create mode 100644 include/nbl/builtin/hlsl/path_tracing/concepts.hlsl create mode 100644 include/nbl/builtin/hlsl/path_tracing/default_accumulator.hlsl create mode 100644 include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl new file mode 100644 index 0000000000..b3dc2dd055 --- /dev/null +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -0,0 +1,205 @@ +#ifndef _NBL_BUILTIN_HLSL_PATH_TRACING_CONCEPTS_INCLUDED_ +#define _NBL_BUILTIN_HLSL_PATH_TRACING_CONCEPTS_INCLUDED_ + +#include + +namespace nbl +{ +namespace hlsl +{ +namespace path_tracing +{ +namespace concepts +{ + +#define NBL_CONCEPT_NAME RandGenerator +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (rand, T) +NBL_CONCEPT_BEGIN(1) +#define rand NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::rng_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::return_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rand()), ::nbl::hlsl::is_same_v, typename T::return_type)) +); +#undef rand +#include + +#define NBL_CONCEPT_NAME RayGenerator +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (raygen, T) +#define NBL_CONCEPT_PARAM_1 (randVec, typename T::vector3_type) +NBL_CONCEPT_BEGIN(2) +#define raygen NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define randVec NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::ray_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((raygen.generate(randVec)), ::nbl::hlsl::is_same_v, typename T::ray_type)) +); +#undef randVec +#undef raygen +#include + +#define NBL_CONCEPT_NAME Intersector +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (intersect, T) +#define NBL_CONCEPT_PARAM_1 (ray, typename T::ray_type) +#define NBL_CONCEPT_PARAM_2 (scene, typename T::scene_type) +NBL_CONCEPT_BEGIN(3) +#define intersect NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define ray NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define scene NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scene_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::ray_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::id_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceRay(ray, scene)), ::nbl::hlsl::is_same_v, typename T::id_type)) +); +#undef scene +#undef ray +#undef intersect +#include + +#define NBL_CONCEPT_NAME MaterialSystem +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (matsys, T) +#define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) +#define NBL_CONCEPT_PARAM_2 (matid, uint32_t) +#define NBL_CONCEPT_PARAM_3 (aniso_inter, typename T::anisotropic_interaction_type) +#define NBL_CONCEPT_PARAM_4 (iso_inter, typename T::isotropic_interaction_type) +#define NBL_CONCEPT_PARAM_5 (aniso_cache, typename T::anisocache_type) +#define NBL_CONCEPT_PARAM_6 (iso_cache, typename T::isocache_type) +#define NBL_CONCEPT_PARAM_7 (params, typename T::create_params_t) +#define NBL_CONCEPT_PARAM_8 (u, typename T::vector3_type) +NBL_CONCEPT_BEGIN(9) +#define matsys NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define matid NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define aniso_inter NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define iso_inter NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +#define aniso_cache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 +#define iso_cache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 +#define params NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_7 +#define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_8 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::measure_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::anisotropic_interaction_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::anisocache_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::isocache_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::create_params_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.eval(matid, params, _sample, iso_inter, iso_cache)), ::nbl::hlsl::is_same_v, typename T::measure_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.generate(matid, params, aniso_inter, u, aniso_cache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.quotient_and_pdf(matid, params, _sample, iso_inter, iso_cache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) +); +#undef u +#undef params +#undef iso_cache +#undef aniso_cache +#undef iso_inter +#undef aniso_inter +#undef matid +#undef _sample +#undef matsys +#include + +#define NBL_CONCEPT_NAME NextEventEstimator +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (nee, T) +#define NBL_CONCEPT_PARAM_1 (ray, typename T::ray_type) +#define NBL_CONCEPT_PARAM_2 (scene, typename T::scene_type) +#define NBL_CONCEPT_PARAM_3 (id, uint32_t) +#define NBL_CONCEPT_PARAM_4 (pdf, typename T::scalar_type) +#define NBL_CONCEPT_PARAM_5 (quo_pdf, typename T::quotient_pdf_type) +#define NBL_CONCEPT_PARAM_6 (v, typename T::vector3_type) +#define NBL_CONCEPT_PARAM_7 (interaction, typename T::interaction_type) +#define NBL_CONCEPT_PARAM_8 (b, bool) +NBL_CONCEPT_BEGIN(9) +#define nee NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define ray NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define scene NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define id NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define pdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +#define quo_pdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 +#define v NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 +#define interaction NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_7 +#define b NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_8 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::scene_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::light_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::ray_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::spectral_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::interaction_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferredEvalAndPdf(pdf, scene, id, ray)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.generate_and_quotient_and_pdf(quo_pdf, pdf, scene, id, v, interaction, b, v, id)), ::nbl::hlsl::is_same_v, typename T::sample_type)) +); +#undef b +#undef interaction +#undef v +#undef quo_pdf +#undef pdf +#undef id +#undef scene +#undef ray +#undef nee +#include + +#define NBL_CONCEPT_NAME Accumulator +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (acc, T) +#define NBL_CONCEPT_PARAM_1 (sampleCount, uint32_t) +#define NBL_CONCEPT_PARAM_2 (_sample, typename T::input_sample_type) +NBL_CONCEPT_BEGIN(3) +#define acc NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define sampleCount NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::input_sample_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((acc.addSample(sampleCount, _sample)), ::nbl::hlsl::is_same_v, void)) +); +#undef _sample +#undef sampleCount +#undef acc +#include + +#define NBL_CONCEPT_NAME Scene +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (scene, T) +#define NBL_CONCEPT_PARAM_1 (intersectP, typename T::vector3_type) +#define NBL_CONCEPT_PARAM_2 (id, typename T::id_type) +NBL_CONCEPT_BEGIN(3) +#define scene NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define intersectP NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define id NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::id_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((scene.getBsdfLightIDs(id)), ::nbl::hlsl::is_same_v, uint32_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((scene.getNormal(id, intersectP)), ::nbl::hlsl::is_same_v, typename T::vector3_type)) +); +#undef id +#undef intersectP +#undef scene +#include + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/path_tracing/default_accumulator.hlsl b/include/nbl/builtin/hlsl/path_tracing/default_accumulator.hlsl new file mode 100644 index 0000000000..d9df50e023 --- /dev/null +++ b/include/nbl/builtin/hlsl/path_tracing/default_accumulator.hlsl @@ -0,0 +1,43 @@ +#ifndef _NBL_BUILTIN_HLSL_DEFAULT_ACCUMULATOR_INCLUDED_ +#define _NBL_BUILTIN_HLSL_DEFAULT_ACCUMULATOR_INCLUDED_ + +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace path_tracing +{ + +template) +struct DefaultAccumulator +{ + using input_sample_type = OutputTypeVec; + using output_storage_type = OutputTypeVec; + using this_t = DefaultAccumulator; + using scalar_type = typename vector_traits::scalar_type; + + static this_t create() + { + this_t retval; + retval.accumulation = promote(0.0f); + + return retval; + } + + void addSample(uint32_t sampleCount, input_sample_type _sample) + { + scalar_type rcpSampleSize = 1.0 / (sampleCount); + accumulation += (_sample - accumulation) * rcpSampleSize; + } + + output_storage_type accumulation; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl new file mode 100644 index 0000000000..8e9e207005 --- /dev/null +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -0,0 +1,290 @@ +#ifndef _NBL_BUILTIN_HLSL_PATH_TRACING_UNIDIRECTIONAL_INCLUDED_ +#define _NBL_BUILTIN_HLSL_PATH_TRACING_UNIDIRECTIONAL_INCLUDED_ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace path_tracing +{ + +// TODO: unsure what to do with this, awaiting refactor or turning into concept +template +struct Tolerance +{ + NBL_CONSTEXPR_STATIC_INLINE T INTERSECTION_ERROR_BOUND_LOG2 = -8.0; + + static T __common(uint32_t depth) + { + T depthRcp = 1.0 / T(depth); + return INTERSECTION_ERROR_BOUND_LOG2; + } + + static T getStart(uint32_t depth) + { + return nbl::hlsl::exp2(__common(depth)); + } + + static T getEnd(uint32_t depth) + { + return 1.0 - nbl::hlsl::exp2(__common(depth) + 1.0); + } +}; + +template && concepts::RayGenerator && + concepts::Intersector && concepts::MaterialSystem && + concepts::NextEventEstimator && concepts::Accumulator && + concepts::Scene) +struct Unidirectional +{ + using this_t = Unidirectional; + using randgen_type = RandGen; + using raygen_type = RayGen; + using intersector_type = Intersector; + using material_system_type = MaterialSystem; + using nee_type = NextEventEstimator; + using scene_type = Scene; + + using scalar_type = typename MaterialSystem::scalar_type; + using vector3_type = vector; + using monochrome_type = vector; + using measure_type = typename MaterialSystem::measure_type; + using output_storage_type = typename Accumulator::output_storage_type; // ? + using sample_type = typename NextEventEstimator::sample_type; + using ray_dir_info_type = typename sample_type::ray_dir_info_type; + using ray_type = typename RayGen::ray_type; + using id_type = typename Intersector::id_type; + using light_type = typename NextEventEstimator::light_type; + using bxdfnode_type = typename MaterialSystem::bxdfnode_type; + using anisotropic_interaction_type = typename MaterialSystem::anisotropic_interaction_type; + using isotropic_interaction_type = typename anisotropic_interaction_type::isotropic_interaction_type; + using anisocache_type = typename MaterialSystem::anisocache_type; + using isocache_type = typename anisocache_type::isocache_type; + using quotient_pdf_type = typename NextEventEstimator::quotient_pdf_type; + + using diffuse_op_type = typename MaterialSystem::diffuse_op_type; + using conductor_op_type = typename MaterialSystem::conductor_op_type; + using dielectric_op_type = typename MaterialSystem::dielectric_op_type; + + vector3_type rand3d(uint32_t protoDimension, uint32_t _sample, uint32_t i) + { + using sequence_type = sampling::QuantizedSequence; + uint32_t address = glsl::bitfieldInsert(protoDimension, _sample, MAX_DEPTH_LOG2, MAX_SAMPLES_LOG2); + sequence_type tmpSeq = vk::RawBufferLoad(pSampleBuffer + (address + i) * sizeof(sequence_type)); + // return tmpSeq.template decode(randGen()); + return sampling::decode(tmpSeq,randGen()); + } + + scalar_type getLuma(NBL_CONST_REF_ARG(vector3_type) col) + { + return hlsl::dot(colorspace::scRGBtoXYZ[1], col); + } + + // TODO: probably will only work with isotropic surfaces, need to do aniso + bool closestHitProgram(uint32_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(scene_type) scene) + { + const id_type objectID = ray.objectID; + const vector3_type intersection = ray.origin + ray.direction * ray.intersectionT; + + uint32_t bsdfLightIDs = scene.getBsdfLightIDs(objectID); + vector3_type N = scene.getNormal(objectID, intersection); + N = nbl::hlsl::normalize(N); + ray_dir_info_type V; + V.setDirection(-ray.direction); + isotropic_interaction_type iso_interaction = isotropic_interaction_type::create(V, N); + iso_interaction.luminosityContributionHint = colorspace::scRGBtoXYZ[1]; + anisotropic_interaction_type interaction = anisotropic_interaction_type::create(iso_interaction); + + vector3_type throughput = ray.payload.throughput; + + // emissive + const uint32_t lightID = glsl::bitfieldExtract(bsdfLightIDs, 16, 16); + if (lightID != light_type::INVALID_ID) + { + float _pdf; + ray.payload.accumulation += nee.deferredEvalAndPdf(_pdf, scene, lightID, ray) * throughput / (1.0 + _pdf * _pdf * ray.payload.otherTechniqueHeuristic); + } + + const uint32_t bsdfID = glsl::bitfieldExtract(bsdfLightIDs, 0, 16); + if (bsdfID == bxdfnode_type::INVALID_ID) + return false; + + bxdfnode_type bxdf = materialSystem.bxdfs[bsdfID]; + + // TODO: ifdef kill diffuse specular paths + + const bool isBSDF = material_system_type::isBSDF(bxdf.materialType); + + vector3_type eps0 = rand3d(depth, _sample, 0u); + vector3_type eps1 = rand3d(depth, _sample, 1u); + + // thresholds + const scalar_type bxdfPdfThreshold = 0.0001; + const scalar_type lumaContributionThreshold = getLuma(colorspace::eotf::sRGB((vector3_type)1.0 / 255.0)); // OETF smallest perceptible value + const vector3_type throughputCIE_Y = colorspace::sRGBtoXYZ[1] * throughput; // TODO: this only works if spectral_type is dim 3 + const measure_type eta = bxdf.params.ior1 / bxdf.params.ior0; + const scalar_type monochromeEta = hlsl::dot(throughputCIE_Y, eta) / (throughputCIE_Y.r + throughputCIE_Y.g + throughputCIE_Y.b); // TODO: imaginary eta? + + // sample lights + const scalar_type neeProbability = 1.0; // BSDFNode_getNEEProb(bsdf); + scalar_type rcpChoiceProb; + sampling::PartitionRandVariable partitionRandVariable; + partitionRandVariable.leftProb = neeProbability; + if (!partitionRandVariable(eps0.z, rcpChoiceProb) && depth < 2u) + { + uint32_t randLightID = uint32_t(float32_t(randGen.rng()) / numeric_limits::max) * nee.lightCount; + quotient_pdf_type neeContrib_pdf; + scalar_type t; + sample_type nee_sample = nee.generate_and_quotient_and_pdf( + neeContrib_pdf, t, + scene, randLightID, intersection, interaction, + isBSDF, eps0, depth + ); + + // We don't allow non watertight transmitters in this renderer + bool validPath = nee_sample.getNdotL() > numeric_limits::min && nee_sample.isValid(); + // but if we allowed non-watertight transmitters (single water surface), it would make sense just to apply this line by itself + bxdf::fresnel::OrientedEtas orientedEta = bxdf::fresnel::OrientedEtas::create(interaction.getNdotV(), hlsl::promote(monochromeEta)); + anisocache_type _cache = anisocache_type::template create(interaction, nee_sample, orientedEta); + validPath = validPath && _cache.getAbsNdotH() >= 0.0; + bxdf.params.eta = monochromeEta; + + if (neeContrib_pdf.pdf < numeric_limits::max) + { + if (nbl::hlsl::any(hlsl::isnan(nee_sample.getL().getDirection()))) + ray.payload.accumulation += vector3_type(1000.f, 0.f, 0.f); + else if (nbl::hlsl::all((vector3_type)69.f == nee_sample.getL().getDirection())) + ray.payload.accumulation += vector3_type(0.f, 1000.f, 0.f); + else if (validPath) + { + // example only uses isotropic bxdfs + quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(bxdf.materialType, bxdf.params, nee_sample, interaction.isotropic, _cache.iso_cache); + neeContrib_pdf.quotient *= bxdf.albedo * throughput * bsdf_quotient_pdf.quotient; + const scalar_type otherGenOverChoice = bsdf_quotient_pdf.pdf * rcpChoiceProb; + const scalar_type otherGenOverLightAndChoice = otherGenOverChoice / bsdf_quotient_pdf.pdf; + neeContrib_pdf.quotient *= otherGenOverChoice / (1.f + otherGenOverLightAndChoice * otherGenOverLightAndChoice); // balance heuristic + + // TODO: ifdef NEE only + // neeContrib_pdf.quotient *= otherGenOverChoice; + + ray_type nee_ray; + nee_ray.origin = intersection + nee_sample.getL().getDirection() * t * Tolerance::getStart(depth); + nee_ray.direction = nee_sample.getL().getDirection(); + nee_ray.intersectionT = t; + if (bsdf_quotient_pdf.pdf < numeric_limits::max && getLuma(neeContrib_pdf.quotient) > lumaContributionThreshold && intersector_type::traceRay(nee_ray, scene).id == -1) + ray.payload.accumulation += neeContrib_pdf.quotient; + } + } + } + + // return false; // NEE only + + // sample BSDF + scalar_type bxdfPdf; + vector3_type bxdfSample; + { + anisocache_type _cache; + sample_type bsdf_sample = materialSystem.generate(bxdf.materialType, bxdf.params, interaction, eps1, _cache); + + if (!bsdf_sample.isValid()) + return false; + + // example only uses isotropic bxdfs + // the value of the bsdf divided by the probability of the sample being generated + quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(bxdf.materialType, bxdf.params, bsdf_sample, interaction.isotropic, _cache.iso_cache); + throughput *= bxdf.albedo * bsdf_quotient_pdf.quotient; + bxdfPdf = bsdf_quotient_pdf.pdf; + bxdfSample = bsdf_sample.getL().getDirection(); + } + + // additional threshold + const float lumaThroughputThreshold = lumaContributionThreshold; + if (bxdfPdf > bxdfPdfThreshold && getLuma(throughput) > lumaThroughputThreshold) + { + ray.payload.throughput = throughput; + scalar_type otherTechniqueHeuristic = neeProbability / bxdfPdf; // numerically stable, don't touch + ray.payload.otherTechniqueHeuristic = otherTechniqueHeuristic * otherTechniqueHeuristic; + + // trace new ray + ray.origin = intersection + bxdfSample * (1.0/*kSceneSize*/) * Tolerance::getStart(depth); + ray.direction = bxdfSample; + if (nee_type::IsPolygonMethodProjectedSolidAngle) + { + ray.normalAtOrigin = interaction.getN(); + ray.wasBSDFAtOrigin = isBSDF; + } + return true; + } + + return false; + } + + void missProgram(NBL_REF_ARG(ray_type) ray) + { + vector3_type finalContribution = ray.payload.throughput; + // #ifdef USE_ENVMAP + // vec2 uv = SampleSphericalMap(_immutable.direction); + // finalContribution *= textureLod(envMap, uv, 0.0).rgb; + // #else + const vector3_type kConstantEnvLightRadiance = vector3_type(0.15, 0.21, 0.3); // TODO: match spectral_type + finalContribution *= kConstantEnvLightRadiance; + ray.payload.accumulation += finalContribution; + // #endif + } + + // Li + void sampleMeasure(uint32_t sampleIndex, uint32_t maxDepth, NBL_CONST_REF_ARG(scene_type) scene, NBL_REF_ARG(Accumulator) accumulator) + { + //scalar_type meanLumaSq = 0.0; + vector3_type uvw = rand3d(0u, sampleIndex, 0u); + ray_type ray = rayGen.generate(uvw); + + // bounces + bool hit = true; + bool rayAlive = true; + for (int d = 1; (d <= maxDepth) && hit && rayAlive; d += 2) + { + ray.intersectionT = numeric_limits::max; + ray.objectID = intersector_type::traceRay(ray, scene); + + hit = ray.objectID.id != -1; + if (hit) + rayAlive = closestHitProgram(1, sampleIndex, ray, scene); + } + if (!hit) + missProgram(ray); + + const uint32_t sampleCount = sampleIndex + 1; + accumulator.addSample(sampleCount, ray.payload.accumulation); + + // TODO: visualize high variance + + // TODO: russian roulette early exit? + } + + NBL_CONSTEXPR_STATIC_INLINE uint32_t MAX_DEPTH_LOG2 = 4u; + NBL_CONSTEXPR_STATIC_INLINE uint32_t MAX_SAMPLES_LOG2 = 10u; + + randgen_type randGen; + raygen_type rayGen; + material_system_type materialSystem; + nee_type nee; + + uint64_t pSampleBuffer; +}; + +} +} +} + +#endif diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index ac0903589a..46ce7f271b 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -308,6 +308,10 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/oren_nayar. 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") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/delta_distribution.hlsl") +#path tracing +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/path_tracing/concepts.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/path_tracing/unidirectional.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/path_tracing/default_accumulator.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 1a7b1859a41dd2a3b0271d7218b91e2e2d7b02a8 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 23 Jan 2026 14:34:55 +0700 Subject: [PATCH 17/69] use correct quantized sequence decode --- include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 8e9e207005..9690f8175b 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -81,8 +81,7 @@ struct Unidirectional using sequence_type = sampling::QuantizedSequence; uint32_t address = glsl::bitfieldInsert(protoDimension, _sample, MAX_DEPTH_LOG2, MAX_SAMPLES_LOG2); sequence_type tmpSeq = vk::RawBufferLoad(pSampleBuffer + (address + i) * sizeof(sequence_type)); - // return tmpSeq.template decode(randGen()); - return sampling::decode(tmpSeq,randGen()); + return tmpSeq.template decode(randGen()); } scalar_type getLuma(NBL_CONST_REF_ARG(vector3_type) col) From 3350351ac46dc04ebacbc94f5b0c395015621fce Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 23 Jan 2026 16:40:08 +0700 Subject: [PATCH 18/69] moved raygen out as basic ray gen with gaussian filter --- .../hlsl/path_tracing/basic_ray_gen.hlsl | 50 +++++++++++++++++++ .../hlsl/path_tracing/gaussian_filter.hlsl | 45 +++++++++++++++++ .../hlsl/path_tracing/unidirectional.hlsl | 11 +++- src/nbl/builtin/CMakeLists.txt | 2 + 4 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 include/nbl/builtin/hlsl/path_tracing/basic_ray_gen.hlsl create mode 100644 include/nbl/builtin/hlsl/path_tracing/gaussian_filter.hlsl diff --git a/include/nbl/builtin/hlsl/path_tracing/basic_ray_gen.hlsl b/include/nbl/builtin/hlsl/path_tracing/basic_ray_gen.hlsl new file mode 100644 index 0000000000..e451789977 --- /dev/null +++ b/include/nbl/builtin/hlsl/path_tracing/basic_ray_gen.hlsl @@ -0,0 +1,50 @@ +#ifndef _NBL_BUILTIN_HLSL_PATH_TRACING_BASIC_RAYGEN_INCLUDED_ +#define _NBL_BUILTIN_HLSL_PATH_TRACING_BASIC_RAYGEN_INCLUDED_ + +#include + +namespace nbl +{ +namespace hlsl +{ +namespace path_tracing +{ + +template +struct BasicRayGenerator +{ + using this_t = BasicRayGenerator; + using ray_type = Ray; + using scalar_type = typename Ray::scalar_type; + using vector3_type = typename Ray::vector3_type; + + using vector2_type = vector; + using vector4_type = vector; + using matrix4x4_type = matrix; + + ray_type generate(const vector3_type randVec) + { + ray_type ray; + ray.origin = camPos; + + vector4_type tmp = NDC; + GaussianFilter filter = GaussianFilter::create(2.5, 1.5); // stochastic reconstruction filter + tmp.xy += pixOffsetParam * filter.sample(randVec); + // for depth of field we could do another stochastic point-pick + tmp = nbl::hlsl::mul(invMVP, tmp); + ray.direction = nbl::hlsl::normalize(tmp.xyz / tmp.w - camPos); + + return ray; + } + + vector2_type pixOffsetParam; + vector3_type camPos; + vector4_type NDC; + matrix4x4_type invMVP; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/path_tracing/gaussian_filter.hlsl b/include/nbl/builtin/hlsl/path_tracing/gaussian_filter.hlsl new file mode 100644 index 0000000000..ff19e89d82 --- /dev/null +++ b/include/nbl/builtin/hlsl/path_tracing/gaussian_filter.hlsl @@ -0,0 +1,45 @@ +#ifndef _NBL_BUILTIN_HLSL_PATH_TRACING_GAUSSIAN_FILTER_INCLUDED_ +#define _NBL_BUILTIN_HLSL_PATH_TRACING_GAUSSIAN_FILTER_INCLUDED_ + +#include + +namespace nbl +{ +namespace hlsl +{ +namespace path_tracing +{ + +template) +struct GaussianFilter +{ + using this_t = GaussianFilter; + using scalar_type = T; + + using vector2_type = vector; + + static this_t create(const scalar_type gaussianFilterCutoff, const scalar_type stddev) + { + this_t retval; + retval.truncation = hlsl::exp(-0.5 * gaussianFilterCutoff * gaussianFilterCutoff); + retval.boxMuller.stddev = stddev; + return retval; + } + + vector2_type sample(const vector randVec) + { + vector2_type remappedRand = randVec.xy; + remappedRand.x *= 1.0 - truncation; + remappedRand.x += truncation; + return boxMuller(remappedRand); + } + + scalar_type truncation; + nbl::hlsl::sampling::BoxMullerTransform boxMuller; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 9690f8175b..8e15056237 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -217,7 +217,7 @@ struct Unidirectional // trace new ray ray.origin = intersection + bxdfSample * (1.0/*kSceneSize*/) * Tolerance::getStart(depth); ray.direction = bxdfSample; - if (nee_type::IsPolygonMethodProjectedSolidAngle) + NBL_IF_CONSTEXPR (nee_type::IsPolygonMethodProjectedSolidAngle) { ray.normalAtOrigin = interaction.getN(); ray.wasBSDFAtOrigin = isBSDF; @@ -232,7 +232,7 @@ struct Unidirectional { vector3_type finalContribution = ray.payload.throughput; // #ifdef USE_ENVMAP - // vec2 uv = SampleSphericalMap(_immutable.direction); + // vec2 uv = SampleSphericalMap(ray.direction); // finalContribution *= textureLod(envMap, uv, 0.0).rgb; // #else const vector3_type kConstantEnvLightRadiance = vector3_type(0.15, 0.21, 0.3); // TODO: match spectral_type @@ -247,6 +247,13 @@ struct Unidirectional //scalar_type meanLumaSq = 0.0; vector3_type uvw = rand3d(0u, sampleIndex, 0u); ray_type ray = rayGen.generate(uvw); + ray.initPayload(); + + NBL_IF_CONSTEXPR (nee_type::IsPolygonMethodProjectedSolidAngle) + { + ray.normalAtOrigin = hlsl::promote(0.0); + ray.wasBSDFAtOrigin = false; + } // bounces bool hit = true; diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index 46ce7f271b..0cf81b1a05 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -312,6 +312,8 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/delta_distr LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/path_tracing/concepts.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/path_tracing/unidirectional.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/path_tracing/default_accumulator.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/path_tracing/gaussian_filter.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/path_tracing/basic_ray_gen.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 5ed90fab3fad8aecc7e3d06084ccfec7a990604d Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 13 Feb 2026 10:47:35 +0700 Subject: [PATCH 19/69] moved nan check from ndotl to ldoth --- include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl | 5 ++++- 1 file changed, 4 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 5d5083af02..1741ef789d 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -202,9 +202,10 @@ struct SCookTorrance // 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); + assert(!hlsl::isnan(NdotL)); // 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 (hlsl::isnan(NdotL) || ComputeMicrofacetNormal::isTransmissionPath(NdotV, NdotL) != transmitted) + if (ComputeMicrofacetNormal::isTransmissionPath(NdotV, NdotL) != transmitted) { valid = false; return sample_type::createInvalid(); // should check if sample direction is invalid @@ -308,6 +309,8 @@ struct SCookTorrance bool transmitted = partitionRandVariable(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); + if (hlsl::isnan(LdotH)) + return sample_type::createInvalid(); bool valid; sample_type s = __generate_common(interaction, localH, NdotV, VdotH, LdotH, transmitted, rcpEta, valid); if (valid) From b8f0c9ffaa6a92747ef5e8b51331d250cc700aee Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 13 Feb 2026 10:50:09 +0700 Subject: [PATCH 20/69] minor fixes because this is ref in hlsl and pointer in c++ --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 0865e7f5e1..cd0255b6a3 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -635,6 +635,7 @@ struct iridescent_helper NBL_UNROLL for (int m=1; m<=2; ++m) { Cm *= r123p; + // TODO: common subexpression, maybe we can hoist it out somehow? Sm = hlsl::promote(2.0) * evalSensitivity(hlsl::promote(scalar_type(m))*D, hlsl::promote(scalar_type(m))*(phi23p+phi21p)); I += Cm*Sm; } @@ -728,7 +729,12 @@ struct Iridescent(this, base_type::iork3, getEtak23(), clampedCosTheta); +#else + return impl::__iridescent_base__call_const(*this, base_type::iork3, getEtak23(), clampedCosTheta); +#endif } vector_type getEtak23() NBL_CONST_MEMBER_FUNC @@ -775,7 +781,11 @@ struct Iridescent(this, getEtak23(), getEtak23(), clampedCosTheta); +#else + return impl::__iridescent_base__call_const(*this, getEtak23(), getEtak23(), clampedCosTheta); +#endif } scalar_type getRefractionOrientedEta() NBL_CONST_MEMBER_FUNC { return base_type::eta13[0]; } From 78dcf935c17d7e1856a277228c611bade43b36a7 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 13 Feb 2026 16:12:57 +0700 Subject: [PATCH 21/69] revert linear back to old version because rcpDiff can be negative --- .../nbl/builtin/hlsl/sampling/bilinear.hlsl | 4 ++-- include/nbl/builtin/hlsl/sampling/linear.hlsl | 20 +++++++++---------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/include/nbl/builtin/hlsl/sampling/bilinear.hlsl b/include/nbl/builtin/hlsl/sampling/bilinear.hlsl index fd3632b994..e1ed03c5e9 100644 --- a/include/nbl/builtin/hlsl/sampling/bilinear.hlsl +++ b/include/nbl/builtin/hlsl/sampling/bilinear.hlsl @@ -30,7 +30,7 @@ struct Bilinear retval.bilinearCoeffs = bilinearCoeffs; retval.bilinearCoeffDiffs = vector2_type(bilinearCoeffs[2]-bilinearCoeffs[0], bilinearCoeffs[3]-bilinearCoeffs[1]); vector2_type twiceAreasUnderXCurve = vector2_type(bilinearCoeffs[0] + bilinearCoeffs[1], bilinearCoeffs[2] + bilinearCoeffs[3]); - retval.twiceAreasUnderXCurveSumOverFour = scalar_type(4.0) / (twiceAreasUnderXCurve[0] + twiceAreasUnderXCurve[1]); + retval.fourOverTwiceAreasUnderXCurveSum = scalar_type(4.0) / (twiceAreasUnderXCurve[0] + twiceAreasUnderXCurve[1]); retval.lineary = Linear::create(twiceAreasUnderXCurve); return retval; } @@ -57,7 +57,7 @@ struct Bilinear // x0y1 x1y1 vector4_type bilinearCoeffs; // (x0y0, x0y1, x1y0, x1y1) vector2_type bilinearCoeffDiffs; - vector2_type fourOverTwiceAreasUnderXCurveSum; + scalar_type fourOverTwiceAreasUnderXCurveSum; Linear lineary; }; diff --git a/include/nbl/builtin/hlsl/sampling/linear.hlsl b/include/nbl/builtin/hlsl/sampling/linear.hlsl index 7127701117..289ea75485 100644 --- a/include/nbl/builtin/hlsl/sampling/linear.hlsl +++ b/include/nbl/builtin/hlsl/sampling/linear.hlsl @@ -24,25 +24,23 @@ struct Linear static Linear create(const vector2_type linearCoeffs) // start and end importance values (start, end), assumed to be at x=0 and x=1 { Linear retval; - scalar_type rcpDiff = 1.0 / (linearCoeffs[0] - linearCoeffs[1]); - retval.linearCoeffStartOverDiff = linearCoeffs[0] * rcpDiff; + retval.linearCoeffStart = linearCoeffs[0]; + retval.rcpDiff = 1.0 / (linearCoeffs[0] - linearCoeffs[1]); vector2_type squaredCoeffs = linearCoeffs * linearCoeffs; - scalar_type squaredRcpDiff = rcpDiff * rcpDiff; - retval.squaredCoeffStartOverDiff = squaredCoeffs[0] * squaredRcpDiff; - retval.squaredCoeffDiffOverDiff = (squaredCoeffs[1] - squaredCoeffs[0]) * squaredRcpDiff; + retval.squaredCoeffStart = squaredCoeffs[0]; + retval.squaredCoeffDiff = squaredCoeffs[1] - squaredCoeffs[0]; return retval; } scalar_type generate(const scalar_type u) { - return hlsl::mix(u, (linearCoeffStartOverDiff - hlsl::sqrt(squaredCoeffStartOverDiff + u * squaredCoeffDiffOverDiff)), hlsl::abs(linearCoeffStartOverDiff) < numeric_limits::max); + return hlsl::mix(u, (linearCoeffStart - hlsl::sqrt(squaredCoeffStart + u * squaredCoeffDiff)) * rcpDiff, hlsl::abs(rcpDiff) < numeric_limits::max); } - // TODO: add forwardPdf and backwardPdf methods, forward computes from u and backwards from the result of generate - - scalar_type linearCoeffStartOverDiff; - scalar_type squaredCoeffStartOverDiff; - scalar_type squaredCoeffDiffOverDiff; + scalar_type linearCoeffStart; + scalar_type rcpDiff; + scalar_type squaredCoeffStart; + scalar_type squaredCoeffDiff; }; } From cf936ed0526b2b331e344356cef7a53fd8fe1043 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 13 Feb 2026 16:56:39 +0700 Subject: [PATCH 22/69] fix conditionalAbsOrMax for vectors --- include/nbl/builtin/hlsl/math/functions.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/nbl/builtin/hlsl/math/functions.hlsl b/include/nbl/builtin/hlsl/math/functions.hlsl index a1c51d4e51..f7db44b9fb 100644 --- a/include/nbl/builtin/hlsl/math/functions.hlsl +++ b/include/nbl/builtin/hlsl/math/functions.hlsl @@ -152,7 +152,7 @@ struct conditionalAbsOrMax_helper(x); - const Uint32VectorWithDimensionOfT mask = cond ? _static_cast(numeric_limits::max >> 1) : _static_cast(numeric_limits::max); + const Uint32VectorWithDimensionOfT mask = cond ? promote(numeric_limits::max >> 1) : promote(numeric_limits::max); const Uint32VectorWithDimensionOfT condAbsAsUint = xAsUintVec & mask; T condAbs = bit_cast(condAbsAsUint); From 5e52a6f2a31c603c68c84319481f28ad74d0f7e0 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 13 Feb 2026 16:57:43 +0700 Subject: [PATCH 23/69] refactor changes to various sampling functions so it runs --- .../hlsl/path_tracing/unidirectional.hlsl | 22 ++++-------- .../projected_spherical_triangle.hlsl | 34 ++++++------------- .../hlsl/sampling/spherical_triangle.hlsl | 2 +- 3 files changed, 18 insertions(+), 40 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 8e15056237..a7b491fa74 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -110,8 +110,11 @@ struct Unidirectional const uint32_t lightID = glsl::bitfieldExtract(bsdfLightIDs, 16, 16); if (lightID != light_type::INVALID_ID) { - float _pdf; - ray.payload.accumulation += nee.deferredEvalAndPdf(_pdf, scene, lightID, ray) * throughput / (1.0 + _pdf * _pdf * ray.payload.otherTechniqueHeuristic); + scalar_type _pdf; + measure_type emissive = nee.deferredEvalAndPdf(_pdf, scene, lightID, ray) * throughput; + scalar_type _pdfSq = hlsl::mix(_pdf, _pdf * _pdf, _pdf < numeric_limits::max); + emissive /= (1.0 + _pdfSq * ray.payload.otherTechniqueHeuristic); + ray.payload.accumulation += emissive; } const uint32_t bsdfID = glsl::bitfieldExtract(bsdfLightIDs, 0, 16); @@ -120,8 +123,6 @@ struct Unidirectional bxdfnode_type bxdf = materialSystem.bxdfs[bsdfID]; - // TODO: ifdef kill diffuse specular paths - const bool isBSDF = material_system_type::isBSDF(bxdf.materialType); vector3_type eps0 = rand3d(depth, _sample, 0u); @@ -135,7 +136,7 @@ struct Unidirectional const scalar_type monochromeEta = hlsl::dot(throughputCIE_Y, eta) / (throughputCIE_Y.r + throughputCIE_Y.g + throughputCIE_Y.b); // TODO: imaginary eta? // sample lights - const scalar_type neeProbability = 1.0; // BSDFNode_getNEEProb(bsdf); + const scalar_type neeProbability = bxdf.getNEEProb(); scalar_type rcpChoiceProb; sampling::PartitionRandVariable partitionRandVariable; partitionRandVariable.leftProb = neeProbability; @@ -160,11 +161,7 @@ struct Unidirectional if (neeContrib_pdf.pdf < numeric_limits::max) { - if (nbl::hlsl::any(hlsl::isnan(nee_sample.getL().getDirection()))) - ray.payload.accumulation += vector3_type(1000.f, 0.f, 0.f); - else if (nbl::hlsl::all((vector3_type)69.f == nee_sample.getL().getDirection())) - ray.payload.accumulation += vector3_type(0.f, 1000.f, 0.f); - else if (validPath) + if (validPath) { // example only uses isotropic bxdfs quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(bxdf.materialType, bxdf.params, nee_sample, interaction.isotropic, _cache.iso_cache); @@ -173,9 +170,6 @@ struct Unidirectional const scalar_type otherGenOverLightAndChoice = otherGenOverChoice / bsdf_quotient_pdf.pdf; neeContrib_pdf.quotient *= otherGenOverChoice / (1.f + otherGenOverLightAndChoice * otherGenOverLightAndChoice); // balance heuristic - // TODO: ifdef NEE only - // neeContrib_pdf.quotient *= otherGenOverChoice; - ray_type nee_ray; nee_ray.origin = intersection + nee_sample.getL().getDirection() * t * Tolerance::getStart(depth); nee_ray.direction = nee_sample.getL().getDirection(); @@ -186,8 +180,6 @@ struct Unidirectional } } - // return false; // NEE only - // sample BSDF scalar_type bxdfPdf; vector3_type bxdfSample; diff --git a/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl b/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl index d01712c2f8..116dbd3cd9 100644 --- a/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl +++ b/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl @@ -29,7 +29,7 @@ struct ProjectedSphericalTriangle static ProjectedSphericalTriangle create(NBL_CONST_REF_ARG(shapes::SphericalTriangle) tri) { ProjectedSphericalTriangle retval; - retval.tri = tri; + retval.sphtri = sampling::SphericalTriangle::create(tri); return retval; } @@ -37,13 +37,13 @@ struct ProjectedSphericalTriangle { 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), hlsl::promote(minimumProjSolidAngle)); + matrix m = matrix(sphtri.tri.vertices[0], sphtri.tri.vertices[1], sphtri.tri.vertices[2]); + const vector3_type bxdfPdfAtVertex = math::conditionalAbsOrMax(isBSDF, hlsl::mul(m, receiverNormal), hlsl::promote(minimumProjSolidAngle)); return bxdfPdfAtVertex.yyxz; } - vector3_type generate(NBL_REF_ARG(scalar_type) rcpPdf, scalar_type solidAngle, const vector3_type cos_vertices, const vector3_type sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, const vector3_type receiverNormal, bool isBSDF, const vector2_type _u) + vector3_type generate(NBL_REF_ARG(scalar_type) rcpPdf, scalar_type solidAngle, scalar_type cos_c, scalar_type csc_b, const vector3_type receiverNormal, bool isBSDF, const vector2_type _u) { vector2_type u; // pre-warp according to proj solid angle approximation @@ -52,7 +52,7 @@ struct ProjectedSphericalTriangle u = bilinear.generate(_u); // now warp the points onto a spherical triangle - const vector3_type L = sphtri.generate(solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, u); + const vector3_type L = sphtri.generate(cos_c, csc_b, u); rcpPdf = solidAngle / bilinear.backwardPdf(u); return L; @@ -60,26 +60,13 @@ struct ProjectedSphericalTriangle vector3_type generate(NBL_REF_ARG(scalar_type) rcpPdf, const vector3_type receiverNormal, bool isBSDF, const vector2_type u) { - const scalar_type cos_a = tri.cos_sides[0]; - const scalar_type cos_c = tri.cos_sides[2]; - const scalar_type csc_b = tri.csc_sides[1]; - const scalar_type csc_c = tri.csc_sides[2]; - vector3_type cos_vertices, sin_vertices; - const scalar_type solidAngle = tri.solidAngle(cos_vertices, sin_vertices); - return generate(rcpPdf, solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, receiverNormal, isBSDF, u); + const scalar_type cos_c = sphtri.tri.cos_sides[2]; + const scalar_type csc_b = sphtri.tri.csc_sides[1]; + const scalar_type solidAngle = sphtri.tri.solidAngle(); + return generate(rcpPdf, solidAngle, cos_c, csc_b, receiverNormal, isBSDF, u); } - scalar_type pdf(scalar_type solidAngle, const vector3_type cos_vertices, const vector3_type sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, const vector3_type receiverNormal, bool receiverWasBSDF, const vector3_type L) - { - scalar_type pdf; - 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); - return pdf * bilinear.backwardPdf(u); - } - - scalar_type pdf(const vector3_type receiverNormal, bool receiverWasBSDF, const vector3_type L) + scalar_type backwardPdf(const vector3_type receiverNormal, bool receiverWasBSDF, const vector3_type L) { scalar_type pdf; const vector2_type u = sphtri.generateInverse(pdf, L); @@ -89,7 +76,6 @@ struct ProjectedSphericalTriangle return pdf * bilinear.backwardPdf(u); } - shapes::SphericalTriangle tri; sampling::SphericalTriangle sphtri; }; diff --git a/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl b/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl index 2f2d12e1df..2b2dc0ccb6 100644 --- a/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl +++ b/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl @@ -84,7 +84,7 @@ struct SphericalTriangle pdf = 1.0 / solidAngle; const scalar_type cosAngleAlongBC_s = nbl::hlsl::dot(L, tri.vertices[1]); - const scalar_type csc_a_ = 1.0 / nbl::hlsl::sqrt(1.0 - cosAngleAlongBC_s * cosAngleAlongBC_s); + const scalar_type csc_a_ = nbl::hlsl::rsqrt(1.0 - cosAngleAlongBC_s * cosAngleAlongBC_s); const scalar_type cos_b_ = nbl::hlsl::dot(L, tri.vertices[0]); const scalar_type cosB_ = (cos_b_ - cosAngleAlongBC_s * cos_c) * csc_a_ * csc_c; From 86e3146265274a505f7390bd278d1b9345add72d Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 13 Feb 2026 17:08:53 +0700 Subject: [PATCH 24/69] some fixes to pt concept type names --- include/nbl/builtin/hlsl/path_tracing/concepts.hlsl | 11 ++++++----- .../nbl/builtin/hlsl/path_tracing/unidirectional.hlsl | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index b3dc2dd055..535939cc5f 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -56,8 +56,8 @@ NBL_CONCEPT_BEGIN(3) NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scene_type)) ((NBL_CONCEPT_REQ_TYPE)(T::ray_type)) - ((NBL_CONCEPT_REQ_TYPE)(T::id_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceRay(ray, scene)), ::nbl::hlsl::is_same_v, typename T::id_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::object_handle_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceRay(ray, scene)), ::nbl::hlsl::is_same_v, typename T::object_handle_type)) ); #undef scene #undef ray @@ -69,7 +69,7 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (matsys, T) #define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) -#define NBL_CONCEPT_PARAM_2 (matid, uint32_t) +#define NBL_CONCEPT_PARAM_2 (matid, typename T::material_id_type) #define NBL_CONCEPT_PARAM_3 (aniso_inter, typename T::anisotropic_interaction_type) #define NBL_CONCEPT_PARAM_4 (iso_inter, typename T::isotropic_interaction_type) #define NBL_CONCEPT_PARAM_5 (aniso_cache, typename T::anisocache_type) @@ -88,6 +88,7 @@ NBL_CONCEPT_BEGIN(9) #define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_8 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::material_id_type)) ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE)(T::measure_type)) @@ -181,14 +182,14 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (scene, T) #define NBL_CONCEPT_PARAM_1 (intersectP, typename T::vector3_type) -#define NBL_CONCEPT_PARAM_2 (id, typename T::id_type) +#define NBL_CONCEPT_PARAM_2 (id, typename T::object_handle_type) NBL_CONCEPT_BEGIN(3) #define scene NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define intersectP NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define id NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) - ((NBL_CONCEPT_REQ_TYPE)(T::id_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::object_handle_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((scene.getBsdfLightIDs(id)), ::nbl::hlsl::is_same_v, uint32_t)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((scene.getNormal(id, intersectP)), ::nbl::hlsl::is_same_v, typename T::vector3_type)) ); diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index a7b491fa74..4426250513 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -63,7 +63,7 @@ struct Unidirectional using sample_type = typename NextEventEstimator::sample_type; using ray_dir_info_type = typename sample_type::ray_dir_info_type; using ray_type = typename RayGen::ray_type; - using id_type = typename Intersector::id_type; + using object_handle_type = typename Intersector::object_handle_type; using light_type = typename NextEventEstimator::light_type; using bxdfnode_type = typename MaterialSystem::bxdfnode_type; using anisotropic_interaction_type = typename MaterialSystem::anisotropic_interaction_type; @@ -92,7 +92,7 @@ struct Unidirectional // TODO: probably will only work with isotropic surfaces, need to do aniso bool closestHitProgram(uint32_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(scene_type) scene) { - const id_type objectID = ray.objectID; + const object_handle_type objectID = ray.objectID; const vector3_type intersection = ray.origin + ray.direction * ray.intersectionT; uint32_t bsdfLightIDs = scene.getBsdfLightIDs(objectID); From addde12192a474a8a27bad7f58d48c3ba1f3a9d5 Mon Sep 17 00:00:00 2001 From: Przemog1 Date: Mon, 16 Feb 2026 05:10:24 +0100 Subject: [PATCH 25/69] RWMC fixes --- examples_tests | 2 +- .../concepts/accessors/loadable_image.hlsl | 6 +- .../builtin/hlsl/rwmc/CascadeAccumulator.hlsl | 80 +++++++++---------- .../builtin/hlsl/rwmc/ResolveParameters.hlsl | 34 ++++---- .../hlsl/rwmc/SplattingParameters.hlsl | 10 ++- include/nbl/builtin/hlsl/rwmc/resolve.hlsl | 32 ++++---- 6 files changed, 85 insertions(+), 79 deletions(-) diff --git a/examples_tests b/examples_tests index dd7de7a89c..4246192d21 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit dd7de7a89cfa5a59970dde4d4744ecf746d77a4a +Subproject commit 4246192d213c0c42a508257bd219c1fca0cb238f diff --git a/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl b/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl index 924ee240d0..f7b5122c81 100644 --- a/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl +++ b/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl @@ -47,8 +47,8 @@ NBL_CONCEPT_END( // declare concept #define NBL_CONCEPT_NAME MipmappedLoadableImage -#define NBL_CONCEPT_TPLT_PRM_KINDS (typename)(typename)(int32_t) -#define NBL_CONCEPT_TPLT_PRM_NAMES (U)(T)(Dims) +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename)(typename)(int32_t)(int32_t) +#define NBL_CONCEPT_TPLT_PRM_NAMES (U)(T)(Dims)(Components) // not the greatest syntax but works #define NBL_CONCEPT_PARAM_0 (a,U) #define NBL_CONCEPT_PARAM_1 (uv,vector) @@ -62,7 +62,7 @@ NBL_CONCEPT_BEGIN(4) #define layer NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define level NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,layer,level)) , ::nbl::hlsl::is_same_v, vector)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,layer,level)) , ::nbl::hlsl::is_same_v, vector)) ); #undef level #undef layer diff --git a/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl b/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl index 593e267a26..cdbb8271d2 100644 --- a/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl @@ -14,36 +14,38 @@ namespace rwmc { template) -struct CascadeAccumulator +struct DefaultCascades { - struct CascadeEntry - { - uint32_t cascadeSampleCounter[CascadeCount]; - CascadeLayerType data[CascadeCount]; + using layer_type = CascadeLayerType; - void addSampleIntoCascadeEntry(CascadeLayerType _sample, uint32_t lowerCascadeIndex, float lowerCascadeLevelWeight, float higherCascadeLevelWeight, uint32_t sampleCount) - { - const float reciprocalSampleCount = 1.0f / float(sampleCount); + uint32_t cascadeSampleCounter[CascadeCount]; + CascadeLayerType data[CascadeCount]; - uint32_t lowerCascadeSampleCount = cascadeSampleCounter[lowerCascadeIndex]; - data[lowerCascadeIndex] += (_sample * lowerCascadeLevelWeight - (sampleCount - lowerCascadeSampleCount) * data[lowerCascadeIndex]) * reciprocalSampleCount; - cascadeSampleCounter[lowerCascadeIndex] = sampleCount; + void addSampleIntoCascadeEntry(CascadeLayerType _sample, uint16_t lowerCascadeIndex, SplattingParameters::scalar_t lowerCascadeLevelWeight, SplattingParameters::scalar_t higherCascadeLevelWeight, uint32_t sampleCount) + { + const float reciprocalSampleCount = 1.0f / float(sampleCount); - uint32_t higherCascadeIndex = lowerCascadeIndex + 1u; - if (higherCascadeIndex < CascadeCount) - { - uint32_t higherCascadeSampleCount = cascadeSampleCounter[higherCascadeIndex]; - data[higherCascadeIndex] += (_sample * higherCascadeLevelWeight - (sampleCount - higherCascadeSampleCount) * data[higherCascadeIndex]) * reciprocalSampleCount; - cascadeSampleCounter[higherCascadeIndex] = sampleCount; - } + uint32_t lowerCascadeSampleCount = cascadeSampleCounter[lowerCascadeIndex]; + data[lowerCascadeIndex] += (_sample * lowerCascadeLevelWeight - (sampleCount - lowerCascadeSampleCount) * data[lowerCascadeIndex]) * reciprocalSampleCount; + cascadeSampleCounter[lowerCascadeIndex] = sampleCount; + + uint16_t higherCascadeIndex = lowerCascadeIndex + 1u; + if (higherCascadeIndex < CascadeCount) + { + uint32_t higherCascadeSampleCount = cascadeSampleCounter[higherCascadeIndex]; + data[higherCascadeIndex] += (_sample * higherCascadeLevelWeight - (sampleCount - higherCascadeSampleCount) * data[higherCascadeIndex]) * reciprocalSampleCount; + cascadeSampleCounter[higherCascadeIndex] = sampleCount; } - }; + } +}; - using cascade_layer_scalar_type = typename vector_traits::scalar_type; - using this_t = CascadeAccumulator; - using input_sample_type = CascadeLayerType; - using output_storage_type = CascadeEntry; - using initialization_data = SplattingParameters; +template +struct CascadeAccumulator +{ + using input_sample_type = typename CascadesType::layer_type; + using cascade_layer_scalar_type = typename vector_traits::scalar_type; + using this_t = CascadeAccumulator; + using output_storage_type = CascadesType; output_storage_type accumulation; SplattingParameters splattingParameters; @@ -53,39 +55,35 @@ struct CascadeAccumulator this_t retval; for (int i = 0; i < CascadeCount; ++i) { - retval.accumulation.data[i] = promote(0.0f); + retval.accumulation.data[i] = promote(0.0f); retval.accumulation.cascadeSampleCounter[i] = 0u; } retval.splattingParameters = settings; return retval; } - - cascade_layer_scalar_type getLuma(NBL_CONST_REF_ARG(CascadeLayerType) col) - { - return hlsl::dot(hlsl::transpose(colorspace::scRGBtoXYZ)[1], col); - } // most of this code is stolen from https://cg.ivd.kit.edu/publications/2018/rwmc/tool/split.cpp void addSample(uint32_t sampleCount, input_sample_type _sample) { const float32_t2 unpackedParams = hlsl::unpackHalf2x16(splattingParameters.packedLog2); - const cascade_layer_scalar_type log2Start = unpackedParams[0]; - const cascade_layer_scalar_type log2Base = unpackedParams[1]; - const cascade_layer_scalar_type luma = getLuma(_sample); - const cascade_layer_scalar_type log2Luma = log2(luma); - const cascade_layer_scalar_type cascade = log2Luma * 1.f / log2Base - log2Start / log2Base; - const cascade_layer_scalar_type clampedCascade = clamp(cascade, 0, CascadeCount - 1); + const SplattingParameters::scalar_t log2Start = unpackedParams[0]; + const SplattingParameters::scalar_t rcpLog2Base = unpackedParams[1]; + const SplattingParameters::scalar_t luma = splattingParameters.getLuma(_sample); + const SplattingParameters::scalar_t log2Luma = log2(luma); + const SplattingParameters::scalar_t cascade = log2Luma * rcpLog2Base - log2Start * rcpLog2Base; + const SplattingParameters::scalar_t lastCascade = CascadeCount - 1; + const SplattingParameters::scalar_t clampedCascade = clamp(cascade, 0, lastCascade); // c<=0 -> 0, c>=Count-1 -> Count-1 - uint32_t lowerCascadeIndex = floor(cascade); + uint16_t lowerCascadeIndex = floor(cascade); // 0 whenever clamped or `cascade` is integer (when `clampedCascade` is integer) - cascade_layer_scalar_type higherCascadeWeight = clampedCascade - floor(clampedCascade); + SplattingParameters::scalar_t higherCascadeWeight = clampedCascade - floor(clampedCascade); // never 0 thanks to magic of `1-fract(x)` - cascade_layer_scalar_type lowerCascadeWeight = cascade_layer_scalar_type(1) - higherCascadeWeight; + SplattingParameters::scalar_t lowerCascadeWeight = SplattingParameters::scalar_t(1) - higherCascadeWeight; // handle super bright sample case - if (cascade > CascadeCount - 1) - lowerCascadeWeight = exp2(log2Start + log2Base * (CascadeCount - 1) - log2Luma); + if (cascade > lastCascade) + lowerCascadeWeight = exp2(log2Start + (1.0f/rcpLog2Base) * (lastCascade) - log2Luma); accumulation.addSampleIntoCascadeEntry(_sample, lowerCascadeIndex, lowerCascadeWeight, higherCascadeWeight, sampleCount); } diff --git a/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl b/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl index 7509eac493..17e3f8d720 100644 --- a/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl @@ -12,7 +12,22 @@ namespace rwmc struct ResolveParameters { - uint32_t lastCascadeIndex; + static ResolveParameters create(float base, uint32_t sampleCount, float minReliableLuma, float kappa) + { + ResolveParameters retval; + retval.initialEmin = minReliableLuma; + retval.reciprocalBase = 1.f / base; + const float N = float(sampleCount); + retval.reciprocalN = 1.f / N; + retval.reciprocalKappa = 1.f / kappa; + // if not interested in exact expected value estimation (kappa!=1.f), can usually accept a bit more variance relative to the image brightness we already have + // allow up to ~ more energy in one sample to lessen bias in some cases + retval.colorReliabilityFactor = base + (1.f - base) * retval.reciprocalKappa; + retval.NOverKappa = N * retval.reciprocalKappa; + + return retval; + } + float initialEmin; // a minimum image brightness that we always consider reliable float reciprocalBase; float reciprocalN; @@ -21,23 +36,6 @@ struct ResolveParameters float NOverKappa; }; -ResolveParameters computeResolveParameters(float base, uint32_t sampleCount, float minReliableLuma, float kappa, uint32_t cascadeSize) -{ - ResolveParameters retval; - retval.lastCascadeIndex = cascadeSize - 1u; - retval.initialEmin = minReliableLuma; - retval.reciprocalBase = 1.f / base; - const float N = float(sampleCount); - retval.reciprocalN = 1.f / N; - retval.reciprocalKappa = 1.f / kappa; - // if not interested in exact expected value estimation (kappa!=1.f), can usually accept a bit more variance relative to the image brightness we already have - // allow up to ~ more energy in one sample to lessen bias in some cases - retval.colorReliabilityFactor = base + (1.f - base) * retval.reciprocalKappa; - retval.NOverKappa = N * retval.reciprocalKappa; - - return retval; -} - } } } diff --git a/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl b/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl index c549d83be6..74b062a2de 100644 --- a/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl @@ -12,10 +12,18 @@ namespace rwmc struct SplattingParameters { + using scalar_t = float32_t; + // float16_t log2Start; 0 - // float16_t log2Base; 1 + // float16_t rcpLog2Base; 1 // pack as Half2x16 int32_t packedLog2; + + template + scalar_t getLuma(NBL_CONST_REF_ARG(CascadeLayerType) col) + { + return hlsl::dot(hlsl::transpose(colorspace::scRGBtoXYZ)[1], col); + } }; } diff --git a/include/nbl/builtin/hlsl/rwmc/resolve.hlsl b/include/nbl/builtin/hlsl/rwmc/resolve.hlsl index 34d7c95960..a3b4236dca 100644 --- a/include/nbl/builtin/hlsl/rwmc/resolve.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/resolve.hlsl @@ -16,8 +16,8 @@ namespace rwmc { // declare concept #define NBL_CONCEPT_NAME ResolveAccessorBase -#define NBL_CONCEPT_TPLT_PRM_KINDS (typename)(typename)(int32_t)(int32_t) -#define NBL_CONCEPT_TPLT_PRM_NAMES (T)(VectorScalarType)(Dims)(Components) +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename)(typename)(int32_t) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T)(VectorScalarType)(Components) // not the greatest syntax but works #define NBL_CONCEPT_PARAM_0 (a,T) #define NBL_CONCEPT_PARAM_1 (vec,vector) @@ -28,6 +28,7 @@ NBL_CONCEPT_BEGIN(2) #define vec NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.calcLuma(vec)), ::nbl::hlsl::is_same_v, VectorScalarType)) + ((NBL_CONCEPT_REQ_TYPE)(T::output_t)) ); #undef a #undef vec @@ -39,14 +40,14 @@ NBL_CONCEPT_END( */ template -NBL_BOOL_CONCEPT ResolveAccessor = ResolveAccessorBase && concepts::accessors::LoadableImage; +NBL_BOOL_CONCEPT ResolveAccessor = ResolveAccessorBase && concepts::accessors::LoadableImage; template struct ResolveAccessorAdaptor { - using output_scalar_type = OutputScalar; + using output_scalar_t = OutputScalar; NBL_CONSTEXPR int32_t Components = 3; - using output_type = vector; + using output_t = vector; NBL_CONSTEXPR int32_t image_dimension = 2; RWTexture2DArray cascade; @@ -57,24 +58,24 @@ struct ResolveAccessorAdaptor } template - output_type get(vector uv, uint16_t layer) + output_t get(vector uv, uint16_t layer) { uint32_t imgWidth, imgHeight, layers; cascade.GetDimensions(imgWidth, imgHeight, layers); int16_t2 cascadeImageDimension = int16_t2(imgWidth, imgHeight); if (any(uv < int16_t2(0, 0)) || any(uv > cascadeImageDimension)) - return promote(0); + return promote(0); return cascade.Load(int32_t3(uv, int32_t(layer))); } }; -template && ResolveAccessor) +template) struct Resolver { - using output_type = OutputColorTypeVec; - using scalar_t = typename vector_traits::scalar_type; + using output_t = typename CascadeAccessor::output_t; + using scalar_t = typename vector_traits::scalar_type; struct CascadeSample { @@ -91,19 +92,20 @@ struct Resolver return retval; } - output_type operator()(NBL_REF_ARG(CascadeAccessor) acc, const int16_t2 coord) + output_t operator()(NBL_REF_ARG(CascadeAccessor) acc, const int16_t2 coord) { scalar_t reciprocalBaseI = 1.f; CascadeSample curr = __sampleCascade(acc, coord, 0u, reciprocalBaseI); - output_type accumulation = promote(0.0f); + output_t accumulation = promote(0.0f); scalar_t Emin = params.initialEmin; scalar_t prevNormalizedCenterLuma, prevNormalizedNeighbourhoodAverageLuma; - for (int16_t i = 0u; i <= params.lastCascadeIndex; i++) + NBL_UNROLL + for (int16_t i = 0u; i <= CascadeCount - 1; i++) { const bool notFirstCascade = i != 0; - const bool notLastCascade = i != params.lastCascadeIndex; + const bool notLastCascade = i != (CascadeCount - 1); CascadeSample next; if (notLastCascade) @@ -160,7 +162,7 @@ struct Resolver CascadeSample __sampleCascade(NBL_REF_ARG(CascadeAccessor) acc, int16_t2 coord, uint16_t cascadeIndex, scalar_t reciprocalBaseI) { - output_type neighbourhood[9]; + output_t neighbourhood[9]; neighbourhood[0] = acc.template get(coord + int16_t2(-1, -1), cascadeIndex); neighbourhood[1] = acc.template get(coord + int16_t2(0, -1), cascadeIndex); neighbourhood[2] = acc.template get(coord + int16_t2(1, -1), cascadeIndex); From 94a5bfb6857ae4e38f7d7fa63cca27fae143547f Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 16 Feb 2026 14:24:43 +0700 Subject: [PATCH 26/69] type alias for id of material and light, also reduced arguments to mat system --- .../builtin/hlsl/path_tracing/concepts.hlsl | 25 ++++++++++--------- .../hlsl/path_tracing/unidirectional.hlsl | 10 ++++---- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index 535939cc5f..ce1a6c2906 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -74,9 +74,8 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_PARAM_4 (iso_inter, typename T::isotropic_interaction_type) #define NBL_CONCEPT_PARAM_5 (aniso_cache, typename T::anisocache_type) #define NBL_CONCEPT_PARAM_6 (iso_cache, typename T::isocache_type) -#define NBL_CONCEPT_PARAM_7 (params, typename T::create_params_t) -#define NBL_CONCEPT_PARAM_8 (u, typename T::vector3_type) -NBL_CONCEPT_BEGIN(9) +#define NBL_CONCEPT_PARAM_7 (u, typename T::vector3_type) +NBL_CONCEPT_BEGIN(8) #define matsys NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define matid NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 @@ -84,8 +83,7 @@ NBL_CONCEPT_BEGIN(9) #define iso_inter NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 #define aniso_cache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 #define iso_cache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 -#define params NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_7 -#define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_8 +#define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_7 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) ((NBL_CONCEPT_REQ_TYPE)(T::material_id_type)) @@ -97,12 +95,11 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::anisocache_type)) ((NBL_CONCEPT_REQ_TYPE)(T::isocache_type)) ((NBL_CONCEPT_REQ_TYPE)(T::create_params_t)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.eval(matid, params, _sample, iso_inter, iso_cache)), ::nbl::hlsl::is_same_v, typename T::measure_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.generate(matid, params, aniso_inter, u, aniso_cache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.quotient_and_pdf(matid, params, _sample, iso_inter, iso_cache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.eval(matid, _sample, iso_inter, iso_cache)), ::nbl::hlsl::is_same_v, typename T::measure_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.generate(matid, aniso_inter, u, aniso_cache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.quotient_and_pdf(matid, _sample, iso_inter, iso_cache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ); #undef u -#undef params #undef iso_cache #undef aniso_cache #undef iso_inter @@ -118,13 +115,14 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_PARAM_0 (nee, T) #define NBL_CONCEPT_PARAM_1 (ray, typename T::ray_type) #define NBL_CONCEPT_PARAM_2 (scene, typename T::scene_type) -#define NBL_CONCEPT_PARAM_3 (id, uint32_t) +#define NBL_CONCEPT_PARAM_3 (id, typename T::light_id_type) #define NBL_CONCEPT_PARAM_4 (pdf, typename T::scalar_type) #define NBL_CONCEPT_PARAM_5 (quo_pdf, typename T::quotient_pdf_type) #define NBL_CONCEPT_PARAM_6 (v, typename T::vector3_type) #define NBL_CONCEPT_PARAM_7 (interaction, typename T::interaction_type) #define NBL_CONCEPT_PARAM_8 (b, bool) -NBL_CONCEPT_BEGIN(9) +#define NBL_CONCEPT_PARAM_9 (depth, uint32_t) +NBL_CONCEPT_BEGIN(10) #define nee NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define ray NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define scene NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 @@ -134,19 +132,22 @@ NBL_CONCEPT_BEGIN(9) #define v NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 #define interaction NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_7 #define b NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_8 +#define depth NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_9 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) ((NBL_CONCEPT_REQ_TYPE)(T::scene_type)) ((NBL_CONCEPT_REQ_TYPE)(T::light_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::light_id_type)) ((NBL_CONCEPT_REQ_TYPE)(T::ray_type)) ((NBL_CONCEPT_REQ_TYPE)(T::spectral_type)) ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE)(T::interaction_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferredEvalAndPdf(pdf, scene, id, ray)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.generate_and_quotient_and_pdf(quo_pdf, pdf, scene, id, v, interaction, b, v, id)), ::nbl::hlsl::is_same_v, typename T::sample_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.generate_and_quotient_and_pdf(quo_pdf, pdf, scene, id, v, interaction, b, v, depth)), ::nbl::hlsl::is_same_v, typename T::sample_type)) ); +#undef depth #undef b #undef interaction #undef v diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 4426250513..583f49110c 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -123,7 +123,7 @@ struct Unidirectional bxdfnode_type bxdf = materialSystem.bxdfs[bsdfID]; - const bool isBSDF = material_system_type::isBSDF(bxdf.materialType); + const bool isBSDF = materialSystem.isBSDF(bsdfID); vector3_type eps0 = rand3d(depth, _sample, 0u); vector3_type eps1 = rand3d(depth, _sample, 1u); @@ -157,14 +157,14 @@ struct Unidirectional bxdf::fresnel::OrientedEtas orientedEta = bxdf::fresnel::OrientedEtas::create(interaction.getNdotV(), hlsl::promote(monochromeEta)); anisocache_type _cache = anisocache_type::template create(interaction, nee_sample, orientedEta); validPath = validPath && _cache.getAbsNdotH() >= 0.0; - bxdf.params.eta = monochromeEta; + materialSystem.bxdfs[bsdfID].params.eta = monochromeEta; if (neeContrib_pdf.pdf < numeric_limits::max) { if (validPath) { // example only uses isotropic bxdfs - quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(bxdf.materialType, bxdf.params, nee_sample, interaction.isotropic, _cache.iso_cache); + quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(bsdfID, nee_sample, interaction.isotropic, _cache.iso_cache); neeContrib_pdf.quotient *= bxdf.albedo * throughput * bsdf_quotient_pdf.quotient; const scalar_type otherGenOverChoice = bsdf_quotient_pdf.pdf * rcpChoiceProb; const scalar_type otherGenOverLightAndChoice = otherGenOverChoice / bsdf_quotient_pdf.pdf; @@ -185,14 +185,14 @@ struct Unidirectional vector3_type bxdfSample; { anisocache_type _cache; - sample_type bsdf_sample = materialSystem.generate(bxdf.materialType, bxdf.params, interaction, eps1, _cache); + sample_type bsdf_sample = materialSystem.generate(bsdfID, interaction, eps1, _cache); if (!bsdf_sample.isValid()) return false; // example only uses isotropic bxdfs // the value of the bsdf divided by the probability of the sample being generated - quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(bxdf.materialType, bxdf.params, bsdf_sample, interaction.isotropic, _cache.iso_cache); + quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(bsdfID, bsdf_sample, interaction.isotropic, _cache.iso_cache); throughput *= bxdf.albedo * bsdf_quotient_pdf.quotient; bxdfPdf = bsdf_quotient_pdf.pdf; bxdfSample = bsdf_sample.getL().getDirection(); From 12279911ed6a30c2ce6472dbb087cf3106e7c1b0 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 16 Feb 2026 16:03:36 +0700 Subject: [PATCH 27/69] change nee to return a struct because of multiple return values --- .../builtin/hlsl/path_tracing/concepts.hlsl | 39 ++++++++----------- .../hlsl/path_tracing/unidirectional.hlsl | 18 +++++---- 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index ce1a6c2906..c493d1698a 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -114,25 +114,19 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (nee, T) #define NBL_CONCEPT_PARAM_1 (ray, typename T::ray_type) -#define NBL_CONCEPT_PARAM_2 (scene, typename T::scene_type) -#define NBL_CONCEPT_PARAM_3 (id, typename T::light_id_type) -#define NBL_CONCEPT_PARAM_4 (pdf, typename T::scalar_type) -#define NBL_CONCEPT_PARAM_5 (quo_pdf, typename T::quotient_pdf_type) -#define NBL_CONCEPT_PARAM_6 (v, typename T::vector3_type) -#define NBL_CONCEPT_PARAM_7 (interaction, typename T::interaction_type) -#define NBL_CONCEPT_PARAM_8 (b, bool) -#define NBL_CONCEPT_PARAM_9 (depth, uint32_t) -NBL_CONCEPT_BEGIN(10) +#define NBL_CONCEPT_PARAM_2 (id, typename T::light_id_type) +#define NBL_CONCEPT_PARAM_3 (v, typename T::vector3_type) +#define NBL_CONCEPT_PARAM_4 (interaction, typename T::interaction_type) +#define NBL_CONCEPT_PARAM_5 (is_bsdf, bool) +#define NBL_CONCEPT_PARAM_6 (depth, uint32_t) +NBL_CONCEPT_BEGIN(7) #define nee NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define ray NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 -#define scene NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 -#define id NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 -#define pdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 -#define quo_pdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 -#define v NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 -#define interaction NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_7 -#define b NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_8 -#define depth NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_9 +#define id NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define v NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define interaction NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +#define is_bsdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 +#define depth NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) @@ -144,17 +138,16 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE)(T::interaction_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferredEvalAndPdf(pdf, scene, id, ray)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.generate_and_quotient_and_pdf(quo_pdf, pdf, scene, id, v, interaction, b, v, depth)), ::nbl::hlsl::is_same_v, typename T::sample_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::eval_pdf_return_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::sample_quotient_return_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferred_eval_and_pdf(id, ray)), ::nbl::hlsl::is_same_v, typename T::eval_pdf_return_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.generate_and_quotient_and_pdf(id, v, interaction, is_bsdf, v, depth)), ::nbl::hlsl::is_same_v, typename T::sample_quotient_return_type)) ); #undef depth -#undef b +#undef is_bsdf #undef interaction #undef v -#undef quo_pdf -#undef pdf #undef id -#undef scene #undef ray #undef nee #include diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 583f49110c..f0659eb321 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -110,9 +110,9 @@ struct Unidirectional const uint32_t lightID = glsl::bitfieldExtract(bsdfLightIDs, 16, 16); if (lightID != light_type::INVALID_ID) { - scalar_type _pdf; - measure_type emissive = nee.deferredEvalAndPdf(_pdf, scene, lightID, ray) * throughput; - scalar_type _pdfSq = hlsl::mix(_pdf, _pdf * _pdf, _pdf < numeric_limits::max); + typename nee_type::eval_pdf_return_type ret = nee.deferred_eval_and_pdf(lightID, ray); + measure_type emissive = ret.radiance * throughput; + scalar_type _pdfSq = hlsl::mix(ret.pdf, ret.pdf * ret.pdf, ret.pdf < numeric_limits::max); emissive /= (1.0 + _pdfSq * ray.payload.otherTechniqueHeuristic); ray.payload.accumulation += emissive; } @@ -143,13 +143,13 @@ struct Unidirectional if (!partitionRandVariable(eps0.z, rcpChoiceProb) && depth < 2u) { uint32_t randLightID = uint32_t(float32_t(randGen.rng()) / numeric_limits::max) * nee.lightCount; - quotient_pdf_type neeContrib_pdf; - scalar_type t; - sample_type nee_sample = nee.generate_and_quotient_and_pdf( - neeContrib_pdf, t, - scene, randLightID, intersection, interaction, + typename nee_type::sample_quotient_return_type ret = nee.generate_and_quotient_and_pdf( + randLightID, intersection, interaction, isBSDF, eps0, depth ); + scalar_type t = ret.newRayMaxT; + sample_type nee_sample = ret.sample_; + quotient_pdf_type neeContrib_pdf = ret.quotient_pdf; // We don't allow non watertight transmitters in this renderer bool validPath = nee_sample.getNdotL() > numeric_limits::min && nee_sample.isValid(); @@ -241,6 +241,8 @@ struct Unidirectional ray_type ray = rayGen.generate(uvw); ray.initPayload(); + nee.scene = scene; + NBL_IF_CONSTEXPR (nee_type::IsPolygonMethodProjectedSolidAngle) { ray.normalAtOrigin = hlsl::promote(0.0); From 676831ec4caba8994a7a6db95f071f6bdced3caf Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 16 Feb 2026 17:11:11 +0700 Subject: [PATCH 28/69] scene does the mapping from object id to light/mat id --- .../builtin/hlsl/path_tracing/concepts.hlsl | 3 ++- .../hlsl/path_tracing/unidirectional.hlsl | 23 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index c493d1698a..d3901ca1aa 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -184,7 +184,8 @@ NBL_CONCEPT_BEGIN(3) NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) ((NBL_CONCEPT_REQ_TYPE)(T::object_handle_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((scene.getBsdfLightIDs(id)), ::nbl::hlsl::is_same_v, uint32_t)) + ((NBL_CONCEPT_REQ_TYPE)(T::mat_light_id_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((scene.getMatLightIDs(id)), ::nbl::hlsl::is_same_v, typename T::mat_light_id_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((scene.getNormal(id, intersectP)), ::nbl::hlsl::is_same_v, typename T::vector3_type)) ); #undef id diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index f0659eb321..7ccd591a15 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -95,7 +95,7 @@ struct Unidirectional const object_handle_type objectID = ray.objectID; const vector3_type intersection = ray.origin + ray.direction * ray.intersectionT; - uint32_t bsdfLightIDs = scene.getBsdfLightIDs(objectID); + typename scene_type::mat_light_id_type matLightID = scene.getMatLightIDs(objectID); vector3_type N = scene.getNormal(objectID, intersection); N = nbl::hlsl::normalize(N); ray_dir_info_type V; @@ -107,23 +107,22 @@ struct Unidirectional vector3_type throughput = ray.payload.throughput; // emissive - const uint32_t lightID = glsl::bitfieldExtract(bsdfLightIDs, 16, 16); - if (lightID != light_type::INVALID_ID) + if (matLightID.isLight()) { - typename nee_type::eval_pdf_return_type ret = nee.deferred_eval_and_pdf(lightID, ray); + typename nee_type::eval_pdf_return_type ret = nee.deferred_eval_and_pdf(matLightID.lightID, ray); measure_type emissive = ret.radiance * throughput; scalar_type _pdfSq = hlsl::mix(ret.pdf, ret.pdf * ret.pdf, ret.pdf < numeric_limits::max); emissive /= (1.0 + _pdfSq * ray.payload.otherTechniqueHeuristic); ray.payload.accumulation += emissive; } - const uint32_t bsdfID = glsl::bitfieldExtract(bsdfLightIDs, 0, 16); - if (bsdfID == bxdfnode_type::INVALID_ID) + if (!matLightID.isBxDF()) return false; - bxdfnode_type bxdf = materialSystem.bxdfs[bsdfID]; + const uint32_t matID = matLightID.matID; + bxdfnode_type bxdf = materialSystem.bxdfs[matID]; - const bool isBSDF = materialSystem.isBSDF(bsdfID); + const bool isBSDF = materialSystem.isBSDF(matID); vector3_type eps0 = rand3d(depth, _sample, 0u); vector3_type eps1 = rand3d(depth, _sample, 1u); @@ -157,14 +156,14 @@ struct Unidirectional bxdf::fresnel::OrientedEtas orientedEta = bxdf::fresnel::OrientedEtas::create(interaction.getNdotV(), hlsl::promote(monochromeEta)); anisocache_type _cache = anisocache_type::template create(interaction, nee_sample, orientedEta); validPath = validPath && _cache.getAbsNdotH() >= 0.0; - materialSystem.bxdfs[bsdfID].params.eta = monochromeEta; + materialSystem.bxdfs[matID].params.eta = monochromeEta; if (neeContrib_pdf.pdf < numeric_limits::max) { if (validPath) { // example only uses isotropic bxdfs - quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(bsdfID, nee_sample, interaction.isotropic, _cache.iso_cache); + quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(matID, nee_sample, interaction.isotropic, _cache.iso_cache); neeContrib_pdf.quotient *= bxdf.albedo * throughput * bsdf_quotient_pdf.quotient; const scalar_type otherGenOverChoice = bsdf_quotient_pdf.pdf * rcpChoiceProb; const scalar_type otherGenOverLightAndChoice = otherGenOverChoice / bsdf_quotient_pdf.pdf; @@ -185,14 +184,14 @@ struct Unidirectional vector3_type bxdfSample; { anisocache_type _cache; - sample_type bsdf_sample = materialSystem.generate(bsdfID, interaction, eps1, _cache); + sample_type bsdf_sample = materialSystem.generate(matID, interaction, eps1, _cache); if (!bsdf_sample.isValid()) return false; // example only uses isotropic bxdfs // the value of the bsdf divided by the probability of the sample being generated - quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(bsdfID, bsdf_sample, interaction.isotropic, _cache.iso_cache); + quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(matID, bsdf_sample, interaction.isotropic, _cache.iso_cache); throughput *= bxdf.albedo * bsdf_quotient_pdf.quotient; bxdfPdf = bsdf_quotient_pdf.pdf; bxdfSample = bsdf_sample.getL().getDirection(); From 3a90b0cd2877a14c0b14c2ce3e17ae3d28d8f15a Mon Sep 17 00:00:00 2001 From: Arkadiusz Lachowicz Date: Mon, 16 Feb 2026 16:05:39 +0100 Subject: [PATCH 29/69] Finalize RWMC review fixes --- examples_tests | 2 +- .../builtin/hlsl/rwmc/CascadeAccumulator.hlsl | 55 ++-- .../builtin/hlsl/rwmc/ResolveParameters.hlsl | 39 ++- .../hlsl/rwmc/SplattingParameters.hlsl | 54 ++-- include/nbl/builtin/hlsl/rwmc/resolve.hlsl | 258 +++++++++--------- 5 files changed, 225 insertions(+), 183 deletions(-) diff --git a/examples_tests b/examples_tests index 4246192d21..130e049899 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 4246192d213c0c42a508257bd219c1fca0cb238f +Subproject commit 130e049899be16b50a5d27d7c67310a24d953530 diff --git a/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl b/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl index cdbb8271d2..e4bc9aa4fa 100644 --- a/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl @@ -13,28 +13,36 @@ namespace hlsl namespace rwmc { -template) +template && concepts::UnsignedIntegralScalar) struct DefaultCascades { using layer_type = CascadeLayerType; + using sample_count_type = SampleCountType; + NBL_CONSTEXPR uint32_t cascade_count = CascadeCount; - uint32_t cascadeSampleCounter[CascadeCount]; - CascadeLayerType data[CascadeCount]; + sample_count_type cascadeSampleCounter[cascade_count]; + CascadeLayerType data[cascade_count]; + + void clear(uint32_t cascadeIx) + { + cascadeSampleCounter[cascadeIx] = sample_count_type(0u); + data[cascadeIx] = promote(0.0f); + } void addSampleIntoCascadeEntry(CascadeLayerType _sample, uint16_t lowerCascadeIndex, SplattingParameters::scalar_t lowerCascadeLevelWeight, SplattingParameters::scalar_t higherCascadeLevelWeight, uint32_t sampleCount) { - const float reciprocalSampleCount = 1.0f / float(sampleCount); + const SplattingParameters::scalar_t reciprocalSampleCount = SplattingParameters::scalar_t(1.0f) / SplattingParameters::scalar_t(sampleCount); - uint32_t lowerCascadeSampleCount = cascadeSampleCounter[lowerCascadeIndex]; + sample_count_type lowerCascadeSampleCount = cascadeSampleCounter[lowerCascadeIndex]; data[lowerCascadeIndex] += (_sample * lowerCascadeLevelWeight - (sampleCount - lowerCascadeSampleCount) * data[lowerCascadeIndex]) * reciprocalSampleCount; - cascadeSampleCounter[lowerCascadeIndex] = sampleCount; + cascadeSampleCounter[lowerCascadeIndex] = sample_count_type(sampleCount); - uint16_t higherCascadeIndex = lowerCascadeIndex + 1u; - if (higherCascadeIndex < CascadeCount) + uint16_t higherCascadeIndex = lowerCascadeIndex + uint16_t(1u); + if (higherCascadeIndex < cascade_count) { - uint32_t higherCascadeSampleCount = cascadeSampleCounter[higherCascadeIndex]; + sample_count_type higherCascadeSampleCount = cascadeSampleCounter[higherCascadeIndex]; data[higherCascadeIndex] += (_sample * higherCascadeLevelWeight - (sampleCount - higherCascadeSampleCount) * data[higherCascadeIndex]) * reciprocalSampleCount; - cascadeSampleCounter[higherCascadeIndex] = sampleCount; + cascadeSampleCounter[higherCascadeIndex] = sample_count_type(sampleCount); } } }; @@ -43,9 +51,9 @@ template struct CascadeAccumulator { using input_sample_type = typename CascadesType::layer_type; - using cascade_layer_scalar_type = typename vector_traits::scalar_type; using this_t = CascadeAccumulator; using output_storage_type = CascadesType; + NBL_CONSTEXPR uint32_t cascade_count = output_storage_type::cascade_count; output_storage_type accumulation; SplattingParameters splattingParameters; @@ -53,11 +61,8 @@ struct CascadeAccumulator static this_t create(NBL_CONST_REF_ARG(SplattingParameters) settings) { this_t retval; - for (int i = 0; i < CascadeCount; ++i) - { - retval.accumulation.data[i] = promote(0.0f); - retval.accumulation.cascadeSampleCounter[i] = 0u; - } + for (uint32_t i = 0u; i < cascade_count; ++i) + retval.accumulation.clear(i); retval.splattingParameters = settings; return retval; @@ -66,16 +71,16 @@ struct CascadeAccumulator // most of this code is stolen from https://cg.ivd.kit.edu/publications/2018/rwmc/tool/split.cpp void addSample(uint32_t sampleCount, input_sample_type _sample) { - const float32_t2 unpackedParams = hlsl::unpackHalf2x16(splattingParameters.packedLog2); - const SplattingParameters::scalar_t log2Start = unpackedParams[0]; - const SplattingParameters::scalar_t rcpLog2Base = unpackedParams[1]; - const SplattingParameters::scalar_t luma = splattingParameters.getLuma(_sample); + const SplattingParameters::scalar_t baseRootOfStart = splattingParameters.baseRootOfStart(); + const SplattingParameters::scalar_t rcpLog2Base = splattingParameters.rcpLog2Base(); + const SplattingParameters::scalar_t luma = splattingParameters.calcLuma(_sample); const SplattingParameters::scalar_t log2Luma = log2(luma); - const SplattingParameters::scalar_t cascade = log2Luma * rcpLog2Base - log2Start * rcpLog2Base; - const SplattingParameters::scalar_t lastCascade = CascadeCount - 1; + const SplattingParameters::scalar_t log2BaseRootOfStart = log2(baseRootOfStart); + const SplattingParameters::scalar_t cascade = log2Luma * rcpLog2Base - log2BaseRootOfStart; + const SplattingParameters::scalar_t lastCascade = cascade_count - 1u; const SplattingParameters::scalar_t clampedCascade = clamp(cascade, 0, lastCascade); // c<=0 -> 0, c>=Count-1 -> Count-1 - uint16_t lowerCascadeIndex = floor(cascade); + uint16_t lowerCascadeIndex = uint16_t(floor(clampedCascade)); // 0 whenever clamped or `cascade` is integer (when `clampedCascade` is integer) SplattingParameters::scalar_t higherCascadeWeight = clampedCascade - floor(clampedCascade); // never 0 thanks to magic of `1-fract(x)` @@ -83,7 +88,7 @@ struct CascadeAccumulator // handle super bright sample case if (cascade > lastCascade) - lowerCascadeWeight = exp2(log2Start + (1.0f/rcpLog2Base) * (lastCascade) - log2Luma); + lowerCascadeWeight = exp2((log2BaseRootOfStart + lastCascade) / rcpLog2Base - log2Luma); accumulation.addSampleIntoCascadeEntry(_sample, lowerCascadeIndex, lowerCascadeWeight, higherCascadeWeight, sampleCount); } @@ -95,4 +100,4 @@ struct CascadeAccumulator } } -#endif \ No newline at end of file +#endif diff --git a/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl b/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl index 17e3f8d720..c9a79d1149 100644 --- a/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl @@ -1,7 +1,8 @@ #ifndef _NBL_BUILTIN_HLSL_RWMC_RESOLVE_PARAMETERS_HLSL_INCLUDED_ -#define _NBL_BUILTIN_HLSL_RWMC_RESOLVE_PARAMETERS_HLSL_INCLUDED_ - -#include "nbl/builtin/hlsl/cpp_compat.hlsl" +#define _NBL_BUILTIN_HLSL_RWMC_RESOLVE_PARAMETERS_HLSL_INCLUDED_ + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include namespace nbl { @@ -10,11 +11,13 @@ namespace hlsl namespace rwmc { -struct ResolveParameters -{ - static ResolveParameters create(float base, uint32_t sampleCount, float minReliableLuma, float kappa) - { - ResolveParameters retval; +struct ResolveParameters +{ + using scalar_t = float32_t; + + static ResolveParameters create(float base, uint32_t sampleCount, float minReliableLuma, float kappa) + { + ResolveParameters retval; retval.initialEmin = minReliableLuma; retval.reciprocalBase = 1.f / base; const float N = float(sampleCount); @@ -25,12 +28,18 @@ struct ResolveParameters retval.colorReliabilityFactor = base + (1.f - base) * retval.reciprocalKappa; retval.NOverKappa = N * retval.reciprocalKappa; - return retval; - } - - float initialEmin; // a minimum image brightness that we always consider reliable - float reciprocalBase; - float reciprocalN; + return retval; + } + + template + scalar_t calcLuma(NBL_CONST_REF_ARG(SampleType) col) + { + return hlsl::dot(hlsl::transpose(colorspace::scRGBtoXYZ)[1], col); + } + + float initialEmin; // a minimum image brightness that we always consider reliable + float reciprocalBase; + float reciprocalN; float reciprocalKappa; float colorReliabilityFactor; float NOverKappa; @@ -40,4 +49,4 @@ struct ResolveParameters } } -#endif \ No newline at end of file +#endif diff --git a/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl b/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl index 74b062a2de..a7c95e9e27 100644 --- a/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl @@ -1,7 +1,8 @@ #ifndef _NBL_BUILTIN_HLSL_RWMC_SPLATTING_PARAMETERS_HLSL_INCLUDED_ -#define _NBL_BUILTIN_HLSL_RWMC_SPLATTING_PARAMETERS_HLSL_INCLUDED_ - -#include "nbl/builtin/hlsl/cpp_compat.hlsl" +#define _NBL_BUILTIN_HLSL_RWMC_SPLATTING_PARAMETERS_HLSL_INCLUDED_ + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include namespace nbl { @@ -10,24 +11,39 @@ namespace hlsl namespace rwmc { -struct SplattingParameters -{ - using scalar_t = float32_t; - - // float16_t log2Start; 0 - // float16_t rcpLog2Base; 1 - // pack as Half2x16 - int32_t packedLog2; - - template - scalar_t getLuma(NBL_CONST_REF_ARG(CascadeLayerType) col) - { - return hlsl::dot(hlsl::transpose(colorspace::scRGBtoXYZ)[1], col); - } -}; +struct SplattingParameters +{ + using scalar_t = float32_t; + + // float16_t baseRootOfStart; 0 + // float16_t rcpLog2Base; 1 + // pack as Half2x16 + int32_t packedLog2; + + float32_t2 unpackedLog2Parameters() + { + return hlsl::unpackHalf2x16(packedLog2); + } + + scalar_t baseRootOfStart() + { + return unpackedLog2Parameters()[0]; + } + + scalar_t rcpLog2Base() + { + return unpackedLog2Parameters()[1]; + } + + template + scalar_t calcLuma(NBL_CONST_REF_ARG(CascadeLayerType) col) + { + return hlsl::dot(hlsl::transpose(colorspace::scRGBtoXYZ)[1], col); + } +}; } } } -#endif \ No newline at end of file +#endif diff --git a/include/nbl/builtin/hlsl/rwmc/resolve.hlsl b/include/nbl/builtin/hlsl/rwmc/resolve.hlsl index a3b4236dca..ac65c2ed03 100644 --- a/include/nbl/builtin/hlsl/rwmc/resolve.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/resolve.hlsl @@ -1,93 +1,105 @@ -#ifndef _NBL_BUILTIN_HLSL_RWMC_RESOLVE_HLSL_INCLUDED_ -#define _NBL_BUILTIN_HLSL_RWMC_RESOLVE_HLSL_INCLUDED_ - -#include "nbl/builtin/hlsl/cpp_compat.hlsl" -#include -#include -#include -#include -#include +#ifndef _NBL_BUILTIN_HLSL_RWMC_RESOLVE_HLSL_INCLUDED_ +#define _NBL_BUILTIN_HLSL_RWMC_RESOLVE_HLSL_INCLUDED_ + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include +#include +#include namespace nbl { namespace hlsl { -namespace rwmc -{ - // declare concept -#define NBL_CONCEPT_NAME ResolveAccessorBase -#define NBL_CONCEPT_TPLT_PRM_KINDS (typename)(typename)(int32_t) -#define NBL_CONCEPT_TPLT_PRM_NAMES (T)(VectorScalarType)(Components) -// not the greatest syntax but works -#define NBL_CONCEPT_PARAM_0 (a,T) -#define NBL_CONCEPT_PARAM_1 (vec,vector) -// start concept -NBL_CONCEPT_BEGIN(2) -// need to be defined AFTER the concept begins -#define a NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 -#define vec NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 -NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.calcLuma(vec)), ::nbl::hlsl::is_same_v, VectorScalarType)) - ((NBL_CONCEPT_REQ_TYPE)(T::output_t)) -); -#undef a -#undef vec -#include - -/* ResolveAccessor is required to: -* - satisfy `LoadableImage` concept requirements -* - implement function called `calcLuma` which calculates luma from a 3 component pixel value -*/ - -template -NBL_BOOL_CONCEPT ResolveAccessor = ResolveAccessorBase && concepts::accessors::LoadableImage; - -template -struct ResolveAccessorAdaptor -{ - using output_scalar_t = OutputScalar; - NBL_CONSTEXPR int32_t Components = 3; +namespace rwmc +{ +// declare concept +#define NBL_CONCEPT_NAME ResolveLumaParamsBase +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename)(typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T)(SampleType) +#define NBL_CONCEPT_PARAM_0 (a,T) +#define NBL_CONCEPT_PARAM_1 (sample,SampleType) +NBL_CONCEPT_BEGIN(2) +#define a NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_t)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointScalar, T::scalar_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template calcLuma(sample)), ::nbl::hlsl::is_same_v, typename T::scalar_t)) +); +#undef sample +#undef a +#include + +template +NBL_BOOL_CONCEPT ResolveLumaParams = ResolveLumaParamsBase; + +// declare concept +#define NBL_CONCEPT_NAME ResolveAccessorBase +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (a,T) +#define NBL_CONCEPT_PARAM_1 (uv,vector) +#define NBL_CONCEPT_PARAM_2 (layer,uint16_t) +NBL_CONCEPT_BEGIN(3) +#define a NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define uv NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define layer NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::output_t)) + ((NBL_CONCEPT_REQ_TYPE)(T::output_scalar_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,layer)), ::nbl::hlsl::is_same_v, typename T::output_t)) +); +#undef layer +#undef uv +#undef a +#include + +template +NBL_BOOL_CONCEPT ResolveAccessor = ResolveAccessorBase && concepts::accessors::LoadableImage; + +template +struct ResolveAccessorAdaptor +{ + using output_scalar_t = OutputScalar; + NBL_CONSTEXPR int32_t Components = 3; using output_t = vector; NBL_CONSTEXPR int32_t image_dimension = 2; - - RWTexture2DArray cascade; - - float32_t calcLuma(NBL_REF_ARG(float32_t3) col) - { - return hlsl::dot(colorspace::scRGB::ToXYZ()[1], col); - } - - template - output_t get(vector uv, uint16_t layer) - { + + RWTexture2DArray cascade; + + template + output_t get(vector uv, uint16_t layer) + { uint32_t imgWidth, imgHeight, layers; - cascade.GetDimensions(imgWidth, imgHeight, layers); - int16_t2 cascadeImageDimension = int16_t2(imgWidth, imgHeight); - - if (any(uv < int16_t2(0, 0)) || any(uv > cascadeImageDimension)) - return promote(0); + cascade.GetDimensions(imgWidth, imgHeight, layers); + int16_t2 cascadeImageDimension = int16_t2(imgWidth, imgHeight); + + if (any(uv < int16_t2(0, 0)) || any(uv >= cascadeImageDimension)) + return promote(0); return cascade.Load(int32_t3(uv, int32_t(layer))); } }; -template) -struct Resolver -{ - using output_t = typename CascadeAccessor::output_t; - using scalar_t = typename vector_traits::scalar_type; - - struct CascadeSample - { - float32_t3 centerValue; - float normalizedCenterLuma; - float normalizedNeighbourhoodAverageLuma; - }; - - static Resolver create(NBL_REF_ARG(ResolveParameters) resolveParameters) - { - Resolver retval; - retval.params = resolveParameters; +template && ResolveLumaParams) +struct Resolver +{ + using output_t = typename CascadeAccessor::output_t; + using output_scalar_t = typename vector_traits::scalar_type; + using scalar_t = typename ResolveParamsType::scalar_t; + NBL_CONSTEXPR static uint16_t last_cascade = CascadeCount - 1u; + + struct CascadeSample + { + output_t centerValue; + float normalizedCenterLuma; + float normalizedNeighbourhoodAverageLuma; + }; + + static Resolver create(NBL_REF_ARG(ResolveParamsType) resolveParameters) + { + Resolver retval; + retval.params = resolveParameters; return retval; } @@ -97,18 +109,18 @@ struct Resolver scalar_t reciprocalBaseI = 1.f; CascadeSample curr = __sampleCascade(acc, coord, 0u, reciprocalBaseI); - output_t accumulation = promote(0.0f); - scalar_t Emin = params.initialEmin; - - scalar_t prevNormalizedCenterLuma, prevNormalizedNeighbourhoodAverageLuma; - NBL_UNROLL - for (int16_t i = 0u; i <= CascadeCount - 1; i++) - { - const bool notFirstCascade = i != 0; - const bool notLastCascade = i != (CascadeCount - 1); - - CascadeSample next; - if (notLastCascade) + output_t accumulation = promote(0.0f); + scalar_t Emin = params.initialEmin; + + scalar_t prevNormalizedCenterLuma, prevNormalizedNeighbourhoodAverageLuma; + NBL_UNROLL + for (uint16_t i = 0u; i <= last_cascade; i++) + { + const bool notFirstCascade = i != 0; + const bool notLastCascade = i != last_cascade; + + CascadeSample next; + if (notLastCascade) { reciprocalBaseI *= params.reciprocalBase; next = __sampleCascade(acc, coord, int16_t(i + 1), reciprocalBaseI); @@ -132,11 +144,11 @@ struct Resolver globalReliability += next.normalizedNeighbourhoodAverageLuma; } // check if above minimum sampling threshold (avg 9 sample occurences in 3x3 neighbourhood), then use per-pixel reliability (NOTE: tertiary op is in reverse) - reliability = globalReliability < params.reciprocalN ? globalReliability : localReliability; - { - const scalar_t accumLuma = acc.calcLuma(accumulation); - if (accumLuma > Emin) - Emin = accumLuma; + reliability = globalReliability < params.reciprocalN ? globalReliability : localReliability; + { + const scalar_t accumLuma = params.template calcLuma(accumulation); + if (accumLuma > Emin) + Emin = accumLuma; const scalar_t colorReliability = Emin * reciprocalBaseI * params.colorReliabilityFactor; @@ -153,40 +165,40 @@ struct Resolver curr = next; } - return accumulation; - } - - ResolveParameters params; + return accumulation; + } + + ResolveParamsType params; // pseudo private stuff: - CascadeSample __sampleCascade(NBL_REF_ARG(CascadeAccessor) acc, int16_t2 coord, uint16_t cascadeIndex, scalar_t reciprocalBaseI) - { - output_t neighbourhood[9]; - neighbourhood[0] = acc.template get(coord + int16_t2(-1, -1), cascadeIndex); - neighbourhood[1] = acc.template get(coord + int16_t2(0, -1), cascadeIndex); - neighbourhood[2] = acc.template get(coord + int16_t2(1, -1), cascadeIndex); - neighbourhood[3] = acc.template get(coord + int16_t2(-1, 0), cascadeIndex); - neighbourhood[4] = acc.template get(coord + int16_t2(0, 0), cascadeIndex); - neighbourhood[5] = acc.template get(coord + int16_t2(1, 0), cascadeIndex); - neighbourhood[6] = acc.template get(coord + int16_t2(-1, 1), cascadeIndex); - neighbourhood[7] = acc.template get(coord + int16_t2(0, 1), cascadeIndex); - neighbourhood[8] = acc.template get(coord + int16_t2(1, 1), cascadeIndex); - - // numerical robustness - float32_t3 excl_hood_sum = ((neighbourhood[0] + neighbourhood[1]) + (neighbourhood[2] + neighbourhood[3])) + - ((neighbourhood[5] + neighbourhood[6]) + (neighbourhood[7] + neighbourhood[8])); - - CascadeSample retval; - retval.centerValue = neighbourhood[4]; - retval.normalizedNeighbourhoodAverageLuma = retval.normalizedCenterLuma = acc.calcLuma(neighbourhood[4]) * reciprocalBaseI; - retval.normalizedNeighbourhoodAverageLuma = (acc.calcLuma(excl_hood_sum) * reciprocalBaseI + retval.normalizedNeighbourhoodAverageLuma) / 9.f; - return retval; - } -}; + CascadeSample __sampleCascade(NBL_REF_ARG(CascadeAccessor) acc, int16_t2 coord, uint16_t cascadeIndex, scalar_t reciprocalBaseI) + { + output_t neighbourhood[9]; + neighbourhood[0] = acc.template get(coord + int16_t2(-1, -1), cascadeIndex); + neighbourhood[1] = acc.template get(coord + int16_t2(0, -1), cascadeIndex); + neighbourhood[2] = acc.template get(coord + int16_t2(1, -1), cascadeIndex); + neighbourhood[3] = acc.template get(coord + int16_t2(-1, 0), cascadeIndex); + neighbourhood[4] = acc.template get(coord + int16_t2(0, 0), cascadeIndex); + neighbourhood[5] = acc.template get(coord + int16_t2(1, 0), cascadeIndex); + neighbourhood[6] = acc.template get(coord + int16_t2(-1, 1), cascadeIndex); + neighbourhood[7] = acc.template get(coord + int16_t2(0, 1), cascadeIndex); + neighbourhood[8] = acc.template get(coord + int16_t2(1, 1), cascadeIndex); + + // numerical robustness + output_t excl_hood_sum = ((neighbourhood[0] + neighbourhood[1]) + (neighbourhood[2] + neighbourhood[3])) + + ((neighbourhood[5] + neighbourhood[6]) + (neighbourhood[7] + neighbourhood[8])); + + CascadeSample retval; + retval.centerValue = neighbourhood[4]; + retval.normalizedNeighbourhoodAverageLuma = retval.normalizedCenterLuma = params.template calcLuma(neighbourhood[4]) * reciprocalBaseI; + retval.normalizedNeighbourhoodAverageLuma = (params.template calcLuma(excl_hood_sum) * reciprocalBaseI + retval.normalizedNeighbourhoodAverageLuma) / 9.f; + return retval; + } +}; } } } -#endif \ No newline at end of file +#endif From 187ced659281777cd98d5b3d449b7ab1aea47990 Mon Sep 17 00:00:00 2001 From: Arkadiusz Lachowicz Date: Mon, 16 Feb 2026 16:42:32 +0100 Subject: [PATCH 30/69] Restrict Resolver template parameters to review scope --- include/nbl/builtin/hlsl/rwmc/resolve.hlsl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/nbl/builtin/hlsl/rwmc/resolve.hlsl b/include/nbl/builtin/hlsl/rwmc/resolve.hlsl index ac65c2ed03..84b0d44580 100644 --- a/include/nbl/builtin/hlsl/rwmc/resolve.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/resolve.hlsl @@ -81,12 +81,12 @@ struct ResolveAccessorAdaptor } }; -template && ResolveLumaParams) +template && ResolveLumaParams) struct Resolver { using output_t = typename CascadeAccessor::output_t; using output_scalar_t = typename vector_traits::scalar_type; - using scalar_t = typename ResolveParamsType::scalar_t; + using scalar_t = typename ResolveParameters::scalar_t; NBL_CONSTEXPR static uint16_t last_cascade = CascadeCount - 1u; struct CascadeSample @@ -96,7 +96,7 @@ struct Resolver float normalizedNeighbourhoodAverageLuma; }; - static Resolver create(NBL_REF_ARG(ResolveParamsType) resolveParameters) + static Resolver create(NBL_REF_ARG(ResolveParameters) resolveParameters) { Resolver retval; retval.params = resolveParameters; @@ -168,7 +168,7 @@ struct Resolver return accumulation; } - ResolveParamsType params; + ResolveParameters params; // pseudo private stuff: From a60406459b66c073e4f4ce929364a1387ecca13c Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 17 Feb 2026 13:59:59 +0700 Subject: [PATCH 31/69] renamed constants in camelcase --- .../nbl/builtin/hlsl/path_tracing/default_accumulator.hlsl | 2 +- include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/default_accumulator.hlsl b/include/nbl/builtin/hlsl/path_tracing/default_accumulator.hlsl index d9df50e023..e4174a7c12 100644 --- a/include/nbl/builtin/hlsl/path_tracing/default_accumulator.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/default_accumulator.hlsl @@ -29,7 +29,7 @@ struct DefaultAccumulator void addSample(uint32_t sampleCount, input_sample_type _sample) { - scalar_type rcpSampleSize = 1.0 / (sampleCount); + scalar_type rcpSampleSize = scalar_type(1.0) / _static_cast(sampleCount); accumulation += (_sample - accumulation) * rcpSampleSize; } diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 7ccd591a15..448217b24e 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -79,7 +79,7 @@ struct Unidirectional vector3_type rand3d(uint32_t protoDimension, uint32_t _sample, uint32_t i) { using sequence_type = sampling::QuantizedSequence; - uint32_t address = glsl::bitfieldInsert(protoDimension, _sample, MAX_DEPTH_LOG2, MAX_SAMPLES_LOG2); + uint32_t address = glsl::bitfieldInsert(protoDimension, _sample, MaxDepthLog2, MaxSamplesLog2); sequence_type tmpSeq = vk::RawBufferLoad(pSampleBuffer + (address + i) * sizeof(sequence_type)); return tmpSeq.template decode(randGen()); } @@ -271,8 +271,8 @@ struct Unidirectional // TODO: russian roulette early exit? } - NBL_CONSTEXPR_STATIC_INLINE uint32_t MAX_DEPTH_LOG2 = 4u; - NBL_CONSTEXPR_STATIC_INLINE uint32_t MAX_SAMPLES_LOG2 = 10u; + NBL_CONSTEXPR_STATIC_INLINE uint32_t MaxDepthLog2 = 4u; + NBL_CONSTEXPR_STATIC_INLINE uint32_t MaxSamplesLog2 = 10u; randgen_type randGen; raygen_type rayGen; From 87c7fb18e33d476946c7bcb510a10298669ce3b0 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 17 Feb 2026 16:22:59 +0700 Subject: [PATCH 32/69] intersector returns a struct with intersection point, interaction, hit bool --- .../builtin/hlsl/path_tracing/concepts.hlsl | 3 +- .../hlsl/path_tracing/unidirectional.hlsl | 37 +++++++------------ 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index d3901ca1aa..a5a5be275d 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -57,7 +57,8 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scene_type)) ((NBL_CONCEPT_REQ_TYPE)(T::ray_type)) ((NBL_CONCEPT_REQ_TYPE)(T::object_handle_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceRay(ray, scene)), ::nbl::hlsl::is_same_v, typename T::object_handle_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::intersect_data_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceRay(ray, scene)), ::nbl::hlsl::is_same_v, typename T::intersect_data_type)) ); #undef scene #undef ray diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 448217b24e..426bba759f 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -59,23 +59,17 @@ struct Unidirectional using vector3_type = vector; using monochrome_type = vector; using measure_type = typename MaterialSystem::measure_type; - using output_storage_type = typename Accumulator::output_storage_type; // ? using sample_type = typename NextEventEstimator::sample_type; using ray_dir_info_type = typename sample_type::ray_dir_info_type; using ray_type = typename RayGen::ray_type; using object_handle_type = typename Intersector::object_handle_type; - using light_type = typename NextEventEstimator::light_type; + using intersect_data_type = typename Intersector::intersect_data_type; using bxdfnode_type = typename MaterialSystem::bxdfnode_type; using anisotropic_interaction_type = typename MaterialSystem::anisotropic_interaction_type; using isotropic_interaction_type = typename anisotropic_interaction_type::isotropic_interaction_type; using anisocache_type = typename MaterialSystem::anisocache_type; - using isocache_type = typename anisocache_type::isocache_type; using quotient_pdf_type = typename NextEventEstimator::quotient_pdf_type; - using diffuse_op_type = typename MaterialSystem::diffuse_op_type; - using conductor_op_type = typename MaterialSystem::conductor_op_type; - using dielectric_op_type = typename MaterialSystem::dielectric_op_type; - vector3_type rand3d(uint32_t protoDimension, uint32_t _sample, uint32_t i) { using sequence_type = sampling::QuantizedSequence; @@ -90,23 +84,19 @@ struct Unidirectional } // TODO: probably will only work with isotropic surfaces, need to do aniso - bool closestHitProgram(uint32_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(scene_type) scene) + bool closestHitProgram(uint32_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(intersect_data_type) intersectData, NBL_CONST_REF_ARG(scene_type) scene) { - const object_handle_type objectID = ray.objectID; - const vector3_type intersection = ray.origin + ray.direction * ray.intersectionT; - - typename scene_type::mat_light_id_type matLightID = scene.getMatLightIDs(objectID); - vector3_type N = scene.getNormal(objectID, intersection); - N = nbl::hlsl::normalize(N); - ray_dir_info_type V; - V.setDirection(-ray.direction); - isotropic_interaction_type iso_interaction = isotropic_interaction_type::create(V, N); + const vector3_type intersection = intersectData.intersection; + + isotropic_interaction_type iso_interaction = intersectData.iso_interaction; + anisotropic_interaction_type interaction = intersectData.aniso_interaction; iso_interaction.luminosityContributionHint = colorspace::scRGBtoXYZ[1]; - anisotropic_interaction_type interaction = anisotropic_interaction_type::create(iso_interaction); + interaction.isotropic.luminosityContributionHint = colorspace::scRGBtoXYZ[1]; vector3_type throughput = ray.payload.throughput; // emissive + typename scene_type::mat_light_id_type matLightID = scene.getMatLightIDs(ray.objectID); if (matLightID.isLight()) { typename nee_type::eval_pdf_return_type ret = nee.deferred_eval_and_pdf(matLightID.lightID, ray); @@ -173,7 +163,7 @@ struct Unidirectional nee_ray.origin = intersection + nee_sample.getL().getDirection() * t * Tolerance::getStart(depth); nee_ray.direction = nee_sample.getL().getDirection(); nee_ray.intersectionT = t; - if (bsdf_quotient_pdf.pdf < numeric_limits::max && getLuma(neeContrib_pdf.quotient) > lumaContributionThreshold && intersector_type::traceRay(nee_ray, scene).id == -1) + if (bsdf_quotient_pdf.pdf < numeric_limits::max && getLuma(neeContrib_pdf.quotient) > lumaContributionThreshold && !intersector_type::traceRay(nee_ray, scene).foundHit) ray.payload.accumulation += neeContrib_pdf.quotient; } } @@ -233,7 +223,7 @@ struct Unidirectional } // Li - void sampleMeasure(uint32_t sampleIndex, uint32_t maxDepth, NBL_CONST_REF_ARG(scene_type) scene, NBL_REF_ARG(Accumulator) accumulator) + void sampleMeasure(uint32_t sampleIndex, uint32_t maxDepth, NBL_REF_ARG(Accumulator) accumulator) { //scalar_type meanLumaSq = 0.0; vector3_type uvw = rand3d(0u, sampleIndex, 0u); @@ -254,11 +244,11 @@ struct Unidirectional for (int d = 1; (d <= maxDepth) && hit && rayAlive; d += 2) { ray.intersectionT = numeric_limits::max; - ray.objectID = intersector_type::traceRay(ray, scene); + intersect_data_type intersection = intersector_type::traceRay(ray, scene); - hit = ray.objectID.id != -1; + hit = intersection.foundHit; if (hit) - rayAlive = closestHitProgram(1, sampleIndex, ray, scene); + rayAlive = closestHitProgram(d, sampleIndex, ray, intersection, scene); } if (!hit) missProgram(ray); @@ -278,6 +268,7 @@ struct Unidirectional raygen_type rayGen; material_system_type materialSystem; nee_type nee; + scene_type scene; uint64_t pSampleBuffer; }; From 9d98071b66862aa77496b6f1147faf06b861abf9 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 17 Feb 2026 16:39:56 +0700 Subject: [PATCH 33/69] randGen should return a unorm float, everything that rand3d() does with quantized sequence too --- .../builtin/hlsl/path_tracing/concepts.hlsl | 7 +++++-- .../hlsl/path_tracing/unidirectional.hlsl | 19 ++++--------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index a5a5be275d..6cc9fd5b1f 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -16,13 +16,16 @@ namespace concepts #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (rand, T) -NBL_CONCEPT_BEGIN(1) +#define NBL_CONCEPT_PARAM_1 (sample_, uint32_t) +NBL_CONCEPT_BEGIN(2) #define rand NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define sample_ NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::rng_type)) ((NBL_CONCEPT_REQ_TYPE)(T::return_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rand()), ::nbl::hlsl::is_same_v, typename T::return_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rand(sample_, sample_, sample_)), ::nbl::hlsl::is_same_v, typename T::return_type)) ); +#undef sample_ #undef rand #include diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 426bba759f..cc6133a1e5 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -6,7 +6,6 @@ #include #include #include -#include #include #include @@ -70,20 +69,12 @@ struct Unidirectional using anisocache_type = typename MaterialSystem::anisocache_type; using quotient_pdf_type = typename NextEventEstimator::quotient_pdf_type; - vector3_type rand3d(uint32_t protoDimension, uint32_t _sample, uint32_t i) - { - using sequence_type = sampling::QuantizedSequence; - uint32_t address = glsl::bitfieldInsert(protoDimension, _sample, MaxDepthLog2, MaxSamplesLog2); - sequence_type tmpSeq = vk::RawBufferLoad(pSampleBuffer + (address + i) * sizeof(sequence_type)); - return tmpSeq.template decode(randGen()); - } - scalar_type getLuma(NBL_CONST_REF_ARG(vector3_type) col) { return hlsl::dot(colorspace::scRGBtoXYZ[1], col); } - // TODO: probably will only work with isotropic surfaces, need to do aniso + // TODO: will only work with isotropic surfaces, need to do aniso bool closestHitProgram(uint32_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(intersect_data_type) intersectData, NBL_CONST_REF_ARG(scene_type) scene) { const vector3_type intersection = intersectData.intersection; @@ -114,8 +105,8 @@ struct Unidirectional const bool isBSDF = materialSystem.isBSDF(matID); - vector3_type eps0 = rand3d(depth, _sample, 0u); - vector3_type eps1 = rand3d(depth, _sample, 1u); + vector3_type eps0 = randGen(depth, _sample, 0u); + vector3_type eps1 = randGen(depth, _sample, 1u); // thresholds const scalar_type bxdfPdfThreshold = 0.0001; @@ -226,7 +217,7 @@ struct Unidirectional void sampleMeasure(uint32_t sampleIndex, uint32_t maxDepth, NBL_REF_ARG(Accumulator) accumulator) { //scalar_type meanLumaSq = 0.0; - vector3_type uvw = rand3d(0u, sampleIndex, 0u); + vector3_type uvw = randGen(0u, sampleIndex, 0u); ray_type ray = rayGen.generate(uvw); ray.initPayload(); @@ -269,8 +260,6 @@ struct Unidirectional material_system_type materialSystem; nee_type nee; scene_type scene; - - uint64_t pSampleBuffer; }; } From acd91aad1b920b0cc3e04a7fa01a96c7296de17e Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 17 Feb 2026 17:02:42 +0700 Subject: [PATCH 34/69] refactor ray struct to be polymorphic on polygon method, new methods --- .../hlsl/path_tracing/basic_ray_gen.hlsl | 7 +++---- .../hlsl/path_tracing/unidirectional.hlsl | 17 ++++------------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/basic_ray_gen.hlsl b/include/nbl/builtin/hlsl/path_tracing/basic_ray_gen.hlsl index e451789977..ab1da9febf 100644 --- a/include/nbl/builtin/hlsl/path_tracing/basic_ray_gen.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/basic_ray_gen.hlsl @@ -24,15 +24,14 @@ struct BasicRayGenerator ray_type generate(const vector3_type randVec) { - ray_type ray; - ray.origin = camPos; - vector4_type tmp = NDC; GaussianFilter filter = GaussianFilter::create(2.5, 1.5); // stochastic reconstruction filter tmp.xy += pixOffsetParam * filter.sample(randVec); // for depth of field we could do another stochastic point-pick tmp = nbl::hlsl::mul(invMVP, tmp); - ray.direction = nbl::hlsl::normalize(tmp.xyz / tmp.w - camPos); + + ray_type ray; + ray.initData(camPos, hlsl::normalize(tmp.xyz / tmp.w - camPos), hlsl::promote(0.0), false); return ray; } diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index cc6133a1e5..28a97ea3fe 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -187,13 +187,10 @@ struct Unidirectional ray.payload.otherTechniqueHeuristic = otherTechniqueHeuristic * otherTechniqueHeuristic; // trace new ray - ray.origin = intersection + bxdfSample * (1.0/*kSceneSize*/) * Tolerance::getStart(depth); - ray.direction = bxdfSample; - NBL_IF_CONSTEXPR (nee_type::IsPolygonMethodProjectedSolidAngle) - { - ray.normalAtOrigin = interaction.getN(); - ray.wasBSDFAtOrigin = isBSDF; - } + vector3_type origin = intersection + bxdfSample * (1.0/*kSceneSize*/) * Tolerance::getStart(depth); + vector3_type direction = bxdfSample; + + ray.initData(origin, direction, interaction.getN(), isBSDF); return true; } @@ -223,12 +220,6 @@ struct Unidirectional nee.scene = scene; - NBL_IF_CONSTEXPR (nee_type::IsPolygonMethodProjectedSolidAngle) - { - ray.normalAtOrigin = hlsl::promote(0.0); - ray.wasBSDFAtOrigin = false; - } - // bounces bool hit = true; bool rayAlive = true; From 70b90d66b329069d5f60cc15bb424da795ef7b50 Mon Sep 17 00:00:00 2001 From: Arkadiusz Lachowicz Date: Tue, 17 Feb 2026 17:15:25 +0100 Subject: [PATCH 35/69] Finalize RWMC review fixes --- examples_tests | 2 +- .../accessors/anisotropically_sampled.hlsl | 2 +- .../concepts/accessors/loadable_image.hlsl | 2 +- .../hlsl/concepts/accessors/mip_mapped.hlsl | 2 +- .../builtin/hlsl/rwmc/CascadeAccumulator.hlsl | 46 ++--- .../builtin/hlsl/rwmc/ResolveParameters.hlsl | 54 +++--- .../hlsl/rwmc/SplattingParameters.hlsl | 24 ++- include/nbl/builtin/hlsl/rwmc/resolve.hlsl | 165 +++++++++--------- 8 files changed, 154 insertions(+), 143 deletions(-) diff --git a/examples_tests b/examples_tests index 130e049899..40ba7631e3 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 130e049899be16b50a5d27d7c67310a24d953530 +Subproject commit 40ba7631e305bbc5f10dfd07c96040e87b9ca4c8 diff --git a/include/nbl/builtin/hlsl/concepts/accessors/anisotropically_sampled.hlsl b/include/nbl/builtin/hlsl/concepts/accessors/anisotropically_sampled.hlsl index e6019d056c..b6b0716143 100644 --- a/include/nbl/builtin/hlsl/concepts/accessors/anisotropically_sampled.hlsl +++ b/include/nbl/builtin/hlsl/concepts/accessors/anisotropically_sampled.hlsl @@ -47,4 +47,4 @@ NBL_CONCEPT_END( } } } -#endif \ No newline at end of file +#endif diff --git a/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl b/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl index f7b5122c81..16fdf41c47 100644 --- a/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl +++ b/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl @@ -73,4 +73,4 @@ NBL_CONCEPT_END( } } } -#endif \ No newline at end of file +#endif diff --git a/include/nbl/builtin/hlsl/concepts/accessors/mip_mapped.hlsl b/include/nbl/builtin/hlsl/concepts/accessors/mip_mapped.hlsl index c49e66617b..81fb6e8502 100644 --- a/include/nbl/builtin/hlsl/concepts/accessors/mip_mapped.hlsl +++ b/include/nbl/builtin/hlsl/concepts/accessors/mip_mapped.hlsl @@ -44,4 +44,4 @@ NBL_CONCEPT_END( } } } -#endif \ No newline at end of file +#endif diff --git a/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl b/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl index e4bc9aa4fa..4be0fdab13 100644 --- a/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl @@ -3,7 +3,6 @@ #include #include #include -#include #include namespace nbl @@ -13,15 +12,15 @@ namespace hlsl namespace rwmc { -template && concepts::UnsignedIntegralScalar) +template && concepts::UnsignedIntegralScalar) struct DefaultCascades { using layer_type = CascadeLayerType; using sample_count_type = SampleCountType; - NBL_CONSTEXPR uint32_t cascade_count = CascadeCount; + NBL_CONSTEXPR_STATIC_INLINE uint32_t CascadeCount = CascadeCountValue; - sample_count_type cascadeSampleCounter[cascade_count]; - CascadeLayerType data[cascade_count]; + sample_count_type cascadeSampleCounter[CascadeCount]; + CascadeLayerType data[CascadeCount]; void clear(uint32_t cascadeIx) { @@ -38,7 +37,7 @@ struct DefaultCascades cascadeSampleCounter[lowerCascadeIndex] = sample_count_type(sampleCount); uint16_t higherCascadeIndex = lowerCascadeIndex + uint16_t(1u); - if (higherCascadeIndex < cascade_count) + if (higherCascadeIndex < CascadeCount) { sample_count_type higherCascadeSampleCount = cascadeSampleCounter[higherCascadeIndex]; data[higherCascadeIndex] += (_sample * higherCascadeLevelWeight - (sampleCount - higherCascadeSampleCount) * data[higherCascadeIndex]) * reciprocalSampleCount; @@ -50,20 +49,24 @@ struct DefaultCascades template struct CascadeAccumulator { + using scalar_t = typename SplattingParameters::scalar_t; using input_sample_type = typename CascadesType::layer_type; using this_t = CascadeAccumulator; - using output_storage_type = CascadesType; - NBL_CONSTEXPR uint32_t cascade_count = output_storage_type::cascade_count; - output_storage_type accumulation; + using cascades_type = CascadesType; + NBL_CONSTEXPR_STATIC_INLINE uint32_t CascadeCount = cascades_type::CascadeCount; + NBL_CONSTEXPR_STATIC_INLINE scalar_t LastCascade = scalar_t(CascadeCount - 1u); + cascades_type accumulation; SplattingParameters splattingParameters; + SplattingParameters::SPrecomputed splattingParametersPrecomputed; static this_t create(NBL_CONST_REF_ARG(SplattingParameters) settings) { this_t retval; - for (uint32_t i = 0u; i < cascade_count; ++i) + for (uint32_t i = 0u; i < CascadeCount; ++i) retval.accumulation.clear(i); retval.splattingParameters = settings; + retval.splattingParametersPrecomputed = settings.template precompute(); return retval; } @@ -71,24 +74,21 @@ struct CascadeAccumulator // most of this code is stolen from https://cg.ivd.kit.edu/publications/2018/rwmc/tool/split.cpp void addSample(uint32_t sampleCount, input_sample_type _sample) { - const SplattingParameters::scalar_t baseRootOfStart = splattingParameters.baseRootOfStart(); - const SplattingParameters::scalar_t rcpLog2Base = splattingParameters.rcpLog2Base(); - const SplattingParameters::scalar_t luma = splattingParameters.calcLuma(_sample); - const SplattingParameters::scalar_t log2Luma = log2(luma); - const SplattingParameters::scalar_t log2BaseRootOfStart = log2(baseRootOfStart); - const SplattingParameters::scalar_t cascade = log2Luma * rcpLog2Base - log2BaseRootOfStart; - const SplattingParameters::scalar_t lastCascade = cascade_count - 1u; - const SplattingParameters::scalar_t clampedCascade = clamp(cascade, 0, lastCascade); + const scalar_t luma = splattingParameters.calcLuma(_sample); + const scalar_t log2Luma = log2(luma); + const scalar_t cascade = log2Luma * splattingParametersPrecomputed.RcpLog2Base - splattingParametersPrecomputed.Log2BaseRootOfStart; + const scalar_t clampedCascade = clamp(cascade, scalar_t(0), LastCascade); + const scalar_t clampedCascadeFloor = floor(clampedCascade); // c<=0 -> 0, c>=Count-1 -> Count-1 - uint16_t lowerCascadeIndex = uint16_t(floor(clampedCascade)); + uint16_t lowerCascadeIndex = uint16_t(clampedCascadeFloor); // 0 whenever clamped or `cascade` is integer (when `clampedCascade` is integer) - SplattingParameters::scalar_t higherCascadeWeight = clampedCascade - floor(clampedCascade); + scalar_t higherCascadeWeight = clampedCascade - clampedCascadeFloor; // never 0 thanks to magic of `1-fract(x)` - SplattingParameters::scalar_t lowerCascadeWeight = SplattingParameters::scalar_t(1) - higherCascadeWeight; + scalar_t lowerCascadeWeight = scalar_t(1) - higherCascadeWeight; // handle super bright sample case - if (cascade > lastCascade) - lowerCascadeWeight = exp2((log2BaseRootOfStart + lastCascade) / rcpLog2Base - log2Luma); + if (cascade > LastCascade) + lowerCascadeWeight = exp2(splattingParametersPrecomputed.BrightSampleLumaBias - log2Luma); accumulation.addSampleIntoCascadeEntry(_sample, lowerCascadeIndex, lowerCascadeWeight, higherCascadeWeight, sampleCount); } diff --git a/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl b/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl index c9a79d1149..8a16cdc2f5 100644 --- a/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl @@ -2,7 +2,7 @@ #define _NBL_BUILTIN_HLSL_RWMC_RESOLVE_PARAMETERS_HLSL_INCLUDED_ #include "nbl/builtin/hlsl/cpp_compat.hlsl" -#include +#include namespace nbl { @@ -11,42 +11,42 @@ namespace hlsl namespace rwmc { -struct ResolveParameters +struct SResolveParameters { using scalar_t = float32_t; - static ResolveParameters create(float base, uint32_t sampleCount, float minReliableLuma, float kappa) + static SResolveParameters create(scalar_t base, uint32_t sampleCount, scalar_t minReliableLuma, scalar_t kappa) { - ResolveParameters retval; - retval.initialEmin = minReliableLuma; - retval.reciprocalBase = 1.f / base; - const float N = float(sampleCount); - retval.reciprocalN = 1.f / N; - retval.reciprocalKappa = 1.f / kappa; - // if not interested in exact expected value estimation (kappa!=1.f), can usually accept a bit more variance relative to the image brightness we already have - // allow up to ~ more energy in one sample to lessen bias in some cases - retval.colorReliabilityFactor = base + (1.f - base) * retval.reciprocalKappa; - retval.NOverKappa = N * retval.reciprocalKappa; - + SResolveParameters retval; + retval.initialEmin = minReliableLuma; + retval.reciprocalBase = 1.f / base; + const scalar_t N = scalar_t(sampleCount); + retval.reciprocalN = 1.f / N; + retval.reciprocalKappa = 1.f / kappa; + // if not interested in exact expected value estimation (kappa!=1.f), can usually accept a bit more variance relative to the image brightness we already have + // allow up to ~ more energy in one sample to lessen bias in some cases + retval.colorReliabilityFactor = base + (1.f - base) * retval.reciprocalKappa; + retval.NOverKappa = N * retval.reciprocalKappa; + return retval; } - template + template scalar_t calcLuma(NBL_CONST_REF_ARG(SampleType) col) { - return hlsl::dot(hlsl::transpose(colorspace::scRGBtoXYZ)[1], col); + return hlsl::dot(hlsl::transpose(Colorspace::ToXYZ())[1], col); } - float initialEmin; // a minimum image brightness that we always consider reliable - float reciprocalBase; - float reciprocalN; - float reciprocalKappa; - float colorReliabilityFactor; - float NOverKappa; -}; - -} -} -} + scalar_t initialEmin; // a minimum image brightness that we always consider reliable + scalar_t reciprocalBase; + scalar_t reciprocalN; + scalar_t reciprocalKappa; + scalar_t colorReliabilityFactor; + scalar_t NOverKappa; +}; + +} +} +} #endif diff --git a/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl b/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl index a7c95e9e27..7cbd52f0a4 100644 --- a/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl @@ -2,7 +2,7 @@ #define _NBL_BUILTIN_HLSL_RWMC_SPLATTING_PARAMETERS_HLSL_INCLUDED_ #include "nbl/builtin/hlsl/cpp_compat.hlsl" -#include +#include namespace nbl { @@ -14,6 +14,12 @@ namespace rwmc struct SplattingParameters { using scalar_t = float32_t; + struct SPrecomputed + { + scalar_t RcpLog2Base; + scalar_t Log2BaseRootOfStart; + scalar_t BrightSampleLumaBias; + }; // float16_t baseRootOfStart; 0 // float16_t rcpLog2Base; 1 @@ -35,10 +41,22 @@ struct SplattingParameters return unpackedLog2Parameters()[1]; } - template + template + SPrecomputed precompute() + { + const scalar_t LastCascade = scalar_t(CascadeCount - 1u); + const float32_t2 unpacked = unpackedLog2Parameters(); + SPrecomputed retval; + retval.RcpLog2Base = unpacked[1]; + retval.Log2BaseRootOfStart = log2(unpacked[0]); + retval.BrightSampleLumaBias = (retval.Log2BaseRootOfStart + LastCascade) / retval.RcpLog2Base; + return retval; + } + + template scalar_t calcLuma(NBL_CONST_REF_ARG(CascadeLayerType) col) { - return hlsl::dot(hlsl::transpose(colorspace::scRGBtoXYZ)[1], col); + return hlsl::dot(hlsl::transpose(Colorspace::ToXYZ())[1], col); } }; diff --git a/include/nbl/builtin/hlsl/rwmc/resolve.hlsl b/include/nbl/builtin/hlsl/rwmc/resolve.hlsl index 84b0d44580..2b2d090fb1 100644 --- a/include/nbl/builtin/hlsl/rwmc/resolve.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/resolve.hlsl @@ -17,98 +17,84 @@ namespace rwmc #define NBL_CONCEPT_TPLT_PRM_KINDS (typename)(typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T)(SampleType) #define NBL_CONCEPT_PARAM_0 (a,T) -#define NBL_CONCEPT_PARAM_1 (sample,SampleType) -NBL_CONCEPT_BEGIN(2) +NBL_CONCEPT_BEGIN(1) #define a NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 -#define sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scalar_t)) - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointScalar, T::scalar_t)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template calcLuma(sample)), ::nbl::hlsl::is_same_v, typename T::scalar_t)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointScalar, typename T::scalar_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)( + (a.template calcLuma(::nbl::hlsl::experimental::declval())), + ::nbl::hlsl::is_same_v, + typename T::scalar_t + )) ); -#undef sample #undef a #include template NBL_BOOL_CONCEPT ResolveLumaParams = ResolveLumaParamsBase; -// declare concept -#define NBL_CONCEPT_NAME ResolveAccessorBase -#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) -#define NBL_CONCEPT_TPLT_PRM_NAMES (T) -#define NBL_CONCEPT_PARAM_0 (a,T) -#define NBL_CONCEPT_PARAM_1 (uv,vector) -#define NBL_CONCEPT_PARAM_2 (layer,uint16_t) -NBL_CONCEPT_BEGIN(3) -#define a NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 -#define uv NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 -#define layer NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 -NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_TYPE)(T::output_t)) - ((NBL_CONCEPT_REQ_TYPE)(T::output_scalar_t)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,layer)), ::nbl::hlsl::is_same_v, typename T::output_t)) -); -#undef layer -#undef uv -#undef a -#include - template -NBL_BOOL_CONCEPT ResolveAccessor = ResolveAccessorBase && concepts::accessors::LoadableImage; - -template -struct ResolveAccessorAdaptor +NBL_BOOL_CONCEPT ResolveAccessor = concepts::accessors::MipmappedLoadableImage; + +template) +struct SResolveAccessorAdaptor { using output_scalar_t = OutputScalar; - NBL_CONSTEXPR int32_t Components = 3; - using output_t = vector; - NBL_CONSTEXPR int32_t image_dimension = 2; + NBL_CONSTEXPR_STATIC_INLINE int32_t Components = 3; + using output_t = vector; + NBL_CONSTEXPR_STATIC_INLINE int32_t image_dimension = 2; - RWTexture2DArray cascade; + template + void get(vector uv, uint16_t layer, uint16_t level, NBL_REF_ARG(output_t) value) + { + typename AccessorType::output_t sampled; + accessor.template get(uv, layer, level, sampled); + value = sampled.xyz; + } template - output_t get(vector uv, uint16_t layer) + output_t get(vector uv, uint16_t layer, uint16_t level) { - uint32_t imgWidth, imgHeight, layers; - cascade.GetDimensions(imgWidth, imgHeight, layers); - int16_t2 cascadeImageDimension = int16_t2(imgWidth, imgHeight); + output_t value; + get(uv, layer, level, value); + return value; + } - if (any(uv < int16_t2(0, 0)) || any(uv >= cascadeImageDimension)) - return promote(0); - - return cascade.Load(int32_t3(uv, int32_t(layer))); - } -}; - -template && ResolveLumaParams) -struct Resolver + AccessorType accessor; +}; + +template && + ResolveLumaParams +) +struct SResolver { using output_t = typename CascadeAccessor::output_t; using output_scalar_t = typename vector_traits::scalar_type; - using scalar_t = typename ResolveParameters::scalar_t; - NBL_CONSTEXPR static uint16_t last_cascade = CascadeCount - 1u; + using scalar_t = typename SResolveParameters::scalar_t; + NBL_CONSTEXPR_STATIC_INLINE uint16_t last_cascade = uint16_t(CascadeCount - 1u); - struct CascadeSample + struct SCascadeSample { output_t centerValue; - float normalizedCenterLuma; - float normalizedNeighbourhoodAverageLuma; + scalar_t normalizedCenterLuma; + scalar_t normalizedNeighbourhoodAverageLuma; }; - static Resolver create(NBL_REF_ARG(ResolveParameters) resolveParameters) + static SResolver create(NBL_REF_ARG(SResolveParameters) resolveParameters) { - Resolver retval; + SResolver retval; retval.params = resolveParameters; - - return retval; + + return retval; } - output_t operator()(NBL_REF_ARG(CascadeAccessor) acc, const int16_t2 coord) - { - scalar_t reciprocalBaseI = 1.f; - CascadeSample curr = __sampleCascade(acc, coord, 0u, reciprocalBaseI); - + output_t operator()(NBL_REF_ARG(CascadeAccessor) acc, const int16_t2 coord) + { + scalar_t reciprocalBaseI = 1.f; + SCascadeSample curr = __sampleCascade(acc, coord, 0u, reciprocalBaseI); + output_t accumulation = promote(0.0f); scalar_t Emin = params.initialEmin; @@ -119,10 +105,10 @@ struct Resolver const bool notFirstCascade = i != 0; const bool notLastCascade = i != last_cascade; - CascadeSample next; + SCascadeSample next; if (notLastCascade) - { - reciprocalBaseI *= params.reciprocalBase; + { + reciprocalBaseI *= params.reciprocalBase; next = __sampleCascade(acc, coord, int16_t(i + 1), reciprocalBaseI); } @@ -168,31 +154,38 @@ struct Resolver return accumulation; } - ResolveParameters params; + SResolveParameters params; // pseudo private stuff: - CascadeSample __sampleCascade(NBL_REF_ARG(CascadeAccessor) acc, int16_t2 coord, uint16_t cascadeIndex, scalar_t reciprocalBaseI) + SCascadeSample __sampleCascade(NBL_REF_ARG(CascadeAccessor) acc, int16_t2 coord, uint16_t cascadeIndex, scalar_t reciprocalBaseI) { - output_t neighbourhood[9]; - neighbourhood[0] = acc.template get(coord + int16_t2(-1, -1), cascadeIndex); - neighbourhood[1] = acc.template get(coord + int16_t2(0, -1), cascadeIndex); - neighbourhood[2] = acc.template get(coord + int16_t2(1, -1), cascadeIndex); - neighbourhood[3] = acc.template get(coord + int16_t2(-1, 0), cascadeIndex); - neighbourhood[4] = acc.template get(coord + int16_t2(0, 0), cascadeIndex); - neighbourhood[5] = acc.template get(coord + int16_t2(1, 0), cascadeIndex); - neighbourhood[6] = acc.template get(coord + int16_t2(-1, 1), cascadeIndex); - neighbourhood[7] = acc.template get(coord + int16_t2(0, 1), cascadeIndex); - neighbourhood[8] = acc.template get(coord + int16_t2(1, 1), cascadeIndex); - - // numerical robustness - output_t excl_hood_sum = ((neighbourhood[0] + neighbourhood[1]) + (neighbourhood[2] + neighbourhood[3])) + - ((neighbourhood[5] + neighbourhood[6]) + (neighbourhood[7] + neighbourhood[8])); - - CascadeSample retval; - retval.centerValue = neighbourhood[4]; - retval.normalizedNeighbourhoodAverageLuma = retval.normalizedCenterLuma = params.template calcLuma(neighbourhood[4]) * reciprocalBaseI; - retval.normalizedNeighbourhoodAverageLuma = (params.template calcLuma(excl_hood_sum) * reciprocalBaseI + retval.normalizedNeighbourhoodAverageLuma) / 9.f; + output_t sampleValue; + scalar_t excl_hood_luma_sum = 0.f; + + acc.template get(coord + int16_t2(-1, -1), cascadeIndex, 0u, sampleValue); + excl_hood_luma_sum += params.template calcLuma(sampleValue); + acc.template get(coord + int16_t2(0, -1), cascadeIndex, 0u, sampleValue); + excl_hood_luma_sum += params.template calcLuma(sampleValue); + acc.template get(coord + int16_t2(1, -1), cascadeIndex, 0u, sampleValue); + excl_hood_luma_sum += params.template calcLuma(sampleValue); + acc.template get(coord + int16_t2(-1, 0), cascadeIndex, 0u, sampleValue); + excl_hood_luma_sum += params.template calcLuma(sampleValue); + + SCascadeSample retval; + acc.template get(coord + int16_t2(0, 0), cascadeIndex, 0u, retval.centerValue); + const scalar_t centerLuma = params.template calcLuma(retval.centerValue); + acc.template get(coord + int16_t2(1, 0), cascadeIndex, 0u, sampleValue); + excl_hood_luma_sum += params.template calcLuma(sampleValue); + acc.template get(coord + int16_t2(-1, 1), cascadeIndex, 0u, sampleValue); + excl_hood_luma_sum += params.template calcLuma(sampleValue); + acc.template get(coord + int16_t2(0, 1), cascadeIndex, 0u, sampleValue); + excl_hood_luma_sum += params.template calcLuma(sampleValue); + acc.template get(coord + int16_t2(1, 1), cascadeIndex, 0u, sampleValue); + excl_hood_luma_sum += params.template calcLuma(sampleValue); + + retval.normalizedCenterLuma = centerLuma * reciprocalBaseI; + retval.normalizedNeighbourhoodAverageLuma = (excl_hood_luma_sum + centerLuma) * reciprocalBaseI / 9.f; return retval; } }; From ea2e5251b54d989fcedf0ad2ddf8d0b2cc3c730e Mon Sep 17 00:00:00 2001 From: Arkadiusz Lachowicz Date: Tue, 17 Feb 2026 17:44:27 +0100 Subject: [PATCH 36/69] Fix accessor concepts for out-param get --- .../accessors/anisotropically_sampled.hlsl | 7 +++++-- .../concepts/accessors/loadable_image.hlsl | 20 ++++++++++++------- .../hlsl/concepts/accessors/mip_mapped.hlsl | 7 +++++-- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/include/nbl/builtin/hlsl/concepts/accessors/anisotropically_sampled.hlsl b/include/nbl/builtin/hlsl/concepts/accessors/anisotropically_sampled.hlsl index b6b0716143..64b919be6a 100644 --- a/include/nbl/builtin/hlsl/concepts/accessors/anisotropically_sampled.hlsl +++ b/include/nbl/builtin/hlsl/concepts/accessors/anisotropically_sampled.hlsl @@ -26,17 +26,20 @@ namespace accessors #define NBL_CONCEPT_PARAM_2 (layer,uint16_t) #define NBL_CONCEPT_PARAM_3 (dU,vector) #define NBL_CONCEPT_PARAM_4 (dV,vector) +#define NBL_CONCEPT_PARAM_5 (outVal,float32_t4) // start concept -NBL_CONCEPT_BEGIN(5) +NBL_CONCEPT_BEGIN(6) // need to be defined AFTER the cocnept begins #define a NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define uv NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define layer NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define dU NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 #define dV NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +#define outVal NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,layer,dU,dV)) , ::nbl::hlsl::is_same_v, float32_t4>)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,layer,dU,dV,outVal)) , ::nbl::hlsl::is_same_v, void)) ); +#undef outVal #undef dV #undef dU #undef layer diff --git a/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl b/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl index 16fdf41c47..4a910a23bd 100644 --- a/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl +++ b/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl @@ -18,9 +18,9 @@ namespace accessors { // concept `LoadableImage` translates to smth like this: -//template -//concept LoadableImage = requires(U a, vector uv, uint16_t layer) { -// ::nbl::hlsl::is_same_v().template get(uv,layer)), vector>; +//template +//concept LoadableImage = requires(U a, vector uv, uint16_t layer, vector outVal) { +// ::nbl::hlsl::is_same_v().template get(uv,layer,outVal)), void>; //}; // declare concept @@ -31,15 +31,18 @@ namespace accessors #define NBL_CONCEPT_PARAM_0 (a,U) #define NBL_CONCEPT_PARAM_1 (uv,vector) #define NBL_CONCEPT_PARAM_2 (layer,uint16_t) +#define NBL_CONCEPT_PARAM_3 (outVal,vector) // start concept -NBL_CONCEPT_BEGIN(3) +NBL_CONCEPT_BEGIN(4) // need to be defined AFTER the concept begins #define a NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define uv NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define layer NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define outVal NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,layer)), ::nbl::hlsl::is_same_v, vector)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,layer,outVal)), ::nbl::hlsl::is_same_v, void)) ); +#undef outVal #undef layer #undef uv #undef a @@ -54,16 +57,19 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_PARAM_1 (uv,vector) #define NBL_CONCEPT_PARAM_2 (layer,uint16_t) #define NBL_CONCEPT_PARAM_3 (level,uint16_t) +#define NBL_CONCEPT_PARAM_4 (outVal,vector) // start concept -NBL_CONCEPT_BEGIN(4) +NBL_CONCEPT_BEGIN(5) // need to be defined AFTER the cocnept begins #define a NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define uv NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define layer NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define level NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define outVal NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,layer,level)) , ::nbl::hlsl::is_same_v, vector)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,layer,level,outVal)) , ::nbl::hlsl::is_same_v, void)) ); +#undef outVal #undef level #undef layer #undef uv diff --git a/include/nbl/builtin/hlsl/concepts/accessors/mip_mapped.hlsl b/include/nbl/builtin/hlsl/concepts/accessors/mip_mapped.hlsl index 81fb6e8502..be7f997925 100644 --- a/include/nbl/builtin/hlsl/concepts/accessors/mip_mapped.hlsl +++ b/include/nbl/builtin/hlsl/concepts/accessors/mip_mapped.hlsl @@ -25,16 +25,19 @@ namespace accessors #define NBL_CONCEPT_PARAM_1 (uv,vector) #define NBL_CONCEPT_PARAM_2 (layer,uint16_t) #define NBL_CONCEPT_PARAM_3 (level,float) +#define NBL_CONCEPT_PARAM_4 (outVal,float32_t4) // start concept -NBL_CONCEPT_BEGIN(4) +NBL_CONCEPT_BEGIN(5) // need to be defined AFTER the cocnept begins #define a NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define uv NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define layer NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define level NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define outVal NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,layer,level)) , ::nbl::hlsl::is_same_v, float32_t4>)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,layer,level,outVal)) , ::nbl::hlsl::is_same_v, void)) ); +#undef outVal #undef level #undef layer #undef uv From 9399ad3d82718d1feb5cc2d39f85f3fec0b846e2 Mon Sep 17 00:00:00 2001 From: Arkadiusz Lachowicz Date: Tue, 17 Feb 2026 18:10:43 +0100 Subject: [PATCH 37/69] Address latest RWMC accessor and splatting review comments --- examples_tests | 2 +- .../accessors/anisotropically_sampled.hlsl | 8 +-- .../concepts/accessors/loadable_image.hlsl | 6 +- .../hlsl/concepts/accessors/mip_mapped.hlsl | 2 +- .../builtin/hlsl/rwmc/CascadeAccumulator.hlsl | 18 +++--- .../hlsl/rwmc/SplattingParameters.hlsl | 59 ++++++++----------- include/nbl/builtin/hlsl/rwmc/resolve.hlsl | 30 ++++------ 7 files changed, 52 insertions(+), 73 deletions(-) diff --git a/examples_tests b/examples_tests index 40ba7631e3..c531404a5e 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 40ba7631e305bbc5f10dfd07c96040e87b9ca4c8 +Subproject commit c531404a5ed53bf0d46f208199520fbd814522bf diff --git a/include/nbl/builtin/hlsl/concepts/accessors/anisotropically_sampled.hlsl b/include/nbl/builtin/hlsl/concepts/accessors/anisotropically_sampled.hlsl index 64b919be6a..76f2c2219a 100644 --- a/include/nbl/builtin/hlsl/concepts/accessors/anisotropically_sampled.hlsl +++ b/include/nbl/builtin/hlsl/concepts/accessors/anisotropically_sampled.hlsl @@ -18,15 +18,15 @@ namespace accessors { // declare concept #define NBL_CONCEPT_NAME AnisotropicallySampled -#define NBL_CONCEPT_TPLT_PRM_KINDS (typename)(int32_t) -#define NBL_CONCEPT_TPLT_PRM_NAMES (U)(Dims) +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename)(int32_t)(int32_t) +#define NBL_CONCEPT_TPLT_PRM_NAMES (U)(Dims)(Components) // not the greatest syntax but works #define NBL_CONCEPT_PARAM_0 (a,U) #define NBL_CONCEPT_PARAM_1 (uv,vector) #define NBL_CONCEPT_PARAM_2 (layer,uint16_t) #define NBL_CONCEPT_PARAM_3 (dU,vector) #define NBL_CONCEPT_PARAM_4 (dV,vector) -#define NBL_CONCEPT_PARAM_5 (outVal,float32_t4) +#define NBL_CONCEPT_PARAM_5 (outVal,vector) // start concept NBL_CONCEPT_BEGIN(6) // need to be defined AFTER the cocnept begins @@ -37,7 +37,7 @@ NBL_CONCEPT_BEGIN(6) #define dV NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 #define outVal NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,layer,dU,dV,outVal)) , ::nbl::hlsl::is_same_v, void)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(outVal,uv,layer,dU,dV)) , ::nbl::hlsl::is_same_v, void)) ); #undef outVal #undef dV diff --git a/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl b/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl index 4a910a23bd..fcc200ad95 100644 --- a/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl +++ b/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl @@ -20,7 +20,7 @@ namespace accessors // concept `LoadableImage` translates to smth like this: //template //concept LoadableImage = requires(U a, vector uv, uint16_t layer, vector outVal) { -// ::nbl::hlsl::is_same_v().template get(uv,layer,outVal)), void>; +// ::nbl::hlsl::is_same_v().template get(outVal,uv,layer)), void>; //}; // declare concept @@ -40,7 +40,7 @@ NBL_CONCEPT_BEGIN(4) #define layer NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define outVal NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,layer,outVal)), ::nbl::hlsl::is_same_v, void)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(outVal,uv,layer)), ::nbl::hlsl::is_same_v, void)) ); #undef outVal #undef layer @@ -67,7 +67,7 @@ NBL_CONCEPT_BEGIN(5) #define level NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 #define outVal NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,layer,level,outVal)) , ::nbl::hlsl::is_same_v, void)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(outVal,uv,layer,level)) , ::nbl::hlsl::is_same_v, void)) ); #undef outVal #undef level diff --git a/include/nbl/builtin/hlsl/concepts/accessors/mip_mapped.hlsl b/include/nbl/builtin/hlsl/concepts/accessors/mip_mapped.hlsl index be7f997925..e8b61d4029 100644 --- a/include/nbl/builtin/hlsl/concepts/accessors/mip_mapped.hlsl +++ b/include/nbl/builtin/hlsl/concepts/accessors/mip_mapped.hlsl @@ -35,7 +35,7 @@ NBL_CONCEPT_BEGIN(5) #define level NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 #define outVal NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,layer,level,outVal)) , ::nbl::hlsl::is_same_v, void)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(outVal,uv,layer,level)) , ::nbl::hlsl::is_same_v, void)) ); #undef outVal #undef level diff --git a/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl b/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl index 4be0fdab13..a52c9302d3 100644 --- a/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl @@ -28,9 +28,9 @@ struct DefaultCascades data[cascadeIx] = promote(0.0f); } - void addSampleIntoCascadeEntry(CascadeLayerType _sample, uint16_t lowerCascadeIndex, SplattingParameters::scalar_t lowerCascadeLevelWeight, SplattingParameters::scalar_t higherCascadeLevelWeight, uint32_t sampleCount) + void addSampleIntoCascadeEntry(CascadeLayerType _sample, uint16_t lowerCascadeIndex, SSplattingParameters::scalar_t lowerCascadeLevelWeight, SSplattingParameters::scalar_t higherCascadeLevelWeight, uint32_t sampleCount) { - const SplattingParameters::scalar_t reciprocalSampleCount = SplattingParameters::scalar_t(1.0f) / SplattingParameters::scalar_t(sampleCount); + const SSplattingParameters::scalar_t reciprocalSampleCount = SSplattingParameters::scalar_t(1.0f) / SSplattingParameters::scalar_t(sampleCount); sample_count_type lowerCascadeSampleCount = cascadeSampleCounter[lowerCascadeIndex]; data[lowerCascadeIndex] += (_sample * lowerCascadeLevelWeight - (sampleCount - lowerCascadeSampleCount) * data[lowerCascadeIndex]) * reciprocalSampleCount; @@ -49,7 +49,7 @@ struct DefaultCascades template struct CascadeAccumulator { - using scalar_t = typename SplattingParameters::scalar_t; + using scalar_t = typename SSplattingParameters::scalar_t; using input_sample_type = typename CascadesType::layer_type; using this_t = CascadeAccumulator; using cascades_type = CascadesType; @@ -57,16 +57,14 @@ struct CascadeAccumulator NBL_CONSTEXPR_STATIC_INLINE scalar_t LastCascade = scalar_t(CascadeCount - 1u); cascades_type accumulation; - SplattingParameters splattingParameters; - SplattingParameters::SPrecomputed splattingParametersPrecomputed; + SSplattingParameters splattingParameters; - static this_t create(NBL_CONST_REF_ARG(SplattingParameters) settings) + static this_t create(NBL_CONST_REF_ARG(SPackedSplattingParameters) settings) { this_t retval; for (uint32_t i = 0u; i < CascadeCount; ++i) retval.accumulation.clear(i); - retval.splattingParameters = settings; - retval.splattingParametersPrecomputed = settings.template precompute(); + retval.splattingParameters = settings.unpack(); return retval; } @@ -76,7 +74,7 @@ struct CascadeAccumulator { const scalar_t luma = splattingParameters.calcLuma(_sample); const scalar_t log2Luma = log2(luma); - const scalar_t cascade = log2Luma * splattingParametersPrecomputed.RcpLog2Base - splattingParametersPrecomputed.Log2BaseRootOfStart; + const scalar_t cascade = log2Luma * splattingParameters.RcpLog2Base - splattingParameters.Log2BaseRootOfStart; const scalar_t clampedCascade = clamp(cascade, scalar_t(0), LastCascade); const scalar_t clampedCascadeFloor = floor(clampedCascade); // c<=0 -> 0, c>=Count-1 -> Count-1 @@ -88,7 +86,7 @@ struct CascadeAccumulator // handle super bright sample case if (cascade > LastCascade) - lowerCascadeWeight = exp2(splattingParametersPrecomputed.BrightSampleLumaBias - log2Luma); + lowerCascadeWeight = exp2(splattingParameters.BrightSampleLumaBias - log2Luma); accumulation.addSampleIntoCascadeEntry(_sample, lowerCascadeIndex, lowerCascadeWeight, higherCascadeWeight, sampleCount); } diff --git a/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl b/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl index 7cbd52f0a4..b89c4ebfbe 100644 --- a/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl @@ -11,53 +11,42 @@ namespace hlsl namespace rwmc { -struct SplattingParameters +struct SSplattingParameters { using scalar_t = float32_t; - struct SPrecomputed + scalar_t RcpLog2Base; + scalar_t Log2BaseRootOfStart; + scalar_t BrightSampleLumaBias; + + template + scalar_t calcLuma(NBL_CONST_REF_ARG(CascadeLayerType) col) { - scalar_t RcpLog2Base; - scalar_t Log2BaseRootOfStart; - scalar_t BrightSampleLumaBias; - }; + return hlsl::dot(hlsl::transpose(Colorspace::ToXYZ())[1], col); + } +}; +struct SPackedSplattingParameters +{ // float16_t baseRootOfStart; 0 // float16_t rcpLog2Base; 1 // pack as Half2x16 - int32_t packedLog2; - - float32_t2 unpackedLog2Parameters() - { - return hlsl::unpackHalf2x16(packedLog2); - } - - scalar_t baseRootOfStart() - { - return unpackedLog2Parameters()[0]; - } + int32_t PackedLog2; - scalar_t rcpLog2Base() - { - return unpackedLog2Parameters()[1]; - } + // float16_t log2BaseRootOfStart; 2 + // float16_t brightSampleLumaBias; 3 + // pack as Half2x16 + int32_t PackedPrecomputed; - template - SPrecomputed precompute() + SSplattingParameters unpack() { - const scalar_t LastCascade = scalar_t(CascadeCount - 1u); - const float32_t2 unpacked = unpackedLog2Parameters(); - SPrecomputed retval; - retval.RcpLog2Base = unpacked[1]; - retval.Log2BaseRootOfStart = log2(unpacked[0]); - retval.BrightSampleLumaBias = (retval.Log2BaseRootOfStart + LastCascade) / retval.RcpLog2Base; + SSplattingParameters retval; + const float32_t2 unpackedLog2 = hlsl::unpackHalf2x16(PackedLog2); + const float32_t2 unpackedPrecomputed = hlsl::unpackHalf2x16(PackedPrecomputed); + retval.RcpLog2Base = unpackedLog2[1]; + retval.Log2BaseRootOfStart = unpackedPrecomputed[0]; + retval.BrightSampleLumaBias = unpackedPrecomputed[1]; return retval; } - - template - scalar_t calcLuma(NBL_CONST_REF_ARG(CascadeLayerType) col) - { - return hlsl::dot(hlsl::transpose(Colorspace::ToXYZ())[1], col); - } }; } diff --git a/include/nbl/builtin/hlsl/rwmc/resolve.hlsl b/include/nbl/builtin/hlsl/rwmc/resolve.hlsl index 2b2d090fb1..a30bd49e74 100644 --- a/include/nbl/builtin/hlsl/rwmc/resolve.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/resolve.hlsl @@ -46,21 +46,13 @@ struct SResolveAccessorAdaptor NBL_CONSTEXPR_STATIC_INLINE int32_t image_dimension = 2; template - void get(vector uv, uint16_t layer, uint16_t level, NBL_REF_ARG(output_t) value) + void get(NBL_REF_ARG(output_t) value, vector uv, uint16_t layer, uint16_t level) { typename AccessorType::output_t sampled; - accessor.template get(uv, layer, level, sampled); + accessor.template get(sampled, uv, layer, level); value = sampled.xyz; } - template - output_t get(vector uv, uint16_t layer, uint16_t level) - { - output_t value; - get(uv, layer, level, value); - return value; - } - AccessorType accessor; }; @@ -163,25 +155,25 @@ struct SResolver output_t sampleValue; scalar_t excl_hood_luma_sum = 0.f; - acc.template get(coord + int16_t2(-1, -1), cascadeIndex, 0u, sampleValue); + acc.template get(sampleValue, coord + int16_t2(-1, -1), cascadeIndex, 0u); excl_hood_luma_sum += params.template calcLuma(sampleValue); - acc.template get(coord + int16_t2(0, -1), cascadeIndex, 0u, sampleValue); + acc.template get(sampleValue, coord + int16_t2(0, -1), cascadeIndex, 0u); excl_hood_luma_sum += params.template calcLuma(sampleValue); - acc.template get(coord + int16_t2(1, -1), cascadeIndex, 0u, sampleValue); + acc.template get(sampleValue, coord + int16_t2(1, -1), cascadeIndex, 0u); excl_hood_luma_sum += params.template calcLuma(sampleValue); - acc.template get(coord + int16_t2(-1, 0), cascadeIndex, 0u, sampleValue); + acc.template get(sampleValue, coord + int16_t2(-1, 0), cascadeIndex, 0u); excl_hood_luma_sum += params.template calcLuma(sampleValue); SCascadeSample retval; - acc.template get(coord + int16_t2(0, 0), cascadeIndex, 0u, retval.centerValue); + acc.template get(retval.centerValue, coord + int16_t2(0, 0), cascadeIndex, 0u); const scalar_t centerLuma = params.template calcLuma(retval.centerValue); - acc.template get(coord + int16_t2(1, 0), cascadeIndex, 0u, sampleValue); + acc.template get(sampleValue, coord + int16_t2(1, 0), cascadeIndex, 0u); excl_hood_luma_sum += params.template calcLuma(sampleValue); - acc.template get(coord + int16_t2(-1, 1), cascadeIndex, 0u, sampleValue); + acc.template get(sampleValue, coord + int16_t2(-1, 1), cascadeIndex, 0u); excl_hood_luma_sum += params.template calcLuma(sampleValue); - acc.template get(coord + int16_t2(0, 1), cascadeIndex, 0u, sampleValue); + acc.template get(sampleValue, coord + int16_t2(0, 1), cascadeIndex, 0u); excl_hood_luma_sum += params.template calcLuma(sampleValue); - acc.template get(coord + int16_t2(1, 1), cascadeIndex, 0u, sampleValue); + acc.template get(sampleValue, coord + int16_t2(1, 1), cascadeIndex, 0u); excl_hood_luma_sum += params.template calcLuma(sampleValue); retval.normalizedCenterLuma = centerLuma * reciprocalBaseI; From 1125f48d1af4ea3fcb0cc416b128ab3cb8f6beb7 Mon Sep 17 00:00:00 2001 From: Arkadiusz Lachowicz Date: Tue, 17 Feb 2026 18:41:03 +0100 Subject: [PATCH 38/69] Improve packed splatting parameter naming --- examples_tests | 2 +- .../nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples_tests b/examples_tests index c531404a5e..af29d6546f 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit c531404a5ed53bf0d46f208199520fbd814522bf +Subproject commit af29d6546ff9f9e7beeea633f7e8de27fb879ba7 diff --git a/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl b/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl index b89c4ebfbe..0b804a1517 100644 --- a/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl @@ -30,21 +30,21 @@ struct SPackedSplattingParameters // float16_t baseRootOfStart; 0 // float16_t rcpLog2Base; 1 // pack as Half2x16 - int32_t PackedLog2; + int32_t PackedBaseRootAndRcpLog2Base; // float16_t log2BaseRootOfStart; 2 // float16_t brightSampleLumaBias; 3 // pack as Half2x16 - int32_t PackedPrecomputed; + int32_t PackedLog2BaseRootAndBrightSampleLumaBias; SSplattingParameters unpack() { SSplattingParameters retval; - const float32_t2 unpackedLog2 = hlsl::unpackHalf2x16(PackedLog2); - const float32_t2 unpackedPrecomputed = hlsl::unpackHalf2x16(PackedPrecomputed); - retval.RcpLog2Base = unpackedLog2[1]; - retval.Log2BaseRootOfStart = unpackedPrecomputed[0]; - retval.BrightSampleLumaBias = unpackedPrecomputed[1]; + const float32_t2 unpackedBaseRootAndRcpLog2Base = hlsl::unpackHalf2x16(PackedBaseRootAndRcpLog2Base); + const float32_t2 unpackedLog2BaseRootAndBrightSampleLumaBias = hlsl::unpackHalf2x16(PackedLog2BaseRootAndBrightSampleLumaBias); + retval.RcpLog2Base = unpackedBaseRootAndRcpLog2Base[1]; + retval.Log2BaseRootOfStart = unpackedLog2BaseRootAndBrightSampleLumaBias[0]; + retval.BrightSampleLumaBias = unpackedLog2BaseRootAndBrightSampleLumaBias[1]; return retval; } }; From 31011e8812b464318efc2504f3089e99dc5f5375 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 18 Feb 2026 12:18:35 +0700 Subject: [PATCH 39/69] fix depth increment, nee should return env radiance --- .../nbl/builtin/hlsl/path_tracing/concepts.hlsl | 1 + .../hlsl/path_tracing/unidirectional.hlsl | 16 +++++----------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index 6cc9fd5b1f..60da47d305 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -146,6 +146,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferred_eval_and_pdf(id, ray)), ::nbl::hlsl::is_same_v, typename T::eval_pdf_return_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.generate_and_quotient_and_pdf(id, v, interaction, is_bsdf, v, depth)), ::nbl::hlsl::is_same_v, typename T::sample_quotient_return_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.get_environment_radiance(ray)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) ); #undef depth #undef is_bsdf diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 28a97ea3fe..0e75895d46 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -105,8 +105,8 @@ struct Unidirectional const bool isBSDF = materialSystem.isBSDF(matID); - vector3_type eps0 = randGen(depth, _sample, 0u); - vector3_type eps1 = randGen(depth, _sample, 1u); + vector3_type eps0 = randGen(depth * 2u, _sample, 0u); + vector3_type eps1 = randGen(depth * 2u, _sample, 1u); // thresholds const scalar_type bxdfPdfThreshold = 0.0001; @@ -120,7 +120,7 @@ struct Unidirectional scalar_type rcpChoiceProb; sampling::PartitionRandVariable partitionRandVariable; partitionRandVariable.leftProb = neeProbability; - if (!partitionRandVariable(eps0.z, rcpChoiceProb) && depth < 2u) + if (!partitionRandVariable(eps0.z, rcpChoiceProb)) { uint32_t randLightID = uint32_t(float32_t(randGen.rng()) / numeric_limits::max) * nee.lightCount; typename nee_type::sample_quotient_return_type ret = nee.generate_and_quotient_and_pdf( @@ -200,14 +200,8 @@ struct Unidirectional void missProgram(NBL_REF_ARG(ray_type) ray) { vector3_type finalContribution = ray.payload.throughput; - // #ifdef USE_ENVMAP - // vec2 uv = SampleSphericalMap(ray.direction); - // finalContribution *= textureLod(envMap, uv, 0.0).rgb; - // #else - const vector3_type kConstantEnvLightRadiance = vector3_type(0.15, 0.21, 0.3); // TODO: match spectral_type - finalContribution *= kConstantEnvLightRadiance; + finalContribution *= nee.get_environment_radiance(ray); ray.payload.accumulation += finalContribution; - // #endif } // Li @@ -223,7 +217,7 @@ struct Unidirectional // bounces bool hit = true; bool rayAlive = true; - for (int d = 1; (d <= maxDepth) && hit && rayAlive; d += 2) + for (int d = 1; (d <= maxDepth) && hit && rayAlive; d++) { ray.intersectionT = numeric_limits::max; intersect_data_type intersection = intersector_type::traceRay(ray, scene); From 0ee653453f49a506e23bb24a4d849fa76f736eb2 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 18 Feb 2026 14:49:55 +0700 Subject: [PATCH 40/69] minor bug fix to depth, throughputCIE --- .../nbl/builtin/hlsl/path_tracing/unidirectional.hlsl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 0e75895d46..da6cc0dce6 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -78,13 +78,13 @@ struct Unidirectional bool closestHitProgram(uint32_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(intersect_data_type) intersectData, NBL_CONST_REF_ARG(scene_type) scene) { const vector3_type intersection = intersectData.intersection; + vector3_type throughput = ray.payload.throughput; + const vector3_type throughputCIE_Y = hlsl::normalize(colorspace::sRGBtoXYZ[1] * throughput); // TODO: this only works if spectral_type is dim 3 isotropic_interaction_type iso_interaction = intersectData.iso_interaction; anisotropic_interaction_type interaction = intersectData.aniso_interaction; - iso_interaction.luminosityContributionHint = colorspace::scRGBtoXYZ[1]; - interaction.isotropic.luminosityContributionHint = colorspace::scRGBtoXYZ[1]; - - vector3_type throughput = ray.payload.throughput; + iso_interaction.luminosityContributionHint = throughputCIE_Y; + interaction.isotropic.luminosityContributionHint = throughputCIE_Y; // emissive typename scene_type::mat_light_id_type matLightID = scene.getMatLightIDs(ray.objectID); @@ -106,12 +106,11 @@ struct Unidirectional const bool isBSDF = materialSystem.isBSDF(matID); vector3_type eps0 = randGen(depth * 2u, _sample, 0u); - vector3_type eps1 = randGen(depth * 2u, _sample, 1u); + vector3_type eps1 = randGen(depth * 2u + 1u, _sample, 1u); // thresholds const scalar_type bxdfPdfThreshold = 0.0001; const scalar_type lumaContributionThreshold = getLuma(colorspace::eotf::sRGB((vector3_type)1.0 / 255.0)); // OETF smallest perceptible value - const vector3_type throughputCIE_Y = colorspace::sRGBtoXYZ[1] * throughput; // TODO: this only works if spectral_type is dim 3 const measure_type eta = bxdf.params.ior1 / bxdf.params.ior0; const scalar_type monochromeEta = hlsl::dot(throughputCIE_Y, eta) / (throughputCIE_Y.r + throughputCIE_Y.g + throughputCIE_Y.b); // TODO: imaginary eta? From b7ec514cc2bef82f4b5df2eb73be74da678632c5 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 18 Feb 2026 17:12:03 +0700 Subject: [PATCH 41/69] material system handles emissive material of lights, nee does only deferred pdf --- .../builtin/hlsl/path_tracing/concepts.hlsl | 22 +++++++++-------- .../hlsl/path_tracing/unidirectional.hlsl | 24 ++++++++++++------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index 60da47d305..a17285f81d 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -120,17 +120,19 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_PARAM_1 (ray, typename T::ray_type) #define NBL_CONCEPT_PARAM_2 (id, typename T::light_id_type) #define NBL_CONCEPT_PARAM_3 (v, typename T::vector3_type) -#define NBL_CONCEPT_PARAM_4 (interaction, typename T::interaction_type) -#define NBL_CONCEPT_PARAM_5 (is_bsdf, bool) -#define NBL_CONCEPT_PARAM_6 (depth, uint32_t) -NBL_CONCEPT_BEGIN(7) +#define NBL_CONCEPT_PARAM_4 (radiance, typename T::spectral_type) +#define NBL_CONCEPT_PARAM_5 (interaction, typename T::interaction_type) +#define NBL_CONCEPT_PARAM_6 (is_bsdf, bool) +#define NBL_CONCEPT_PARAM_7 (depth, uint32_t) +NBL_CONCEPT_BEGIN(8) #define nee NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define ray NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define id NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define v NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 -#define interaction NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 -#define is_bsdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 -#define depth NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 +#define radiance NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +#define interaction NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 +#define is_bsdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 +#define depth NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_7 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) @@ -142,15 +144,15 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE)(T::interaction_type)) - ((NBL_CONCEPT_REQ_TYPE)(T::eval_pdf_return_type)) ((NBL_CONCEPT_REQ_TYPE)(T::sample_quotient_return_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferred_eval_and_pdf(id, ray)), ::nbl::hlsl::is_same_v, typename T::eval_pdf_return_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.generate_and_quotient_and_pdf(id, v, interaction, is_bsdf, v, depth)), ::nbl::hlsl::is_same_v, typename T::sample_quotient_return_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferred_pdf(id, ray)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.generate_and_quotient_and_pdf(id, radiance, v, interaction, is_bsdf, v, depth)), ::nbl::hlsl::is_same_v, typename T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.get_environment_radiance(ray)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) ); #undef depth #undef is_bsdf #undef interaction +#undef radiance #undef v #undef id #undef ray diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index da6cc0dce6..39099f4499 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -88,21 +88,26 @@ struct Unidirectional // emissive typename scene_type::mat_light_id_type matLightID = scene.getMatLightIDs(ray.objectID); - if (matLightID.isLight()) + const uint32_t matID = matLightID.matID; + const bool isEmissive = materialSystem.hasEmission(matID); + if (isEmissive) { - typename nee_type::eval_pdf_return_type ret = nee.deferred_eval_and_pdf(matLightID.lightID, ray); - measure_type emissive = ret.radiance * throughput; - scalar_type _pdfSq = hlsl::mix(ret.pdf, ret.pdf * ret.pdf, ret.pdf < numeric_limits::max); - emissive /= (1.0 + _pdfSq * ray.payload.otherTechniqueHeuristic); + measure_type emissive = materialSystem.getEmission(matID, interaction.isotropic); + + const uint32_t lightID = matLightID.lightID; + if (matLightID.isLight()) + { + const scalar_type pdf = nee.deferred_pdf(lightID, ray); + scalar_type pdfSq = hlsl::mix(pdf, pdf * pdf, pdf < numeric_limits::max); + emissive *= ray.foundEmissiveMIS(pdfSq); + } ray.payload.accumulation += emissive; } - if (!matLightID.isBxDF()) + if (!matLightID.isBxDF() || isEmissive) return false; - const uint32_t matID = matLightID.matID; bxdfnode_type bxdf = materialSystem.bxdfs[matID]; - const bool isBSDF = materialSystem.isBSDF(matID); vector3_type eps0 = randGen(depth * 2u, _sample, 0u); @@ -122,8 +127,9 @@ struct Unidirectional if (!partitionRandVariable(eps0.z, rcpChoiceProb)) { uint32_t randLightID = uint32_t(float32_t(randGen.rng()) / numeric_limits::max) * nee.lightCount; + measure_type lightEmission = materialSystem.getEmission(nee.lights[randLightID].emissiveMatID, interaction.isotropic); typename nee_type::sample_quotient_return_type ret = nee.generate_and_quotient_and_pdf( - randLightID, intersection, interaction, + randLightID, lightEmission, intersection, interaction, isBSDF, eps0, depth ); scalar_type t = ret.newRayMaxT; From e02ae4134de03f6bce64183619c430584bd1c68b Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 19 Feb 2026 11:15:34 +0700 Subject: [PATCH 42/69] made some vars members to assign, gaussian takes vec2 --- .../nbl/builtin/hlsl/path_tracing/basic_ray_gen.hlsl | 2 +- .../nbl/builtin/hlsl/path_tracing/gaussian_filter.hlsl | 4 ++-- .../nbl/builtin/hlsl/path_tracing/unidirectional.hlsl | 10 ++++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/basic_ray_gen.hlsl b/include/nbl/builtin/hlsl/path_tracing/basic_ray_gen.hlsl index ab1da9febf..f9f5661bbd 100644 --- a/include/nbl/builtin/hlsl/path_tracing/basic_ray_gen.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/basic_ray_gen.hlsl @@ -26,7 +26,7 @@ struct BasicRayGenerator { vector4_type tmp = NDC; GaussianFilter filter = GaussianFilter::create(2.5, 1.5); // stochastic reconstruction filter - tmp.xy += pixOffsetParam * filter.sample(randVec); + tmp.xy += pixOffsetParam * filter.sample(randVec.xy); // for depth of field we could do another stochastic point-pick tmp = nbl::hlsl::mul(invMVP, tmp); diff --git a/include/nbl/builtin/hlsl/path_tracing/gaussian_filter.hlsl b/include/nbl/builtin/hlsl/path_tracing/gaussian_filter.hlsl index ff19e89d82..6e27749405 100644 --- a/include/nbl/builtin/hlsl/path_tracing/gaussian_filter.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/gaussian_filter.hlsl @@ -26,9 +26,9 @@ struct GaussianFilter return retval; } - vector2_type sample(const vector randVec) + vector2_type sample(const vector2_type randVec) { - vector2_type remappedRand = randVec.xy; + vector2_type remappedRand = randVec; remappedRand.x *= 1.0 - truncation; remappedRand.x += truncation; return boxMuller(remappedRand); diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 39099f4499..6baf3be7f2 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -71,7 +71,7 @@ struct Unidirectional scalar_type getLuma(NBL_CONST_REF_ARG(vector3_type) col) { - return hlsl::dot(colorspace::scRGBtoXYZ[1], col); + return hlsl::dot(spectralTypeToLumaCoeffs, col); } // TODO: will only work with isotropic surfaces, need to do aniso @@ -79,7 +79,7 @@ struct Unidirectional { const vector3_type intersection = intersectData.intersection; vector3_type throughput = ray.payload.throughput; - const vector3_type throughputCIE_Y = hlsl::normalize(colorspace::sRGBtoXYZ[1] * throughput); // TODO: this only works if spectral_type is dim 3 + const vector3_type throughputCIE_Y = hlsl::normalize(spectralTypeToLumaCoeffs * throughput); isotropic_interaction_type iso_interaction = intersectData.iso_interaction; anisotropic_interaction_type interaction = intersectData.aniso_interaction; @@ -114,8 +114,6 @@ struct Unidirectional vector3_type eps1 = randGen(depth * 2u + 1u, _sample, 1u); // thresholds - const scalar_type bxdfPdfThreshold = 0.0001; - const scalar_type lumaContributionThreshold = getLuma(colorspace::eotf::sRGB((vector3_type)1.0 / 255.0)); // OETF smallest perceptible value const measure_type eta = bxdf.params.ior1 / bxdf.params.ior0; const scalar_type monochromeEta = hlsl::dot(throughputCIE_Y, eta) / (throughputCIE_Y.r + throughputCIE_Y.g + throughputCIE_Y.b); // TODO: imaginary eta? @@ -250,6 +248,10 @@ struct Unidirectional material_system_type materialSystem; nee_type nee; scene_type scene; + + scalar_type bxdfPdfThreshold; + scalar_type lumaContributionThreshold; // OETF smallest perceptible value + measure_type spectralTypeToLumaCoeffs; }; } From b27ec2dfa92ba6b0d6b11f954d543facd8a6fe1b Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 19 Feb 2026 14:03:18 +0700 Subject: [PATCH 43/69] nee chooses the light so takes material system with emissive instead --- .../builtin/hlsl/path_tracing/concepts.hlsl | 13 +++++-- .../hlsl/path_tracing/unidirectional.hlsl | 39 ++++++++----------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index a17285f81d..5b871f062f 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -113,6 +113,11 @@ NBL_CONCEPT_END( #undef matsys #include +namespace impl +{ +struct DummyMaterialSystem {}; +} + #define NBL_CONCEPT_NAME NextEventEstimator #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) @@ -120,7 +125,7 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_PARAM_1 (ray, typename T::ray_type) #define NBL_CONCEPT_PARAM_2 (id, typename T::light_id_type) #define NBL_CONCEPT_PARAM_3 (v, typename T::vector3_type) -#define NBL_CONCEPT_PARAM_4 (radiance, typename T::spectral_type) +#define NBL_CONCEPT_PARAM_4 (matSys, impl::DummyMaterialSystem) #define NBL_CONCEPT_PARAM_5 (interaction, typename T::interaction_type) #define NBL_CONCEPT_PARAM_6 (is_bsdf, bool) #define NBL_CONCEPT_PARAM_7 (depth, uint32_t) @@ -129,7 +134,7 @@ NBL_CONCEPT_BEGIN(8) #define ray NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define id NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define v NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 -#define radiance NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +#define matSys NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 #define interaction NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 #define is_bsdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 #define depth NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_7 @@ -146,13 +151,13 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::interaction_type)) ((NBL_CONCEPT_REQ_TYPE)(T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferred_pdf(id, ray)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.generate_and_quotient_and_pdf(id, radiance, v, interaction, is_bsdf, v, depth)), ::nbl::hlsl::is_same_v, typename T::sample_quotient_return_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.template generate_and_quotient_and_pdf(matSys, v, interaction, is_bsdf, v, depth)), ::nbl::hlsl::is_same_v, typename T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.get_environment_radiance(ray)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) ); #undef depth #undef is_bsdf #undef interaction -#undef radiance +#undef matSys #undef v #undef id #undef ray diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 6baf3be7f2..9e6635d3be 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -124,10 +124,8 @@ struct Unidirectional partitionRandVariable.leftProb = neeProbability; if (!partitionRandVariable(eps0.z, rcpChoiceProb)) { - uint32_t randLightID = uint32_t(float32_t(randGen.rng()) / numeric_limits::max) * nee.lightCount; - measure_type lightEmission = materialSystem.getEmission(nee.lights[randLightID].emissiveMatID, interaction.isotropic); - typename nee_type::sample_quotient_return_type ret = nee.generate_and_quotient_and_pdf( - randLightID, lightEmission, intersection, interaction, + typename nee_type::sample_quotient_return_type ret = nee.template generate_and_quotient_and_pdf( + materialSystem, intersection, interaction, isBSDF, eps0, depth ); scalar_type t = ret.newRayMaxT; @@ -135,31 +133,26 @@ struct Unidirectional quotient_pdf_type neeContrib_pdf = ret.quotient_pdf; // We don't allow non watertight transmitters in this renderer - bool validPath = nee_sample.getNdotL() > numeric_limits::min && nee_sample.isValid(); // but if we allowed non-watertight transmitters (single water surface), it would make sense just to apply this line by itself bxdf::fresnel::OrientedEtas orientedEta = bxdf::fresnel::OrientedEtas::create(interaction.getNdotV(), hlsl::promote(monochromeEta)); anisocache_type _cache = anisocache_type::template create(interaction, nee_sample, orientedEta); - validPath = validPath && _cache.getAbsNdotH() >= 0.0; materialSystem.bxdfs[matID].params.eta = monochromeEta; - if (neeContrib_pdf.pdf < numeric_limits::max) + if (neeContrib_pdf.pdf > scalar_type(0.0)) { - if (validPath) - { - // example only uses isotropic bxdfs - quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(matID, nee_sample, interaction.isotropic, _cache.iso_cache); - neeContrib_pdf.quotient *= bxdf.albedo * throughput * bsdf_quotient_pdf.quotient; - const scalar_type otherGenOverChoice = bsdf_quotient_pdf.pdf * rcpChoiceProb; - const scalar_type otherGenOverLightAndChoice = otherGenOverChoice / bsdf_quotient_pdf.pdf; - neeContrib_pdf.quotient *= otherGenOverChoice / (1.f + otherGenOverLightAndChoice * otherGenOverLightAndChoice); // balance heuristic - - ray_type nee_ray; - nee_ray.origin = intersection + nee_sample.getL().getDirection() * t * Tolerance::getStart(depth); - nee_ray.direction = nee_sample.getL().getDirection(); - nee_ray.intersectionT = t; - if (bsdf_quotient_pdf.pdf < numeric_limits::max && getLuma(neeContrib_pdf.quotient) > lumaContributionThreshold && !intersector_type::traceRay(nee_ray, scene).foundHit) - ray.payload.accumulation += neeContrib_pdf.quotient; - } + // example only uses isotropic bxdfs + quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(matID, nee_sample, interaction.isotropic, _cache.iso_cache); + neeContrib_pdf.quotient *= bxdf.albedo * throughput * bsdf_quotient_pdf.quotient; + const scalar_type otherGenOverChoice = bsdf_quotient_pdf.pdf * rcpChoiceProb; + const scalar_type otherGenOverLightAndChoice = otherGenOverChoice / bsdf_quotient_pdf.pdf; + neeContrib_pdf.quotient *= otherGenOverChoice / (1.f + otherGenOverLightAndChoice * otherGenOverLightAndChoice); // balance heuristic + + ray_type nee_ray; + nee_ray.origin = intersection + nee_sample.getL().getDirection() * t * Tolerance::getStart(depth); + nee_ray.direction = nee_sample.getL().getDirection(); + nee_ray.intersectionT = t; + if (bsdf_quotient_pdf.pdf < numeric_limits::max && getLuma(neeContrib_pdf.quotient) > lumaContributionThreshold && !intersector_type::traceRay(nee_ray, scene).foundHit) + ray.payload.accumulation += neeContrib_pdf.quotient; } } From 233f9b8fe49c2183a611d4701c48d79162310bde Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 19 Feb 2026 15:50:09 +0700 Subject: [PATCH 44/69] fixes to mis usage, moved albedo into material --- .../nbl/builtin/hlsl/path_tracing/unidirectional.hlsl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 9e6635d3be..d9f9b85b9c 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -142,16 +142,15 @@ struct Unidirectional { // example only uses isotropic bxdfs quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(matID, nee_sample, interaction.isotropic, _cache.iso_cache); - neeContrib_pdf.quotient *= bxdf.albedo * throughput * bsdf_quotient_pdf.quotient; - const scalar_type otherGenOverChoice = bsdf_quotient_pdf.pdf * rcpChoiceProb; - const scalar_type otherGenOverLightAndChoice = otherGenOverChoice / bsdf_quotient_pdf.pdf; - neeContrib_pdf.quotient *= otherGenOverChoice / (1.f + otherGenOverLightAndChoice * otherGenOverLightAndChoice); // balance heuristic + neeContrib_pdf.quotient *= materialSystem.eval(matID, nee_sample, interaction.isotropic, _cache.iso_cache) * rcpChoiceProb; + const scalar_type otherGenOverLightAndChoice = bsdf_quotient_pdf.pdf * rcpChoiceProb / neeContrib_pdf.pdf; + neeContrib_pdf.quotient /= 1.f + otherGenOverLightAndChoice * otherGenOverLightAndChoice; // balance heuristic ray_type nee_ray; nee_ray.origin = intersection + nee_sample.getL().getDirection() * t * Tolerance::getStart(depth); nee_ray.direction = nee_sample.getL().getDirection(); nee_ray.intersectionT = t; - if (bsdf_quotient_pdf.pdf < numeric_limits::max && getLuma(neeContrib_pdf.quotient) > lumaContributionThreshold && !intersector_type::traceRay(nee_ray, scene).foundHit) + if (getLuma(neeContrib_pdf.quotient) > lumaContributionThreshold && !intersector_type::traceRay(nee_ray, scene).foundHit) ray.payload.accumulation += neeContrib_pdf.quotient; } } @@ -169,7 +168,7 @@ struct Unidirectional // example only uses isotropic bxdfs // the value of the bsdf divided by the probability of the sample being generated quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(matID, bsdf_sample, interaction.isotropic, _cache.iso_cache); - throughput *= bxdf.albedo * bsdf_quotient_pdf.quotient; + throughput *= bsdf_quotient_pdf.quotient; bxdfPdf = bsdf_quotient_pdf.pdf; bxdfSample = bsdf_sample.getL().getDirection(); } From 2e19c8591d7e6b05a1ba0a5ecec99608fc24075b Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 19 Feb 2026 16:01:07 +0700 Subject: [PATCH 45/69] nee shouldn't copy scene again --- include/nbl/builtin/hlsl/path_tracing/concepts.hlsl | 9 ++++++--- .../nbl/builtin/hlsl/path_tracing/unidirectional.hlsl | 7 +++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index 5b871f062f..3468fdc6e9 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -129,7 +129,8 @@ struct DummyMaterialSystem {}; #define NBL_CONCEPT_PARAM_5 (interaction, typename T::interaction_type) #define NBL_CONCEPT_PARAM_6 (is_bsdf, bool) #define NBL_CONCEPT_PARAM_7 (depth, uint32_t) -NBL_CONCEPT_BEGIN(8) +#define NBL_CONCEPT_PARAM_8 (scene, typename T::scene_type) +NBL_CONCEPT_BEGIN(9) #define nee NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define ray NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define id NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 @@ -138,6 +139,7 @@ NBL_CONCEPT_BEGIN(8) #define interaction NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 #define is_bsdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 #define depth NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_7 +#define scene NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_8 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) @@ -150,10 +152,11 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE)(T::interaction_type)) ((NBL_CONCEPT_REQ_TYPE)(T::sample_quotient_return_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferred_pdf(id, ray)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.template generate_and_quotient_and_pdf(matSys, v, interaction, is_bsdf, v, depth)), ::nbl::hlsl::is_same_v, typename T::sample_quotient_return_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferred_pdf(id, ray, scene)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.template generate_and_quotient_and_pdf(matSys, scene, v, interaction, is_bsdf, v, depth)), ::nbl::hlsl::is_same_v, typename T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.get_environment_radiance(ray)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) ); +#undef scene #undef depth #undef is_bsdf #undef interaction diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index d9f9b85b9c..edd01d0ab9 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -97,7 +97,7 @@ struct Unidirectional const uint32_t lightID = matLightID.lightID; if (matLightID.isLight()) { - const scalar_type pdf = nee.deferred_pdf(lightID, ray); + const scalar_type pdf = nee.deferred_pdf(lightID, ray, scene); scalar_type pdfSq = hlsl::mix(pdf, pdf * pdf, pdf < numeric_limits::max); emissive *= ray.foundEmissiveMIS(pdfSq); } @@ -122,10 +122,11 @@ struct Unidirectional scalar_type rcpChoiceProb; sampling::PartitionRandVariable partitionRandVariable; partitionRandVariable.leftProb = neeProbability; + assert(neeProbability >= 0.0 && neeProbability <= 1.0) if (!partitionRandVariable(eps0.z, rcpChoiceProb)) { typename nee_type::sample_quotient_return_type ret = nee.template generate_and_quotient_and_pdf( - materialSystem, intersection, interaction, + materialSystem, scene, intersection, interaction, isBSDF, eps0, depth ); scalar_type t = ret.newRayMaxT; @@ -207,8 +208,6 @@ struct Unidirectional ray_type ray = rayGen.generate(uvw); ray.initPayload(); - nee.scene = scene; - // bounces bool hit = true; bool rayAlive = true; From f9cd1907020a592181c5194f313e23f0b3a670cf Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 19 Feb 2026 17:03:52 +0700 Subject: [PATCH 46/69] intersector does traceShadowRay as well --- include/nbl/builtin/hlsl/path_tracing/concepts.hlsl | 7 ++++++- include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index 3468fdc6e9..13de99cff4 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -52,17 +52,22 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_PARAM_0 (intersect, T) #define NBL_CONCEPT_PARAM_1 (ray, typename T::ray_type) #define NBL_CONCEPT_PARAM_2 (scene, typename T::scene_type) -NBL_CONCEPT_BEGIN(3) +#define NBL_CONCEPT_PARAM_3 (objectID, typename T::object_handle_type) +NBL_CONCEPT_BEGIN(4) #define intersect NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define ray NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define scene NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define objectID NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) ((NBL_CONCEPT_REQ_TYPE)(T::scene_type)) ((NBL_CONCEPT_REQ_TYPE)(T::ray_type)) ((NBL_CONCEPT_REQ_TYPE)(T::object_handle_type)) ((NBL_CONCEPT_REQ_TYPE)(T::intersect_data_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceRay(ray, scene)), ::nbl::hlsl::is_same_v, typename T::intersect_data_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceShadowRay(ray, scene, objectID)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ); +#undef objectID #undef scene #undef ray #undef intersect diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index edd01d0ab9..c98b231c39 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -151,8 +151,8 @@ struct Unidirectional nee_ray.origin = intersection + nee_sample.getL().getDirection() * t * Tolerance::getStart(depth); nee_ray.direction = nee_sample.getL().getDirection(); nee_ray.intersectionT = t; - if (getLuma(neeContrib_pdf.quotient) > lumaContributionThreshold && !intersector_type::traceRay(nee_ray, scene).foundHit) - ray.payload.accumulation += neeContrib_pdf.quotient; + if (getLuma(neeContrib_pdf.quotient) > lumaContributionThreshold) + ray.payload.accumulation += neeContrib_pdf.quotient * intersector_type::traceShadowRay(nee_ray, scene, ret.lightObjectID); } } From 740ff00deeb28a28061aec710d2e0bcd7353bbca Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 20 Feb 2026 12:06:38 +0700 Subject: [PATCH 47/69] use ray methods instead of calling members --- .../hlsl/path_tracing/unidirectional.hlsl | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index c98b231c39..235aa6894a 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -75,7 +75,7 @@ struct Unidirectional } // TODO: will only work with isotropic surfaces, need to do aniso - bool closestHitProgram(uint32_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(intersect_data_type) intersectData, NBL_CONST_REF_ARG(scene_type) scene) + bool closestHitProgram(uint32_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(intersect_data_type) intersectData) { const vector3_type intersection = intersectData.intersection; vector3_type throughput = ray.payload.throughput; @@ -101,7 +101,7 @@ struct Unidirectional scalar_type pdfSq = hlsl::mix(pdf, pdf * pdf, pdf < numeric_limits::max); emissive *= ray.foundEmissiveMIS(pdfSq); } - ray.payload.accumulation += emissive; + ray.addPayloadContribution(emissive); } if (!matLightID.isBxDF() || isEmissive) @@ -131,7 +131,7 @@ struct Unidirectional ); scalar_type t = ret.newRayMaxT; sample_type nee_sample = ret.sample_; - quotient_pdf_type neeContrib_pdf = ret.quotient_pdf; + quotient_pdf_type neeContrib = ret.quotient_pdf; // We don't allow non watertight transmitters in this renderer // but if we allowed non-watertight transmitters (single water surface), it would make sense just to apply this line by itself @@ -139,20 +139,20 @@ struct Unidirectional anisocache_type _cache = anisocache_type::template create(interaction, nee_sample, orientedEta); materialSystem.bxdfs[matID].params.eta = monochromeEta; - if (neeContrib_pdf.pdf > scalar_type(0.0)) + if (neeContrib.pdf > scalar_type(0.0)) { // example only uses isotropic bxdfs quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(matID, nee_sample, interaction.isotropic, _cache.iso_cache); - neeContrib_pdf.quotient *= materialSystem.eval(matID, nee_sample, interaction.isotropic, _cache.iso_cache) * rcpChoiceProb; - const scalar_type otherGenOverLightAndChoice = bsdf_quotient_pdf.pdf * rcpChoiceProb / neeContrib_pdf.pdf; - neeContrib_pdf.quotient /= 1.f + otherGenOverLightAndChoice * otherGenOverLightAndChoice; // balance heuristic + neeContrib.quotient *= materialSystem.eval(matID, nee_sample, interaction.isotropic, _cache.iso_cache) * rcpChoiceProb; + const scalar_type otherGenOverLightAndChoice = bsdf_quotient_pdf.pdf * rcpChoiceProb / neeContrib.pdf; + neeContrib.quotient /= 1.f + otherGenOverLightAndChoice * otherGenOverLightAndChoice; // balance heuristic ray_type nee_ray; nee_ray.origin = intersection + nee_sample.getL().getDirection() * t * Tolerance::getStart(depth); nee_ray.direction = nee_sample.getL().getDirection(); nee_ray.intersectionT = t; - if (getLuma(neeContrib_pdf.quotient) > lumaContributionThreshold) - ray.payload.accumulation += neeContrib_pdf.quotient * intersector_type::traceShadowRay(nee_ray, scene, ret.lightObjectID); + if (getLuma(neeContrib.quotient) > lumaContributionThreshold) + ray.addPayloadContribution(neeContrib.quotient * intersector_type::traceShadowRay(nee_ray, scene, ret.lightObjectID)); } } @@ -178,9 +178,8 @@ struct Unidirectional const float lumaThroughputThreshold = lumaContributionThreshold; if (bxdfPdf > bxdfPdfThreshold && getLuma(throughput) > lumaThroughputThreshold) { - ray.payload.throughput = throughput; scalar_type otherTechniqueHeuristic = neeProbability / bxdfPdf; // numerically stable, don't touch - ray.payload.otherTechniqueHeuristic = otherTechniqueHeuristic * otherTechniqueHeuristic; + ray.setPayloadMISWeights(throughput, otherTechniqueHeuristic * otherTechniqueHeuristic); // trace new ray vector3_type origin = intersection + bxdfSample * (1.0/*kSceneSize*/) * Tolerance::getStart(depth); @@ -197,7 +196,7 @@ struct Unidirectional { vector3_type finalContribution = ray.payload.throughput; finalContribution *= nee.get_environment_radiance(ray); - ray.payload.accumulation += finalContribution; + ray.addPayloadContribution(finalContribution); } // Li @@ -218,7 +217,7 @@ struct Unidirectional hit = intersection.foundHit; if (hit) - rayAlive = closestHitProgram(d, sampleIndex, ray, intersection, scene); + rayAlive = closestHitProgram(d, sampleIndex, ray, intersection); } if (!hit) missProgram(ray); From d96e3379768108bce1bc983025acc45551f8c6d3 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 20 Feb 2026 12:07:03 +0700 Subject: [PATCH 48/69] minor fixes to check ldoth only when transmit --- .../builtin/hlsl/bxdf/base/cook_torrance_base.hlsl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 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 1741ef789d..30639d6b7c 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -308,9 +308,16 @@ struct SCookTorrance partitionRandVariable.leftProb = reflectance; bool transmitted = partitionRandVariable(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); - if (hlsl::isnan(LdotH)) - return sample_type::createInvalid(); + scalar_type LdotH; + if (transmitted) + { + scalar_type det = rcpEta.value2[0]*VdotH*VdotH + scalar_type(1.0) - rcpEta.value2[0]; + if (det < scalar_type(0.0)) + return sample_type::createInvalid(); + LdotH = ieee754::copySign(hlsl::sqrt(det), -VdotH); + } + else + LdotH = VdotH; bool valid; sample_type s = __generate_common(interaction, localH, NdotV, VdotH, LdotH, transmitted, rcpEta, valid); if (valid) From 8a614c3fcf643c30fccaf291890461c16d31361e Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 20 Feb 2026 15:02:40 +0700 Subject: [PATCH 49/69] more getters and setters for ray, result structs --- .../builtin/hlsl/path_tracing/concepts.hlsl | 6 ++-- .../hlsl/path_tracing/unidirectional.hlsl | 30 ++++++++----------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index 13de99cff4..7f4cece79c 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -63,8 +63,8 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scene_type)) ((NBL_CONCEPT_REQ_TYPE)(T::ray_type)) ((NBL_CONCEPT_REQ_TYPE)(T::object_handle_type)) - ((NBL_CONCEPT_REQ_TYPE)(T::intersect_data_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceRay(ray, scene)), ::nbl::hlsl::is_same_v, typename T::intersect_data_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::closest_hit_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceRay(ray, scene)), ::nbl::hlsl::is_same_v, typename T::closest_hit_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceShadowRay(ray, scene, objectID)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ); #undef objectID @@ -133,7 +133,7 @@ struct DummyMaterialSystem {}; #define NBL_CONCEPT_PARAM_4 (matSys, impl::DummyMaterialSystem) #define NBL_CONCEPT_PARAM_5 (interaction, typename T::interaction_type) #define NBL_CONCEPT_PARAM_6 (is_bsdf, bool) -#define NBL_CONCEPT_PARAM_7 (depth, uint32_t) +#define NBL_CONCEPT_PARAM_7 (depth, uint16_t) #define NBL_CONCEPT_PARAM_8 (scene, typename T::scene_type) NBL_CONCEPT_BEGIN(9) #define nee NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 235aa6894a..5adf629e46 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -62,7 +62,7 @@ struct Unidirectional using ray_dir_info_type = typename sample_type::ray_dir_info_type; using ray_type = typename RayGen::ray_type; using object_handle_type = typename Intersector::object_handle_type; - using intersect_data_type = typename Intersector::intersect_data_type; + using closest_hit_type = typename Intersector::closest_hit_type; using bxdfnode_type = typename MaterialSystem::bxdfnode_type; using anisotropic_interaction_type = typename MaterialSystem::anisotropic_interaction_type; using isotropic_interaction_type = typename anisotropic_interaction_type::isotropic_interaction_type; @@ -75,16 +75,10 @@ struct Unidirectional } // TODO: will only work with isotropic surfaces, need to do aniso - bool closestHitProgram(uint32_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(intersect_data_type) intersectData) + bool closestHitProgram(uint16_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(closest_hit_type) intersectData) { - const vector3_type intersection = intersectData.intersection; - vector3_type throughput = ray.payload.throughput; - const vector3_type throughputCIE_Y = hlsl::normalize(spectralTypeToLumaCoeffs * throughput); - - isotropic_interaction_type iso_interaction = intersectData.iso_interaction; - anisotropic_interaction_type interaction = intersectData.aniso_interaction; - iso_interaction.luminosityContributionHint = throughputCIE_Y; - interaction.isotropic.luminosityContributionHint = throughputCIE_Y; + anisotropic_interaction_type interaction = intersectData.getInteraction(); + isotropic_interaction_type iso_interaction = interaction.isotropic; // emissive typename scene_type::mat_light_id_type matLightID = scene.getMatLightIDs(ray.objectID); @@ -113,7 +107,9 @@ struct Unidirectional vector3_type eps0 = randGen(depth * 2u, _sample, 0u); vector3_type eps1 = randGen(depth * 2u + 1u, _sample, 1u); - // thresholds + const vector3_type intersectP = intersectData.getPosition(); + vector3_type throughput = ray.getPayloadThroughput(); + const vector3_type throughputCIE_Y = hlsl::normalize(spectralTypeToLumaCoeffs * throughput); const measure_type eta = bxdf.params.ior1 / bxdf.params.ior0; const scalar_type monochromeEta = hlsl::dot(throughputCIE_Y, eta) / (throughputCIE_Y.r + throughputCIE_Y.g + throughputCIE_Y.b); // TODO: imaginary eta? @@ -126,7 +122,7 @@ struct Unidirectional if (!partitionRandVariable(eps0.z, rcpChoiceProb)) { typename nee_type::sample_quotient_return_type ret = nee.template generate_and_quotient_and_pdf( - materialSystem, scene, intersection, interaction, + materialSystem, scene, intersectP, interaction, isBSDF, eps0, depth ); scalar_type t = ret.newRayMaxT; @@ -148,7 +144,7 @@ struct Unidirectional neeContrib.quotient /= 1.f + otherGenOverLightAndChoice * otherGenOverLightAndChoice; // balance heuristic ray_type nee_ray; - nee_ray.origin = intersection + nee_sample.getL().getDirection() * t * Tolerance::getStart(depth); + nee_ray.origin = intersectP + nee_sample.getL().getDirection() * t * Tolerance::getStart(depth); nee_ray.direction = nee_sample.getL().getDirection(); nee_ray.intersectionT = t; if (getLuma(neeContrib.quotient) > lumaContributionThreshold) @@ -182,7 +178,7 @@ struct Unidirectional ray.setPayloadMISWeights(throughput, otherTechniqueHeuristic * otherTechniqueHeuristic); // trace new ray - vector3_type origin = intersection + bxdfSample * (1.0/*kSceneSize*/) * Tolerance::getStart(depth); + vector3_type origin = intersectP + bxdfSample * (1.0/*kSceneSize*/) * Tolerance::getStart(depth); vector3_type direction = bxdfSample; ray.initData(origin, direction, interaction.getN(), isBSDF); @@ -210,12 +206,12 @@ struct Unidirectional // bounces bool hit = true; bool rayAlive = true; - for (int d = 1; (d <= maxDepth) && hit && rayAlive; d++) + for (uint16_t d = 1; (d <= maxDepth) && hit && rayAlive; d++) { ray.intersectionT = numeric_limits::max; - intersect_data_type intersection = intersector_type::traceRay(ray, scene); + closest_hit_type intersection = intersector_type::traceRay(ray, scene); - hit = intersection.foundHit; + hit = intersection.foundHit(); if (hit) rayAlive = closestHitProgram(d, sampleIndex, ray, intersection); } From 76ed2536bf36a62fce1bc455075373b68e1bb7d8 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 20 Feb 2026 16:45:52 +0700 Subject: [PATCH 50/69] made tolerance method a type alias in nee with new static method --- .../builtin/hlsl/path_tracing/concepts.hlsl | 1 + .../hlsl/path_tracing/unidirectional.hlsl | 44 ++++++------------- 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index 7f4cece79c..eef1662be9 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -157,6 +157,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE)(T::interaction_type)) ((NBL_CONCEPT_REQ_TYPE)(T::sample_quotient_return_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::tolerance_method_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferred_pdf(id, ray, scene)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.template generate_and_quotient_and_pdf(matSys, scene, v, interaction, is_bsdf, v, depth)), ::nbl::hlsl::is_same_v, typename T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.get_environment_radiance(ray)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 5adf629e46..fdae6ba266 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -16,29 +16,6 @@ namespace hlsl namespace path_tracing { -// TODO: unsure what to do with this, awaiting refactor or turning into concept -template -struct Tolerance -{ - NBL_CONSTEXPR_STATIC_INLINE T INTERSECTION_ERROR_BOUND_LOG2 = -8.0; - - static T __common(uint32_t depth) - { - T depthRcp = 1.0 / T(depth); - return INTERSECTION_ERROR_BOUND_LOG2; - } - - static T getStart(uint32_t depth) - { - return nbl::hlsl::exp2(__common(depth)); - } - - static T getEnd(uint32_t depth) - { - return 1.0 - nbl::hlsl::exp2(__common(depth) + 1.0); - } -}; - template && concepts::RayGenerator && concepts::Intersector && concepts::MaterialSystem && @@ -68,6 +45,7 @@ struct Unidirectional using isotropic_interaction_type = typename anisotropic_interaction_type::isotropic_interaction_type; using anisocache_type = typename MaterialSystem::anisocache_type; using quotient_pdf_type = typename NextEventEstimator::quotient_pdf_type; + using tolerance_method_type = typename NextEventEstimator::tolerance_method_type; scalar_type getLuma(NBL_CONST_REF_ARG(vector3_type) col) { @@ -81,7 +59,7 @@ struct Unidirectional isotropic_interaction_type iso_interaction = interaction.isotropic; // emissive - typename scene_type::mat_light_id_type matLightID = scene.getMatLightIDs(ray.objectID); + typename scene_type::mat_light_id_type matLightID = scene.getMatLightIDs(intersectData.getObjectID()); const uint32_t matID = matLightID.matID; const bool isEmissive = materialSystem.hasEmission(matID); if (isEmissive) @@ -143,10 +121,12 @@ struct Unidirectional const scalar_type otherGenOverLightAndChoice = bsdf_quotient_pdf.pdf * rcpChoiceProb / neeContrib.pdf; neeContrib.quotient /= 1.f + otherGenOverLightAndChoice * otherGenOverLightAndChoice; // balance heuristic + const vector3_type origin = intersectP; + const vector3_type direction = nee_sample.getL().getDirection(); ray_type nee_ray; - nee_ray.origin = intersectP + nee_sample.getL().getDirection() * t * Tolerance::getStart(depth); - nee_ray.direction = nee_sample.getL().getDirection(); - nee_ray.intersectionT = t; + nee_ray.initData(origin, direction, interaction.getN(), isBSDF); + nee_ray.setT(t); + tolerance_method_type::template adjust(nee_ray, direction, depth); if (getLuma(neeContrib.quotient) > lumaContributionThreshold) ray.addPayloadContribution(neeContrib.quotient * intersector_type::traceShadowRay(nee_ray, scene, ret.lightObjectID)); } @@ -178,10 +158,12 @@ struct Unidirectional ray.setPayloadMISWeights(throughput, otherTechniqueHeuristic * otherTechniqueHeuristic); // trace new ray - vector3_type origin = intersectP + bxdfSample * (1.0/*kSceneSize*/) * Tolerance::getStart(depth); + vector3_type origin = intersectP; vector3_type direction = bxdfSample; - ray.initData(origin, direction, interaction.getN(), isBSDF); + ray.setT(1.0/*kSceneSize*/); + tolerance_method_type::template adjust(ray, direction, depth); + return true; } @@ -190,7 +172,7 @@ struct Unidirectional void missProgram(NBL_REF_ARG(ray_type) ray) { - vector3_type finalContribution = ray.payload.throughput; + vector3_type finalContribution = ray.getPayloadThroughput(); finalContribution *= nee.get_environment_radiance(ray); ray.addPayloadContribution(finalContribution); } @@ -208,7 +190,7 @@ struct Unidirectional bool rayAlive = true; for (uint16_t d = 1; (d <= maxDepth) && hit && rayAlive; d++) { - ray.intersectionT = numeric_limits::max; + ray.setT(numeric_limits::max); closest_hit_type intersection = intersector_type::traceRay(ray, scene); hit = intersection.foundHit(); From da544f81dd521441fa43457169181fd43452f117 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 23 Feb 2026 12:24:09 +0700 Subject: [PATCH 51/69] add more concepts for additional structs in pt --- .../builtin/hlsl/path_tracing/concepts.hlsl | 63 ++++++++++++++++++- .../hlsl/path_tracing/unidirectional.hlsl | 14 ++--- 2 files changed, 68 insertions(+), 9 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index eef1662be9..bba3575407 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -46,6 +46,24 @@ NBL_CONCEPT_END( #undef raygen #include +#define NBL_CONCEPT_NAME IntersectorClosestHit +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (closest_hit, T) +NBL_CONCEPT_BEGIN(1) +#define closest_hit NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::object_handle_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::interaction_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((closest_hit.foundHit()), ::nbl::hlsl::is_same_v, bool)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((closest_hit.getObjectID()), ::nbl::hlsl::is_same_v, typename T::object_handle_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((closest_hit.getPosition()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((closest_hit.getInteraction()), ::nbl::hlsl::is_same_v, typename T::interaction_type)) +); +#undef closest_hit +#include + #define NBL_CONCEPT_NAME Intersector #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) @@ -64,6 +82,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::ray_type)) ((NBL_CONCEPT_REQ_TYPE)(T::object_handle_type)) ((NBL_CONCEPT_REQ_TYPE)(T::closest_hit_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(IntersectorClosestHit, typename T::closest_hit_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceRay(ray, scene)), ::nbl::hlsl::is_same_v, typename T::closest_hit_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceShadowRay(ray, scene, objectID)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ); @@ -73,6 +92,19 @@ NBL_CONCEPT_END( #undef intersect #include +#define NBL_CONCEPT_NAME BxdfNode +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (node, T) +NBL_CONCEPT_BEGIN(1) +#define intersect NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.getNEEProb()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) +); +#undef intersect +#include + #define NBL_CONCEPT_NAME MaterialSystem #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) @@ -84,7 +116,8 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_PARAM_5 (aniso_cache, typename T::anisocache_type) #define NBL_CONCEPT_PARAM_6 (iso_cache, typename T::isocache_type) #define NBL_CONCEPT_PARAM_7 (u, typename T::vector3_type) -NBL_CONCEPT_BEGIN(8) +#define NBL_CONCEPT_PARAM_8 (cie_y, typename T::measure_type) +NBL_CONCEPT_BEGIN(9) #define matsys NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define matid NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 @@ -93,6 +126,7 @@ NBL_CONCEPT_BEGIN(8) #define aniso_cache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 #define iso_cache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 #define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_7 +#define cie_y NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_8 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) ((NBL_CONCEPT_REQ_TYPE)(T::material_id_type)) @@ -103,11 +137,18 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) ((NBL_CONCEPT_REQ_TYPE)(T::anisocache_type)) ((NBL_CONCEPT_REQ_TYPE)(T::isocache_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::bxdfnode_type)) ((NBL_CONCEPT_REQ_TYPE)(T::create_params_t)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(BxdfNode, typename T::bxdfnode_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.eval(matid, _sample, iso_inter, iso_cache)), ::nbl::hlsl::is_same_v, typename T::measure_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.generate(matid, aniso_inter, u, aniso_cache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.quotient_and_pdf(matid, _sample, iso_inter, iso_cache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.getBxDFNode(matid)), ::nbl::hlsl::is_same_v, typename T::bxdfnode_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.hasEmission(matid)), ::nbl::hlsl::is_same_v, bool)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.setMonochromeEta(matid, cie_y)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.getEmission(matid, iso_inter)), ::nbl::hlsl::is_same_v, typename T::measure_type)) ); +#undef cie_y #undef u #undef iso_cache #undef aniso_cache @@ -123,6 +164,25 @@ namespace impl struct DummyMaterialSystem {}; } +#define NBL_CONCEPT_NAME NextEventEstimatorSampleQuotientReturn +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (sqr, T) +NBL_CONCEPT_BEGIN(1) +#define sqr NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::object_handle_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((sqr.getSample()), ::nbl::hlsl::is_same_v, typename T::sample_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((sqr.getQuotientPdf()), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((sqr.getT()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((sqr.getLightObjectID()), ::nbl::hlsl::is_same_v, typename T::object_handle_type)) +); +#undef sqr +#include + #define NBL_CONCEPT_NAME NextEventEstimator #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) @@ -158,6 +218,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::interaction_type)) ((NBL_CONCEPT_REQ_TYPE)(T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_TYPE)(T::tolerance_method_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(NextEventEstimatorSampleQuotientReturn, typename T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferred_pdf(id, ray, scene)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.template generate_and_quotient_and_pdf(matSys, scene, v, interaction, is_bsdf, v, depth)), ::nbl::hlsl::is_same_v, typename T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.get_environment_radiance(ray)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index fdae6ba266..a5b1eb715b 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -79,7 +79,7 @@ struct Unidirectional if (!matLightID.isBxDF() || isEmissive) return false; - bxdfnode_type bxdf = materialSystem.bxdfs[matID]; + bxdfnode_type bxdf = materialSystem.getBxDFNode(matID); const bool isBSDF = materialSystem.isBSDF(matID); vector3_type eps0 = randGen(depth * 2u, _sample, 0u); @@ -88,8 +88,6 @@ struct Unidirectional const vector3_type intersectP = intersectData.getPosition(); vector3_type throughput = ray.getPayloadThroughput(); const vector3_type throughputCIE_Y = hlsl::normalize(spectralTypeToLumaCoeffs * throughput); - const measure_type eta = bxdf.params.ior1 / bxdf.params.ior0; - const scalar_type monochromeEta = hlsl::dot(throughputCIE_Y, eta) / (throughputCIE_Y.r + throughputCIE_Y.g + throughputCIE_Y.b); // TODO: imaginary eta? // sample lights const scalar_type neeProbability = bxdf.getNEEProb(); @@ -103,15 +101,15 @@ struct Unidirectional materialSystem, scene, intersectP, interaction, isBSDF, eps0, depth ); - scalar_type t = ret.newRayMaxT; - sample_type nee_sample = ret.sample_; - quotient_pdf_type neeContrib = ret.quotient_pdf; + scalar_type t = ret.getT(); + sample_type nee_sample = ret.getSample(); + quotient_pdf_type neeContrib = ret.getQuotientPdf(); // We don't allow non watertight transmitters in this renderer // but if we allowed non-watertight transmitters (single water surface), it would make sense just to apply this line by itself + const scalar_type monochromeEta = materialSystem.setMonochromeEta(matID, throughputCIE_Y); bxdf::fresnel::OrientedEtas orientedEta = bxdf::fresnel::OrientedEtas::create(interaction.getNdotV(), hlsl::promote(monochromeEta)); anisocache_type _cache = anisocache_type::template create(interaction, nee_sample, orientedEta); - materialSystem.bxdfs[matID].params.eta = monochromeEta; if (neeContrib.pdf > scalar_type(0.0)) { @@ -128,7 +126,7 @@ struct Unidirectional nee_ray.setT(t); tolerance_method_type::template adjust(nee_ray, direction, depth); if (getLuma(neeContrib.quotient) > lumaContributionThreshold) - ray.addPayloadContribution(neeContrib.quotient * intersector_type::traceShadowRay(nee_ray, scene, ret.lightObjectID)); + ray.addPayloadContribution(neeContrib.quotient * intersector_type::traceShadowRay(nee_ray, scene, ret.getLightObjectID())); } } From 15003cb5e8b62313e98591a61d614daa71c456b1 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 23 Feb 2026 15:18:13 +0700 Subject: [PATCH 52/69] added concept for ray type --- .../builtin/hlsl/path_tracing/concepts.hlsl | 38 +++++++++++++++++++ .../hlsl/path_tracing/unidirectional.hlsl | 2 +- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index bba3575407..099d29e618 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -29,6 +29,41 @@ NBL_CONCEPT_END( #undef rand #include +#define NBL_CONCEPT_NAME Ray +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (ray, T) +#define NBL_CONCEPT_PARAM_1 (v, typename T::vector3_type) +#define NBL_CONCEPT_PARAM_2 (b, bool) +#define NBL_CONCEPT_PARAM_3 (scalar, typename T::scalar_type) +#define NBL_CONCEPT_PARAM_4 (color, typename T::spectral_type) +NBL_CONCEPT_BEGIN(5) +#define ray NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define v NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define b NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define scalar NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define color NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +NBL_CONCEPT_END( + ((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)((ray.initData(v, v, v, b)), ::nbl::hlsl::is_same_v, void)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.initPayload()), ::nbl::hlsl::is_same_v, void)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.foundEmissiveMIS(scalar)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.addPayloadContribution(color)), ::nbl::hlsl::is_same_v, void)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.getPayloadAccumulatiion()), ::nbl::hlsl::is_same_v, typename T::spectral_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.setPayloadMISWeights(color, scalar)), ::nbl::hlsl::is_same_v, void)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.setT(scalar)), ::nbl::hlsl::is_same_v, void)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.getT()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.getPayloadThroughput()), ::nbl::hlsl::is_same_v, typename T::spectral_type)) +); +#undef color +#undef scalar +#undef b +#undef v +#undef ray +#include + #define NBL_CONCEPT_NAME RayGenerator #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) @@ -40,6 +75,7 @@ NBL_CONCEPT_BEGIN(2) NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) ((NBL_CONCEPT_REQ_TYPE)(T::ray_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(Ray, typename T::ray_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((raygen.generate(randVec)), ::nbl::hlsl::is_same_v, typename T::ray_type)) ); #undef randVec @@ -83,6 +119,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::object_handle_type)) ((NBL_CONCEPT_REQ_TYPE)(T::closest_hit_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(IntersectorClosestHit, typename T::closest_hit_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(Ray, typename T::ray_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceRay(ray, scene)), ::nbl::hlsl::is_same_v, typename T::closest_hit_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceShadowRay(ray, scene, objectID)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ); @@ -219,6 +256,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_TYPE)(T::tolerance_method_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(NextEventEstimatorSampleQuotientReturn, typename T::sample_quotient_return_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(Ray, typename T::ray_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferred_pdf(id, ray, scene)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.template generate_and_quotient_and_pdf(matSys, scene, v, interaction, is_bsdf, v, depth)), ::nbl::hlsl::is_same_v, typename T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.get_environment_radiance(ray)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index a5b1eb715b..9b67488c56 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -199,7 +199,7 @@ struct Unidirectional missProgram(ray); const uint32_t sampleCount = sampleIndex + 1; - accumulator.addSample(sampleCount, ray.payload.accumulation); + accumulator.addSample(sampleCount, ray.getPayloadAccumulatiion()); // TODO: visualize high variance From 0a85285c3d3618029c662f47dd1a1ec2e05ddd80 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 23 Feb 2026 15:55:30 +0700 Subject: [PATCH 53/69] added a create param struct for resolve params --- .../builtin/hlsl/rwmc/ResolveParameters.hlsl | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl b/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl index 6ed6dc99ce..afd5bf0573 100644 --- a/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl @@ -15,17 +15,26 @@ struct SResolveParameters { using scalar_t = float32_t; - static SResolveParameters create(scalar_t base, uint32_t sampleCount, scalar_t minReliableLuma, scalar_t kappa) + struct SCreateParams + { + scalar_t minReliableLuma; + scalar_t kappa; + scalar_t start; + scalar_t base; + uint32_t sampleCount; + }; + + static SResolveParameters create(SCreateParams params) { SResolveParameters retval; - retval.initialEmin = minReliableLuma; - retval.reciprocalBase = 1.f / base; - const scalar_t N = scalar_t(sampleCount); + retval.initialEmin = params.minReliableLuma; + retval.reciprocalBase = 1.f / params.base; + const scalar_t N = scalar_t(params.sampleCount); retval.reciprocalN = 1.f / N; - retval.reciprocalKappa = 1.f / kappa; + retval.reciprocalKappa = 1.f / params.kappa; // if not interested in exact expected value estimation (kappa!=1.f), can usually accept a bit more variance relative to the image brightness we already have // allow up to ~ more energy in one sample to lessen bias in some cases - retval.colorReliabilityFactor = base + (1.f - base) * retval.reciprocalKappa; + retval.colorReliabilityFactor = params.base + (1.f - params.base) * retval.reciprocalKappa; retval.NOverKappa = N * retval.reciprocalKappa; return retval; From 60728b73854bb1787c3e197f06a9595dd23760d7 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 23 Feb 2026 16:21:21 +0700 Subject: [PATCH 54/69] renamed intersector traceRay to traceClosestHit --- include/nbl/builtin/hlsl/path_tracing/concepts.hlsl | 2 +- include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index 099d29e618..826764edab 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -120,7 +120,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::closest_hit_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(IntersectorClosestHit, typename T::closest_hit_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(Ray, typename T::ray_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceRay(ray, scene)), ::nbl::hlsl::is_same_v, typename T::closest_hit_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceClosestHit(ray, scene)), ::nbl::hlsl::is_same_v, typename T::closest_hit_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceShadowRay(ray, scene, objectID)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ); #undef objectID diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 9b67488c56..fab13dac8d 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -189,7 +189,7 @@ struct Unidirectional for (uint16_t d = 1; (d <= maxDepth) && hit && rayAlive; d++) { ray.setT(numeric_limits::max); - closest_hit_type intersection = intersector_type::traceRay(ray, scene); + closest_hit_type intersection = intersector_type::traceClosestHit(ray, scene); hit = intersection.foundHit(); if (hit) From 8a29fa2de3c69285c712ff6394c47f9a202bd13e Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 24 Feb 2026 10:58:59 +0700 Subject: [PATCH 55/69] concept for material light id --- .../builtin/hlsl/path_tracing/concepts.hlsl | 18 ++++++++++++++++++ .../hlsl/path_tracing/unidirectional.hlsl | 6 +++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index 826764edab..a8c126ff72 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -291,6 +291,23 @@ NBL_CONCEPT_END( #undef acc #include +#define NBL_CONCEPT_NAME MaterialLightID +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (id, T) +NBL_CONCEPT_BEGIN(1) +#define id NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::light_id_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::material_id_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((id.getLightID()), ::nbl::hlsl::is_same_v, typename T::light_id_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((id.getMaterialID()), ::nbl::hlsl::is_same_v, typename T::material_id_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((id.isLight()), ::nbl::hlsl::is_same_v, bool)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((id.isMaterial()), ::nbl::hlsl::is_same_v, bool)) +); +#undef id +#include + #define NBL_CONCEPT_NAME Scene #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) @@ -305,6 +322,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) ((NBL_CONCEPT_REQ_TYPE)(T::object_handle_type)) ((NBL_CONCEPT_REQ_TYPE)(T::mat_light_id_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(MaterialLightID, typename T::mat_light_id_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((scene.getMatLightIDs(id)), ::nbl::hlsl::is_same_v, typename T::mat_light_id_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((scene.getNormal(id, intersectP)), ::nbl::hlsl::is_same_v, typename T::vector3_type)) ); diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index fab13dac8d..63f421792e 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -60,13 +60,13 @@ struct Unidirectional // emissive typename scene_type::mat_light_id_type matLightID = scene.getMatLightIDs(intersectData.getObjectID()); - const uint32_t matID = matLightID.matID; + const typename material_system_type::material_id_type matID = matLightID.getMaterialID(); const bool isEmissive = materialSystem.hasEmission(matID); if (isEmissive) { measure_type emissive = materialSystem.getEmission(matID, interaction.isotropic); - const uint32_t lightID = matLightID.lightID; + const typename nee_type::light_id_type lightID = matLightID.getLightID(); if (matLightID.isLight()) { const scalar_type pdf = nee.deferred_pdf(lightID, ray, scene); @@ -76,7 +76,7 @@ struct Unidirectional ray.addPayloadContribution(emissive); } - if (!matLightID.isBxDF() || isEmissive) + if (!matLightID.isMaterial() || isEmissive) return false; bxdfnode_type bxdf = materialSystem.getBxDFNode(matID); From 9a227eba345d98d250bead3e5fae4a9e26bae9b9 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 24 Feb 2026 16:03:23 +0700 Subject: [PATCH 56/69] removed redundant param for randgen operator --- include/nbl/builtin/hlsl/path_tracing/concepts.hlsl | 11 +++++++---- .../nbl/builtin/hlsl/path_tracing/unidirectional.hlsl | 9 ++++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index a8c126ff72..585565fe38 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2026 - 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_PATH_TRACING_CONCEPTS_INCLUDED_ #define _NBL_BUILTIN_HLSL_PATH_TRACING_CONCEPTS_INCLUDED_ @@ -16,16 +19,16 @@ namespace concepts #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (rand, T) -#define NBL_CONCEPT_PARAM_1 (sample_, uint32_t) +#define NBL_CONCEPT_PARAM_1 (sampleIx, uint32_t) NBL_CONCEPT_BEGIN(2) #define rand NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 -#define sample_ NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define sampleIx NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::rng_type)) ((NBL_CONCEPT_REQ_TYPE)(T::return_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rand(sample_, sample_, sample_)), ::nbl::hlsl::is_same_v, typename T::return_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rand(sampleIx, sampleIx)), ::nbl::hlsl::is_same_v, typename T::return_type)) ); -#undef sample_ +#undef sampleIx #undef rand #include diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 63f421792e..338211e50a 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2026 - 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_PATH_TRACING_UNIDIRECTIONAL_INCLUDED_ #define _NBL_BUILTIN_HLSL_PATH_TRACING_UNIDIRECTIONAL_INCLUDED_ @@ -82,8 +85,8 @@ struct Unidirectional bxdfnode_type bxdf = materialSystem.getBxDFNode(matID); const bool isBSDF = materialSystem.isBSDF(matID); - vector3_type eps0 = randGen(depth * 2u, _sample, 0u); - vector3_type eps1 = randGen(depth * 2u + 1u, _sample, 1u); + vector3_type eps0 = randGen(depth * 2u, _sample); + vector3_type eps1 = randGen(depth * 2u + 1u, _sample); const vector3_type intersectP = intersectData.getPosition(); vector3_type throughput = ray.getPayloadThroughput(); @@ -179,7 +182,7 @@ struct Unidirectional void sampleMeasure(uint32_t sampleIndex, uint32_t maxDepth, NBL_REF_ARG(Accumulator) accumulator) { //scalar_type meanLumaSq = 0.0; - vector3_type uvw = randGen(0u, sampleIndex, 0u); + vector3_type uvw = randGen(0u, sampleIndex); ray_type ray = rayGen.generate(uvw); ray.initPayload(); From a5061808110847a3d2ca5ecf90af471003141f78 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 24 Feb 2026 16:57:15 +0700 Subject: [PATCH 57/69] added a create method to pack all the required splatting params --- .../builtin/hlsl/rwmc/SplattingParameters.hlsl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl b/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl index 4ce4f43200..91404faf10 100644 --- a/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl @@ -37,6 +37,21 @@ struct SPackedSplattingParameters // pack as Half2x16 int32_t PackedLog2BaseRootAndBrightSampleLumaBias; + static SPackedSplattingParameters create(float32_t base, float32_t start, uint32_t cascadeCount) + { + const float32_t rcpLog2Base = 1.0f / hlsl::log2(base); + const float32_t baseRootOfStart = hlsl::exp2(hlsl::log2(start) * rcpLog2Base); + const float32_t log2BaseRootOfStart = hlsl::log2(baseRootOfStart); + const float32_t brightSampleLumaBias = (log2BaseRootOfStart + float32_t(cascadeCount - 1u)) / rcpLog2Base; + float32_t2 packLogs = float32_t2(baseRootOfStart, rcpLog2Base); + float32_t2 packPrecomputed = float32_t2(log2BaseRootOfStart, brightSampleLumaBias); + + SPackedSplattingParameters retval; + retval.PackedBaseRootAndRcpLog2Base = hlsl::packHalf2x16(packLogs); + retval.PackedLog2BaseRootAndBrightSampleLumaBias = hlsl::packHalf2x16(packPrecomputed); + return retval; + } + SSplattingParameters unpack() { SSplattingParameters retval; From 4ceb3f213d13ede00babe1779ae26fce8b1baab5 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 25 Feb 2026 11:05:32 +0700 Subject: [PATCH 58/69] scene returns an interaction, instead of just normal --- include/nbl/builtin/hlsl/path_tracing/concepts.hlsl | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index 585565fe38..74e5afa60b 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -311,24 +311,33 @@ NBL_CONCEPT_END( #undef id #include +namespace impl +{ +struct DummyRay {}; +} + #define NBL_CONCEPT_NAME Scene #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (scene, T) #define NBL_CONCEPT_PARAM_1 (intersectP, typename T::vector3_type) #define NBL_CONCEPT_PARAM_2 (id, typename T::object_handle_type) -NBL_CONCEPT_BEGIN(3) +#define NBL_CONCEPT_PARAM_3 (ray, impl::DummyRay) +NBL_CONCEPT_BEGIN(4) #define scene NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define intersectP NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define id NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define ray NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) ((NBL_CONCEPT_REQ_TYPE)(T::object_handle_type)) ((NBL_CONCEPT_REQ_TYPE)(T::mat_light_id_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::interaction_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(MaterialLightID, typename T::mat_light_id_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((scene.getMatLightIDs(id)), ::nbl::hlsl::is_same_v, typename T::mat_light_id_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((scene.getNormal(id, intersectP)), ::nbl::hlsl::is_same_v, typename T::vector3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((scene.template getInteraction(id, intersectP, ray)), ::nbl::hlsl::is_same_v, typename T::interaction_type)) ); +#undef ray #undef id #undef intersectP #undef scene From 917acc59131cec83cbd2d6c270d586f083844de5 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 25 Feb 2026 11:36:36 +0700 Subject: [PATCH 59/69] minor fixes to spherical rectangle stuff --- .../hlsl/sampling/spherical_rectangle.hlsl | 2 +- .../hlsl/shapes/spherical_rectangle.hlsl | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl b/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl index bcd4f19b7a..4c3f02e5f2 100644 --- a/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl +++ b/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl @@ -59,7 +59,7 @@ struct SphericalRectangle const scalar_type CLAMP_EPS = 1e-5; // flip z axis if r0.z > 0 - r0.z = ieee754::flipSignIfRHSNegative(r0.z, -r0.z); + r0.z = -hlsl::abs(r0.z); vector3_type r1 = r0 + vector3_type(rect.extents.x, rect.extents.y, 0); const scalar_type au = uv.x * S + k; diff --git a/include/nbl/builtin/hlsl/shapes/spherical_rectangle.hlsl b/include/nbl/builtin/hlsl/shapes/spherical_rectangle.hlsl index 5e23774640..3890d1a2db 100644 --- a/include/nbl/builtin/hlsl/shapes/spherical_rectangle.hlsl +++ b/include/nbl/builtin/hlsl/shapes/spherical_rectangle.hlsl @@ -30,6 +30,31 @@ namespace shapes // You can compute it from an OBB matrix (as given by/to imguizmo to position a [0,1]^2 rectangle mesh where Z+ is the front face. +/* +matrix check = mul(modelSpace,tranpose(modelSpace)); +// orthogonality (don't need to check the other 3 lower half numbers, cause MM^T is symmetric) +assert(check[0][1]==0.f); +assert(check[0][2]==0.f); +assert(check[1][2]==0.f); +// the scales are squared +const vector2_type scalesSq = vector2_type(check[0][0],check[1][1]); +const vector2_type scalesRcp = rsqrt(scalesSq); +// only rotation, scale needs to be thrown away +basis = tranpose(modelSpace); +// right now `mul(basis,fromObserver)` will apply extent scales on the dot product +// need to remove that +basis[0] *= scalesRcp[0]; +basis[1] *= scalesRcp[1]; +// but also back it up so we know the size of the original rectangle +extents = promote(vector2_type>(1.f)/scalesRcp; +if (dontAssertZScaleIsOne) + basis[2] *= rsqrt(check[2][2]); +else +{ + assert(check[2][2]==1.f); +} +*/ + // Now, can apply translation: // 1) post-rotation so a it automatically gets added during a affine pseudo-mul of a 3x4, so pseudo_mul(basis,observer) // 2) pre-rotation so you keep a worldspace rectangle origin and subtract it before, e.g. mul(basis,worldSpaceOrigin-observer) - this one is possibly better due to next point From 4ea58c84ed7ab8ec7e14d71b22c77656ba878410 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 25 Feb 2026 14:08:59 +0700 Subject: [PATCH 60/69] remove unused variable in packed splatting params --- .../hlsl/rwmc/SplattingParameters.hlsl | 31 +++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl b/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl index 91404faf10..939c30815b 100644 --- a/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl @@ -27,39 +27,32 @@ struct SSplattingParameters struct SPackedSplattingParameters { - // float16_t baseRootOfStart; 0 - // float16_t rcpLog2Base; 1 + // float16_t rcpLog2Base; 0 + // float16_t log2BaseRootOfStart; 1 // pack as Half2x16 - int32_t PackedBaseRootAndRcpLog2Base; - - // float16_t log2BaseRootOfStart; 2 - // float16_t brightSampleLumaBias; 3 - // pack as Half2x16 - int32_t PackedLog2BaseRootAndBrightSampleLumaBias; + int32_t PackedRcpLog2BaseAndLog2BaseRoot; + float32_t BrightSampleLumaBias; static SPackedSplattingParameters create(float32_t base, float32_t start, uint32_t cascadeCount) { const float32_t rcpLog2Base = 1.0f / hlsl::log2(base); - const float32_t baseRootOfStart = hlsl::exp2(hlsl::log2(start) * rcpLog2Base); - const float32_t log2BaseRootOfStart = hlsl::log2(baseRootOfStart); + const float32_t log2BaseRootOfStart = hlsl::log2(start) * rcpLog2Base; const float32_t brightSampleLumaBias = (log2BaseRootOfStart + float32_t(cascadeCount - 1u)) / rcpLog2Base; - float32_t2 packLogs = float32_t2(baseRootOfStart, rcpLog2Base); - float32_t2 packPrecomputed = float32_t2(log2BaseRootOfStart, brightSampleLumaBias); + float32_t2 packLogs = float32_t2(rcpLog2Base, log2BaseRootOfStart); SPackedSplattingParameters retval; - retval.PackedBaseRootAndRcpLog2Base = hlsl::packHalf2x16(packLogs); - retval.PackedLog2BaseRootAndBrightSampleLumaBias = hlsl::packHalf2x16(packPrecomputed); + retval.PackedRcpLog2BaseAndLog2BaseRoot = hlsl::packHalf2x16(packLogs); + retval.BrightSampleLumaBias = brightSampleLumaBias; return retval; } SSplattingParameters unpack() { SSplattingParameters retval; - const float32_t2 unpackedBaseRootAndRcpLog2Base = hlsl::unpackHalf2x16(PackedBaseRootAndRcpLog2Base); - const float32_t2 unpackedLog2BaseRootAndBrightSampleLumaBias = hlsl::unpackHalf2x16(PackedLog2BaseRootAndBrightSampleLumaBias); - retval.RcpLog2Base = unpackedBaseRootAndRcpLog2Base[1]; - retval.Log2BaseRootOfStart = unpackedLog2BaseRootAndBrightSampleLumaBias[0]; - retval.BrightSampleLumaBias = unpackedLog2BaseRootAndBrightSampleLumaBias[1]; + const float32_t2 unpackedRcpLog2BaseAndLog2BaseRoot = hlsl::unpackHalf2x16(PackedRcpLog2BaseAndLog2BaseRoot); + retval.RcpLog2Base = unpackedRcpLog2BaseAndLog2BaseRoot[0]; + retval.Log2BaseRootOfStart = unpackedRcpLog2BaseAndLog2BaseRoot[1]; + retval.BrightSampleLumaBias = BrightSampleLumaBias; return retval; } }; From 885e58f62e5a3c20fd1362adaddaf66ab1448de4 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 25 Feb 2026 14:32:53 +0700 Subject: [PATCH 61/69] minor changes to concept for intersector, nee; document concept symbols --- .../builtin/hlsl/path_tracing/concepts.hlsl | 8 +++---- .../hlsl/path_tracing/unidirectional.hlsl | 21 +++++++++---------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index 74e5afa60b..c0da906b5a 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -123,8 +123,8 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::closest_hit_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(IntersectorClosestHit, typename T::closest_hit_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(Ray, typename T::ray_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceClosestHit(ray, scene)), ::nbl::hlsl::is_same_v, typename T::closest_hit_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceShadowRay(ray, scene, objectID)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceClosestHit(scene, ray)), ::nbl::hlsl::is_same_v, typename T::closest_hit_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((intersect.traceShadowRay(scene, ray, objectID)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ); #undef objectID #undef scene @@ -260,8 +260,8 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::tolerance_method_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(NextEventEstimatorSampleQuotientReturn, typename T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(Ray, typename T::ray_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferred_pdf(id, ray, scene)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.template generate_and_quotient_and_pdf(matSys, scene, v, interaction, is_bsdf, v, depth)), ::nbl::hlsl::is_same_v, typename T::sample_quotient_return_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferred_pdf(scene, id, ray)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.template generate_and_quotient_and_pdf(scene, matSys, v/*origin*/, interaction, is_bsdf, v/*xi*/, depth)), ::nbl::hlsl::is_same_v, typename T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.get_environment_radiance(ray)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) ); #undef scene diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 338211e50a..6f9291b0b7 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -72,7 +72,7 @@ struct Unidirectional const typename nee_type::light_id_type lightID = matLightID.getLightID(); if (matLightID.isLight()) { - const scalar_type pdf = nee.deferred_pdf(lightID, ray, scene); + const scalar_type pdf = nee.deferred_pdf(scene, lightID, ray); scalar_type pdfSq = hlsl::mix(pdf, pdf * pdf, pdf < numeric_limits::max); emissive *= ray.foundEmissiveMIS(pdfSq); } @@ -101,7 +101,7 @@ struct Unidirectional if (!partitionRandVariable(eps0.z, rcpChoiceProb)) { typename nee_type::sample_quotient_return_type ret = nee.template generate_and_quotient_and_pdf( - materialSystem, scene, intersectP, interaction, + scene, materialSystem, intersectP, interaction, isBSDF, eps0, depth ); scalar_type t = ret.getT(); @@ -129,7 +129,7 @@ struct Unidirectional nee_ray.setT(t); tolerance_method_type::template adjust(nee_ray, direction, depth); if (getLuma(neeContrib.quotient) > lumaContributionThreshold) - ray.addPayloadContribution(neeContrib.quotient * intersector_type::traceShadowRay(nee_ray, scene, ret.getLightObjectID())); + ray.addPayloadContribution(neeContrib.quotient * intersector_type::traceShadowRay(scene, nee_ray, ret.getLightObjectID())); } } @@ -187,18 +187,17 @@ struct Unidirectional ray.initPayload(); // bounces - bool hit = true; - bool rayAlive = true; - for (uint16_t d = 1; (d <= maxDepth) && hit && rayAlive; d++) + bool continuePath = true; + for (uint16_t d = 1; (d <= maxDepth) && continuePath; d++) { ray.setT(numeric_limits::max); - closest_hit_type intersection = intersector_type::traceClosestHit(ray, scene); + closest_hit_type intersection = intersector_type::traceClosestHit(scene, ray); - hit = intersection.foundHit(); - if (hit) - rayAlive = closestHitProgram(d, sampleIndex, ray, intersection); + continuePath = intersection.foundHit(); + if (continuePath) + continuePath &= closestHitProgram(d, sampleIndex, ray, intersection); } - if (!hit) + if (!continuePath) missProgram(ray); const uint32_t sampleCount = sampleIndex + 1; From 9aeb80c6d313d7859a13e63248ee89072b13faab Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 25 Feb 2026 16:42:56 +0700 Subject: [PATCH 62/69] changes to ray methods to separate interaction from init, nee takes an interaction instead of just isBSDF --- .../hlsl/path_tracing/basic_ray_gen.hlsl | 2 +- .../builtin/hlsl/path_tracing/concepts.hlsl | 31 ++++++++++--------- .../hlsl/path_tracing/unidirectional.hlsl | 22 +++++++------ 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/basic_ray_gen.hlsl b/include/nbl/builtin/hlsl/path_tracing/basic_ray_gen.hlsl index f9f5661bbd..898b3b0bd9 100644 --- a/include/nbl/builtin/hlsl/path_tracing/basic_ray_gen.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/basic_ray_gen.hlsl @@ -31,7 +31,7 @@ struct BasicRayGenerator tmp = nbl::hlsl::mul(invMVP, tmp); ray_type ray; - ray.initData(camPos, hlsl::normalize(tmp.xyz / tmp.w - camPos), hlsl::promote(0.0), false); + ray.init(camPos, hlsl::normalize(tmp.xyz / tmp.w - camPos)); return ray; } diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index c0da906b5a..ca6aa888cb 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -32,25 +32,31 @@ NBL_CONCEPT_END( #undef rand #include +namespace impl +{ +struct DummyInteraction {}; +} + #define NBL_CONCEPT_NAME Ray #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (ray, T) #define NBL_CONCEPT_PARAM_1 (v, typename T::vector3_type) -#define NBL_CONCEPT_PARAM_2 (b, bool) +#define NBL_CONCEPT_PARAM_2 (interaction, impl::DummyInteraction) #define NBL_CONCEPT_PARAM_3 (scalar, typename T::scalar_type) #define NBL_CONCEPT_PARAM_4 (color, typename T::spectral_type) NBL_CONCEPT_BEGIN(5) #define ray NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define v NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 -#define b NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define interaction NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define scalar NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 #define color NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 NBL_CONCEPT_END( ((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)((ray.initData(v, v, v, b)), ::nbl::hlsl::is_same_v, void)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.init(v/*origin*/, v/*direction*/)), ::nbl::hlsl::is_same_v, void)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.template initInteraction(interaction)), ::nbl::hlsl::is_same_v, void)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.initPayload()), ::nbl::hlsl::is_same_v, void)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.foundEmissiveMIS(scalar)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.addPayloadContribution(color)), ::nbl::hlsl::is_same_v, void)) @@ -62,7 +68,7 @@ NBL_CONCEPT_END( ); #undef color #undef scalar -#undef b +#undef interaction #undef v #undef ray #include @@ -183,7 +189,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.eval(matid, _sample, iso_inter, iso_cache)), ::nbl::hlsl::is_same_v, typename T::measure_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.generate(matid, aniso_inter, u, aniso_cache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.quotient_and_pdf(matid, _sample, iso_inter, iso_cache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.getBxDFNode(matid)), ::nbl::hlsl::is_same_v, typename T::bxdfnode_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.getBxDFNode(matid, aniso_inter)), ::nbl::hlsl::is_same_v, typename T::bxdfnode_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.hasEmission(matid)), ::nbl::hlsl::is_same_v, bool)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.setMonochromeEta(matid, cie_y)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.getEmission(matid, iso_inter)), ::nbl::hlsl::is_same_v, typename T::measure_type)) @@ -232,19 +238,17 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_PARAM_3 (v, typename T::vector3_type) #define NBL_CONCEPT_PARAM_4 (matSys, impl::DummyMaterialSystem) #define NBL_CONCEPT_PARAM_5 (interaction, typename T::interaction_type) -#define NBL_CONCEPT_PARAM_6 (is_bsdf, bool) -#define NBL_CONCEPT_PARAM_7 (depth, uint16_t) -#define NBL_CONCEPT_PARAM_8 (scene, typename T::scene_type) -NBL_CONCEPT_BEGIN(9) +#define NBL_CONCEPT_PARAM_6 (depth, uint16_t) +#define NBL_CONCEPT_PARAM_7 (scene, typename T::scene_type) +NBL_CONCEPT_BEGIN(8) #define nee NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define ray NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define id NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define v NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 #define matSys NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 #define interaction NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 -#define is_bsdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 -#define depth NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_7 -#define scene NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_8 +#define depth NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 +#define scene NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_7 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) @@ -261,12 +265,11 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(NextEventEstimatorSampleQuotientReturn, typename T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(Ray, typename T::ray_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferred_pdf(scene, id, ray)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.template generate_and_quotient_and_pdf(scene, matSys, v/*origin*/, interaction, is_bsdf, v/*xi*/, depth)), ::nbl::hlsl::is_same_v, typename T::sample_quotient_return_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.template generate_and_quotient_and_pdf(scene, matSys, v/*origin*/, interaction, v/*xi*/, depth)), ::nbl::hlsl::is_same_v, typename T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.get_environment_radiance(ray)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) ); #undef scene #undef depth -#undef is_bsdf #undef interaction #undef matSys #undef v diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 6f9291b0b7..e9e74ee7b4 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -59,7 +59,7 @@ struct Unidirectional bool closestHitProgram(uint16_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(closest_hit_type) intersectData) { anisotropic_interaction_type interaction = intersectData.getInteraction(); - isotropic_interaction_type iso_interaction = interaction.isotropic; + isotropic_interaction_type iso_interaction = interaction.getIsotropic(); // emissive typename scene_type::mat_light_id_type matLightID = scene.getMatLightIDs(intersectData.getObjectID()); @@ -67,7 +67,7 @@ struct Unidirectional const bool isEmissive = materialSystem.hasEmission(matID); if (isEmissive) { - measure_type emissive = materialSystem.getEmission(matID, interaction.isotropic); + measure_type emissive = materialSystem.getEmission(matID, iso_interaction); const typename nee_type::light_id_type lightID = matLightID.getLightID(); if (matLightID.isLight()) @@ -82,8 +82,8 @@ struct Unidirectional if (!matLightID.isMaterial() || isEmissive) return false; - bxdfnode_type bxdf = materialSystem.getBxDFNode(matID); - const bool isBSDF = materialSystem.isBSDF(matID); + bxdfnode_type bxdf = materialSystem.getBxDFNode(matID, interaction); + // const bool isBSDF = materialSystem.isBSDF(matID); vector3_type eps0 = randGen(depth * 2u, _sample); vector3_type eps1 = randGen(depth * 2u + 1u, _sample); @@ -102,7 +102,7 @@ struct Unidirectional { typename nee_type::sample_quotient_return_type ret = nee.template generate_and_quotient_and_pdf( scene, materialSystem, intersectP, interaction, - isBSDF, eps0, depth + eps0, depth ); scalar_type t = ret.getT(); sample_type nee_sample = ret.getSample(); @@ -117,15 +117,16 @@ struct Unidirectional if (neeContrib.pdf > scalar_type(0.0)) { // example only uses isotropic bxdfs - quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(matID, nee_sample, interaction.isotropic, _cache.iso_cache); - neeContrib.quotient *= materialSystem.eval(matID, nee_sample, interaction.isotropic, _cache.iso_cache) * rcpChoiceProb; + quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(matID, nee_sample, iso_interaction, _cache.iso_cache); + neeContrib.quotient *= materialSystem.eval(matID, nee_sample, iso_interaction, _cache.iso_cache) * rcpChoiceProb; const scalar_type otherGenOverLightAndChoice = bsdf_quotient_pdf.pdf * rcpChoiceProb / neeContrib.pdf; neeContrib.quotient /= 1.f + otherGenOverLightAndChoice * otherGenOverLightAndChoice; // balance heuristic const vector3_type origin = intersectP; const vector3_type direction = nee_sample.getL().getDirection(); ray_type nee_ray; - nee_ray.initData(origin, direction, interaction.getN(), isBSDF); + nee_ray.init(origin, direction); + nee_ray.template initInteraction(interaction); nee_ray.setT(t); tolerance_method_type::template adjust(nee_ray, direction, depth); if (getLuma(neeContrib.quotient) > lumaContributionThreshold) @@ -145,7 +146,7 @@ struct Unidirectional // example only uses isotropic bxdfs // the value of the bsdf divided by the probability of the sample being generated - quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(matID, bsdf_sample, interaction.isotropic, _cache.iso_cache); + quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(matID, bsdf_sample, iso_interaction, _cache.iso_cache); throughput *= bsdf_quotient_pdf.quotient; bxdfPdf = bsdf_quotient_pdf.pdf; bxdfSample = bsdf_sample.getL().getDirection(); @@ -161,7 +162,8 @@ struct Unidirectional // trace new ray vector3_type origin = intersectP; vector3_type direction = bxdfSample; - ray.initData(origin, direction, interaction.getN(), isBSDF); + ray.init(origin, direction); + ray.template initInteraction(interaction); ray.setT(1.0/*kSceneSize*/); tolerance_method_type::template adjust(ray, direction, depth); From 575f15b2ed2c5d30d51bcbdccf904f6a0130312f Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 25 Feb 2026 17:08:17 +0700 Subject: [PATCH 63/69] fix normalize throughputCIE, minor changes to method names --- .../builtin/hlsl/path_tracing/concepts.hlsl | 4 ++-- .../hlsl/path_tracing/unidirectional.hlsl | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index ca6aa888cb..54445db042 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -192,7 +192,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.getBxDFNode(matid, aniso_inter)), ::nbl::hlsl::is_same_v, typename T::bxdfnode_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.hasEmission(matid)), ::nbl::hlsl::is_same_v, bool)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.setMonochromeEta(matid, cie_y)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.getEmission(matid, iso_inter)), ::nbl::hlsl::is_same_v, typename T::measure_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.getEmission(matid, aniso_inter)), ::nbl::hlsl::is_same_v, typename T::measure_type)) ); #undef cie_y #undef u @@ -309,7 +309,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((id.getLightID()), ::nbl::hlsl::is_same_v, typename T::light_id_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((id.getMaterialID()), ::nbl::hlsl::is_same_v, typename T::material_id_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((id.isLight()), ::nbl::hlsl::is_same_v, bool)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((id.isMaterial()), ::nbl::hlsl::is_same_v, bool)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((id.canContinuePath()), ::nbl::hlsl::is_same_v, bool)) ); #undef id #include diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index e9e74ee7b4..9d9c540b86 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -64,10 +64,9 @@ struct Unidirectional // emissive typename scene_type::mat_light_id_type matLightID = scene.getMatLightIDs(intersectData.getObjectID()); const typename material_system_type::material_id_type matID = matLightID.getMaterialID(); - const bool isEmissive = materialSystem.hasEmission(matID); - if (isEmissive) + if (materialSystem.hasEmission(matID)) { - measure_type emissive = materialSystem.getEmission(matID, iso_interaction); + measure_type emissive = materialSystem.getEmission(matID, interaction); const typename nee_type::light_id_type lightID = matLightID.getLightID(); if (matLightID.isLight()) @@ -79,18 +78,23 @@ struct Unidirectional ray.addPayloadContribution(emissive); } - if (!matLightID.isMaterial() || isEmissive) + if (!matLightID.canContinuePath()) return false; bxdfnode_type bxdf = materialSystem.getBxDFNode(matID, interaction); - // const bool isBSDF = materialSystem.isBSDF(matID); vector3_type eps0 = randGen(depth * 2u, _sample); vector3_type eps1 = randGen(depth * 2u + 1u, _sample); const vector3_type intersectP = intersectData.getPosition(); vector3_type throughput = ray.getPayloadThroughput(); - const vector3_type throughputCIE_Y = hlsl::normalize(spectralTypeToLumaCoeffs * throughput); + measure_type throughputCIE_Y = spectralTypeToLumaCoeffs * throughput; + { + scalar_type sum_throughput = throughputCIE_Y[0]; + NBL_UNROLL for (uint16_t i = 1; i < vector_traits::Dimension; i++) + sum_throughput += throughputCIE_Y[i]; + throughputCIE_Y /= sum_throughput; + } // sample lights const scalar_type neeProbability = bxdf.getNEEProb(); @@ -210,9 +214,6 @@ struct Unidirectional // TODO: russian roulette early exit? } - NBL_CONSTEXPR_STATIC_INLINE uint32_t MaxDepthLog2 = 4u; - NBL_CONSTEXPR_STATIC_INLINE uint32_t MaxSamplesLog2 = 10u; - randgen_type randGen; raygen_type rayGen; material_system_type materialSystem; From b5a4feb637eaff20c0d991d732b8acb91f95fb7f Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 26 Feb 2026 12:31:17 +0700 Subject: [PATCH 64/69] use nbl this macro for fresnel iridescent that uses this --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index cd0255b6a3..23e5ff327e 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -729,12 +729,7 @@ struct Iridescent(this, base_type::iork3, getEtak23(), clampedCosTheta); -#else - return impl::__iridescent_base__call_const(*this, base_type::iork3, getEtak23(), clampedCosTheta); -#endif + return impl::__iridescent_base__call_const(NBL_DEREF_THIS, base_type::iork3, getEtak23(), clampedCosTheta); } vector_type getEtak23() NBL_CONST_MEMBER_FUNC @@ -781,11 +776,7 @@ struct Iridescent(this, getEtak23(), getEtak23(), clampedCosTheta); -#else - return impl::__iridescent_base__call_const(*this, getEtak23(), getEtak23(), clampedCosTheta); -#endif + return impl::__iridescent_base__call_const(NBL_DEREF_THIS, getEtak23(), getEtak23(), clampedCosTheta); } scalar_type getRefractionOrientedEta() NBL_CONST_MEMBER_FUNC { return base_type::eta13[0]; } From 120e17904d65f8aa1c74a418162a273894ce9496 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 26 Feb 2026 12:31:58 +0700 Subject: [PATCH 65/69] added some asserts, material system should take aniso types and decide internally to use iso --- .../nbl/builtin/hlsl/path_tracing/concepts.hlsl | 4 ++-- .../builtin/hlsl/path_tracing/unidirectional.hlsl | 15 +++++++-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index 54445db042..f13c7a24cd 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -186,9 +186,9 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::bxdfnode_type)) ((NBL_CONCEPT_REQ_TYPE)(T::create_params_t)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(BxdfNode, typename T::bxdfnode_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.eval(matid, _sample, iso_inter, iso_cache)), ::nbl::hlsl::is_same_v, typename T::measure_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.eval(matid, _sample, aniso_inter, aniso_cache)), ::nbl::hlsl::is_same_v, typename T::measure_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.generate(matid, aniso_inter, u, aniso_cache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.quotient_and_pdf(matid, _sample, iso_inter, iso_cache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.quotient_and_pdf(matid, _sample, aniso_inter, aniso_cache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.getBxDFNode(matid, aniso_inter)), ::nbl::hlsl::is_same_v, typename T::bxdfnode_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.hasEmission(matid)), ::nbl::hlsl::is_same_v, bool)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.setMonochromeEta(matid, cie_y)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 9d9c540b86..a14f136044 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -59,7 +59,6 @@ struct Unidirectional bool closestHitProgram(uint16_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(closest_hit_type) intersectData) { anisotropic_interaction_type interaction = intersectData.getInteraction(); - isotropic_interaction_type iso_interaction = interaction.getIsotropic(); // emissive typename scene_type::mat_light_id_type matLightID = scene.getMatLightIDs(intersectData.getObjectID()); @@ -72,7 +71,8 @@ struct Unidirectional if (matLightID.isLight()) { const scalar_type pdf = nee.deferred_pdf(scene, lightID, ray); - scalar_type pdfSq = hlsl::mix(pdf, pdf * pdf, pdf < numeric_limits::max); + assert(!hlsl::isinf(pdf)); + const scalar_type pdfSq = hlsl::mix(pdf, pdf * pdf, pdf < numeric_limits::max); emissive *= ray.foundEmissiveMIS(pdfSq); } ray.addPayloadContribution(emissive); @@ -101,7 +101,7 @@ struct Unidirectional scalar_type rcpChoiceProb; sampling::PartitionRandVariable partitionRandVariable; partitionRandVariable.leftProb = neeProbability; - assert(neeProbability >= 0.0 && neeProbability <= 1.0) + assert(neeProbability >= 0.0 && neeProbability <= 1.0); if (!partitionRandVariable(eps0.z, rcpChoiceProb)) { typename nee_type::sample_quotient_return_type ret = nee.template generate_and_quotient_and_pdf( @@ -120,9 +120,8 @@ struct Unidirectional if (neeContrib.pdf > scalar_type(0.0)) { - // example only uses isotropic bxdfs - quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(matID, nee_sample, iso_interaction, _cache.iso_cache); - neeContrib.quotient *= materialSystem.eval(matID, nee_sample, iso_interaction, _cache.iso_cache) * rcpChoiceProb; + quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(matID, nee_sample, interaction, _cache); + neeContrib.quotient *= materialSystem.eval(matID, nee_sample, interaction, _cache) * rcpChoiceProb; const scalar_type otherGenOverLightAndChoice = bsdf_quotient_pdf.pdf * rcpChoiceProb / neeContrib.pdf; neeContrib.quotient /= 1.f + otherGenOverLightAndChoice * otherGenOverLightAndChoice; // balance heuristic @@ -148,9 +147,8 @@ struct Unidirectional if (!bsdf_sample.isValid()) return false; - // example only uses isotropic bxdfs // the value of the bsdf divided by the probability of the sample being generated - quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(matID, bsdf_sample, iso_interaction, _cache.iso_cache); + quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(matID, bsdf_sample, interaction, _cache); throughput *= bsdf_quotient_pdf.quotient; bxdfPdf = bsdf_quotient_pdf.pdf; bxdfSample = bsdf_sample.getL().getDirection(); @@ -161,6 +159,7 @@ struct Unidirectional if (bxdfPdf > bxdfPdfThreshold && getLuma(throughput) > lumaThroughputThreshold) { scalar_type otherTechniqueHeuristic = neeProbability / bxdfPdf; // numerically stable, don't touch + assert(!hlsl::isinf(otherTechniqueHeuristic)); ray.setPayloadMISWeights(throughput, otherTechniqueHeuristic * otherTechniqueHeuristic); // trace new ray From 5e243d9a8f6f4496314cff9288769c47ef9a407e Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 26 Feb 2026 14:22:39 +0700 Subject: [PATCH 66/69] correct pdf method for mat system, added shouldDoMIS for ray payload --- include/nbl/builtin/hlsl/path_tracing/concepts.hlsl | 1 + .../nbl/builtin/hlsl/path_tracing/unidirectional.hlsl | 9 ++++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index f13c7a24cd..d52c91a31d 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -58,6 +58,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.init(v/*origin*/, v/*direction*/)), ::nbl::hlsl::is_same_v, void)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.template initInteraction(interaction)), ::nbl::hlsl::is_same_v, void)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.initPayload()), ::nbl::hlsl::is_same_v, void)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.shouldDoMIS()), ::nbl::hlsl::is_same_v, bool)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.foundEmissiveMIS(scalar)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.addPayloadContribution(color)), ::nbl::hlsl::is_same_v, void)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.getPayloadAccumulatiion()), ::nbl::hlsl::is_same_v, typename T::spectral_type)) diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index a14f136044..e0d47dd27e 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -68,12 +68,11 @@ struct Unidirectional measure_type emissive = materialSystem.getEmission(matID, interaction); const typename nee_type::light_id_type lightID = matLightID.getLightID(); - if (matLightID.isLight()) + if (ray.shouldDoMIS() && matLightID.isLight()) { const scalar_type pdf = nee.deferred_pdf(scene, lightID, ray); assert(!hlsl::isinf(pdf)); - const scalar_type pdfSq = hlsl::mix(pdf, pdf * pdf, pdf < numeric_limits::max); - emissive *= ray.foundEmissiveMIS(pdfSq); + emissive *= ray.foundEmissiveMIS(pdf * pdf); } ray.addPayloadContribution(emissive); } @@ -120,9 +119,9 @@ struct Unidirectional if (neeContrib.pdf > scalar_type(0.0)) { - quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(matID, nee_sample, interaction, _cache); + const scalar_type bsdf_pdf = materialSystem.pdf(matID, nee_sample, interaction, _cache); neeContrib.quotient *= materialSystem.eval(matID, nee_sample, interaction, _cache) * rcpChoiceProb; - const scalar_type otherGenOverLightAndChoice = bsdf_quotient_pdf.pdf * rcpChoiceProb / neeContrib.pdf; + const scalar_type otherGenOverLightAndChoice = bsdf_pdf * rcpChoiceProb / neeContrib.pdf; neeContrib.quotient /= 1.f + otherGenOverLightAndChoice * otherGenOverLightAndChoice; // balance heuristic const vector3_type origin = intersectP; From 40d585c2fc43d2ddcfc56873a3e9ad6c48b9e29a Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 26 Feb 2026 16:50:20 +0700 Subject: [PATCH 67/69] added some comments regarding future use of MIS, scene takes and intersected ray and gives intersection --- .../nbl/builtin/hlsl/path_tracing/concepts.hlsl | 16 +++++++--------- .../hlsl/path_tracing/unidirectional.hlsl | 6 +++++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index d52c91a31d..50ff3d60d0 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -318,20 +318,19 @@ NBL_CONCEPT_END( namespace impl { struct DummyRay {}; +struct DummyIntersect {}; } #define NBL_CONCEPT_NAME Scene #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (scene, T) -#define NBL_CONCEPT_PARAM_1 (intersectP, typename T::vector3_type) -#define NBL_CONCEPT_PARAM_2 (id, typename T::object_handle_type) -#define NBL_CONCEPT_PARAM_3 (ray, impl::DummyRay) -NBL_CONCEPT_BEGIN(4) +#define NBL_CONCEPT_PARAM_1 (id, typename T::object_handle_type) +#define NBL_CONCEPT_PARAM_2 (ray, impl::DummyRay) +NBL_CONCEPT_BEGIN(3) #define scene NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 -#define intersectP NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 -#define id NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 -#define ray NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define id NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define ray NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) ((NBL_CONCEPT_REQ_TYPE)(T::object_handle_type)) @@ -339,11 +338,10 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::interaction_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(MaterialLightID, typename T::mat_light_id_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((scene.getMatLightIDs(id)), ::nbl::hlsl::is_same_v, typename T::mat_light_id_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((scene.template getInteraction(id, intersectP, ray)), ::nbl::hlsl::is_same_v, typename T::interaction_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((scene.template getIntersection(id, ray)), ::nbl::hlsl::is_same_v, impl::DummyIntersect)) ); #undef ray #undef id -#undef intersectP #undef scene #include diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index e0d47dd27e..d61ecfdbeb 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -55,7 +55,6 @@ struct Unidirectional return hlsl::dot(spectralTypeToLumaCoeffs, col); } - // TODO: will only work with isotropic surfaces, need to do aniso bool closestHitProgram(uint16_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(closest_hit_type) intersectData) { anisotropic_interaction_type interaction = intersectData.getInteraction(); @@ -117,8 +116,13 @@ struct Unidirectional bxdf::fresnel::OrientedEtas orientedEta = bxdf::fresnel::OrientedEtas::create(interaction.getNdotV(), hlsl::promote(monochromeEta)); anisocache_type _cache = anisocache_type::template create(interaction, nee_sample, orientedEta); + // While NEE or other generators are not supposed to pick up Delta lobes by accident, we need the MIS weights to add up to 1 for the non-delta lobes. + // So we need to weigh the Delta lobes as if the MIS weight is always 1, but other areas regularly. + // Meaning that eval's pdf should equal quotient's pdf , this way even the diffuse contributions coming from within a specular lobe get a MIS weight near 0 for NEE. + // This stops a discrepancy in MIS weights and NEE mistakenly trying to add non-delta lobe contributions with a MIS weight > 0 and creating energy from thin air. if (neeContrib.pdf > scalar_type(0.0)) { + // we'll need an `eval_and_mis_weight` and `quotient_and_mis_weight` const scalar_type bsdf_pdf = materialSystem.pdf(matID, nee_sample, interaction, _cache); neeContrib.quotient *= materialSystem.eval(matID, nee_sample, interaction, _cache) * rcpChoiceProb; const scalar_type otherGenOverLightAndChoice = bsdf_pdf * rcpChoiceProb / neeContrib.pdf; From 8d8b4af53fba23390e2092963b8ea590e5d37209 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 27 Feb 2026 10:48:31 +0700 Subject: [PATCH 68/69] minor name changes of methods --- .../builtin/hlsl/path_tracing/concepts.hlsl | 4 ++-- .../hlsl/path_tracing/unidirectional.hlsl | 18 ++++++------------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index 50ff3d60d0..99b57d1bf6 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -56,13 +56,13 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) ((NBL_CONCEPT_REQ_TYPE)(T::spectral_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.init(v/*origin*/, v/*direction*/)), ::nbl::hlsl::is_same_v, void)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.template initInteraction(interaction)), ::nbl::hlsl::is_same_v, void)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.template setInteraction(interaction)), ::nbl::hlsl::is_same_v, void)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.initPayload()), ::nbl::hlsl::is_same_v, void)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.shouldDoMIS()), ::nbl::hlsl::is_same_v, bool)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.foundEmissiveMIS(scalar)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.addPayloadContribution(color)), ::nbl::hlsl::is_same_v, void)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.getPayloadAccumulatiion()), ::nbl::hlsl::is_same_v, typename T::spectral_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.setPayloadMISWeights(color, scalar)), ::nbl::hlsl::is_same_v, void)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.updateThroughputAndMISWeights(color, scalar)), ::nbl::hlsl::is_same_v, void)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.setT(scalar)), ::nbl::hlsl::is_same_v, void)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.getT()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ray.getPayloadThroughput()), ::nbl::hlsl::is_same_v, typename T::spectral_type)) diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index d61ecfdbeb..037a1c9b01 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -85,14 +85,7 @@ struct Unidirectional vector3_type eps1 = randGen(depth * 2u + 1u, _sample); const vector3_type intersectP = intersectData.getPosition(); - vector3_type throughput = ray.getPayloadThroughput(); - measure_type throughputCIE_Y = spectralTypeToLumaCoeffs * throughput; - { - scalar_type sum_throughput = throughputCIE_Y[0]; - NBL_UNROLL for (uint16_t i = 1; i < vector_traits::Dimension; i++) - sum_throughput += throughputCIE_Y[i]; - throughputCIE_Y /= sum_throughput; - } + measure_type throughputCIE_Y = interaction.getLuminosityContributionHint(); // sample lights const scalar_type neeProbability = bxdf.getNEEProb(); @@ -132,7 +125,7 @@ struct Unidirectional const vector3_type direction = nee_sample.getL().getDirection(); ray_type nee_ray; nee_ray.init(origin, direction); - nee_ray.template initInteraction(interaction); + nee_ray.template setInteraction(interaction); nee_ray.setT(t); tolerance_method_type::template adjust(nee_ray, direction, depth); if (getLuma(neeContrib.quotient) > lumaContributionThreshold) @@ -143,6 +136,7 @@ struct Unidirectional // sample BSDF scalar_type bxdfPdf; vector3_type bxdfSample; + vector3_type throughput = ray.getPayloadThroughput(); { anisocache_type _cache; sample_type bsdf_sample = materialSystem.generate(matID, interaction, eps1, _cache); @@ -163,13 +157,13 @@ struct Unidirectional { scalar_type otherTechniqueHeuristic = neeProbability / bxdfPdf; // numerically stable, don't touch assert(!hlsl::isinf(otherTechniqueHeuristic)); - ray.setPayloadMISWeights(throughput, otherTechniqueHeuristic * otherTechniqueHeuristic); + ray.updateThroughputAndMISWeights(throughput, otherTechniqueHeuristic * otherTechniqueHeuristic); // trace new ray vector3_type origin = intersectP; vector3_type direction = bxdfSample; ray.init(origin, direction); - ray.template initInteraction(interaction); + ray.template setInteraction(interaction); ray.setT(1.0/*kSceneSize*/); tolerance_method_type::template adjust(ray, direction, depth); @@ -203,7 +197,7 @@ struct Unidirectional continuePath = intersection.foundHit(); if (continuePath) - continuePath &= closestHitProgram(d, sampleIndex, ray, intersection); + continuePath = closestHitProgram(d, sampleIndex, ray, intersection); } if (!continuePath) missProgram(ray); From 82ee23cfa6f9ef8661a09af5ba7f8c1e186f8e90 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 27 Feb 2026 11:50:30 +0700 Subject: [PATCH 69/69] offset ray origin by geometric normal, not ray dir --- include/nbl/builtin/hlsl/path_tracing/concepts.hlsl | 1 + include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index 99b57d1bf6..a4fa54e4b8 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -106,6 +106,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((closest_hit.getObjectID()), ::nbl::hlsl::is_same_v, typename T::object_handle_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((closest_hit.getPosition()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((closest_hit.getInteraction()), ::nbl::hlsl::is_same_v, typename T::interaction_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((closest_hit.getGeometricNormal()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) ); #undef closest_hit #include diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 037a1c9b01..beb229e8d9 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -127,7 +127,7 @@ struct Unidirectional nee_ray.init(origin, direction); nee_ray.template setInteraction(interaction); nee_ray.setT(t); - tolerance_method_type::template adjust(nee_ray, direction, depth); + tolerance_method_type::template adjust(nee_ray, intersectData.getGeometricNormal(), depth); if (getLuma(neeContrib.quotient) > lumaContributionThreshold) ray.addPayloadContribution(neeContrib.quotient * intersector_type::traceShadowRay(scene, nee_ray, ret.getLightObjectID())); } @@ -164,8 +164,7 @@ struct Unidirectional vector3_type direction = bxdfSample; ray.init(origin, direction); ray.template setInteraction(interaction); - ray.setT(1.0/*kSceneSize*/); - tolerance_method_type::template adjust(ray, direction, depth); + tolerance_method_type::template adjust(ray, intersectData.getGeometricNormal(), depth); return true; }