From b99ae6e6ceb43f620b4e8c6300f495e67530615f Mon Sep 17 00:00:00 2001 From: kevyuu Date: Tue, 11 Nov 2025 15:50:19 +0700 Subject: [PATCH 01/10] Fix small bug in GenericDataAccessor definition --- .../builtin/hlsl/concepts/accessors/generic_shared_data.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/nbl/builtin/hlsl/concepts/accessors/generic_shared_data.hlsl b/include/nbl/builtin/hlsl/concepts/accessors/generic_shared_data.hlsl index cc22595444..ab7a87c7dd 100644 --- a/include/nbl/builtin/hlsl/concepts/accessors/generic_shared_data.hlsl +++ b/include/nbl/builtin/hlsl/concepts/accessors/generic_shared_data.hlsl @@ -69,7 +69,7 @@ NBL_CONCEPT_END( #include template -NBL_BOOL_CONCEPT GenericDataAccessor = GenericWriteAccessor && GenericWriteAccessor; +NBL_BOOL_CONCEPT GenericDataAccessor = GenericReadAccessor && GenericWriteAccessor; } } From b9537ea7f623ca275236079acf9a8cd43e910909 Mon Sep 17 00:00:00 2001 From: kevyuu Date: Tue, 11 Nov 2025 15:51:30 +0700 Subject: [PATCH 02/10] First draft of Warpmap Generation workgroup implementation --- .../hlsl/concepts/accessors/envmap.hlsl | 47 ++++++++ .../nbl/builtin/hlsl/workgroup/envmap.hlsl | 108 ++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 include/nbl/builtin/hlsl/concepts/accessors/envmap.hlsl create mode 100644 include/nbl/builtin/hlsl/workgroup/envmap.hlsl diff --git a/include/nbl/builtin/hlsl/concepts/accessors/envmap.hlsl b/include/nbl/builtin/hlsl/concepts/accessors/envmap.hlsl new file mode 100644 index 0000000000..1d1ad2a344 --- /dev/null +++ b/include/nbl/builtin/hlsl/concepts/accessors/envmap.hlsl @@ -0,0 +1,47 @@ +#ifndef _NBL_BUILTIN_HLSL_CONCEPTS_ACCESSORS_ENVMAP_INCLUDED_ +#define _NBL_BUILTIN_HLSL_CONCEPTS_ACCESSORS_ENVMAP_INCLUDED_ + +#include "nbl/builtin/hlsl/concepts/accessors/generic_shared_data.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace workgroup +{ +namespace envmap +{ +// declare concept +#define NBL_CONCEPT_NAME LuminanceReadAccessor +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (U) +// not the greatest syntax but works +#define NBL_CONCEPT_PARAM_0 (a,U) +#define NBL_CONCEPT_PARAM_1 (uv,uint32_t2) +#define NBL_CONCEPT_PARAM_2 (level,uint32_t) +#define NBL_CONCEPT_PARAM_3 (offset,uint32_t2) +// start concept +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 level NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define offset NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,level,offset)) , ::nbl::hlsl::is_same_v, float32_t4>)) +); +#undef offset +#undef level +#undef uv +#undef a +#include + +template +NBL_BOOL_CONCEPT WarpmapWriteAccessor = concepts::accessors::GenericWriteAccessor; + +} +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/workgroup/envmap.hlsl b/include/nbl/builtin/hlsl/workgroup/envmap.hlsl new file mode 100644 index 0000000000..df452fb0e8 --- /dev/null +++ b/include/nbl/builtin/hlsl/workgroup/envmap.hlsl @@ -0,0 +1,108 @@ + +#ifndef _NBL_BUILTIN_HLSL_WORKGROUP_ENVMAP_IMPORTANCE_SAMPLING_INCLUDED_ +#define _NBL_BUILTIN_HLSL_WORKGROUP_ENVMAP_IMPORTANCE_SAMPLING_INCLUDED_ + +namespace nbl +{ +namespace hlsl +{ +namespace workgroup +{ +namespace envmap +{ +namespace impl +{ + bool choseSecond(float first, float second, NBL_REF_ARG(float) xi) + { + // numerical resilience against IEEE754 + float firstProb = 1.0f / (1.0f + second / first); + float dummy = 0.0f; + return math::partitionRandVariable(firstProb, xi, dummy); + } + +} + +} +} +} +} + +#ifdef __HLSL_VERSION +namespace nbl +{ +namespace hlsl +{ +namespace workgroup +{ +namespace envmap +{ + +struct WarpmapGeneration +{ + + template && envmap::WarpmapWriteAccessor) + // TODO(kevinyu): Should lumapMapSize and warpMapSize provided by Accessor? + static void __call(NBL_CONST_REF_ARG(LuminanceAccessor) luminanceAccessor, NBL_REF_ARG(OutputAcessor) outputAccessor, uint32_t2 lumaMapSize, uint32_t2 warpMapSize) + { + const uint32_t threadID = uint32_t(SubgroupContiguousIndex()); + const uint32_t lastWarpMapPixel = warpMapSize - uint32_t2(1, 1); + + if (all(threadID < warpMapSize)) + { + float32_t2 xi = float32_t2(threadID) / float32_t2(lastWarpMapPixel); + + uint32_t2 p; + p.y = 0; + + // TODO(kevinyu): Implement findMSB + const uint32_t2 mip2x1 = findMSB(lumaMapSize.x) - 1; + // do one split in the X axis first cause penultimate full mip would have been 2x1 + p.x = impl::choseSecond(luminanceAccessor.get(uint32_t2(0, 0), mip2x1, uint32_t2(0, 0)), luminanceAccessor.get(uint32_t2(0, 0), mip2x1, uint32_t2(1, 0), xi.x) ? 1 : 0; + for (uint32_t i = mip2x1; i != 0;) + { + --i; + p <<= 1; + const float32_t4 values = float32_t4( + luminanceAccessor.get(p, i, uint32_t2(0, 1)), + luminanceAccessor.get(p, i, uint32_t2(1, 1)), + luminanceAccessor.get(p, i, uint32_t2(1, 0)), + luminanceAccessor.get(p, i, uint32_t2(0, 0)) + ); + + float32_t wx_0, wx_1; + { + const float32_t wy_0 = values[3] + values[2]; + const float32_t wy_1 = values[1] + values[0]; + if (impl::choseSecond(wy_0, wy_1, xi.y)) + { + p.y |= 1; + wx_0 = values[0]; + wx_1 = values[1]; + } + else + { + wx_0 = values[3]; + wx_1 = values[2]; + } + } + + if (impl::choseSecond(wx_0, wx_1, xi.x)) + { + p.x |= 1; + } + } + + const float32_t2 directionUV = (float32_t2(p.x, p.y) + xi) / float32_t2(lumaMapSize); + outputAccessor.set(threadID, directionUV); + } + } + +}; + +} +} +} +} +#endif + +#endif \ No newline at end of file From a7371738facb18bf22e89335a377492e8e3e58f5 Mon Sep 17 00:00:00 2001 From: kevyuu Date: Tue, 18 Nov 2025 16:48:13 +0700 Subject: [PATCH 03/10] Add warp concept --- include/nbl/builtin/hlsl/concepts/warp.hlsl | 41 +++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 include/nbl/builtin/hlsl/concepts/warp.hlsl diff --git a/include/nbl/builtin/hlsl/concepts/warp.hlsl b/include/nbl/builtin/hlsl/concepts/warp.hlsl new file mode 100644 index 0000000000..e9e981a243 --- /dev/null +++ b/include/nbl/builtin/hlsl/concepts/warp.hlsl @@ -0,0 +1,41 @@ +#ifndef _NBL_BUILTIN_HLSL_CONCEPTS_WARP_INCLUDED_ +#define _NBL_BUILTIN_HLSL_CONCEPTS_WARP_INCLUDED_ + +#include "nbl/builtin/hlsl/concepts/accessors/generic_shared_data.hlsl" +#include "nbl/builtin/hlsl/fft/common.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace concepts +{ + +// declare concept +#define NBL_CONCEPT_NAME WARP +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename)(typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (U)(C) +// not the greatest syntax but works +#define NBL_CONCEPT_PARAM_0 (warp,U) +#define NBL_CONCEPT_PARAM_1 (uv,float32_t2) +#define NBL_CONCEPT_PARAM_2 (out,C) +// start concept +NBL_CONCEPT_BEGIN(3) +#define warp NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define uv NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define out NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template warp(uv)) , ::nbl::hlsl::is_same_v, C)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template forwardDensity(uv)) , ::nbl::hlsl::is_same_v, float32_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template backwardDensity(out)) , ::nbl::hlsl::is_same_v, float32_t)) +); +#undef out +#undef warp +#undef uv +#include + +} +} +} + +#endif \ No newline at end of file From 64349db3ac20d982fcffd871cc43308c0b0c3cc7 Mon Sep 17 00:00:00 2001 From: kevyuu Date: Tue, 18 Nov 2025 16:48:37 +0700 Subject: [PATCH 04/10] Add spherical warp --- include/nbl/builtin/hlsl/warp/spherical.hlsl | 53 ++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 include/nbl/builtin/hlsl/warp/spherical.hlsl diff --git a/include/nbl/builtin/hlsl/warp/spherical.hlsl b/include/nbl/builtin/hlsl/warp/spherical.hlsl new file mode 100644 index 0000000000..10c341f06b --- /dev/null +++ b/include/nbl/builtin/hlsl/warp/spherical.hlsl @@ -0,0 +1,53 @@ +#ifndef _NBL_BUILTIN_HLSL_WARP_SPHERICAL_INCLUDED_ +#define _NBL_BUILTIN_HLSL_WARP_SPHERICAL_INCLUDED_ + +#include + +namespace nbl +{ +namespace hlsl +{ +namespace warp +{ + + class Spherical + { + public: + using codomain_type = float32_t3; + + template ) + static codomain_type warp(const UV uv) + { + const float32_t phi = 2 * uv.x * numbers::pi; + const float32_t theta = uv.y * numbers::pi; + float32_t3 dir; + dir.x = cos(uv.x * 2.f * numbers::pi); + dir.y = sqrt(1.f - dir.x * dir.x); + if (uv.x > 0.5f) dir.y = -dir.y; + const float32_t cosTheta = cos(theta); + float32_t sinTheta = (1.0 - cosTheta * cosTheta); + dir.xy *= sinTheta; + dir.z = cosTheta; + return dir; + } + + template ) + static float32_t forwardDensity(const UV uv) + { + const float32_t theta = uv.y * numbers::pi; + return 1.0f / (sin(theta) * 2 * PI * PI); + + } + + template ) + static float32_t backwardDensity(const C out) + { + //TODO(kevinyu): Derive this density + } + }; + +} +} +} + +#endif \ No newline at end of file From e44fcf44b71164cc938d49c740c0be634c8e5d8b Mon Sep 17 00:00:00 2001 From: kevyuu Date: Tue, 18 Nov 2025 16:49:02 +0700 Subject: [PATCH 05/10] Remove envmap accessors.hlsl --- .../hlsl/concepts/accessors/envmap.hlsl | 47 ------------------- 1 file changed, 47 deletions(-) delete mode 100644 include/nbl/builtin/hlsl/concepts/accessors/envmap.hlsl diff --git a/include/nbl/builtin/hlsl/concepts/accessors/envmap.hlsl b/include/nbl/builtin/hlsl/concepts/accessors/envmap.hlsl deleted file mode 100644 index 1d1ad2a344..0000000000 --- a/include/nbl/builtin/hlsl/concepts/accessors/envmap.hlsl +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef _NBL_BUILTIN_HLSL_CONCEPTS_ACCESSORS_ENVMAP_INCLUDED_ -#define _NBL_BUILTIN_HLSL_CONCEPTS_ACCESSORS_ENVMAP_INCLUDED_ - -#include "nbl/builtin/hlsl/concepts/accessors/generic_shared_data.hlsl" - -namespace nbl -{ -namespace hlsl -{ -namespace workgroup -{ -namespace envmap -{ -// declare concept -#define NBL_CONCEPT_NAME LuminanceReadAccessor -#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) -#define NBL_CONCEPT_TPLT_PRM_NAMES (U) -// not the greatest syntax but works -#define NBL_CONCEPT_PARAM_0 (a,U) -#define NBL_CONCEPT_PARAM_1 (uv,uint32_t2) -#define NBL_CONCEPT_PARAM_2 (level,uint32_t) -#define NBL_CONCEPT_PARAM_3 (offset,uint32_t2) -// start concept -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 level NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 -#define offset NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 -NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,level,offset)) , ::nbl::hlsl::is_same_v, float32_t4>)) -); -#undef offset -#undef level -#undef uv -#undef a -#include - -template -NBL_BOOL_CONCEPT WarpmapWriteAccessor = concepts::accessors::GenericWriteAccessor; - -} -} -} -} - -#endif \ No newline at end of file From 9b29dfd0a08acd2414cb67a6ac2ea72ed2386e4c Mon Sep 17 00:00:00 2001 From: kevyuu Date: Tue, 18 Nov 2025 16:49:19 +0700 Subject: [PATCH 06/10] Hierarchical image sampling implementation --- .../hlsl/sampling/hierarchical_image.hlsl | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 include/nbl/builtin/hlsl/sampling/hierarchical_image.hlsl diff --git a/include/nbl/builtin/hlsl/sampling/hierarchical_image.hlsl b/include/nbl/builtin/hlsl/sampling/hierarchical_image.hlsl new file mode 100644 index 0000000000..bfcd9ffec7 --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/hierarchical_image.hlsl @@ -0,0 +1,136 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_SAMPLING_HIERARCHICAL_IMAGE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_HIERARCHICAL_IMAGE_INCLUDED_ + +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +class HierarchicalImage +{ + private: + + static float32_t3 calculateSampleAndPdf(float32_t4 dirsX, float32_t4 dirsY, float32_t2 unnormCoord, uint32_t2 lastWarpmapPixel, NBL_REF_ARG(float32_t) pdf) + { + const float32_t2 interpolant = frac(unnormCoord); + const float32_t4x2 uvs = transpose(float32_t2x4(dirsX, dirsY)); + + const float32_t2 xDiffs[] = { + uvs[2] - uvs[3], + uvs[1] - uvs[0] + }; + const float32_t2 yVals[] = { + xDiffs[0] * interpolant.x + uvs[3], + xDiffs[1] * interpolant.x + uvs[0] + }; + const float32_t2 yDiff = yVals[1] - yVals[0]; + const float32_t2 uv = yDiff * interpolant.y + yVals[0]; + + // Note(kevinyu): sinTheta is calculated twice inside PostWarp::warp and PostWarp::forwardDensity + const float32_t3 L = PostWarp::warp(uv); + + const float detInterpolJacobian = determinant(float32_t2x2( + lerp(xDiffs[0], xDiffs[1], interpolant.y), // first column dFdx + yDiff // second column dFdy + )); + + pdf = abs(PostWarp::forwardDensity(uv) / (detInterpolJacobian * float32_t(lastWarpmapPixel.x * lastWarpmapPixel.y)); + + return L; + } + + public: + template ) + static float32_t2 binarySearch(NBL_CONST_REF_ARG(LuminanceAccessor) luminanceAccessor, const uint32_t2 lumaMapSize, const float32_t2 xi, const bool aspect2x1) + { + + uint32_t2 p = uint32_t2(0, 0); + + if (aspect2x1) { + // TODO(kevinyu): Implement findMSB + const uint32_t2 mip2x1 = findMSB(lumaMapSize.x) - 1; + + // do one split in the X axis first cause penultimate full mip would have been 2x1 + p.x = impl::choseSecond(luminanceAccessor.fetch(uint32_t2(0, 0), mip2x1), luminanceAccessor.fetch(uint32_t2(0, 1), mip2x1), xi.x) ? 1 : 0; + } + + for (uint32_t i = mip2x1; i != 0;) + { + --i; + p <<= 1; + const float32_t4 values = luminanceAccessor.gather(p, i); + float32_t wx_0, wx_1; + { + const float32_t wy_0 = values[3] + values[2]; + const float32_t wy_1 = values[1] + values[0]; + if (impl::choseSecond(wy_0, wy_1, xi.y)) + { + p.y |= 1; + wx_0 = values[0]; + wx_1 = values[1]; + } + else + { + wx_0 = values[3]; + wx_1 = values[2]; + } + } + + if (impl::choseSecond(wx_0, wx_1, xi.x)) + p.x |= 1; + } + + // TODO(kevinyu): Add some comment why we add xi. + const float32_t2 directionUV = (float32_t2(p.x, p.y) + xi) / float32_t2(lumaMapSize); + return directionUV; + } + + + template && Warp) + static float32_t3 sampleWarpmap(NBL_CONST_REF_ARG(WarpmapAccessor) warpmap, const uint32_t2 warpmapSize, const float32_t2 xi, NBL_REF_ARG(float32_t) pdf) { + + // TODO(kevinyu): Add some comment why we substract by 1 + const uint32_t3 lastWarpmapPixel = warpmapSize - uint32_t3(1, 1, 1); + + const float32_t2 unnormCoord = xi * lastWarpmapPixel; + const float32_t2 interpolant = frac(unnormCoord); + const float32_t2 warpSampleCoord = (unnormCoord + float32_t2(0.5f, 0.5f)) / float32_t2(warpmapSize.x, warpmapSize.y); + const float32_t4 dirsX = warpmap.gatherU(warpSampleCoord); + const float32_t4 dirsY = warpmap.gatherV(warpSampleCoord); + + return calculateSampleAndPdf(dirsX, dirsY, unnormCoord, lastWarpmapPixel, pdf); + + } + + template && Warp) + static float32_t3 sample(NBL_CONST_REF_ARG(LuminanceReadAccessor) luminanceMap, const uint32_t2 lumaMapSize, const bool lumaAspect2x1, const uint32_t2 warpmapSize, const float32_t2 xi, NBL_REF_ARG(float32_t) pdf) { + + const uint32_t3 lastWarpmapPixel = warpmapSize - uint32_t3(1, 1, 1); + const float32_t2 unnormCoord = xi * lastWarpmapPixel; + const float32_t2 warpSampleCoord = (unnormCoord + float32_t2(0.5f, 0.5f)) / float32_t2(warpmapSize.x, warpmapSize.y); + const float32_t2 dir0 = binarySearch(luminanceMap, lumaMapSize, warpSampleCoord + float32_t2(0, 1), lumaAspect2x1); + const float32_t2 dir1 = binarySearch(luminanceMap, lumaMapSize, warpSampleCoord + float32_t2(1, 1), lumaAspect2x1); + const float32_t2 dir2 = binarySearch(luminanceMap, lumaMapSize, warpSampleCoord + float32_t2(1, 0), lumaAspect2x1); + const float32_t2 dir3 = binarySearch(luminanceMap, lumaMapSize, warpSampleCoord, lumaAspect2x1); + + const float32_t4 dirsX = float32_t4(dir0.x, dir1.x, dir2.x, dir3.x); + const float32_t4 dirsY = float32_t4(dir1.y, dir1.y, dir2.y, dir3.y); + + return calculateSampleAndPdf(dirsX, dirsY, unnormCoord, lastWarpmapPixel, pdf); + + } +}; + +} +} + +#endif From 8d682b948a2ca616c6c5681c4be7bcd12b586246 Mon Sep 17 00:00:00 2001 From: kevyuu Date: Sat, 20 Dec 2025 07:38:52 +0700 Subject: [PATCH 07/10] Remove envmap.hlsl --- .../nbl/builtin/hlsl/workgroup/envmap.hlsl | 108 ------------------ 1 file changed, 108 deletions(-) delete mode 100644 include/nbl/builtin/hlsl/workgroup/envmap.hlsl diff --git a/include/nbl/builtin/hlsl/workgroup/envmap.hlsl b/include/nbl/builtin/hlsl/workgroup/envmap.hlsl deleted file mode 100644 index df452fb0e8..0000000000 --- a/include/nbl/builtin/hlsl/workgroup/envmap.hlsl +++ /dev/null @@ -1,108 +0,0 @@ - -#ifndef _NBL_BUILTIN_HLSL_WORKGROUP_ENVMAP_IMPORTANCE_SAMPLING_INCLUDED_ -#define _NBL_BUILTIN_HLSL_WORKGROUP_ENVMAP_IMPORTANCE_SAMPLING_INCLUDED_ - -namespace nbl -{ -namespace hlsl -{ -namespace workgroup -{ -namespace envmap -{ -namespace impl -{ - bool choseSecond(float first, float second, NBL_REF_ARG(float) xi) - { - // numerical resilience against IEEE754 - float firstProb = 1.0f / (1.0f + second / first); - float dummy = 0.0f; - return math::partitionRandVariable(firstProb, xi, dummy); - } - -} - -} -} -} -} - -#ifdef __HLSL_VERSION -namespace nbl -{ -namespace hlsl -{ -namespace workgroup -{ -namespace envmap -{ - -struct WarpmapGeneration -{ - - template && envmap::WarpmapWriteAccessor) - // TODO(kevinyu): Should lumapMapSize and warpMapSize provided by Accessor? - static void __call(NBL_CONST_REF_ARG(LuminanceAccessor) luminanceAccessor, NBL_REF_ARG(OutputAcessor) outputAccessor, uint32_t2 lumaMapSize, uint32_t2 warpMapSize) - { - const uint32_t threadID = uint32_t(SubgroupContiguousIndex()); - const uint32_t lastWarpMapPixel = warpMapSize - uint32_t2(1, 1); - - if (all(threadID < warpMapSize)) - { - float32_t2 xi = float32_t2(threadID) / float32_t2(lastWarpMapPixel); - - uint32_t2 p; - p.y = 0; - - // TODO(kevinyu): Implement findMSB - const uint32_t2 mip2x1 = findMSB(lumaMapSize.x) - 1; - // do one split in the X axis first cause penultimate full mip would have been 2x1 - p.x = impl::choseSecond(luminanceAccessor.get(uint32_t2(0, 0), mip2x1, uint32_t2(0, 0)), luminanceAccessor.get(uint32_t2(0, 0), mip2x1, uint32_t2(1, 0), xi.x) ? 1 : 0; - for (uint32_t i = mip2x1; i != 0;) - { - --i; - p <<= 1; - const float32_t4 values = float32_t4( - luminanceAccessor.get(p, i, uint32_t2(0, 1)), - luminanceAccessor.get(p, i, uint32_t2(1, 1)), - luminanceAccessor.get(p, i, uint32_t2(1, 0)), - luminanceAccessor.get(p, i, uint32_t2(0, 0)) - ); - - float32_t wx_0, wx_1; - { - const float32_t wy_0 = values[3] + values[2]; - const float32_t wy_1 = values[1] + values[0]; - if (impl::choseSecond(wy_0, wy_1, xi.y)) - { - p.y |= 1; - wx_0 = values[0]; - wx_1 = values[1]; - } - else - { - wx_0 = values[3]; - wx_1 = values[2]; - } - } - - if (impl::choseSecond(wx_0, wx_1, xi.x)) - { - p.x |= 1; - } - } - - const float32_t2 directionUV = (float32_t2(p.x, p.y) + xi) / float32_t2(lumaMapSize); - outputAccessor.set(threadID, directionUV); - } - } - -}; - -} -} -} -} -#endif - -#endif \ No newline at end of file From 890f7c6080a4d234d231b24ce9e1073f9e106f34 Mon Sep 17 00:00:00 2001 From: kevyuu Date: Mon, 22 Dec 2025 23:59:45 +0700 Subject: [PATCH 08/10] Move to sampling namespace and implement backward density --- include/nbl/builtin/hlsl/concepts/warp.hlsl | 41 -------------- .../hlsl/sampling/hierarchical_image.hlsl | 2 +- include/nbl/builtin/hlsl/sampling/warp.hlsl | 54 +++++++++++++++++++ .../{warp => sampling/warps}/spherical.hlsl | 29 ++++++---- 4 files changed, 73 insertions(+), 53 deletions(-) delete mode 100644 include/nbl/builtin/hlsl/concepts/warp.hlsl create mode 100644 include/nbl/builtin/hlsl/sampling/warp.hlsl rename include/nbl/builtin/hlsl/{warp => sampling/warps}/spherical.hlsl (51%) diff --git a/include/nbl/builtin/hlsl/concepts/warp.hlsl b/include/nbl/builtin/hlsl/concepts/warp.hlsl deleted file mode 100644 index e9e981a243..0000000000 --- a/include/nbl/builtin/hlsl/concepts/warp.hlsl +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef _NBL_BUILTIN_HLSL_CONCEPTS_WARP_INCLUDED_ -#define _NBL_BUILTIN_HLSL_CONCEPTS_WARP_INCLUDED_ - -#include "nbl/builtin/hlsl/concepts/accessors/generic_shared_data.hlsl" -#include "nbl/builtin/hlsl/fft/common.hlsl" - -namespace nbl -{ -namespace hlsl -{ -namespace concepts -{ - -// declare concept -#define NBL_CONCEPT_NAME WARP -#define NBL_CONCEPT_TPLT_PRM_KINDS (typename)(typename) -#define NBL_CONCEPT_TPLT_PRM_NAMES (U)(C) -// not the greatest syntax but works -#define NBL_CONCEPT_PARAM_0 (warp,U) -#define NBL_CONCEPT_PARAM_1 (uv,float32_t2) -#define NBL_CONCEPT_PARAM_2 (out,C) -// start concept -NBL_CONCEPT_BEGIN(3) -#define warp NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 -#define uv NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 -#define out NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 -NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template warp(uv)) , ::nbl::hlsl::is_same_v, C)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template forwardDensity(uv)) , ::nbl::hlsl::is_same_v, float32_t)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template backwardDensity(out)) , ::nbl::hlsl::is_same_v, float32_t)) -); -#undef out -#undef warp -#undef uv -#include - -} -} -} - -#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/sampling/hierarchical_image.hlsl b/include/nbl/builtin/hlsl/sampling/hierarchical_image.hlsl index bfcd9ffec7..f2b2750703 100644 --- a/include/nbl/builtin/hlsl/sampling/hierarchical_image.hlsl +++ b/include/nbl/builtin/hlsl/sampling/hierarchical_image.hlsl @@ -5,7 +5,7 @@ #ifndef _NBL_BUILTIN_HLSL_SAMPLING_HIERARCHICAL_IMAGE_INCLUDED_ #define _NBL_BUILTIN_HLSL_SAMPLING_HIERARCHICAL_IMAGE_INCLUDED_ -#include +#include #include namespace nbl diff --git a/include/nbl/builtin/hlsl/sampling/warp.hlsl b/include/nbl/builtin/hlsl/sampling/warp.hlsl new file mode 100644 index 0000000000..b8936c09f3 --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/warp.hlsl @@ -0,0 +1,54 @@ +#ifndef _NBL_BUILTIN_HLSL_CONCEPTS_WARP_INCLUDED_ +#define _NBL_BUILTIN_HLSL_CONCEPTS_WARP_INCLUDED_ + +#include "nbl/builtin/hlsl/concepts/accessors/generic_shared_data.hlsl" +#include "nbl/builtin/hlsl/fft/common.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template +struct WarpResult +{ + C dst; + float32_t density; +}; + +namespace concepts +{ + +// declare concept +#define NBL_CONCEPT_NAME WARP +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (U) +// not the greatest syntax but works +#define NBL_CONCEPT_PARAM_0 (warper,U) +#define NBL_CONCEPT_PARAM_1 (xi,typename U::domain_type) +#define NBL_CONCEPT_PARAM_2 (dst,typename U::codomain_type) +// start concept +NBL_CONCEPT_BEGIN(3) +#define warper NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define xi NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define dst NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(U::domain_type)) + ((NBL_CONCEPT_REQ_TYPE)(U::codomain_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((warper.template warp(xi)) , ::nbl::hlsl::is_same_v, WarpResult)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((warper.template forwardDensity(xi)) , ::nbl::hlsl::is_same_v, float32_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((warper.template backwardDensity(dst)) , ::nbl::hlsl::is_same_v, float32_t)) +); +#undef dst +#undef xi +#undef warper +#include + +} +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/warp/spherical.hlsl b/include/nbl/builtin/hlsl/sampling/warps/spherical.hlsl similarity index 51% rename from include/nbl/builtin/hlsl/warp/spherical.hlsl rename to include/nbl/builtin/hlsl/sampling/warps/spherical.hlsl index 10c341f06b..095e138d60 100644 --- a/include/nbl/builtin/hlsl/warp/spherical.hlsl +++ b/include/nbl/builtin/hlsl/sampling/warps/spherical.hlsl @@ -2,21 +2,24 @@ #define _NBL_BUILTIN_HLSL_WARP_SPHERICAL_INCLUDED_ #include +#include +#include namespace nbl { namespace hlsl { +namespace sampling +{ namespace warp { - - class Spherical + struct Spherical { - public: + using domain_type = float32_t2; using codomain_type = float32_t3; - template ) - static codomain_type warp(const UV uv) + template ) + static WarpResult warp(const D uv) { const float32_t phi = 2 * uv.x * numbers::pi; const float32_t theta = uv.y * numbers::pi; @@ -28,26 +31,30 @@ namespace warp float32_t sinTheta = (1.0 - cosTheta * cosTheta); dir.xy *= sinTheta; dir.z = cosTheta; - return dir; + WarpResult warpResult; + warpResult.dst = dir; + warpResult.density = 1 / (sinTheta * numbers::pi * numbers::pi); + return warpResult; } - template ) - static float32_t forwardDensity(const UV uv) + template ) + static float32_t forwardDensity(const D uv) { const float32_t theta = uv.y * numbers::pi; - return 1.0f / (sin(theta) * 2 * PI * PI); + return 1.0f / (sin(theta) * 2 * numbers::pi * numbers::pi); } template ) - static float32_t backwardDensity(const C out) + static float32_t backwardDensity(const C dst) { - //TODO(kevinyu): Derive this density + return 1.0f / (sqrt(1.0f - dst.z * dst.z) * 2 * numbers::pi * numbers::pi); } }; } } } +} #endif \ No newline at end of file From f99c63ba8b8c4ce97dd73d18c1f3b9c51b321eac Mon Sep 17 00:00:00 2001 From: kevyuu Date: Tue, 23 Dec 2025 00:02:15 +0700 Subject: [PATCH 09/10] Remove private, public from hierarchical_image --- include/nbl/builtin/hlsl/sampling/hierarchical_image.hlsl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/nbl/builtin/hlsl/sampling/hierarchical_image.hlsl b/include/nbl/builtin/hlsl/sampling/hierarchical_image.hlsl index f2b2750703..de50a6b0d3 100644 --- a/include/nbl/builtin/hlsl/sampling/hierarchical_image.hlsl +++ b/include/nbl/builtin/hlsl/sampling/hierarchical_image.hlsl @@ -15,9 +15,8 @@ namespace hlsl namespace sampling { -class HierarchicalImage +struct HierarchicalImage { - private: static float32_t3 calculateSampleAndPdf(float32_t4 dirsX, float32_t4 dirsY, float32_t2 unnormCoord, uint32_t2 lastWarpmapPixel, NBL_REF_ARG(float32_t) pdf) { @@ -48,7 +47,6 @@ class HierarchicalImage return L; } - public: template ) static float32_t2 binarySearch(NBL_CONST_REF_ARG(LuminanceAccessor) luminanceAccessor, const uint32_t2 lumaMapSize, const float32_t2 xi, const bool aspect2x1) { From 3ff2791f1bb47feb3a232fcfecc1dca0005dc461 Mon Sep 17 00:00:00 2001 From: kevyuu Date: Tue, 23 Dec 2025 01:06:03 +0700 Subject: [PATCH 10/10] Refactor hierarchical image to keep accessor and common data as member --- .../hlsl/sampling/hierarchical_image.hlsl | 61 +++++++++---------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/include/nbl/builtin/hlsl/sampling/hierarchical_image.hlsl b/include/nbl/builtin/hlsl/sampling/hierarchical_image.hlsl index de50a6b0d3..5509ce65c3 100644 --- a/include/nbl/builtin/hlsl/sampling/hierarchical_image.hlsl +++ b/include/nbl/builtin/hlsl/sampling/hierarchical_image.hlsl @@ -14,12 +14,21 @@ namespace hlsl { namespace sampling { - +template && hierarchical_image::LuminanceReadAccessor && Warp) struct HierarchicalImage { - - static float32_t3 calculateSampleAndPdf(float32_t4 dirsX, float32_t4 dirsY, float32_t2 unnormCoord, uint32_t2 lastWarpmapPixel, NBL_REF_ARG(float32_t) pdf) + using scalar_type = T; + using vector2_type = vector; + using vector3_type = vector; + using vector4_type = vector; + LuminanceAccessor accessor; + uint32_t2 lumaMapSize; + bool lumaAspect2x1; + uint32_t2 lastWarpPixel; + + static vector2_type calculateSampleAndPdf(NBL_REF_ARG(scalar_type) rcpPdf, vector4_type dirsX, vector4_type dirsY, vector2_type unnormCoord, uint32_t2 lastWarpPixel) { + // TODO(kevinyu): Convert float32_t to scalar_type const float32_t2 interpolant = frac(unnormCoord); const float32_t4x2 uvs = transpose(float32_t2x4(dirsX, dirsY)); @@ -42,15 +51,23 @@ struct HierarchicalImage yDiff // second column dFdy )); - pdf = abs(PostWarp::forwardDensity(uv) / (detInterpolJacobian * float32_t(lastWarpmapPixel.x * lastWarpmapPixel.y)); + rcpPdf = abs((detInterpolJacobian * scalar_t(lastWarpPixel.x * lastWarpPixel.y) / PostWarp::forwardDensity(uv)); return L; } - template ) - static float32_t2 binarySearch(NBL_CONST_REF_ARG(LuminanceAccessor) luminanceAccessor, const uint32_t2 lumaMapSize, const float32_t2 xi, const bool aspect2x1) + static HierarchicalImage create(NBL_CONST_REF_ARG(LuminanceAccessor) accessor, const uint32_t2 lumaMapSize, const bool lumaAspect2x1, const uint32_t2 warpSize) { + HierarchicalImage result; + result.accessor = accessor; + result.lumaMapSize = lumaMapSize; + result.lumaAspect2x1 = lumaAspect2x1; + result.lastWarpPixel = warpSize - uint32_t2(1, 1); + return result; + } + static vector binarySearch(const vector xi) + { uint32_t2 p = uint32_t2(0, 0); if (aspect2x1) { @@ -92,28 +109,9 @@ struct HierarchicalImage return directionUV; } - - template && Warp) - static float32_t3 sampleWarpmap(NBL_CONST_REF_ARG(WarpmapAccessor) warpmap, const uint32_t2 warpmapSize, const float32_t2 xi, NBL_REF_ARG(float32_t) pdf) { - - // TODO(kevinyu): Add some comment why we substract by 1 - const uint32_t3 lastWarpmapPixel = warpmapSize - uint32_t3(1, 1, 1); - - const float32_t2 unnormCoord = xi * lastWarpmapPixel; - const float32_t2 interpolant = frac(unnormCoord); - const float32_t2 warpSampleCoord = (unnormCoord + float32_t2(0.5f, 0.5f)) / float32_t2(warpmapSize.x, warpmapSize.y); - const float32_t4 dirsX = warpmap.gatherU(warpSampleCoord); - const float32_t4 dirsY = warpmap.gatherV(warpSampleCoord); - - return calculateSampleAndPdf(dirsX, dirsY, unnormCoord, lastWarpmapPixel, pdf); - - } - - template && Warp) - static float32_t3 sample(NBL_CONST_REF_ARG(LuminanceReadAccessor) luminanceMap, const uint32_t2 lumaMapSize, const bool lumaAspect2x1, const uint32_t2 warpmapSize, const float32_t2 xi, NBL_REF_ARG(float32_t) pdf) { - - const uint32_t3 lastWarpmapPixel = warpmapSize - uint32_t3(1, 1, 1); - const float32_t2 unnormCoord = xi * lastWarpmapPixel; + uint32_t2 generate(NBL_REF_ARG(scalar_type) rcpPdf, vector xi) + { + const float32_t2 unnormCoord = xi * lastWarpPixel; const float32_t2 warpSampleCoord = (unnormCoord + float32_t2(0.5f, 0.5f)) / float32_t2(warpmapSize.x, warpmapSize.y); const float32_t2 dir0 = binarySearch(luminanceMap, lumaMapSize, warpSampleCoord + float32_t2(0, 1), lumaAspect2x1); const float32_t2 dir1 = binarySearch(luminanceMap, lumaMapSize, warpSampleCoord + float32_t2(1, 1), lumaAspect2x1); @@ -123,11 +121,12 @@ struct HierarchicalImage const float32_t4 dirsX = float32_t4(dir0.x, dir1.x, dir2.x, dir3.x); const float32_t4 dirsY = float32_t4(dir1.y, dir1.y, dir2.y, dir3.y); - return calculateSampleAndPdf(dirsX, dirsY, unnormCoord, lastWarpmapPixel, pdf); - - } + return calculateSampleAndPdf(rcpPdf, dirsX, dirsY, unnormCoord, lastWarpPixel); + } }; +//TODO(kevinyu): Impelemnt cached warp map sampler + } }