Skip to content

Conversation

@kevyuu
Copy link
Contributor

@kevyuu kevyuu commented Dec 19, 2025

Description

Rework environment map importance sampling to vulkan and hlsl

Testing

Rework example 0 to use vulkan and hlsl

TODO list:

  • Implement Warpmap Generation in hlsl
  • Implement Warpmap hierarchical map sampling in hlsl

// 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)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out is a keyword

Comment on lines 1 to 12
#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
{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sampling directory and namespace, then concepts

#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))
Copy link
Member

@devshgraphicsprogramming devshgraphicsprogramming Dec 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might want to rename uv to xi instead and also require that U has typdefs for xi_type and codomain_type

this will allow you to keep the concept same for warps of cubemaps or 1D functions

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also may want to make warp return pair<codomain_type,pdf_type> because there's usually useful data allowing for faster computation fo both quantities than

const auto _out = w.warp(xi);
const float pdf = w.backwardDensity(_out);

Comment on lines +10 to +11
namespace warp
{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sampling namespace, anything to do with PDFs is sampling

template <typename C NBL_FUNC_REQUIRES(is_same_v<C, codomain_type>)
static float32_t backwardDensity(const C out)
{
//TODO(kevinyu): Derive this density

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1.f/sin(theta) == rsqrt(1.f-C.z*C.z) and you can subsitutre it into same equation as forwardDensity

Comment on lines 29 to 30
((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))
Copy link
Member

@devshgraphicsprogramming devshgraphicsprogramming Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just thinking, since we'll rewrite our stuff from https://github.com/Devsh-Graphics-Programming/Nabla/blob/master/include/nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl into this

we should also add methods called just forward and backward returning quotient_and_pdf each (so we need quotient_type too)

Ah wait, this should be for a Sampler, a warp is also a Sampler but has exact quotients, so no RGB possible, the quotient is just the reciprocal of the density.

Sampler concept will be for things like cos_weighted_spheres.

static float32_t forwardDensity(const UV uv)
{
const float32_t theta = uv.y * numbers::pi<float32_t>;
return 1.0f / (sin(theta) * 2 * PI * PI);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you using PI from a define!?


class HierarchicalImage
{
private:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's no private or public, if you want to indicate something is private, prefix the identifier with __

}

public:
template <typename LuminanceAccessor NBL_FUNC_REQUIRES (hierarchical_image::LuminanceReadAccessor<LuminanceAccessor>)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LuminanceAccessor and requires clause goes on HierarchicalImage

{
private:

static float32_t3 calculateSampleAndPdf(float32_t4 dirsX, float32_t4 dirsY, float32_t2 unnormCoord, uint32_t2 lastWarpmapPixel, NBL_REF_ARG(float32_t) pdf)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

things like lastWarpmapPixel go into members, basically you want to store everything that is "constant"/common in members were you to "draw" multiple samples from the same hierarhical image but with different xi

Comment on lines 98 to 130
template <typename WarpmapAccessor, typename PostWarp NBL_FUNC_REQUIRES(hierarchical_image::WarpmapReadAccessor<WarpmapAccessor>&& Warp<PostWarp, float32_t3>)
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 <typename LuminanceAccessor, typename PostWarp NBL_FUNC_REQUIRES(hierarchical_image::LuminanceReadAccessor<LuminanceAccessor>&& Warp<PostWarp, float32_t3>)
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);

}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

split the ground truth binary search hierarchical image sampler and the approximate warp map hierarchical image sampler into two separate classes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants