Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
f181602
New example, copy of 61_UI, updated a lot, visualizer, still not "sol…
karimsayedre Dec 3, 2025
93861bd
Make camera account for up direction, corrected framebuffer resolutio…
karimsayedre Dec 6, 2025
adb15ed
sphere arc "cube edge" in solid angle view, more reliable resizing of…
karimsayedre Dec 6, 2025
008e2ee
Scaling by pressing G to prevent conflict with WASD camera movement, …
karimsayedre Dec 6, 2025
4290f4a
better clipping of arcs behind the hemisphere
karimsayedre Dec 7, 2025
ba068c4
WIP quick push for shader code
karimsayedre Dec 8, 2025
91ae865
Fixed main camera aspect ratio, added 27 configurations for cube silh…
karimsayedre Dec 8, 2025
0124cc9
Shader fixes, bast uint16 resolutionf to float
karimsayedre Dec 8, 2025
a35eddd
Better color for non-silhouette edges
karimsayedre Dec 8, 2025
1c6458d
A lot more debuggability, and:
karimsayedre Dec 17, 2025
2e306fc
better (still not perfect) manual inverse of rotation matrix
karimsayedre Dec 17, 2025
12486d4
Fixed faster inverse of rotation matrix, thanks Matt!
karimsayedre Dec 17, 2025
1961a89
Fast clipping, less branches, also
karimsayedre Dec 20, 2025
b5d8abc
Merge branch 'master' into solid-angle-vis
karimsayedre Dec 23, 2025
86bd5e2
Merge branch 'master' into solid-angle-vis
karimsayedre Dec 23, 2025
086af9e
Sample and visualize samples on the OBB,
karimsayedre Dec 31, 2025
06c6764
Merge remote-tracking branch 'origin/hlsl_path_tracer' into solid-ang…
karimsayedre Jan 6, 2026
15e4d5d
added benchmark code for sampling, visualization of rays in 3D view, …
karimsayedre Jan 6, 2026
3e39f03
Projected Parallelogram sampling
karimsayedre Jan 21, 2026
16ba43a
Merge branch 'master' into solid-angle-vis
karimsayedre Feb 17, 2026
2b034eb
huge shader refactor, more debug UI, also:
karimsayedre Feb 17, 2026
2caf943
Merge branch 'sampler-concepts' into solid-angle-vis
karimsayedre Apr 10, 2026
21c9c04
Merge branch 'sampler-concepts' into solid-angle-vis
karimsayedre Apr 12, 2026
b5d1d5b
Merge branch 'sampler-concepts' into solid-angle-vis
karimsayedre Apr 13, 2026
5eeb473
make NEE work in ex 31 with Global L solid angle sampling of spherica…
devshgraphicsprogramming Apr 16, 2026
89ecce1
prep for rendering with PSA rectangle
devshgraphicsprogramming Apr 17, 2026
fb5cfa2
jacobian tests, better benchmarks, addressed comments
karimsayedre Apr 21, 2026
a4559b9
alias table is packed, 2 versions, consolidated WORKGROUP_SIZE for te…
karimsayedre Apr 24, 2026
74103b5
Merge branch 'master' into new-sampler-concepts
karimsayedre Apr 24, 2026
47fa6b5
Merge branch 'new-sampler-concepts' of github.com:Devsh-Graphics-Prog…
karimsayedre Apr 27, 2026
07e7696
major refactor, better drawing, better benchmarks
karimsayedre Apr 27, 2026
f573c61
Kelvin Stokes centroid, new O(N) algo
karimsayedre Apr 28, 2026
f55fc3f
fix CI
karimsayedre Apr 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 59 additions & 49 deletions 31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,7 @@ struct ShapeSampling<T, PST_TRIANGLE, PPM_APPROX_PROJECTED_SOLID_ANGLE>
const vector3_type tri_vertices[3] = {tri.vertex0, tri.vertex1, tri.vertex2};
shapes::SphericalTriangle<scalar_type> st = shapes::SphericalTriangle<scalar_type>::create(tri_vertices, ray.origin);
sampling::ProjectedSphericalTriangle<scalar_type> pst = sampling::ProjectedSphericalTriangle<scalar_type>::create(st, ray.normalAtOrigin, ray.wasBSDFAtOrigin);
const scalar_type pdf = pst.backwardPdf(L);
// if `pdf` is NAN then the triangle's projected solid angle was close to 0.0, if its close to INF then the triangle was very small
return pdf < numeric_limits<scalar_type>::max ? pdf : numeric_limits<scalar_type>::max;
return pst.backwardWeight(L);
}

template<class Aniso>
Expand Down Expand Up @@ -252,6 +250,7 @@ template<typename T>
struct ShapeSampling<T, PST_RECTANGLE, PPM_SOLID_ANGLE>
{
using scalar_type = T;
using vector2_type = vector<T, 2>;
using vector3_type = vector<T, 3>;

static ShapeSampling<T, PST_RECTANGLE, PPM_SOLID_ANGLE> create(NBL_CONST_REF_ARG(Shape<T, PST_RECTANGLE>) rect)
Expand All @@ -268,49 +267,58 @@ struct ShapeSampling<T, PST_RECTANGLE, PPM_SOLID_ANGLE>
matrix<scalar_type, 3, 3> rectNormalBasis;
vector<T, 2> rectExtents;
rect.getNormalBasis(rectNormalBasis, rectExtents);

shapes::SphericalRectangle<scalar_type> sphR0;
sphR0.origin = rect.offset;
sphR0.extents = rectExtents;
sphR0.basis = rectNormalBasis;
scalar_type solidAngle = sphR0.solidAngle(ray.origin).value;
if (solidAngle > numeric_limits<scalar_type>::min)
pdf = 1.f / solidAngle;
else
pdf = bit_cast<scalar_type>(numeric_limits<scalar_type>::infinity);
return pdf;

// 1.f/0.f gives infinity no special checks needed
return 1.f / sphR0.solidAngle(ray.origin).value;
}

template<class Aniso>
vector3_type generateAndPdfAndWeight(NBL_REF_ARG(scalar_type) pdf, NBL_REF_ARG(scalar_type) weight, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(Aniso) interaction, NBL_CONST_REF_ARG(vector3_type) xi)
{
const vector3_type N = rect.getNormalTimesArea();
const vector3_type origin2origin = rect.offset - origin;

matrix<scalar_type, 3, 3> rectNormalBasis;
vector<T, 2> rectExtents;
rect.getNormalBasis(rectNormalBasis, rectExtents);

shapes::SphericalRectangle<scalar_type> sphR0;
sphR0.origin = rect.offset;
sphR0.extents = rectExtents;
sphR0.basis = rectNormalBasis;
vector3_type L = hlsl::promote<vector3_type>(0.0);

//
sampling::SphericalRectangle<scalar_type> ssph = sampling::SphericalRectangle<scalar_type>::create(sphR0, origin);
if ( ssph.solidAngle > numeric_limits<scalar_type>::min)
typename sampling::SphericalRectangle<scalar_type>::cache_type cache;

vector3_type L = hlsl::promote<vector3_type>(0.0);
const bool FastVersion = true;
if (FastVersion)
{
typename sampling::SphericalRectangle<scalar_type>::cache_type cache;
const vector3_type localDir = ssph.generate(xi.xy, cache);
// not sure if generate() can produce NaN/inf when solidAngle > min
assert(!hlsl::any(hlsl::isinf(localDir) || hlsl::isnan(localDir)));
// transform local direction to world space
L = localDir.x * rectNormalBasis[0] + localDir.y * rectNormalBasis[1] + localDir.z * rectNormalBasis[2];
pdf = ssph.forwardPdf(xi.xy, cache);
weight = ssph.forwardWeight(xi.xy, cache);
// actually the slowest
//L = ssph.generate(xi.xy, cache);
//newRayMaxT = ssph.computeHitT(L);

// fastest
const vector3_type localL = ssph.generateNormalizedLocal(xi.xy,cache,newRayMaxT);
assert(!hlsl::any(hlsl::isinf(localL) || hlsl::isnan(localL)));
L = hlsl::mul(hlsl::transpose(ssph.basis),localL);
}
else
weight = bit_cast<scalar_type>(numeric_limits<scalar_type>::infinity);
{
L = ssph.generateUnnormalized(xi.xy,cache);
assert(!hlsl::any(hlsl::isinf(L) || hlsl::isnan(L)));
const scalar_type rcpLen = hlsl::rsqrt(hlsl::dot(L,L));
newRayMaxT = 1.f / rcpLen;
L *= rcpLen;
}
// prevent self intersections against the emitter
newRayMaxT -= 0.0001f;

newRayMaxT = hlsl::dot<vector3_type>(N, origin2origin) / hlsl::dot<vector3_type>(N, L);
pdf = ssph.forwardPdf(xi.xy,cache);
weight = ssph.forwardWeight(xi.xy,cache);
return L;
}

Expand All @@ -329,7 +337,6 @@ struct EffectivePolygonMethod<PST_SPHERE, PPM>
NBL_CONSTEXPR_STATIC_INLINE NEEPolygonMethod value = PPM_SOLID_ANGLE;
};


// Projected solid angle NEE for rectangles using "Practical Warps":
// bilinear warp over 4-corner NdotL + spherical rectangle sampling.
// Same grazing-angle limitations as the triangle variant -- see comments
Expand Down Expand Up @@ -359,21 +366,12 @@ struct ShapeSampling<T, PST_RECTANGLE, PPM_APPROX_PROJECTED_SOLID_ANGLE>
sphR0.extents = rectExtents;
sphR0.basis = rectNormalBasis;
sampling::ProjectedSphericalRectangle<scalar_type> psr = sampling::ProjectedSphericalRectangle<scalar_type>::create(sphR0, ray.origin, ray.normalAtOrigin, ray.wasBSDFAtOrigin);
// Reconstruct normalized [0,1]^2 position on the rectangle from the ray direction
const vector3_type N = rect.getNormalTimesArea();
const scalar_type t = hlsl::dot<vector3_type>(N, rect.offset - ray.origin) / hlsl::dot<vector3_type>(N, ray.direction);
const vector3_type hitPoint = ray.origin + ray.direction * t;
const vector3_type localHit = hitPoint - rect.offset;
const vector<T, 2> p = vector<T, 2>(hlsl::dot(localHit, rectNormalBasis[0]) / rectExtents.x, hlsl::dot(localHit, rectNormalBasis[1]) / rectExtents.y);
const scalar_type pdf = psr.backwardPdf(p);
return pdf < numeric_limits<scalar_type>::max ? pdf : numeric_limits<scalar_type>::max;
return psr.backwardWeight(ray.direction);
}

template<class Aniso>
vector3_type generateAndPdfAndWeight(NBL_REF_ARG(scalar_type) pdf, NBL_REF_ARG(scalar_type) weight, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(Aniso) interaction, NBL_CONST_REF_ARG(vector3_type) xi)
{
const vector3_type N = rect.getNormalTimesArea();
const vector3_type origin2origin = rect.offset - origin;

matrix<scalar_type, 3, 3> rectNormalBasis;
vector<T, 2> rectExtents;
Expand All @@ -382,25 +380,37 @@ struct ShapeSampling<T, PST_RECTANGLE, PPM_APPROX_PROJECTED_SOLID_ANGLE>
sphR0.origin = rect.offset;
sphR0.extents = rectExtents;
sphR0.basis = rectNormalBasis;
vector3_type L = hlsl::promote<vector3_type>(0.0);

sampling::ProjectedSphericalRectangle<scalar_type> psr = sampling::ProjectedSphericalRectangle<scalar_type>::create(sphR0, origin, interaction.getN(), interaction.isMaterialBSDF());
const scalar_type solidAngle = psr.sphrect.solidAngle;
if (solidAngle > numeric_limits<scalar_type>::min)
typename sampling::ProjectedSphericalRectangle<scalar_type>::cache_type cache;

vector3_type L = hlsl::promote<vector3_type>(0.0);
const bool FastVersion = true;
if (FastVersion)
{
typename sampling::ProjectedSphericalRectangle<scalar_type>::cache_type cache;
const vector3_type localDir = psr.generate(xi.xy, cache);
// not sure if generate() can produce NaN/inf when solidAngle > min
assert(!hlsl::any(hlsl::isinf(localDir) || hlsl::isnan(localDir)));
// transform local direction to world space
L = localDir.x * rectNormalBasis[0] + localDir.y * rectNormalBasis[1] + localDir.z * rectNormalBasis[2];
pdf = psr.forwardPdf(xi.xy, cache);
weight = psr.forwardWeight(xi.xy, cache);
// actually the slowest
//L = psr.generate(xi.xy, cache);
//newRayMaxT = psr.sphrect.computeHitT(L);

// fastest
const vector3_type localL = psr.generateNormalizedLocal(xi.xy,cache,newRayMaxT);
assert(!hlsl::any(hlsl::isinf(localL) || hlsl::isnan(localL)));
// hopefully CSE kicks in for the `UsePdfAsWeight==true`
L = hlsl::mul(hlsl::transpose(psr.sphrect.basis),localL);
}
else
weight = bit_cast<scalar_type>(numeric_limits<scalar_type>::infinity);
// TODO: `improved_spherical_rect` branch merge
newRayMaxT = hlsl::dot<vector3_type>(N, origin2origin) / hlsl::dot<vector3_type>(N, L);
{
L = psr.generateUnnormalized(xi.xy,cache);
assert(!hlsl::any(hlsl::isinf(L) || hlsl::isnan(L)));
const scalar_type rcpLen = hlsl::rsqrt(hlsl::dot(L,L));
newRayMaxT = 1.f / rcpLen;
L *= rcpLen;
}
// prevent self intersections against the emitter
newRayMaxT -= 0.0001f;

pdf = psr.forwardPdf(xi.xy,cache);
weight = psr.forwardWeight(xi.xy,cache);
return L;
}

Expand Down
2 changes: 1 addition & 1 deletion 31_HLSLPathTracer/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui
nullptr,
nullptr
);
m_presentPipeline = fsTriProtoPPln.createPipeline(fragSpec, presentLayout.get(), scRes->getRenderpass(), 0u, {}, hlsl::SurfaceTransform::FLAG_BITS::IDENTITY_BIT, m_pipelineCache.object.get());
m_presentPipeline = fsTriProtoPPln.createPipeline(fragSpec, presentLayout.get(), scRes->getRenderpass(), 0u, {}, {}, hlsl::SurfaceTransform::FLAG_BITS::IDENTITY_BIT, m_pipelineCache.object.get());
if (!m_presentPipeline)
return logFail("Could not create Graphics Pipeline!");
m_pipelineCache.dirty = true;
Expand Down
Loading