From eab0f70c674f93b86ac4649805ac7baaed8d0af0 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 3 Feb 2025 14:52:11 +0700 Subject: [PATCH 01/53] copied ex 30 to new ex 31 --- 31_HLSLPathTracer/CMakeLists.txt | 37 + .../app_resources/glsl/common.glsl | 811 +++++++++++ .../app_resources/glsl/litByRectangle.comp | 182 +++ .../app_resources/glsl/litBySphere.comp | 60 + .../app_resources/glsl/litByTriangle.comp | 105 ++ .../app_resources/hlsl/present.frag.hlsl | 19 + 31_HLSLPathTracer/config.json.template | 28 + .../include/nbl/this_example/common.hpp | 17 + 31_HLSLPathTracer/main.cpp | 1276 +++++++++++++++++ 31_HLSLPathTracer/pipeline.groovy | 50 + CMakeLists.txt | 2 + 11 files changed, 2587 insertions(+) create mode 100644 31_HLSLPathTracer/CMakeLists.txt create mode 100644 31_HLSLPathTracer/app_resources/glsl/common.glsl create mode 100644 31_HLSLPathTracer/app_resources/glsl/litByRectangle.comp create mode 100644 31_HLSLPathTracer/app_resources/glsl/litBySphere.comp create mode 100644 31_HLSLPathTracer/app_resources/glsl/litByTriangle.comp create mode 100644 31_HLSLPathTracer/app_resources/hlsl/present.frag.hlsl create mode 100644 31_HLSLPathTracer/config.json.template create mode 100644 31_HLSLPathTracer/include/nbl/this_example/common.hpp create mode 100644 31_HLSLPathTracer/main.cpp create mode 100644 31_HLSLPathTracer/pipeline.groovy diff --git a/31_HLSLPathTracer/CMakeLists.txt b/31_HLSLPathTracer/CMakeLists.txt new file mode 100644 index 000000000..07b0fd396 --- /dev/null +++ b/31_HLSLPathTracer/CMakeLists.txt @@ -0,0 +1,37 @@ +include(common RESULT_VARIABLE RES) +if(NOT RES) + message(FATAL_ERROR "common.cmake not found. Should be in {repo_root}/cmake directory") +endif() + +if(NBL_BUILD_IMGUI) + set(NBL_INCLUDE_SERACH_DIRECTORIES + "${CMAKE_CURRENT_SOURCE_DIR}/include" + ) + + list(APPEND NBL_LIBRARIES + imtestengine + "${NBL_EXT_IMGUI_UI_LIB}" + ) + + nbl_create_executable_project("" "" "${NBL_INCLUDE_SERACH_DIRECTORIES}" "${NBL_LIBRARIES}" "${NBL_EXECUTABLE_PROJECT_CREATION_PCH_TARGET}") + + if(NBL_EMBED_BUILTIN_RESOURCES) + set(_BR_TARGET_ ${EXECUTABLE_NAME}_builtinResourceData) + set(RESOURCE_DIR "app_resources") + + get_filename_component(_SEARCH_DIRECTORIES_ "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE) + get_filename_component(_OUTPUT_DIRECTORY_SOURCE_ "${CMAKE_CURRENT_BINARY_DIR}/src" ABSOLUTE) + get_filename_component(_OUTPUT_DIRECTORY_HEADER_ "${CMAKE_CURRENT_BINARY_DIR}/include" ABSOLUTE) + + file(GLOB_RECURSE BUILTIN_RESOURCE_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/${RESOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/${RESOURCE_DIR}/*") + foreach(RES_FILE ${BUILTIN_RESOURCE_FILES}) + LIST_BUILTIN_RESOURCE(RESOURCES_TO_EMBED "${RES_FILE}") + endforeach() + + ADD_CUSTOM_BUILTIN_RESOURCES(${_BR_TARGET_} RESOURCES_TO_EMBED "${_SEARCH_DIRECTORIES_}" "${RESOURCE_DIR}" "nbl::this_example::builtin" "${_OUTPUT_DIRECTORY_HEADER_}" "${_OUTPUT_DIRECTORY_SOURCE_}") + + LINK_BUILTIN_RESOURCES_TO_TARGET(${EXECUTABLE_NAME} ${_BR_TARGET_}) + endif() +endif() + + diff --git a/31_HLSLPathTracer/app_resources/glsl/common.glsl b/31_HLSLPathTracer/app_resources/glsl/common.glsl new file mode 100644 index 000000000..2463f82cf --- /dev/null +++ b/31_HLSLPathTracer/app_resources/glsl/common.glsl @@ -0,0 +1,811 @@ +// Copyright (C) 2018-2020 - 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 + +// firefly and variance reduction techniques +//#define KILL_DIFFUSE_SPECULAR_PATHS +//#define VISUALIZE_HIGH_VARIANCE + +// debug +//#define NEE_ONLY + +layout(set = 2, binding = 0) uniform sampler2D envMap; +layout(set = 2, binding = 1) uniform usamplerBuffer sampleSequence; +layout(set = 2, binding = 2) uniform usampler2D scramblebuf; + +layout(set=0, binding=0, rgba16f) uniform image2D outImage; + +#ifndef _NBL_GLSL_WORKGROUP_SIZE_ +#define _NBL_GLSL_WORKGROUP_SIZE_ 32 +layout(local_size_x=_NBL_GLSL_WORKGROUP_SIZE_, local_size_y=_NBL_GLSL_WORKGROUP_SIZE_, local_size_z=1) in; +#endif + +ivec2 getCoordinates() { + return ivec2(gl_GlobalInvocationID.xy); +} + +vec2 getTexCoords() { + ivec2 imageSize = imageSize(outImage); + ivec2 iCoords = getCoordinates(); + return vec2(float(iCoords.x) / imageSize.x, 1.0 - float(iCoords.y) / imageSize.y); +} + + +#include +#include +#include + +#include + +layout(push_constant, row_major) uniform constants +{ + mat4 invMVP; + int sampleCount; + int depth; +} PTPushConstant; + +#define INVALID_ID_16BIT 0xffffu +struct Sphere +{ + vec3 position; + float radius2; + uint bsdfLightIDs; +}; + +Sphere Sphere_Sphere(in vec3 position, in float radius, in uint bsdfID, in uint lightID) +{ + Sphere sphere; + sphere.position = position; + sphere.radius2 = radius*radius; + sphere.bsdfLightIDs = bitfieldInsert(bsdfID,lightID,16,16); + return sphere; +} + +// return intersection distance if found, nbl_glsl_FLT_NAN otherwise +float Sphere_intersect(in Sphere sphere, in vec3 origin, in vec3 direction) +{ + vec3 relOrigin = origin-sphere.position; + float relOriginLen2 = dot(relOrigin,relOrigin); + const float radius2 = sphere.radius2; + + float dirDotRelOrigin = dot(direction,relOrigin); + float det = radius2-relOriginLen2+dirDotRelOrigin*dirDotRelOrigin; + + // do some speculative math here + float detsqrt = sqrt(det); + return -dirDotRelOrigin+(relOriginLen2>radius2 ? (-detsqrt):detsqrt); +} + +vec3 Sphere_getNormal(in Sphere sphere, in vec3 position) +{ + const float radiusRcp = inversesqrt(sphere.radius2); + return (position-sphere.position)*radiusRcp; +} + +float Sphere_getSolidAngle_impl(in float cosThetaMax) +{ + return 2.0*nbl_glsl_PI*(1.0-cosThetaMax); +} +float Sphere_getSolidAngle(in Sphere sphere, in vec3 origin) +{ + float cosThetaMax = sqrt(1.0-sphere.radius2/nbl_glsl_lengthSq(sphere.position-origin)); + return Sphere_getSolidAngle_impl(cosThetaMax); +} + + +Sphere spheres[SPHERE_COUNT] = { + Sphere_Sphere(vec3(0.0,-100.5,-1.0),100.0,0u,INVALID_ID_16BIT), + Sphere_Sphere(vec3(2.0,0.0,-1.0),0.5,1u,INVALID_ID_16BIT), + Sphere_Sphere(vec3(0.0,0.0,-1.0),0.5,2u,INVALID_ID_16BIT), + Sphere_Sphere(vec3(-2.0,0.0,-1.0),0.5,3u,INVALID_ID_16BIT), + Sphere_Sphere(vec3(2.0,0.0,1.0),0.5,4u,INVALID_ID_16BIT), + Sphere_Sphere(vec3(0.0,0.0,1.0),0.5,4u,INVALID_ID_16BIT), + Sphere_Sphere(vec3(-2.0,0.0,1.0),0.5,5u,INVALID_ID_16BIT), + Sphere_Sphere(vec3(0.5,1.0,0.5),0.5,6u,INVALID_ID_16BIT) +#if SPHERE_COUNT>8 + ,Sphere_Sphere(vec3(-1.5,1.5,0.0),0.3,INVALID_ID_16BIT,0u) +#endif +}; + + +struct Triangle +{ + vec3 vertex0; + uint bsdfLightIDs; + vec3 vertex1; + uint padding0; + vec3 vertex2; + uint padding1; +}; + +Triangle Triangle_Triangle(in mat3 vertices, in uint bsdfID, in uint lightID) +{ + Triangle tri; + tri.vertex0 = vertices[0]; + tri.vertex1 = vertices[1]; + tri.vertex2 = vertices[2]; + // + tri.bsdfLightIDs = bitfieldInsert(bsdfID, lightID, 16, 16); + return tri; +} + +// return intersection distance if found, nbl_glsl_FLT_NAN otherwise +float Triangle_intersect(in Triangle tri, in vec3 origin, in vec3 direction) +{ + const vec3 edges[2] = vec3[2](tri.vertex1-tri.vertex0,tri.vertex2-tri.vertex0); + + const vec3 h = cross(direction,edges[1]); + const float a = dot(edges[0],h); + + const vec3 relOrigin = origin-tri.vertex0; + + const float u = dot(relOrigin,h)/a; + + const vec3 q = cross(relOrigin,edges[0]); + const float v = dot(direction,q)/a; + + const float t = dot(edges[1],q)/a; + + return t>0.f&&u>=0.f&&v>=0.f&&(u+v)<=1.f ? t:nbl_glsl_FLT_NAN; +} + +vec3 Triangle_getNormalTimesArea_impl(in mat2x3 edges) +{ + return cross(edges[0],edges[1])*0.5; +} +vec3 Triangle_getNormalTimesArea(in Triangle tri) +{ + return Triangle_getNormalTimesArea_impl(mat2x3(tri.vertex1-tri.vertex0,tri.vertex2-tri.vertex0)); +} + + + +struct Rectangle +{ + vec3 offset; + uint bsdfLightIDs; + vec3 edge0; + uint padding0; + vec3 edge1; + uint padding1; +}; + +Rectangle Rectangle_Rectangle(in vec3 offset, in vec3 edge0, in vec3 edge1, in uint bsdfID, in uint lightID) +{ + Rectangle rect; + rect.offset = offset; + rect.edge0 = edge0; + rect.edge1 = edge1; + // + rect.bsdfLightIDs = bitfieldInsert(bsdfID, lightID, 16, 16); + return rect; +} + +void Rectangle_getNormalBasis(in Rectangle rect, out mat3 basis, out vec2 extents) +{ + extents = vec2(length(rect.edge0), length(rect.edge1)); + basis[0] = rect.edge0/extents[0]; + basis[1] = rect.edge1/extents[1]; + basis[2] = normalize(cross(basis[0],basis[1])); +} + +// return intersection distance if found, nbl_glsl_FLT_NAN otherwise +float Rectangle_intersect(in Rectangle rect, in vec3 origin, in vec3 direction) +{ + const vec3 h = cross(direction,rect.edge1); + const float a = dot(rect.edge0,h); + + const vec3 relOrigin = origin-rect.offset; + + const float u = dot(relOrigin,h)/a; + + const vec3 q = cross(relOrigin,rect.edge0); + const float v = dot(direction,q)/a; + + const float t = dot(rect.edge1,q)/a; + + const bool intersection = t>0.f&&u>=0.f&&v>=0.f&&u<=1.f&&v<=1.f; + return intersection ? t:nbl_glsl_FLT_NAN; +} + +vec3 Rectangle_getNormalTimesArea(in Rectangle rect) +{ + return cross(rect.edge0,rect.edge1); +} + + + +#define DIFFUSE_OP 0u +#define CONDUCTOR_OP 1u +#define DIELECTRIC_OP 2u +#define OP_BITS_OFFSET 0 +#define OP_BITS_SIZE 2 +struct BSDFNode +{ + uvec4 data[2]; +}; + +uint BSDFNode_getType(in BSDFNode node) +{ + return bitfieldExtract(node.data[0].w,OP_BITS_OFFSET,OP_BITS_SIZE); +} +bool BSDFNode_isBSDF(in BSDFNode node) +{ + return BSDFNode_getType(node)==DIELECTRIC_OP; +} +bool BSDFNode_isNotDiffuse(in BSDFNode node) +{ + return BSDFNode_getType(node)!=DIFFUSE_OP; +} +float BSDFNode_getRoughness(in BSDFNode node) +{ + return uintBitsToFloat(node.data[1].w); +} +vec3 BSDFNode_getRealEta(in BSDFNode node) +{ + return uintBitsToFloat(node.data[0].rgb); +} +vec3 BSDFNode_getImaginaryEta(in BSDFNode node) +{ + return uintBitsToFloat(node.data[1].rgb); +} +mat2x3 BSDFNode_getEta(in BSDFNode node) +{ + return mat2x3(BSDFNode_getRealEta(node),BSDFNode_getImaginaryEta(node)); +} +#include +vec3 BSDFNode_getReflectance(in BSDFNode node, in float VdotH) +{ + const vec3 albedoOrRealIoR = uintBitsToFloat(node.data[0].rgb); + if (BSDFNode_isNotDiffuse(node)) + return nbl_glsl_fresnel_conductor(albedoOrRealIoR, BSDFNode_getImaginaryEta(node), VdotH); + else + return albedoOrRealIoR; +} + +float BSDFNode_getNEEProb(in BSDFNode bsdf) +{ + const float alpha = BSDFNode_isNotDiffuse(bsdf) ? BSDFNode_getRoughness(bsdf):1.0; + return min(8.0*alpha,1.0); +} + +#include +#include +float getLuma(in vec3 col) +{ + return dot(transpose(nbl_glsl_scRGBtoXYZ)[1],col); +} + +#define BSDF_COUNT 7 +BSDFNode bsdfs[BSDF_COUNT] = { + {{uvec4(floatBitsToUint(vec3(0.8,0.8,0.8)),DIFFUSE_OP),floatBitsToUint(vec4(0.0,0.0,0.0,0.0))}}, + {{uvec4(floatBitsToUint(vec3(0.8,0.4,0.4)),DIFFUSE_OP),floatBitsToUint(vec4(0.0,0.0,0.0,0.0))}}, + {{uvec4(floatBitsToUint(vec3(0.4,0.8,0.4)),DIFFUSE_OP),floatBitsToUint(vec4(0.0,0.0,0.0,0.0))}}, + {{uvec4(floatBitsToUint(vec3(1.02,1.02,1.3)),CONDUCTOR_OP),floatBitsToUint(vec4(1.0,1.0,2.0,0.0))}}, + {{uvec4(floatBitsToUint(vec3(1.02,1.3,1.02)),CONDUCTOR_OP),floatBitsToUint(vec4(1.0,2.0,1.0,0.0))}}, + {{uvec4(floatBitsToUint(vec3(1.02,1.3,1.02)),CONDUCTOR_OP),floatBitsToUint(vec4(1.0,2.0,1.0,0.15))}}, + {{uvec4(floatBitsToUint(vec3(1.4,1.45,1.5)),DIELECTRIC_OP),floatBitsToUint(vec4(0.0,0.0,0.0,0.0625))}} +}; + + +struct Light +{ + vec3 radiance; + uint objectID; +}; + +vec3 Light_getRadiance(in Light light) +{ + return light.radiance; +} +uint Light_getObjectID(in Light light) +{ + return light.objectID; +} + + +#define LIGHT_COUNT 1 +float scene_getLightChoicePdf(in Light light) +{ + return 1.0/float(LIGHT_COUNT); +} + + +#define LIGHT_COUNT 1 +Light lights[LIGHT_COUNT] = +{ + { + vec3(30.0,25.0,15.0), +#ifdef POLYGON_METHOD + 0u +#else + 8u +#endif + } +}; + + + +#define ANY_HIT_FLAG (-2147483648) +#define DEPTH_BITS_COUNT 8 +#define DEPTH_BITS_OFFSET (31-DEPTH_BITS_COUNT) +struct ImmutableRay_t +{ + vec3 origin; + vec3 direction; +#if POLYGON_METHOD==2 + vec3 normalAtOrigin; + bool wasBSDFAtOrigin; +#endif +}; +struct MutableRay_t +{ + float intersectionT; + uint objectID; + /* irrelevant here + uint triangleID; + vec2 barycentrics; + */ +}; +struct Payload_t +{ + vec3 accumulation; + float otherTechniqueHeuristic; + vec3 throughput; + #ifdef KILL_DIFFUSE_SPECULAR_PATHS + bool hasDiffuse; + #endif +}; + +struct Ray_t +{ + ImmutableRay_t _immutable; + MutableRay_t _mutable; + Payload_t _payload; +}; + + +#define INTERSECTION_ERROR_BOUND_LOG2 (-8.0) +float getTolerance_common(in uint depth) +{ + float depthRcp = 1.0/float(depth); + return INTERSECTION_ERROR_BOUND_LOG2;// *depthRcp*depthRcp; +} +float getStartTolerance(in uint depth) +{ + return exp2(getTolerance_common(depth)); +} +float getEndTolerance(in uint depth) +{ + return 1.0-exp2(getTolerance_common(depth)+1.0); +} + + +vec2 SampleSphericalMap(vec3 v) +{ + vec2 uv = vec2(atan(v.z, v.x), asin(v.y)); + uv *= nbl_glsl_RECIPROCAL_PI*0.5; + uv += 0.5; + return uv; +} + +void missProgram(in ImmutableRay_t _immutable, inout Payload_t _payload) +{ + vec3 finalContribution = _payload.throughput; + // #define USE_ENVMAP +#ifdef USE_ENVMAP + vec2 uv = SampleSphericalMap(_immutable.direction); + finalContribution *= textureLod(envMap, uv, 0.0).rgb; +#else + const vec3 kConstantEnvLightRadiance = vec3(0.15, 0.21, 0.3); + finalContribution *= kConstantEnvLightRadiance; + _payload.accumulation += finalContribution; +#endif +} + +#include +#include +#include +#include +#include +#include +#include +nbl_glsl_LightSample nbl_glsl_bsdf_cos_generate(in nbl_glsl_AnisotropicViewSurfaceInteraction interaction, in vec3 u, in BSDFNode bsdf, in float monochromeEta, out nbl_glsl_AnisotropicMicrofacetCache _cache) +{ + const float a = BSDFNode_getRoughness(bsdf); + const mat2x3 ior = BSDFNode_getEta(bsdf); + + // fresnel stuff for dielectrics + float orientedEta, rcpOrientedEta; + const bool viewerInsideMedium = nbl_glsl_getOrientedEtas(orientedEta,rcpOrientedEta,interaction.isotropic.NdotV,monochromeEta); + + nbl_glsl_LightSample smpl; + nbl_glsl_AnisotropicMicrofacetCache dummy; + switch (BSDFNode_getType(bsdf)) + { + case DIFFUSE_OP: + smpl = nbl_glsl_oren_nayar_cos_generate(interaction,u.xy,a*a); + break; + case CONDUCTOR_OP: + smpl = nbl_glsl_ggx_cos_generate(interaction,u.xy,a,a,_cache); + break; + default: + smpl = nbl_glsl_ggx_dielectric_cos_generate(interaction,u,a,a,monochromeEta,_cache); + break; + } + return smpl; +} + +vec3 nbl_glsl_bsdf_cos_remainder_and_pdf(out float pdf, in nbl_glsl_LightSample _sample, in nbl_glsl_AnisotropicViewSurfaceInteraction interaction, in BSDFNode bsdf, in float monochromeEta, in nbl_glsl_AnisotropicMicrofacetCache _cache) +{ + // are V and L on opposite sides of the surface? + const bool transmitted = nbl_glsl_isTransmissionPath(interaction.isotropic.NdotV,_sample.NdotL); + + // is the BSDF or BRDF, if it is then we make the dot products `abs` before `max(,0.0)` + const bool transmissive = BSDFNode_isBSDF(bsdf); + const float clampedNdotL = nbl_glsl_conditionalAbsOrMax(transmissive,_sample.NdotL,0.0); + const float clampedNdotV = nbl_glsl_conditionalAbsOrMax(transmissive,interaction.isotropic.NdotV,0.0); + + vec3 remainder; + + const float minimumProjVectorLen = 0.00000001; + if (clampedNdotV>minimumProjVectorLen && clampedNdotL>minimumProjVectorLen) + { + // fresnel stuff for conductors (but reflectance also doubles as albedo) + const mat2x3 ior = BSDFNode_getEta(bsdf); + const vec3 reflectance = BSDFNode_getReflectance(bsdf,_cache.isotropic.VdotH); + + // fresnel stuff for dielectrics + float orientedEta, rcpOrientedEta; + const bool viewerInsideMedium = nbl_glsl_getOrientedEtas(orientedEta,rcpOrientedEta,interaction.isotropic.NdotV,monochromeEta); + + // + const float VdotL = dot(interaction.isotropic.V.dir,_sample.L); + + // + const float a = max(BSDFNode_getRoughness(bsdf),0.0001); // TODO: @Crisspl 0-roughness still doesn't work! Also Beckmann has a weird dark rim instead as fresnel!? + const float a2 = a*a; + + // TODO: refactor into Material Compiler-esque thing + switch (BSDFNode_getType(bsdf)) + { + case DIFFUSE_OP: + remainder = reflectance*nbl_glsl_oren_nayar_cos_remainder_and_pdf_wo_clamps(pdf,a*a,VdotL,clampedNdotL,clampedNdotV); + break; + case CONDUCTOR_OP: + remainder = nbl_glsl_ggx_cos_remainder_and_pdf_wo_clamps(pdf,nbl_glsl_ggx_trowbridge_reitz(a2,_cache.isotropic.NdotH2),clampedNdotL,_sample.NdotL2,clampedNdotV,interaction.isotropic.NdotV_squared,reflectance,a2); + break; + default: + remainder = vec3(nbl_glsl_ggx_dielectric_cos_remainder_and_pdf(pdf, _sample, interaction.isotropic, _cache.isotropic, monochromeEta, a*a)); + break; + } + } + else + remainder = vec3(0.0); + return remainder; +} + +layout (constant_id = 0) const int MAX_DEPTH_LOG2 = 4; +layout (constant_id = 1) const int MAX_SAMPLES_LOG2 = 10; + + +#include + +mat2x3 rand3d(in uint protoDimension, in uint _sample, inout nbl_glsl_xoroshiro64star_state_t scramble_state) +{ + mat2x3 retval; + uint address = bitfieldInsert(protoDimension,_sample,MAX_DEPTH_LOG2,MAX_SAMPLES_LOG2); + for (int i=0; i<2u; i++) + { + uvec3 seqVal = texelFetch(sampleSequence,int(address)+i).xyz; + seqVal ^= uvec3(nbl_glsl_xoroshiro64star(scramble_state),nbl_glsl_xoroshiro64star(scramble_state),nbl_glsl_xoroshiro64star(scramble_state)); + retval[i] = vec3(seqVal)*uintBitsToFloat(0x2f800004u); + } + return retval; +} + + +void traceRay_extraShape(inout int objectID, inout float intersectionT, in vec3 origin, in vec3 direction); +int traceRay(inout float intersectionT, in vec3 origin, in vec3 direction) +{ + const bool anyHit = intersectionT!=nbl_glsl_FLT_MAX; + + int objectID = -1; + for (int i=0; i0.0 && tnbl_glsl_FLT_MIN; + // but if we allowed non-watertight transmitters (single water surface), it would make sense just to apply this line by itself + nbl_glsl_AnisotropicMicrofacetCache _cache; + validPath = validPath && nbl_glsl_calcAnisotropicMicrofacetCache(_cache, interaction, nee_sample, monochromeEta); + if (lightPdflumaContributionThreshold && traceRay(t,intersection+nee_sample.L*t*getStartTolerance(depth),nee_sample.L)==-1) + ray._payload.accumulation += neeContrib; + }} + } +#if NEE_ONLY + return false; +#endif + // sample BSDF + float bsdfPdf; vec3 bsdfSampleL; + { + nbl_glsl_AnisotropicMicrofacetCache _cache; + nbl_glsl_LightSample bsdf_sample = nbl_glsl_bsdf_cos_generate(interaction,epsilon[1],bsdf,monochromeEta,_cache); + // the value of the bsdf divided by the probability of the sample being generated + throughput *= nbl_glsl_bsdf_cos_remainder_and_pdf(bsdfPdf,bsdf_sample,interaction,bsdf,monochromeEta,_cache); + // + bsdfSampleL = bsdf_sample.L; + } + + // additional threshold + const float lumaThroughputThreshold = lumaContributionThreshold; + if (bsdfPdf>bsdfPdfThreshold && getLuma(throughput)>lumaThroughputThreshold) + { + ray._payload.throughput = throughput; + ray._payload.otherTechniqueHeuristic = neeProbability/bsdfPdf; // numerically stable, don't touch + ray._payload.otherTechniqueHeuristic *= ray._payload.otherTechniqueHeuristic; + + // trace new ray + ray._immutable.origin = intersection+bsdfSampleL*(1.0/*kSceneSize*/)*getStartTolerance(depth); + ray._immutable.direction = bsdfSampleL; + #if POLYGON_METHOD==2 + ray._immutable.normalAtOrigin = interaction.isotropic.N; + ray._immutable.wasBSDFAtOrigin = isBSDF; + #endif + return true; + } + } + return false; +} + +void main() +{ + const ivec2 imageExtents = imageSize(outImage); + const ivec2 coords = getCoordinates(); + vec2 texCoord = vec2(coords) / vec2(imageExtents); + texCoord.y = 1.0 - texCoord.y; + + if (false == (all(lessThanEqual(ivec2(0),coords)) && all(greaterThan(imageExtents,coords)))) { + return; + } + + if (((PTPushConstant.depth-1)>>MAX_DEPTH_LOG2)>0 || ((PTPushConstant.sampleCount-1)>>MAX_SAMPLES_LOG2)>0) + { + vec4 pixelCol = vec4(1.0,0.0,0.0,1.0); + imageStore(outImage, coords, pixelCol); + return; + } + + nbl_glsl_xoroshiro64star_state_t scramble_start_state = texelFetch(scramblebuf,coords,0).rg; + const vec2 pixOffsetParam = vec2(1.0)/vec2(textureSize(scramblebuf,0)); + + + const mat4 invMVP = PTPushConstant.invMVP; + + vec4 NDC = vec4(texCoord*vec2(2.0,-2.0)+vec2(-1.0,1.0),0.0,1.0); + vec3 camPos; + { + vec4 tmp = invMVP*NDC; + camPos = tmp.xyz/tmp.w; + NDC.z = 1.0; + } + + vec3 color = vec3(0.0); + float meanLumaSquared = 0.0; + // TODO: if we collapse the nested for loop, then all GPUs will get `PTPushConstant.depth` factor speedup, not just NV with separate PC + for (int i=0; i5.0) + color = vec3(1.0,0.0,0.0); + #endif + + vec4 pixelCol = vec4(color, 1.0); + imageStore(outImage, coords, pixelCol); +} +/** TODO: Improving Rendering + +Now: +- Always MIS (path correlated reuse) +- Test MIS alpha (roughness) scheme + +Many Lights: +- Path Guiding +- Light Importance Lists/Classification +- Spatio-Temporal Reservoir Sampling + +Indirect Light: +- Bidirectional Path Tracing +- Uniform Path Sampling / Vertex Connection and Merging / Path Space Regularization + +Animations: +- A-SVGF / BMFR +**/ \ No newline at end of file diff --git a/31_HLSLPathTracer/app_resources/glsl/litByRectangle.comp b/31_HLSLPathTracer/app_resources/glsl/litByRectangle.comp new file mode 100644 index 000000000..300cef559 --- /dev/null +++ b/31_HLSLPathTracer/app_resources/glsl/litByRectangle.comp @@ -0,0 +1,182 @@ +// Copyright (C) 2018-2020 - 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 + +#version 430 core +#extension GL_GOOGLE_include_directive : require + +#define SPHERE_COUNT 8 +#define POLYGON_METHOD 1 // 0 area sampling, 1 solid angle sampling, 2 approximate projected solid angle sampling +#include "common.glsl" + +#define RECTANGLE_COUNT 1 +const vec3 edge0 = normalize(vec3(2,0,-1)); +const vec3 edge1 = normalize(vec3(2,-5,4)); +Rectangle rectangles[RECTANGLE_COUNT] = { + Rectangle_Rectangle(vec3(-3.8,0.35,1.3),edge0*7.0,edge1*0.1,INVALID_ID_16BIT,0u) +}; + + +void traceRay_extraShape(inout int objectID, inout float intersectionT, in vec3 origin, in vec3 direction) +{ + for (int i=0; i0.0 && t +#include +#include + +float nbl_glsl_light_deferred_pdf(in Light light, in Ray_t ray) +{ + const Rectangle rect = rectangles[Light_getObjectID(light)]; + + const ImmutableRay_t _immutable = ray._immutable; + const vec3 L = _immutable.direction; +#if POLYGON_METHOD==0 + const float dist = ray._mutable.intersectionT; + return dist*dist/abs(dot(Rectangle_getNormalTimesArea(rect),L)); +#else + #ifdef TRIANGLE_REFERENCE + const mat3 sphericalVertices[2] = + { + nbl_glsl_shapes_getSphericalTriangle(mat3(rect.offset,rect.offset+rect.edge0,rect.offset+rect.edge1),_immutable.origin), + nbl_glsl_shapes_getSphericalTriangle(mat3(rect.offset+rect.edge1,rect.offset+rect.edge0,rect.offset+rect.edge0+rect.edge1),_immutable.origin) + }; + float solidAngle[2]; + vec3 cos_vertices[2],sin_vertices[2]; + float cos_a[2],cos_c[2],csc_b[2],csc_c[2]; + for (uint i=0u; i<2u; i++) + solidAngle[i] = nbl_glsl_shapes_SolidAngleOfTriangle(sphericalVertices[i],cos_vertices[i],sin_vertices[i],cos_a[i],cos_c[i],csc_b[i],csc_c[i]); + const float rectSolidAngle = solidAngle[0]+solidAngle[1]; + #if POLYGON_METHOD==1 + return 1.f/rectSolidAngle; + #elif POLYGON_METHOD==2 + // TODO: figure out what breaks for a directly visible light under MIS + if (rectSolidAngle > nbl_glsl_FLT_MIN) + { + const vec2 bary = nbl_glsl_barycentric_reconstructBarycentrics(L*ray._mutable.intersectionT+_immutable.origin-rect.offset,mat2x3(rect.edge0,rect.edge1)); + const uint i = bary.x>=0.f&&bary.y>=0.f&&(bary.x+bary.y)<=1.f ? 0u:1u; + + float pdf = nbl_glsl_sampling_probProjectedSphericalTriangleSample(solidAngle[i],cos_vertices[i],sin_vertices[i],cos_a[i],cos_c[i],csc_b[i],csc_c[i],sphericalVertices[i],_immutable.normalAtOrigin,_immutable.wasBSDFAtOrigin,L); + pdf *= solidAngle[i]/rectSolidAngle; + return pdf; + } + else + return nbl_glsl_FLT_INF; + #endif + #else + float pdf; + mat3 rectNormalBasis; + vec2 rectExtents; + Rectangle_getNormalBasis(rect, rectNormalBasis, rectExtents); + vec3 sphR0 = nbl_glsl_shapes_getSphericalRectangle(_immutable.origin, rect.offset, rectNormalBasis); + float solidAngle = nbl_glsl_shapes_SolidAngleOfRectangle(sphR0, rectExtents); + if (solidAngle > nbl_glsl_FLT_MIN) + { + #if POLYGON_METHOD==1 + pdf = 1.f/solidAngle; + #else + #error + #endif + } + else + pdf = nbl_glsl_FLT_INF; + return pdf; + #endif +#endif +} + +vec3 nbl_glsl_light_generate_and_pdf(out float pdf, out float newRayMaxT, in vec3 origin, in nbl_glsl_AnisotropicViewSurfaceInteraction interaction, in bool isBSDF, in vec3 xi, in uint objectID) +{ + const Rectangle rect = rectangles[objectID]; + const vec3 N = Rectangle_getNormalTimesArea(rect); + + const vec3 origin2origin = rect.offset-origin; +#if POLYGON_METHOD==0 + vec3 L = origin2origin+rect.edge0*xi.x+rect.edge1*xi.y; // TODO: refactor + + const float distanceSq = dot(L,L); + const float rcpDistance = inversesqrt(distanceSq); + L *= rcpDistance; + + pdf = distanceSq/abs(dot(N,L)); + newRayMaxT = 1.0/rcpDistance; + return L; +#else + #ifdef TRIANGLE_REFERENCE + const mat3 sphericalVertices[2] = + { + nbl_glsl_shapes_getSphericalTriangle(mat3(rect.offset,rect.offset+rect.edge0,rect.offset+rect.edge1),origin), + nbl_glsl_shapes_getSphericalTriangle(mat3(rect.offset+rect.edge1,rect.offset+rect.edge0,rect.offset+rect.edge0+rect.edge1),origin) + }; + float solidAngle[2]; + vec3 cos_vertices[2],sin_vertices[2]; + float cos_a[2],cos_c[2],csc_b[2],csc_c[2]; + for (uint i=0u; i<2u; i++) + solidAngle[i] = nbl_glsl_shapes_SolidAngleOfTriangle(sphericalVertices[i],cos_vertices[i],sin_vertices[i],cos_a[i],cos_c[i],csc_b[i],csc_c[i]); + vec3 L = vec3(0.f,0.f,0.f); + const float rectangleSolidAngle = solidAngle[0]+solidAngle[1]; + if (rectangleSolidAngle > nbl_glsl_FLT_MIN) + { + float rcpTriangleChoiceProb; + const uint i = nbl_glsl_partitionRandVariable(solidAngle[0]/rectangleSolidAngle,xi.z,rcpTriangleChoiceProb) ? 1u:0u; + #if POLYGON_METHOD==1 + L = nbl_glsl_sampling_generateSphericalTriangleSample(solidAngle[i],cos_vertices[i],sin_vertices[i],cos_a[i],cos_c[i],csc_b[i],csc_c[i],sphericalVertices[i],xi.xy); + pdf = 1.f/rectangleSolidAngle; + #elif POLYGON_METHOD==2 + float rcpPdf; + L = nbl_glsl_sampling_generateProjectedSphericalTriangleSample(rcpPdf,solidAngle[i],cos_vertices[i],sin_vertices[i],cos_a[i],cos_c[i],csc_b[i],csc_c[i],sphericalVertices[i],interaction.isotropic.N,isBSDF,xi.xy); + pdf = 1.f/(rcpPdf*rcpTriangleChoiceProb); + #endif + } + else + pdf = nbl_glsl_FLT_INF; + #else + mat3 rectNormalBasis; + vec2 rectExtents; + Rectangle_getNormalBasis(rect, rectNormalBasis, rectExtents); + vec3 sphR0 = nbl_glsl_shapes_getSphericalRectangle(origin, rect.offset, rectNormalBasis); + vec3 L = vec3(0.f,0.f,0.f); + float solidAngle; + vec2 sphUv = nbl_glsl_sampling_generateSphericalRectangleSample(sphR0, rectExtents, xi.xy, solidAngle); + if (solidAngle > nbl_glsl_FLT_MIN) + { + #if POLYGON_METHOD==1 + vec3 sph_sample = sphUv[0] * rect.edge0 + sphUv[1] * rect.edge1 + rect.offset; + L = normalize(sph_sample - origin); + pdf = 1.f/solidAngle; + #else + #error + #endif + } + else + pdf = nbl_glsl_FLT_INF; + #endif + newRayMaxT = dot(N,origin2origin)/dot(N,L); + return L; +#endif +} + + +uint getBSDFLightIDAndDetermineNormal(out vec3 normal, in uint objectID, in vec3 intersection) +{ + if (objectID0.0) + { + const float rcpDistance = inversesqrt(distanceSQ); + Z *= rcpDistance; + + const float cosThetaMax = sqrt(cosThetaMax2); + const float cosTheta = mix(1.0,cosThetaMax,xi.x); + + vec3 L = Z*cosTheta; + + const float cosTheta2 = cosTheta*cosTheta; + const float sinTheta = sqrt(1.0-cosTheta2); + float sinPhi,cosPhi; + nbl_glsl_sincos(2.0*nbl_glsl_PI*xi.y-nbl_glsl_PI,sinPhi,cosPhi); + mat2x3 XY = nbl_glsl_frisvad(Z); + + L += (XY[0]*cosPhi+XY[1]*sinPhi)*sinTheta; + + newRayMaxT = (cosTheta-sqrt(cosTheta2-cosThetaMax2))/rcpDistance; + pdf = 1.0/Sphere_getSolidAngle_impl(cosThetaMax); + return L; + } + pdf = 0.0; + return vec3(0.0,0.0,0.0); +} + +uint getBSDFLightIDAndDetermineNormal(out vec3 normal, in uint objectID, in vec3 intersection) +{ + Sphere sphere = spheres[objectID]; + normal = Sphere_getNormal(sphere,intersection); + return sphere.bsdfLightIDs; +} \ No newline at end of file diff --git a/31_HLSLPathTracer/app_resources/glsl/litByTriangle.comp b/31_HLSLPathTracer/app_resources/glsl/litByTriangle.comp new file mode 100644 index 000000000..ba23c82e5 --- /dev/null +++ b/31_HLSLPathTracer/app_resources/glsl/litByTriangle.comp @@ -0,0 +1,105 @@ +// Copyright (C) 2018-2020 - 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 + +#version 430 core +#extension GL_GOOGLE_include_directive : require + +#define SPHERE_COUNT 8 +#define POLYGON_METHOD 1 // 0 area sampling, 1 solid angle sampling, 2 approximate projected solid angle sampling +#include "common.glsl" + +#define TRIANGLE_COUNT 1 +Triangle triangles[TRIANGLE_COUNT] = { + Triangle_Triangle(mat3(vec3(-1.8,0.35,0.3),vec3(-1.2,0.35,0.0),vec3(-1.5,0.8,-0.3))*10.0,INVALID_ID_16BIT,0u) +}; + +void traceRay_extraShape(inout int objectID, inout float intersectionT, in vec3 origin, in vec3 direction) +{ + for (int i=0; i0.0 && t +float nbl_glsl_light_deferred_pdf(in Light light, in Ray_t ray) +{ + const Triangle tri = triangles[Light_getObjectID(light)]; + + const vec3 L = ray._immutable.direction; +#if POLYGON_METHOD==0 + const float dist = ray._mutable.intersectionT; + return dist*dist/abs(dot(Triangle_getNormalTimesArea(tri),L)); +#else + const ImmutableRay_t _immutable = ray._immutable; + const mat3 sphericalVertices = nbl_glsl_shapes_getSphericalTriangle(mat3(tri.vertex0,tri.vertex1,tri.vertex2),_immutable.origin); + #if POLYGON_METHOD==1 + const float rcpProb = nbl_glsl_shapes_SolidAngleOfTriangle(sphericalVertices); + // if `rcpProb` is NAN then the triangle's solid angle was close to 0.0 + return rcpProb>nbl_glsl_FLT_MIN ? (1.0/rcpProb):nbl_glsl_FLT_MAX; + #elif POLYGON_METHOD==2 + const float pdf = nbl_glsl_sampling_probProjectedSphericalTriangleSample(sphericalVertices,_immutable.normalAtOrigin,_immutable.wasBSDFAtOrigin,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 pdfnbl_glsl_FLT_MIN ? (1.0/rcpPdf):0.0; + + const vec3 N = Triangle_getNormalTimesArea(tri); + newRayMaxT = dot(N,tri.vertex0-origin)/dot(N,L); + return L; +#endif +} + + +uint getBSDFLightIDAndDetermineNormal(out vec3 normal, in uint objectID, in vec3 intersection) +{ + if (objectID +using namespace nbl::hlsl; +using namespace ext::FullScreenTriangle; + +// binding 0 set 0 +[[vk::combinedImageSampler]] [[vk::binding(0, 0)]] Texture2D texture; +[[vk::combinedImageSampler]] [[vk::binding(0, 0)]] SamplerState samplerState; + +[[vk::location(0)]] float32_t4 main(SVertexAttributes vxAttr) : SV_Target0 +{ + return float32_t4(texture.Sample(samplerState, vxAttr.uv).rgb, 1.0f); +} \ No newline at end of file diff --git a/31_HLSLPathTracer/config.json.template b/31_HLSLPathTracer/config.json.template new file mode 100644 index 000000000..24adf54fb --- /dev/null +++ b/31_HLSLPathTracer/config.json.template @@ -0,0 +1,28 @@ +{ + "enableParallelBuild": true, + "threadsPerBuildProcess" : 2, + "isExecuted": false, + "scriptPath": "", + "cmake": { + "configurations": [ "Release", "Debug", "RelWithDebInfo" ], + "buildModes": [], + "requiredOptions": [] + }, + "profiles": [ + { + "backend": "vulkan", + "platform": "windows", + "buildModes": [], + "runConfiguration": "Release", + "gpuArchitectures": [] + } + ], + "dependencies": [], + "data": [ + { + "dependencies": [], + "command": [""], + "outputs": [] + } + ] +} diff --git a/31_HLSLPathTracer/include/nbl/this_example/common.hpp b/31_HLSLPathTracer/include/nbl/this_example/common.hpp new file mode 100644 index 000000000..ff3dd8095 --- /dev/null +++ b/31_HLSLPathTracer/include/nbl/this_example/common.hpp @@ -0,0 +1,17 @@ +#ifndef __NBL_THIS_EXAMPLE_COMMON_H_INCLUDED__ +#define __NBL_THIS_EXAMPLE_COMMON_H_INCLUDED__ + +#include + +// common api +#include "CCamera.hpp" +#include "SimpleWindowedApplication.hpp" +#include "nbl/application_templates/MonoAssetManagerAndBuiltinResourceApplication.hpp" +#include "CEventCallback.hpp" + +// example's own headers +#include "nbl/ui/ICursorControl.h" +#include "nbl/ext/ImGui/ImGui.h" +#include "imgui/imgui_internal.h" + +#endif // __NBL_THIS_EXAMPLE_COMMON_H_INCLUDED__ \ No newline at end of file diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp new file mode 100644 index 000000000..73434a852 --- /dev/null +++ b/31_HLSLPathTracer/main.cpp @@ -0,0 +1,1276 @@ +// Copyright (C) 2018-2020 - 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 + +#include "nbl/this_example/common.hpp" +#include "nbl/asset/interchange/IImageAssetHandlerBase.h" +#include "nbl/ext/FullScreenTriangle/FullScreenTriangle.h" +#include "nbl/builtin/hlsl/surface_transform.h" + +using namespace nbl; +using namespace core; +using namespace hlsl; +using namespace system; +using namespace asset; +using namespace ui; +using namespace video; + +struct PTPushConstant { + matrix4SIMD invMVP; + int sampleCount; + int depth; +}; + +// TODO: Add a QueryPool for timestamping once its ready +// TODO: Do buffer creation using assConv +class ComputeShaderPathtracer final : public examples::SimpleWindowedApplication, public application_templates::MonoAssetManagerAndBuiltinResourceApplication +{ + using device_base_t = examples::SimpleWindowedApplication; + using asset_base_t = application_templates::MonoAssetManagerAndBuiltinResourceApplication; + using clock_t = std::chrono::steady_clock; + + enum E_LIGHT_GEOMETRY : uint8_t + { + ELG_SPHERE, + ELG_TRIANGLE, + ELG_RECTANGLE, + ELG_COUNT + }; + + constexpr static inline uint32_t2 WindowDimensions = { 1280, 720 }; + constexpr static inline uint32_t MaxFramesInFlight = 5; + constexpr static inline clock_t::duration DisplayImageDuration = std::chrono::milliseconds(900); + constexpr static inline uint32_t DefaultWorkGroupSize = 16u; + constexpr static inline uint32_t MaxDescriptorCount = 256u; + constexpr static inline uint32_t MaxDepthLog2 = 4u; // 5 + constexpr static inline uint32_t MaxSamplesLog2 = 10u; // 18 + constexpr static inline uint32_t MaxBufferDimensions = 3u << MaxDepthLog2; + constexpr static inline uint32_t MaxBufferSamples = 1u << MaxSamplesLog2; + constexpr static inline uint8_t MaxUITextureCount = 1u; + static inline std::string DefaultImagePathsFile = "envmap/envmap_0.exr"; + static inline std::string OwenSamplerFilePath = "owen_sampler_buffer.bin"; + static inline std::array PTShaderPaths = { "app_resources/glsl/litBySphere.comp", "app_resources/glsl/litByTriangle.comp", "app_resources/glsl/litByRectangle.comp" }; + static inline std::string PresentShaderPath = "app_resources/hlsl/present.frag.hlsl"; + + const char* shaderNames[E_LIGHT_GEOMETRY::ELG_COUNT] = { + "ELG_SPHERE", + "ELG_TRIANGLE", + "ELG_RECTANGLE" + }; + + public: + inline ComputeShaderPathtracer(const path& _localInputCWD, const path& _localOutputCWD, const path& _sharedInputCWD, const path& _sharedOutputCWD) + : IApplicationFramework(_localInputCWD, _localOutputCWD, _sharedInputCWD, _sharedOutputCWD) {} + + inline bool isComputeOnly() const override { return false; } + + inline core::vector getSurfaces() const override + { + if (!m_surface) + { + { + auto windowCallback = core::make_smart_refctd_ptr(smart_refctd_ptr(m_inputSystem), smart_refctd_ptr(m_logger)); + IWindow::SCreationParams params = {}; + params.callback = core::make_smart_refctd_ptr(); + params.width = WindowDimensions.x; + params.height = WindowDimensions.y; + params.x = 32; + params.y = 32; + params.flags = ui::IWindow::ECF_HIDDEN | IWindow::ECF_BORDERLESS | IWindow::ECF_RESIZABLE; + params.windowCaption = "ComputeShaderPathtracer"; + params.callback = windowCallback; + const_cast&>(m_window) = m_winMgr->createWindow(std::move(params)); + } + + auto surface = CSurfaceVulkanWin32::create(smart_refctd_ptr(m_api), smart_refctd_ptr_static_cast(m_window)); + const_cast&>(m_surface) = nbl::video::CSimpleResizeSurface::create(std::move(surface)); + } + + if (m_surface) + return { {m_surface->getSurface()/*,EQF_NONE*/} }; + + return {}; + } + + inline bool onAppInitialized(smart_refctd_ptr&& system) override + { + // Init systems + { + m_inputSystem = make_smart_refctd_ptr(logger_opt_smart_ptr(smart_refctd_ptr(m_logger))); + + // Remember to call the base class initialization! + if (!device_base_t::onAppInitialized(smart_refctd_ptr(system))) + return false; + if (!asset_base_t::onAppInitialized(std::move(system))) + return false; + + m_semaphore = m_device->createSemaphore(m_realFrameIx); + + if (!m_semaphore) + return logFail("Failed to create semaphore!"); + } + + // Create renderpass and init surface + nbl::video::IGPURenderpass* renderpass; + { + ISwapchain::SCreationParams swapchainParams = { .surface = smart_refctd_ptr(m_surface->getSurface()) }; + if (!swapchainParams.deduceFormat(m_physicalDevice)) + return logFail("Could not choose a Surface Format for the Swapchain!"); + + const static IGPURenderpass::SCreationParams::SSubpassDependency dependencies[] = + { + { + .srcSubpass = IGPURenderpass::SCreationParams::SSubpassDependency::External, + .dstSubpass = 0, + .memoryBarrier = + { + .srcStageMask = asset::PIPELINE_STAGE_FLAGS::COPY_BIT, + .srcAccessMask = asset::ACCESS_FLAGS::TRANSFER_WRITE_BIT, + .dstStageMask = asset::PIPELINE_STAGE_FLAGS::COLOR_ATTACHMENT_OUTPUT_BIT, + .dstAccessMask = asset::ACCESS_FLAGS::COLOR_ATTACHMENT_WRITE_BIT + } + }, + { + .srcSubpass = 0, + .dstSubpass = IGPURenderpass::SCreationParams::SSubpassDependency::External, + .memoryBarrier = + { + .srcStageMask = asset::PIPELINE_STAGE_FLAGS::COLOR_ATTACHMENT_OUTPUT_BIT, + .srcAccessMask = asset::ACCESS_FLAGS::COLOR_ATTACHMENT_WRITE_BIT + } + }, + IGPURenderpass::SCreationParams::DependenciesEnd + }; + + auto scResources = std::make_unique(m_device.get(), swapchainParams.surfaceFormat.format, dependencies); + renderpass = scResources->getRenderpass(); + + if (!renderpass) + return logFail("Failed to create Renderpass!"); + + auto gQueue = getGraphicsQueue(); + if (!m_surface || !m_surface->init(gQueue, std::move(scResources), swapchainParams.sharedParams)) + return logFail("Could not create Window & Surface or initialize the Surface!"); + } + + // image upload utils + { + m_scratchSemaphore = m_device->createSemaphore(0); + if (!m_scratchSemaphore) + return logFail("Could not create Scratch Semaphore"); + m_scratchSemaphore->setObjectDebugName("Scratch Semaphore"); + // we don't want to overcomplicate the example with multi-queue + m_intendedSubmit.queue = getGraphicsQueue(); + // wait for nothing before upload + m_intendedSubmit.waitSemaphores = {}; + m_intendedSubmit.waitSemaphores = {}; + // fill later + m_intendedSubmit.scratchCommandBuffers = {}; + m_intendedSubmit.scratchSemaphore = { + .semaphore = m_scratchSemaphore.get(), + .value = 0, + .stageMask = PIPELINE_STAGE_FLAGS::ALL_TRANSFER_BITS + }; + } + + // Create command pool and buffers + { + auto gQueue = getGraphicsQueue(); + m_cmdPool = m_device->createCommandPool(gQueue->getFamilyIndex(), IGPUCommandPool::CREATE_FLAGS::RESET_COMMAND_BUFFER_BIT); + if (!m_cmdPool) + return logFail("Couldn't create Command Pool!"); + + if (!m_cmdPool->createCommandBuffers(IGPUCommandPool::BUFFER_LEVEL::PRIMARY, { m_cmdBufs.data(), MaxFramesInFlight })) + return logFail("Couldn't create Command Buffer!"); + } + + ISampler::SParams samplerParams = { + .AnisotropicFilter = 0 + }; + auto defaultSampler = m_device->createSampler(samplerParams); + + // Create descriptors and pipeline for the pathtracer + { + auto convertDSLayoutCPU2GPU = [&](smart_refctd_ptr cpuLayout) { + auto converter = CAssetConverter::create({ .device = m_device.get() }); + CAssetConverter::SInputs inputs = {}; + inputs.readCache = converter.get(); + inputs.logger = m_logger.get(); + CAssetConverter::SConvertParams params = {}; + params.utilities = m_utils.get(); + + std::get>(inputs.assets) = { &cpuLayout.get(),1 }; + // don't need to assert that we don't need to provide patches since layouts are not patchable + //assert(true); + auto reservation = converter->reserve(inputs); + // the `.value` is just a funny way to make the `smart_refctd_ptr` copyable + auto gpuLayout = reservation.getGPUObjects().front().value; + if (!gpuLayout) { + m_logger->log("Failed to convert %s into an IGPUDescriptorSetLayout handle", ILogger::ELL_ERROR); + std::exit(-1); + } + + return gpuLayout; + }; + auto convertDSCPU2GPU = [&](smart_refctd_ptr cpuDS) { + auto converter = CAssetConverter::create({ .device = m_device.get() }); + CAssetConverter::SInputs inputs = {}; + inputs.readCache = converter.get(); + inputs.logger = m_logger.get(); + CAssetConverter::SConvertParams params = {}; + params.utilities = m_utils.get(); + + std::get>(inputs.assets) = { &cpuDS.get(), 1 }; + // don't need to assert that we don't need to provide patches since layouts are not patchable + //assert(true); + auto reservation = converter->reserve(inputs); + // the `.value` is just a funny way to make the `smart_refctd_ptr` copyable + auto gpuDS = reservation.getGPUObjects().front().value; + if (!gpuDS) { + m_logger->log("Failed to convert %s into an IGPUDescriptorSet handle", ILogger::ELL_ERROR); + std::exit(-1); + } + + return gpuDS; + }; + + std::array descriptorSet0Bindings = {}; + std::array descriptorSet3Bindings = {}; + std::array presentDescriptorSetBindings; + + descriptorSet0Bindings[0] = { + .binding = 0u, + .type = nbl::asset::IDescriptor::E_TYPE::ET_STORAGE_IMAGE, + .createFlags = ICPUDescriptorSetLayout::SBinding::E_CREATE_FLAGS::ECF_NONE, + .stageFlags = IShader::E_SHADER_STAGE::ESS_COMPUTE, + .count = 1u, + .immutableSamplers = nullptr + }; + descriptorSet3Bindings[0] = { + .binding = 0u, + .type = nbl::asset::IDescriptor::E_TYPE::ET_COMBINED_IMAGE_SAMPLER, + .createFlags = ICPUDescriptorSetLayout::SBinding::E_CREATE_FLAGS::ECF_NONE, + .stageFlags = IShader::E_SHADER_STAGE::ESS_COMPUTE, + .count = 1u, + .immutableSamplers = nullptr + }; + descriptorSet3Bindings[1] = { + .binding = 1u, + .type = nbl::asset::IDescriptor::E_TYPE::ET_UNIFORM_TEXEL_BUFFER, + .createFlags = ICPUDescriptorSetLayout::SBinding::E_CREATE_FLAGS::ECF_NONE, + .stageFlags = IShader::E_SHADER_STAGE::ESS_COMPUTE, + .count = 1u, + .immutableSamplers = nullptr + }; + descriptorSet3Bindings[2] = { + .binding = 2u, + .type = nbl::asset::IDescriptor::E_TYPE::ET_COMBINED_IMAGE_SAMPLER, + .createFlags = ICPUDescriptorSetLayout::SBinding::E_CREATE_FLAGS::ECF_NONE, + .stageFlags = IShader::E_SHADER_STAGE::ESS_COMPUTE, + .count = 1u, + .immutableSamplers = nullptr + }; + presentDescriptorSetBindings[0] = { + .binding = 0u, + .type = nbl::asset::IDescriptor::E_TYPE::ET_COMBINED_IMAGE_SAMPLER, + .createFlags = ICPUDescriptorSetLayout::SBinding::E_CREATE_FLAGS::ECF_NONE, + .stageFlags = IShader::E_SHADER_STAGE::ESS_FRAGMENT, + .count = 1u, + .immutableSamplers = &defaultSampler + }; + + auto cpuDescriptorSetLayout0 = make_smart_refctd_ptr(descriptorSet0Bindings); + auto cpuDescriptorSetLayout2 = make_smart_refctd_ptr(descriptorSet3Bindings); + + auto gpuDescriptorSetLayout0 = convertDSLayoutCPU2GPU(cpuDescriptorSetLayout0); + auto gpuDescriptorSetLayout2 = convertDSLayoutCPU2GPU(cpuDescriptorSetLayout2); + auto gpuPresentDescriptorSetLayout = m_device->createDescriptorSetLayout(presentDescriptorSetBindings); + + auto cpuDescriptorSet0 = make_smart_refctd_ptr(std::move(cpuDescriptorSetLayout0)); + auto cpuDescriptorSet2 = make_smart_refctd_ptr(std::move(cpuDescriptorSetLayout2)); + + m_descriptorSet0 = convertDSCPU2GPU(cpuDescriptorSet0); + m_descriptorSet2 = convertDSCPU2GPU(cpuDescriptorSet2); + + smart_refctd_ptr presentDSPool; + { + const video::IGPUDescriptorSetLayout* const layouts[] = { gpuPresentDescriptorSetLayout.get() }; + const uint32_t setCounts[] = { 1u }; + presentDSPool = m_device->createDescriptorPoolForDSLayouts(IDescriptorPool::E_CREATE_FLAGS::ECF_NONE, layouts, setCounts); + } + m_presentDescriptorSet = presentDSPool->createDescriptorSet(gpuPresentDescriptorSetLayout); + + // Create Shaders + auto loadAndCompileShader = [&](std::string pathToShader) + { + IAssetLoader::SAssetLoadParams lp = {}; + lp.workingDirectory = localInputCWD; + auto assetBundle = m_assetMgr->getAsset(pathToShader, lp); + const auto assets = assetBundle.getContents(); + if (assets.empty()) + { + m_logger->log("Could not load shader: ", ILogger::ELL_ERROR, pathToShader); + std::exit(-1); + } + + auto source = IAsset::castDown(assets[0]); + // The down-cast should not fail! + assert(source); + + // this time we skip the use of the asset converter since the ICPUShader->IGPUShader path is quick and simple + auto shader = m_device->createShader(source.get()); + if (!shader) + { + m_logger->log("Shader creationed failed: %s!", ILogger::ELL_ERROR, pathToShader); + std::exit(-1); + } + + return shader; + }; + + // Create compute pipelines + { + for (int index = 0; index < E_LIGHT_GEOMETRY::ELG_COUNT; index++) { + auto ptShader = loadAndCompileShader(PTShaderPaths[index]); + const nbl::asset::SPushConstantRange pcRange = { + .stageFlags = IShader::E_SHADER_STAGE::ESS_COMPUTE, + .offset = 0, + .size = sizeof(PTPushConstant) + }; + auto ptPipelineLayout = m_device->createPipelineLayout( + { &pcRange, 1 }, + core::smart_refctd_ptr(gpuDescriptorSetLayout0), + nullptr, + core::smart_refctd_ptr(gpuDescriptorSetLayout2), + nullptr + ); + if (!ptPipelineLayout) { + return logFail("Failed to create Pathtracing pipeline layout"); + } + + IGPUComputePipeline::SCreationParams params = {}; + params.layout = ptPipelineLayout.get(); + params.shader.shader = ptShader.get(); + params.shader.entryPoint = "main"; + params.shader.entries = nullptr; + params.shader.requireFullSubgroups = true; + params.shader.requiredSubgroupSize = static_cast(5); + if (!m_device->createComputePipelines(nullptr, { ¶ms, 1 }, m_PTPipelines.data() + index)) { + return logFail("Failed to create compute pipeline!\n"); + } + } + } + + // Create graphics pipeline + { + auto scRes = static_cast(m_surface->getSwapchainResources()); + ext::FullScreenTriangle::ProtoPipeline fsTriProtoPPln(m_assetMgr.get(), m_device.get(), m_logger.get()); + if (!fsTriProtoPPln) + return logFail("Failed to create Full Screen Triangle protopipeline or load its vertex shader!"); + + // Load Fragment Shader + auto fragmentShader = loadAndCompileShader(PresentShaderPath); + if (!fragmentShader) + return logFail("Failed to Load and Compile Fragment Shader: lumaMeterShader!"); + + const IGPUShader::SSpecInfo fragSpec = { + .entryPoint = "main", + .shader = fragmentShader.get() + }; + + auto presentLayout = m_device->createPipelineLayout( + {}, + core::smart_refctd_ptr(gpuPresentDescriptorSetLayout), + nullptr, + nullptr, + nullptr + ); + m_presentPipeline = fsTriProtoPPln.createPipeline(fragSpec, presentLayout.get(), scRes->getRenderpass()); + if (!m_presentPipeline) + return logFail("Could not create Graphics Pipeline!"); + + } + } + + // load CPUImages and convert to GPUImages + smart_refctd_ptr envMap, scrambleMap; + { + auto convertImgCPU2GPU = [&](std::span cpuImgs) { + auto queue = getGraphicsQueue(); + auto cmdbuf = m_cmdBufs[0].get(); + cmdbuf->reset(IGPUCommandBuffer::RESET_FLAGS::NONE); + std::array commandBufferInfo = { cmdbuf }; + core::smart_refctd_ptr imgFillSemaphore = m_device->createSemaphore(0); + imgFillSemaphore->setObjectDebugName("Image Fill Semaphore"); + + auto converter = CAssetConverter::create({ .device = m_device.get() }); + // We don't want to generate mip-maps for these images, to ensure that we must override the default callbacks. + struct SInputs final : CAssetConverter::SInputs + { + // we also need to override this to have concurrent sharing + inline std::span getSharedOwnershipQueueFamilies(const size_t groupCopyID, const asset::ICPUImage* buffer, const CAssetConverter::patch_t& patch) const override + { + if (familyIndices.size() > 1) + return familyIndices; + return {}; + } + + inline uint8_t getMipLevelCount(const size_t groupCopyID, const ICPUImage* image, const CAssetConverter::patch_t& patch) const override + { + return image->getCreationParameters().mipLevels; + } + inline uint16_t needToRecomputeMips(const size_t groupCopyID, const ICPUImage* image, const CAssetConverter::patch_t& patch) const override + { + return 0b0u; + } + + std::vector familyIndices; + } inputs = {}; + inputs.readCache = converter.get(); + inputs.logger = m_logger.get(); + { + const core::set uniqueFamilyIndices = { queue->getFamilyIndex(), queue->getFamilyIndex() }; + inputs.familyIndices = { uniqueFamilyIndices.begin(),uniqueFamilyIndices.end() }; + } + // scratch command buffers for asset converter transfer commands + SIntendedSubmitInfo transfer = { + .queue = queue, + .waitSemaphores = {}, + .prevCommandBuffers = {}, + .scratchCommandBuffers = commandBufferInfo, + .scratchSemaphore = { + .semaphore = imgFillSemaphore.get(), + .value = 0, + // because of layout transitions + .stageMask = PIPELINE_STAGE_FLAGS::ALL_COMMANDS_BITS + } + }; + // as per the `SIntendedSubmitInfo` one commandbuffer must be begun + cmdbuf->begin(IGPUCommandBuffer::USAGE::ONE_TIME_SUBMIT_BIT); + // Normally we'd have to inherit and override the `getFinalOwnerQueueFamily` callback to ensure that the + // compute queue becomes the owner of the buffers and images post-transfer, but in this example we use concurrent sharing + CAssetConverter::SConvertParams params = {}; + params.transfer = &transfer; + params.utilities = m_utils.get(); + + std::get>(inputs.assets) = cpuImgs; + // assert that we don't need to provide patches + assert(cpuImgs[0]->getImageUsageFlags().hasFlags(ICPUImage::E_USAGE_FLAGS::EUF_SAMPLED_BIT)); + auto reservation = converter->reserve(inputs); + // the `.value` is just a funny way to make the `smart_refctd_ptr` copyable + auto gpuImgs = reservation.getGPUObjects(); + for (auto& gpuImg : gpuImgs) { + if (!gpuImg) { + m_logger->log("Failed to convert %s into an IGPUImage handle", ILogger::ELL_ERROR, DefaultImagePathsFile); + std::exit(-1); + } + } + + // and launch the conversions + m_api->startCapture(); + auto result = reservation.convert(params); + m_api->endCapture(); + if (!result.blocking() && result.copy() != IQueue::RESULT::SUCCESS) { + m_logger->log("Failed to record or submit conversions", ILogger::ELL_ERROR); + std::exit(-1); + } + + envMap = gpuImgs[0].value; + scrambleMap = gpuImgs[1].value; + }; + + smart_refctd_ptr envMapCPU, scrambleMapCPU; + { + IAssetLoader::SAssetLoadParams lp; + lp.workingDirectory = this->sharedInputCWD; + SAssetBundle bundle = m_assetMgr->getAsset(DefaultImagePathsFile, lp); + if (bundle.getContents().empty()) { + m_logger->log("Couldn't load an asset.", ILogger::ELL_ERROR); + std::exit(-1); + } + + envMapCPU = IAsset::castDown(bundle.getContents()[0]); + if (!envMapCPU) { + m_logger->log("Couldn't load an asset.", ILogger::ELL_ERROR); + std::exit(-1); + } + }; + { + asset::ICPUImage::SCreationParams info; + info.format = asset::E_FORMAT::EF_R32G32_UINT; + info.type = asset::ICPUImage::ET_2D; + auto extent = envMapCPU->getCreationParameters().extent; + info.extent.width = extent.width; + info.extent.height = extent.height; + info.extent.depth = 1u; + info.mipLevels = 1u; + info.arrayLayers = 1u; + info.samples = asset::ICPUImage::E_SAMPLE_COUNT_FLAGS::ESCF_1_BIT; + info.flags = static_cast(0u); + info.usage = asset::IImage::EUF_TRANSFER_SRC_BIT | asset::IImage::EUF_SAMPLED_BIT; + + scrambleMapCPU = ICPUImage::create(std::move(info)); + const uint32_t texelFormatByteSize = getTexelOrBlockBytesize(scrambleMapCPU->getCreationParameters().format); + const uint32_t texelBufferSize = scrambleMapCPU->getImageDataSizeInBytes(); + auto texelBuffer = ICPUBuffer::create({ texelBufferSize }); + + core::RandomSampler rng(0xbadc0ffeu); + auto out = reinterpret_cast(texelBuffer->getPointer()); + for (auto index = 0u; index < texelBufferSize / 4; index++) { + out[index] = rng.nextSample(); + } + + auto regions = core::make_refctd_dynamic_array>(1u); + ICPUImage::SBufferCopy& region = regions->front(); + region.imageSubresource.aspectMask = IImage::E_ASPECT_FLAGS::EAF_COLOR_BIT; + region.imageSubresource.mipLevel = 0u; + region.imageSubresource.baseArrayLayer = 0u; + region.imageSubresource.layerCount = 1u; + region.bufferOffset = 0u; + region.bufferRowLength = IImageAssetHandlerBase::calcPitchInBlocks(extent.width, texelFormatByteSize); + region.bufferImageHeight = 0u; + region.imageOffset = { 0u, 0u, 0u }; + region.imageExtent = scrambleMapCPU->getCreationParameters().extent; + + scrambleMapCPU->setBufferAndRegions(std::move(texelBuffer), regions); + } + + std::array cpuImgs = { envMapCPU.get(), scrambleMapCPU.get()}; + convertImgCPU2GPU(cpuImgs); + } + + // create views for textures + { + auto createHDRIImage = [this](const asset::E_FORMAT colorFormat, const uint32_t width, const uint32_t height) -> smart_refctd_ptr { + IGPUImage::SCreationParams imgInfo; + imgInfo.format = colorFormat; + imgInfo.type = IGPUImage::ET_2D; + imgInfo.extent.width = width; + imgInfo.extent.height = height; + imgInfo.extent.depth = 1u; + imgInfo.mipLevels = 1u; + imgInfo.arrayLayers = 1u; + imgInfo.samples = IGPUImage::ESCF_1_BIT; + imgInfo.flags = static_cast(0u); + imgInfo.usage = asset::IImage::EUF_STORAGE_BIT | asset::IImage::EUF_TRANSFER_DST_BIT | asset::IImage::EUF_SAMPLED_BIT; + + auto image = m_device->createImage(std::move(imgInfo)); + auto imageMemReqs = image->getMemoryReqs(); + imageMemReqs.memoryTypeBits &= m_device->getPhysicalDevice()->getDeviceLocalMemoryTypeBits(); + m_device->allocate(imageMemReqs, image.get()); + + return image; + }; + auto createHDRIImageView = [this](smart_refctd_ptr img) -> smart_refctd_ptr + { + auto format = img->getCreationParameters().format; + IGPUImageView::SCreationParams imgViewInfo; + imgViewInfo.image = std::move(img); + imgViewInfo.format = format; + imgViewInfo.viewType = IGPUImageView::ET_2D; + imgViewInfo.flags = static_cast(0u); + imgViewInfo.subresourceRange.aspectMask = IImage::E_ASPECT_FLAGS::EAF_COLOR_BIT; + imgViewInfo.subresourceRange.baseArrayLayer = 0u; + imgViewInfo.subresourceRange.baseMipLevel = 0u; + imgViewInfo.subresourceRange.layerCount = 1u; + imgViewInfo.subresourceRange.levelCount = 1u; + + return m_device->createImageView(std::move(imgViewInfo)); + }; + + auto params = envMap->getCreationParameters(); + auto extent = params.extent; + envMap->setObjectDebugName("Env Map"); + m_envMapView = createHDRIImageView(envMap); + m_envMapView->setObjectDebugName("Env Map View"); + scrambleMap->setObjectDebugName("Scramble Map"); + m_scrambleView = createHDRIImageView(scrambleMap); + m_scrambleView->setObjectDebugName("Scramble Map View"); + auto outImg = createHDRIImage(asset::E_FORMAT::EF_R16G16B16A16_SFLOAT, WindowDimensions.x, WindowDimensions.y); + outImg->setObjectDebugName("Output Image"); + m_outImgView = createHDRIImageView(outImg); + m_outImgView->setObjectDebugName("Output Image View"); + } + + // create sequence buffer view + { + // TODO: do this better use asset manager to get the ICPUBuffer from `.bin` + auto createBufferFromCacheFile = [this]( + system::path filename, + size_t bufferSize, + void *data, + smart_refctd_ptr& buffer + ) -> std::pair, bool> + { + ISystem::future_t> owenSamplerFileFuture; + ISystem::future_t owenSamplerFileReadFuture; + size_t owenSamplerFileBytesRead; + + m_system->createFile(owenSamplerFileFuture, localOutputCWD / filename, IFile::ECF_READ); + smart_refctd_ptr owenSamplerFile; + + if (owenSamplerFileFuture.wait()) + { + owenSamplerFileFuture.acquire().move_into(owenSamplerFile); + if (!owenSamplerFile) + return { nullptr, false }; + + owenSamplerFile->read(owenSamplerFileReadFuture, data, 0, bufferSize); + if (owenSamplerFileReadFuture.wait()) + { + owenSamplerFileReadFuture.acquire().move_into(owenSamplerFileBytesRead); + + if (owenSamplerFileBytesRead < bufferSize) + { + buffer = asset::ICPUBuffer::create({ sizeof(uint32_t) * bufferSize }); + return { owenSamplerFile, false }; + } + + buffer = asset::ICPUBuffer::create({ { sizeof(uint32_t) * bufferSize }, data }); + } + } + + return { owenSamplerFile, true }; + }; + auto writeBufferIntoCacheFile = [this](smart_refctd_ptr file, size_t bufferSize, void* data) + { + ISystem::future_t owenSamplerFileWriteFuture; + size_t owenSamplerFileBytesWritten; + + file->write(owenSamplerFileWriteFuture, data, 0, bufferSize); + if (owenSamplerFileWriteFuture.wait()) + owenSamplerFileWriteFuture.acquire().move_into(owenSamplerFileBytesWritten); + }; + + constexpr size_t bufferSize = MaxBufferDimensions * MaxBufferSamples; + std::array data = {}; + smart_refctd_ptr sampleSeq; + + auto cacheBufferResult = createBufferFromCacheFile(sharedOutputCWD/OwenSamplerFilePath, bufferSize, data.data(), sampleSeq); + if (!cacheBufferResult.second) + { + core::OwenSampler sampler(MaxBufferDimensions, 0xdeadbeefu); + + ICPUBuffer::SCreationParams params = {}; + params.size = MaxBufferDimensions*MaxBufferSamples*sizeof(uint32_t); + sampleSeq = ICPUBuffer::create(std::move(params)); + + auto out = reinterpret_cast(sampleSeq->getPointer()); + for (auto dim = 0u; dim < MaxBufferDimensions; dim++) + for (uint32_t i = 0; i < MaxBufferSamples; i++) + { + out[i * MaxBufferDimensions + dim] = sampler.sample(dim, i); + } + if (cacheBufferResult.first) + writeBufferIntoCacheFile(cacheBufferResult.first, bufferSize, out); + } + + IGPUBuffer::SCreationParams params = {}; + params.usage = asset::IBuffer::EUF_TRANSFER_DST_BIT | asset::IBuffer::EUF_UNIFORM_TEXEL_BUFFER_BIT; + params.size = sampleSeq->getSize(); + + // we don't want to overcomplicate the example with multi-queue + auto queue = getGraphicsQueue(); + auto cmdbuf = m_cmdBufs[0].get(); + cmdbuf->reset(IGPUCommandBuffer::RESET_FLAGS::NONE); + IQueue::SSubmitInfo::SCommandBufferInfo cmdbufInfo = { cmdbuf }; + m_intendedSubmit.scratchCommandBuffers = { &cmdbufInfo, 1 }; + + cmdbuf->begin(IGPUCommandBuffer::USAGE::ONE_TIME_SUBMIT_BIT); + m_api->startCapture(); + auto bufferFuture = m_utils->createFilledDeviceLocalBufferOnDedMem( + m_intendedSubmit, + std::move(params), + sampleSeq->getPointer() + ); + m_api->endCapture(); + bufferFuture.wait(); + auto buffer = bufferFuture.get(); + + m_sequenceBufferView = m_device->createBufferView({ 0u, buffer->get()->getSize(), *buffer }, asset::E_FORMAT::EF_R32G32B32_UINT); + m_sequenceBufferView->setObjectDebugName("Sequence Buffer"); + } + + // Update Descriptors + { + ISampler::SParams samplerParams0 = { + ISampler::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE, + ISampler::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE, + ISampler::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE, + ISampler::ETBC_FLOAT_OPAQUE_BLACK, + ISampler::ETF_LINEAR, + ISampler::ETF_LINEAR, + ISampler::ESMM_LINEAR, + 0u, + false, + ECO_ALWAYS + }; + auto sampler0 = m_device->createSampler(samplerParams0); + ISampler::SParams samplerParams1 = { + ISampler::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE, + ISampler::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE, + ISampler::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE, + ISampler::ETBC_INT_OPAQUE_BLACK, + ISampler::ETF_NEAREST, + ISampler::ETF_NEAREST, + ISampler::ESMM_NEAREST, + 0u, + false, + ECO_ALWAYS + }; + auto sampler1 = m_device->createSampler(samplerParams1); + + std::array writeDSInfos = {}; + writeDSInfos[0].desc = m_outImgView; + writeDSInfos[0].info.image.imageLayout = IImage::LAYOUT::GENERAL; + writeDSInfos[1].desc = m_envMapView; + // ISampler::SParams samplerParams = { ISampler::ETC_CLAMP_TO_EDGE, ISampler::ETC_CLAMP_TO_EDGE, ISampler::ETC_CLAMP_TO_EDGE, ISampler::ETBC_FLOAT_OPAQUE_BLACK, ISampler::ETF_LINEAR, ISampler::ETF_LINEAR, ISampler::ESMM_LINEAR, 0u, false, ECO_ALWAYS }; + writeDSInfos[1].info.combinedImageSampler.sampler = sampler0; + writeDSInfos[1].info.combinedImageSampler.imageLayout = asset::IImage::LAYOUT::READ_ONLY_OPTIMAL; + writeDSInfos[2].desc = m_sequenceBufferView; + writeDSInfos[3].desc = m_scrambleView; + // ISampler::SParams samplerParams = { ISampler::ETC_CLAMP_TO_EDGE, ISampler::ETC_CLAMP_TO_EDGE, ISampler::ETC_CLAMP_TO_EDGE, ISampler::ETBC_INT_OPAQUE_BLACK, ISampler::ETF_NEAREST, ISampler::ETF_NEAREST, ISampler::ESMM_NEAREST, 0u, false, ECO_ALWAYS }; + writeDSInfos[3].info.combinedImageSampler.sampler = sampler1; + writeDSInfos[3].info.combinedImageSampler.imageLayout = asset::IImage::LAYOUT::READ_ONLY_OPTIMAL; + writeDSInfos[4].desc = m_outImgView; + writeDSInfos[4].info.image.imageLayout = IImage::LAYOUT::READ_ONLY_OPTIMAL; + + std::array writeDescriptorSets = {}; + writeDescriptorSets[0] = { + .dstSet = m_descriptorSet0.get(), + .binding = 0, + .arrayElement = 0u, + .count = 1u, + .info = &writeDSInfos[0] + }; + writeDescriptorSets[1] = { + .dstSet = m_descriptorSet2.get(), + .binding = 0, + .arrayElement = 0u, + .count = 1u, + .info = &writeDSInfos[1] + }; + writeDescriptorSets[2] = { + .dstSet = m_descriptorSet2.get(), + .binding = 1, + .arrayElement = 0u, + .count = 1u, + .info = &writeDSInfos[2] + }; + writeDescriptorSets[3] = { + .dstSet = m_descriptorSet2.get(), + .binding = 2, + .arrayElement = 0u, + .count = 1u, + .info = &writeDSInfos[3] + }; + writeDescriptorSets[4] = { + .dstSet = m_presentDescriptorSet.get(), + .binding = 0, + .arrayElement = 0u, + .count = 1u, + .info = &writeDSInfos[4] + }; + + m_device->updateDescriptorSets(writeDescriptorSets, {}); + } + + // Create ui descriptors + { + using binding_flags_t = IGPUDescriptorSetLayout::SBinding::E_CREATE_FLAGS; + { + IGPUSampler::SParams params; + params.AnisotropicFilter = 1u; + params.TextureWrapU = ISampler::E_TEXTURE_CLAMP::ETC_REPEAT; + params.TextureWrapV = ISampler::E_TEXTURE_CLAMP::ETC_REPEAT; + params.TextureWrapW = ISampler::E_TEXTURE_CLAMP::ETC_REPEAT; + + m_ui.samplers.gui = m_device->createSampler(params); + m_ui.samplers.gui->setObjectDebugName("Nabla IMGUI UI Sampler"); + } + + std::array, 69u> immutableSamplers; + for (auto& it : immutableSamplers) + it = smart_refctd_ptr(m_ui.samplers.scene); + + immutableSamplers[nbl::ext::imgui::UI::FontAtlasTexId] = smart_refctd_ptr(m_ui.samplers.gui); + + nbl::ext::imgui::UI::SCreationParameters params; + + params.resources.texturesInfo = { .setIx = 0u, .bindingIx = 0u }; + params.resources.samplersInfo = { .setIx = 0u, .bindingIx = 1u }; + params.assetManager = m_assetMgr; + params.pipelineCache = nullptr; + params.pipelineLayout = nbl::ext::imgui::UI::createDefaultPipelineLayout(m_utils->getLogicalDevice(), params.resources.texturesInfo, params.resources.samplersInfo, MaxUITextureCount); + params.renderpass = smart_refctd_ptr(renderpass); + params.streamingBuffer = nullptr; + params.subpassIx = 0u; + params.transfer = getTransferUpQueue(); + params.utilities = m_utils; + { + m_ui.manager = ext::imgui::UI::create(std::move(params)); + + // note that we use default layout provided by our extension, but you are free to create your own by filling nbl::ext::imgui::UI::S_CREATION_PARAMETERS::resources + const auto* descriptorSetLayout = m_ui.manager->getPipeline()->getLayout()->getDescriptorSetLayout(0u); + const auto& params = m_ui.manager->getCreationParameters(); + + IDescriptorPool::SCreateInfo descriptorPoolInfo = {}; + descriptorPoolInfo.maxDescriptorCount[static_cast(asset::IDescriptor::E_TYPE::ET_SAMPLER)] = (uint32_t)nbl::ext::imgui::UI::DefaultSamplerIx::COUNT; + descriptorPoolInfo.maxDescriptorCount[static_cast(asset::IDescriptor::E_TYPE::ET_SAMPLED_IMAGE)] = MaxUITextureCount; + descriptorPoolInfo.maxSets = 1u; + descriptorPoolInfo.flags = IDescriptorPool::E_CREATE_FLAGS::ECF_UPDATE_AFTER_BIND_BIT; + + m_guiDescriptorSetPool = m_device->createDescriptorPool(std::move(descriptorPoolInfo)); + assert(m_guiDescriptorSetPool); + + m_guiDescriptorSetPool->createDescriptorSets(1u, &descriptorSetLayout, &m_ui.descriptorSet); + assert(m_ui.descriptorSet); + } + } + m_ui.manager->registerListener( + [this]() -> void { + ImGuiIO& io = ImGui::GetIO(); + + m_camera.setProjectionMatrix([&]() + { + static matrix4SIMD projection; + + projection = matrix4SIMD::buildProjectionMatrixPerspectiveFovRH(core::radians(fov), io.DisplaySize.x / io.DisplaySize.y, zNear, zFar); + + return projection; + }()); + + ImGui::SetNextWindowPos(ImVec2(1024, 100), ImGuiCond_Appearing); + ImGui::SetNextWindowSize(ImVec2(256, 256), ImGuiCond_Appearing); + + // create a window and insert the inspector + ImGui::SetNextWindowPos(ImVec2(10, 10), ImGuiCond_Appearing); + ImGui::SetNextWindowSize(ImVec2(320, 340), ImGuiCond_Appearing); + ImGui::Begin("Controls"); + + ImGui::SameLine(); + + ImGui::Text("Camera"); + + ImGui::SliderFloat("Move speed", &moveSpeed, 0.1f, 10.f); + ImGui::SliderFloat("Rotate speed", &rotateSpeed, 0.1f, 10.f); + ImGui::SliderFloat("Fov", &fov, 20.f, 150.f); + ImGui::SliderFloat("zNear", &zNear, 0.1f, 100.f); + ImGui::SliderFloat("zFar", &zFar, 110.f, 10000.f); + ImGui::ListBox("Shader", &PTPipline, shaderNames, E_LIGHT_GEOMETRY::ELG_COUNT); + ImGui::SliderInt("SPP", &spp, 1, MaxBufferSamples); + ImGui::SliderInt("Depth", &depth, 1, MaxBufferDimensions / 3); + + ImGui::Text("X: %f Y: %f", io.MousePos.x, io.MousePos.y); + + ImGui::End(); + } + ); + + // Set Camera + { + core::vectorSIMDf cameraPosition(0, 5, -10); + matrix4SIMD proj = matrix4SIMD::buildProjectionMatrixPerspectiveFovRH( + core::radians(60.0f), + WindowDimensions.x / WindowDimensions.y, + 0.01f, + 500.0f + ); + m_camera = Camera(cameraPosition, core::vectorSIMDf(0, 0, 0), proj); + } + + m_winMgr->setWindowSize(m_window.get(), WindowDimensions.x, WindowDimensions.y); + m_surface->recreateSwapchain(); + m_winMgr->show(m_window.get()); + m_oracle.reportBeginFrameRecord(); + m_camera.mapKeysToWASD(); + + return true; + } + + bool updateGUIDescriptorSet() + { + // texture atlas, note we don't create info & write pair for the font sampler because UI extension's is immutable and baked into DS layout + static std::array descriptorInfo; + static IGPUDescriptorSet::SWriteDescriptorSet writes[MaxUITextureCount]; + + descriptorInfo[nbl::ext::imgui::UI::FontAtlasTexId].info.image.imageLayout = IImage::LAYOUT::READ_ONLY_OPTIMAL; + descriptorInfo[nbl::ext::imgui::UI::FontAtlasTexId].desc = smart_refctd_ptr(m_ui.manager->getFontAtlasView()); + + for (uint32_t i = 0; i < descriptorInfo.size(); ++i) + { + writes[i].dstSet = m_ui.descriptorSet.get(); + writes[i].binding = 0u; + writes[i].arrayElement = i; + writes[i].count = 1u; + } + writes[nbl::ext::imgui::UI::FontAtlasTexId].info = descriptorInfo.data() + nbl::ext::imgui::UI::FontAtlasTexId; + + return m_device->updateDescriptorSets(writes, {}); + } + + inline void workLoopBody() override + { + // framesInFlight: ensuring safe execution of command buffers and acquires, `framesInFlight` only affect semaphore waits, don't use this to index your resources because it can change with swapchain recreation. + const uint32_t framesInFlight = core::min(MaxFramesInFlight, m_surface->getMaxAcquiresInFlight()); + // We block for semaphores for 2 reasons here: + // A) Resource: Can't use resource like a command buffer BEFORE previous use is finished! [MaxFramesInFlight] + // B) Acquire: Can't have more acquires in flight than a certain threshold returned by swapchain or your surface helper class. [MaxAcquiresInFlight] + if (m_realFrameIx >= framesInFlight) + { + const ISemaphore::SWaitInfo cbDonePending[] = + { + { + .semaphore = m_semaphore.get(), + .value = m_realFrameIx + 1 - framesInFlight + } + }; + if (m_device->blockForSemaphores(cbDonePending) != ISemaphore::WAIT_RESULT::SUCCESS) + return; + } + const auto resourceIx = m_realFrameIx % MaxFramesInFlight; + + m_api->startCapture(); + + // CPU events + update(); + + auto queue = getGraphicsQueue(); + auto cmdbuf = m_cmdBufs[resourceIx].get(); + + if (!keepRunning()) + return; + + // render whole scene to offline frame buffer & submit + { + cmdbuf->reset(IGPUCommandBuffer::RESET_FLAGS::NONE); + // disregard surface/swapchain transformation for now + const auto viewProjectionMatrix = m_camera.getConcatenatedMatrix(); + PTPushConstant pc; + viewProjectionMatrix.getInverseTransform(pc.invMVP); + pc.sampleCount = spp; + pc.depth = depth; + + // safe to proceed + // upload buffer data + cmdbuf->beginDebugMarker("ComputeShaderPathtracer IMGUI Frame"); + cmdbuf->begin(IGPUCommandBuffer::USAGE::ONE_TIME_SUBMIT_BIT); + + // TRANSITION m_outImgView to GENERAL (because of descriptorSets0 -> ComputeShader Writes into the image) + { + const IGPUCommandBuffer::SImageMemoryBarrier imgBarriers[] = { + { + .barrier = { + .dep = { + .srcStageMask = PIPELINE_STAGE_FLAGS::ALL_TRANSFER_BITS, + .srcAccessMask = ACCESS_FLAGS::TRANSFER_WRITE_BIT, + .dstStageMask = PIPELINE_STAGE_FLAGS::COMPUTE_SHADER_BIT, + .dstAccessMask = ACCESS_FLAGS::SHADER_WRITE_BITS + } + }, + .image = m_outImgView->getCreationParameters().image.get(), + .subresourceRange = { + .aspectMask = IImage::EAF_COLOR_BIT, + .baseMipLevel = 0u, + .levelCount = 1u, + .baseArrayLayer = 0u, + .layerCount = 1u + }, + .oldLayout = IImage::LAYOUT::UNDEFINED, + .newLayout = IImage::LAYOUT::GENERAL + } + }; + cmdbuf->pipelineBarrier(E_DEPENDENCY_FLAGS::EDF_NONE, { .imgBarriers = imgBarriers }); + } + + // cube envmap handle + { + auto pipeline = m_PTPipelines[PTPipline].get(); + cmdbuf->bindComputePipeline(pipeline); + cmdbuf->bindDescriptorSets(EPBP_COMPUTE, pipeline->getLayout(), 0u, 1u, &m_descriptorSet0.get()); + cmdbuf->bindDescriptorSets(EPBP_COMPUTE, pipeline->getLayout(), 2u, 1u, &m_descriptorSet2.get()); + cmdbuf->pushConstants(pipeline->getLayout(), IShader::E_SHADER_STAGE::ESS_COMPUTE, 0, sizeof(PTPushConstant), &pc); + cmdbuf->dispatch(1 + (WindowDimensions.x - 1) / DefaultWorkGroupSize, 1 + (WindowDimensions.y - 1) / DefaultWorkGroupSize, 1u); + } + + // TRANSITION m_outImgView to READ (because of descriptorSets0 -> ComputeShader Writes into the image) + { + const IGPUCommandBuffer::SImageMemoryBarrier imgBarriers[] = { + { + .barrier = { + .dep = { + .srcStageMask = PIPELINE_STAGE_FLAGS::COMPUTE_SHADER_BIT, + .srcAccessMask = ACCESS_FLAGS::SHADER_WRITE_BITS, + .dstStageMask = PIPELINE_STAGE_FLAGS::FRAGMENT_SHADER_BIT, + .dstAccessMask = ACCESS_FLAGS::SHADER_READ_BITS + } + }, + .image = m_outImgView->getCreationParameters().image.get(), + .subresourceRange = { + .aspectMask = IImage::EAF_COLOR_BIT, + .baseMipLevel = 0u, + .levelCount = 1u, + .baseArrayLayer = 0u, + .layerCount = 1u + }, + .oldLayout = IImage::LAYOUT::GENERAL, + .newLayout = IImage::LAYOUT::READ_ONLY_OPTIMAL + } + }; + cmdbuf->pipelineBarrier(E_DEPENDENCY_FLAGS::EDF_NONE, { .imgBarriers = imgBarriers }); + } + + // TODO: tone mapping and stuff + } + + asset::SViewport viewport; + { + viewport.minDepth = 1.f; + viewport.maxDepth = 0.f; + viewport.x = 0u; + viewport.y = 0u; + viewport.width = WindowDimensions.x; + viewport.height = WindowDimensions.y; + } + cmdbuf->setViewport(0u, 1u, &viewport); + + + VkRect2D defaultScisors[] = { {.offset = {(int32_t)viewport.x, (int32_t)viewport.y}, .extent = {(uint32_t)viewport.width, (uint32_t)viewport.height}} }; + cmdbuf->setScissor(defaultScisors); + + const VkRect2D currentRenderArea = + { + .offset = {0,0}, + .extent = {m_window->getWidth(),m_window->getHeight()} + }; + auto scRes = static_cast(m_surface->getSwapchainResources()); + + // Upload m_outImg to swapchain + UI + { + const IGPUCommandBuffer::SRenderpassBeginInfo info = + { + .framebuffer = scRes->getFramebuffer(m_currentImageAcquire.imageIndex), + .colorClearValues = &clearColor, + .depthStencilClearValues = nullptr, + .renderArea = currentRenderArea + }; + nbl::video::ISemaphore::SWaitInfo waitInfo = { .semaphore = m_semaphore.get(), .value = m_realFrameIx + 1u }; + + cmdbuf->beginRenderPass(info, IGPUCommandBuffer::SUBPASS_CONTENTS::INLINE); + + cmdbuf->bindGraphicsPipeline(m_presentPipeline.get()); + cmdbuf->bindDescriptorSets(EPBP_GRAPHICS, m_presentPipeline->getLayout(), 0, 1u, &m_presentDescriptorSet.get()); + ext::FullScreenTriangle::recordDrawCall(cmdbuf); + + const auto uiParams = m_ui.manager->getCreationParameters(); + auto* uiPipeline = m_ui.manager->getPipeline(); + cmdbuf->bindGraphicsPipeline(uiPipeline); + cmdbuf->bindDescriptorSets(EPBP_GRAPHICS, uiPipeline->getLayout(), uiParams.resources.texturesInfo.setIx, 1u, &m_ui.descriptorSet.get()); + m_ui.manager->render(cmdbuf, waitInfo); + + cmdbuf->endRenderPass(); + } + + cmdbuf->end(); + { + const IQueue::SSubmitInfo::SSemaphoreInfo rendered[] = + { + { + .semaphore = m_semaphore.get(), + .value = ++m_realFrameIx, + .stageMask = PIPELINE_STAGE_FLAGS::COLOR_ATTACHMENT_OUTPUT_BIT + } + }; + { + { + const IQueue::SSubmitInfo::SCommandBufferInfo commandBuffers[] = + { + {.cmdbuf = cmdbuf } + }; + + const IQueue::SSubmitInfo::SSemaphoreInfo acquired[] = + { + { + .semaphore = m_currentImageAcquire.semaphore, + .value = m_currentImageAcquire.acquireCount, + .stageMask = PIPELINE_STAGE_FLAGS::NONE + } + }; + const IQueue::SSubmitInfo infos[] = + { + { + .waitSemaphores = acquired, + .commandBuffers = commandBuffers, + .signalSemaphores = rendered + } + }; + + updateGUIDescriptorSet(); + + if (queue->submit(infos) != IQueue::RESULT::SUCCESS) + m_realFrameIx--; + } + } + + m_window->setCaption("[Nabla Engine] Computer Path Tracer"); + m_surface->present(m_currentImageAcquire.imageIndex, rendered); + } + m_api->endCapture(); + } + + inline bool keepRunning() override + { + if (m_surface->irrecoverable()) + return false; + + return true; + } + + inline bool onAppTerminated() override + { + return device_base_t::onAppTerminated(); + } + + inline void update() + { + m_camera.setMoveSpeed(moveSpeed); + m_camera.setRotateSpeed(rotateSpeed); + + static std::chrono::microseconds previousEventTimestamp{}; + + m_inputSystem->getDefaultMouse(&mouse); + m_inputSystem->getDefaultKeyboard(&keyboard); + + auto updatePresentationTimestamp = [&]() + { + m_currentImageAcquire = m_surface->acquireNextImage(); + + m_oracle.reportEndFrameRecord(); + const auto timestamp = m_oracle.getNextPresentationTimeStamp(); + m_oracle.reportBeginFrameRecord(); + + return timestamp; + }; + + const auto nextPresentationTimestamp = updatePresentationTimestamp(); + + struct + { + std::vector mouse{}; + std::vector keyboard{}; + } capturedEvents; + + m_camera.beginInputProcessing(nextPresentationTimestamp); + { + mouse.consumeEvents([&](const IMouseEventChannel::range_t& events) -> void + { + m_camera.mouseProcess(events); // don't capture the events, only let camera handle them with its impl + + for (const auto& e : events) // here capture + { + if (e.timeStamp < previousEventTimestamp) + continue; + + previousEventTimestamp = e.timeStamp; + capturedEvents.mouse.emplace_back(e); + + if (e.type == nbl::ui::SMouseEvent::EET_SCROLL) + gcIndex = std::clamp(int16_t(gcIndex) + int16_t(core::sign(e.scrollEvent.verticalScroll)), int64_t(0), int64_t(ELG_COUNT - (uint8_t)1u)); + } + }, m_logger.get()); + + keyboard.consumeEvents([&](const IKeyboardEventChannel::range_t& events) -> void + { + m_camera.keyboardProcess(events); // don't capture the events, only let camera handle them with its impl + + for (const auto& e : events) // here capture + { + if (e.timeStamp < previousEventTimestamp) + continue; + + previousEventTimestamp = e.timeStamp; + capturedEvents.keyboard.emplace_back(e); + } + }, m_logger.get()); + } + m_camera.endInputProcessing(nextPresentationTimestamp); + + const core::SRange mouseEvents(capturedEvents.mouse.data(), capturedEvents.mouse.data() + capturedEvents.mouse.size()); + const core::SRange keyboardEvents(capturedEvents.keyboard.data(), capturedEvents.keyboard.data() + capturedEvents.keyboard.size()); + const auto cursorPosition = m_window->getCursorControl()->getPosition(); + const auto mousePosition = float32_t2(cursorPosition.x, cursorPosition.y) - float32_t2(m_window->getX(), m_window->getY()); + + const ext::imgui::UI::SUpdateParameters params = + { + .mousePosition = mousePosition, + .displaySize = { m_window->getWidth(), m_window->getHeight() }, + .mouseEvents = mouseEvents, + .keyboardEvents = keyboardEvents + }; + + m_ui.manager->update(params); + } + + private: + smart_refctd_ptr m_window; + smart_refctd_ptr> m_surface; + + // gpu resources + smart_refctd_ptr m_cmdPool; + std::array, E_LIGHT_GEOMETRY::ELG_COUNT> m_PTPipelines; + smart_refctd_ptr m_presentPipeline; + uint64_t m_realFrameIx = 0; + std::array, MaxFramesInFlight> m_cmdBufs; + ISimpleManagedSurface::SAcquireResult m_currentImageAcquire = {}; + smart_refctd_ptr m_descriptorSet0, m_descriptorSet2, m_presentDescriptorSet; + + core::smart_refctd_ptr m_guiDescriptorSetPool; + + // system resources + core::smart_refctd_ptr m_inputSystem; + InputSystem::ChannelReader mouse; + InputSystem::ChannelReader keyboard; + + // pathtracer resources + smart_refctd_ptr m_envMapView, m_scrambleView; + smart_refctd_ptr m_sequenceBufferView; + smart_refctd_ptr m_outImgView; + + // sync + smart_refctd_ptr m_semaphore; + + // image upload resources + smart_refctd_ptr m_scratchSemaphore; + SIntendedSubmitInfo m_intendedSubmit; + + struct C_UI + { + nbl::core::smart_refctd_ptr manager; + + struct + { + core::smart_refctd_ptr gui, scene; + } samplers; + + core::smart_refctd_ptr descriptorSet; + } m_ui; + + Camera m_camera; + + video::CDumbPresentationOracle m_oracle; + + uint16_t gcIndex = {}; // note: this is dirty however since I assume only single object in scene I can leave it now, when this example is upgraded to support multiple objects this needs to be changed + + float fov = 60.f, zNear = 0.1f, zFar = 10000.f, moveSpeed = 1.f, rotateSpeed = 1.f; + float viewWidth = 10.f; + float camYAngle = 165.f / 180.f * 3.14159f; + float camXAngle = 32.f / 180.f * 3.14159f; + int PTPipline = E_LIGHT_GEOMETRY::ELG_SPHERE; + int spp = 32; + int depth = 3; + + bool m_firstFrame = true; + IGPUCommandBuffer::SClearColorValue clearColor = { .float32 = {0.f,0.f,0.f,1.f} }; +}; + +NBL_MAIN_FUNC(ComputeShaderPathtracer) diff --git a/31_HLSLPathTracer/pipeline.groovy b/31_HLSLPathTracer/pipeline.groovy new file mode 100644 index 000000000..955e77cec --- /dev/null +++ b/31_HLSLPathTracer/pipeline.groovy @@ -0,0 +1,50 @@ +import org.DevshGraphicsProgramming.Agent +import org.DevshGraphicsProgramming.BuilderInfo +import org.DevshGraphicsProgramming.IBuilder + +class CHLSLPathTracerBuilder extends IBuilder +{ + public CHLSLPathTracerBuilder(Agent _agent, _info) + { + super(_agent, _info) + } + + @Override + public boolean prepare(Map axisMapping) + { + return true + } + + @Override + public boolean build(Map axisMapping) + { + IBuilder.CONFIGURATION config = axisMapping.get("CONFIGURATION") + IBuilder.BUILD_TYPE buildType = axisMapping.get("BUILD_TYPE") + + def nameOfBuildDirectory = getNameOfBuildDirectory(buildType) + def nameOfConfig = getNameOfConfig(config) + + agent.execute("cmake --build ${info.rootProjectPath}/${nameOfBuildDirectory}/${info.targetProjectPathRelativeToRoot} --target ${info.targetBaseName} --config ${nameOfConfig} -j12 -v") + + return true + } + + @Override + public boolean test(Map axisMapping) + { + return true + } + + @Override + public boolean install(Map axisMapping) + { + return true + } +} + +def create(Agent _agent, _info) +{ + return new CHLSLPathTracerBuilder(_agent, _info) +} + +return this diff --git a/CMakeLists.txt b/CMakeLists.txt index 935354ed7..aa84caa6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,8 @@ if(NBL_BUILD_EXAMPLES) # Showcase compute pathtracing add_subdirectory(30_ComputeShaderPathTracer EXCLUDE_FROM_ALL) + add_subdirectory(31_HLSLPathTracer EXCLUDE_FROM_ALL) + add_subdirectory(38_EXRSplit EXCLUDE_FROM_ALL) # if (NBL_BUILD_MITSUBA_LOADER AND NBL_BUILD_OPTIX) # add_subdirectory(39_DenoiserTonemapper EXCLUDE_FROM_ALL) From 85211238891bff05b749cda5f36c8b2610210666 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 3 Feb 2025 15:51:28 +0700 Subject: [PATCH 02/53] ignore events on imgui focus --- 31_HLSLPathTracer/main.cpp | 49 ++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index 73434a852..018468e46 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -1112,7 +1112,7 @@ class ComputeShaderPathtracer final : public examples::SimpleWindowedApplication } } - m_window->setCaption("[Nabla Engine] Computer Path Tracer"); + m_window->setCaption("[Nabla Engine] HLSL Compute Path Tracer"); m_surface->present(m_currentImageAcquire.imageIndex, rendered); } m_api->endCapture(); @@ -1162,36 +1162,39 @@ class ComputeShaderPathtracer final : public examples::SimpleWindowedApplication m_camera.beginInputProcessing(nextPresentationTimestamp); { + const auto& io = ImGui::GetIO(); mouse.consumeEvents([&](const IMouseEventChannel::range_t& events) -> void - { - m_camera.mouseProcess(events); // don't capture the events, only let camera handle them with its impl - - for (const auto& e : events) // here capture { - if (e.timeStamp < previousEventTimestamp) - continue; + if (!io.WantCaptureMouse) + m_camera.mouseProcess(events); // don't capture the events, only let camera handle them with its impl - previousEventTimestamp = e.timeStamp; - capturedEvents.mouse.emplace_back(e); + for (const auto& e : events) // here capture + { + if (e.timeStamp < previousEventTimestamp) + continue; - if (e.type == nbl::ui::SMouseEvent::EET_SCROLL) - gcIndex = std::clamp(int16_t(gcIndex) + int16_t(core::sign(e.scrollEvent.verticalScroll)), int64_t(0), int64_t(ELG_COUNT - (uint8_t)1u)); - } - }, m_logger.get()); + previousEventTimestamp = e.timeStamp; + capturedEvents.mouse.emplace_back(e); + if (e.type == nbl::ui::SMouseEvent::EET_SCROLL) + gcIndex = std::clamp(int16_t(gcIndex) + int16_t(core::sign(e.scrollEvent.verticalScroll)), int64_t(0), int64_t(ELG_COUNT - (uint8_t)1u)); + } + }, m_logger.get()); + keyboard.consumeEvents([&](const IKeyboardEventChannel::range_t& events) -> void - { - m_camera.keyboardProcess(events); // don't capture the events, only let camera handle them with its impl - - for (const auto& e : events) // here capture { - if (e.timeStamp < previousEventTimestamp) - continue; + if (!io.WantCaptureKeyboard) + m_camera.keyboardProcess(events); // don't capture the events, only let camera handle them with its impl - previousEventTimestamp = e.timeStamp; - capturedEvents.keyboard.emplace_back(e); - } - }, m_logger.get()); + for (const auto& e : events) // here capture + { + if (e.timeStamp < previousEventTimestamp) + continue; + + previousEventTimestamp = e.timeStamp; + capturedEvents.keyboard.emplace_back(e); + } + }, m_logger.get()); } m_camera.endInputProcessing(nextPresentationTimestamp); From b171724bb0db3bf6f144d6eb077e95ddea806cbd Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 4 Feb 2025 14:16:06 +0700 Subject: [PATCH 03/53] initial files for pathtracer --- .../app_resources/hlsl/common.hlsl | 49 ++++++++++++ .../app_resources/hlsl/intersector.hlsl | 27 +++++++ .../app_resources/hlsl/material_system.hlsl | 20 +++++ .../hlsl/next_event_estimator.hlsl | 20 +++++ .../app_resources/hlsl/pathtracer.hlsl | 32 ++++++++ .../app_resources/hlsl/rand_gen.hlsl | 38 +++++++++ .../app_resources/hlsl/ray_gen.hlsl | 80 +++++++++++++++++++ 7 files changed, 266 insertions(+) create mode 100644 31_HLSLPathTracer/app_resources/hlsl/common.hlsl create mode 100644 31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl create mode 100644 31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl create mode 100644 31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl create mode 100644 31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl create mode 100644 31_HLSLPathTracer/app_resources/hlsl/rand_gen.hlsl create mode 100644 31_HLSLPathTracer/app_resources/hlsl/ray_gen.hlsl diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl new file mode 100644 index 000000000..694defc08 --- /dev/null +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -0,0 +1,49 @@ +#ifndef _NBL_HLSL_EXT_PATHTRACING_COMMON_INCLUDED_ +#define _NBL_HLSL_EXT_PATHTRACING_COMMON_INCLUDED_ + +namespace nbl +{ +namespace hlsl +{ +namespace ext +{ + +template +struct Payload +{ + using this_t = Payload; + using scalar_type = T; + using vector3_type = vector; + + vector3_type accumulation; + scalar_type otherTechniqueHeuristic; + vector3_type throughput; + // #ifdef KILL_DIFFUSE_SPECULAR_PATHS + // bool hasDiffuse; + // #endif +}; + +template +struct Ray +{ + using this_t = Ray; + using scalar_type = T; + using vector3_type = vector; + + // immutable + vector3_type origin; + vector3_type direction; + // TODO: polygon method == 2 stuff + + // mutable + scalar_type intersectionT; + uint32_t objectID; + + Payload payload; +}; + +} +} +} + +#endif \ No newline at end of file diff --git a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl new file mode 100644 index 000000000..5d12d6d18 --- /dev/null +++ b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl @@ -0,0 +1,27 @@ +#ifndef _NBL_HLSL_EXT_INTERSECTOR_INCLUDED_ +#define _NBL_HLSL_EXT_INTERSECTOR_INCLUDED_ + +namespace nbl +{ +namespace hlsl +{ +namespace ext +{ +namespace Intersector +{ + +// ray query method + +// ray tracing pipeline method + +struct Procedural +{ + +}; + +} +} +} +} + +#endif \ No newline at end of file diff --git a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl new file mode 100644 index 000000000..6f635ab68 --- /dev/null +++ b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl @@ -0,0 +1,20 @@ +#ifndef _NBL_HLSL_EXT_MATERIAL_SYSTEM_INCLUDED_ +#define _NBL_HLSL_EXT_MATERIAL_SYSTEM_INCLUDED_ + +namespace nbl +{ +namespace hlsl +{ +namespace ext +{ +namespace MaterialSystem +{ + + + +} +} +} +} + +#endif \ No newline at end of file diff --git a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl new file mode 100644 index 000000000..1afa8d12e --- /dev/null +++ b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl @@ -0,0 +1,20 @@ +#ifndef _NBL_HLSL_EXT_NEXT_EVENT_ESTIMATOR_INCLUDED_ +#define _NBL_HLSL_EXT_NEXT_EVENT_ESTIMATOR_INCLUDED_ + +namespace nbl +{ +namespace hlsl +{ +namespace ext +{ +namespace NextEventEstimator +{ + + + +} +} +} +} + +#endif \ No newline at end of file diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl new file mode 100644 index 000000000..9d2e8c260 --- /dev/null +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -0,0 +1,32 @@ +#ifndef _NBL_HLSL_EXT_PATHTRACER_INCLUDED_ +#define _NBL_HLSL_EXT_PATHTRACER_INCLUDED_ + +namespace nbl +{ +namespace hlsl +{ +namespace ext +{ +namespace PathTracer +{ + +template +struct Unidirectional +{ + using this_t = Unidirectional; + + static this_t create(RandGen randGen, + RayGen rayGen, + Intersector intersector, + MaterialSystem materialSystem, + /* PathGuider pathGuider, */ + NextEventEstimator nee) + {} +}; + +} +} +} +} + +#endif \ No newline at end of file diff --git a/31_HLSLPathTracer/app_resources/hlsl/rand_gen.hlsl b/31_HLSLPathTracer/app_resources/hlsl/rand_gen.hlsl new file mode 100644 index 000000000..949c2064b --- /dev/null +++ b/31_HLSLPathTracer/app_resources/hlsl/rand_gen.hlsl @@ -0,0 +1,38 @@ +#ifndef _NBL_HLSL_EXT_RANDGEN_INCLUDED_ +#define _NBL_HLSL_EXT_RANDGEN_INCLUDED_ + +namespace nbl +{ +namespace hlsl +{ +namespace ext +{ +namespace RandGen +{ + +template +struct Uniform3D +{ + using rng_type = RNG; + + static Uniform3D create(uint32_t2 seed) + { + Uniform3D retval; + retval.rng = rng_type::construct(seed); + return retval; + } + + float32_t3 operator()() + { + return float32_t3(uint32_t3(rng(), rng(), rng())); + } + + rng_type rng; +}; + +} +} +} +} + +#endif \ No newline at end of file diff --git a/31_HLSLPathTracer/app_resources/hlsl/ray_gen.hlsl b/31_HLSLPathTracer/app_resources/hlsl/ray_gen.hlsl new file mode 100644 index 000000000..467ef2bd4 --- /dev/null +++ b/31_HLSLPathTracer/app_resources/hlsl/ray_gen.hlsl @@ -0,0 +1,80 @@ +#ifndef _NBL_HLSL_EXT_RAYGEN_INCLUDED_ +#define _NBL_HLSL_EXT_RAYGEN_INCLUDED_ + +#include "common.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace ext +{ +namespace RayGen +{ + +template +struct Basic +{ + using this_t = Basic; + 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; + + static this_t create(NBL_CONST_REF_ARG(vector2_type) pixOffsetParam, NBL_CONST_REF_ARG(vector3_type) camPos, NBL_CONST_REF_ARG(vector4_type) NDC, NBL_CONST_REF_ARG(matrix4x4_type) invMVP) + { + this_t retval; + retval.pixOffsetParam = pixOffsetParam; + retval.camPos = camPos; + retval.NDC = NDC; + retval.invMVP = invMVP; + return retval; + } + + ray_type generate(NBL_CONST_REF_ARG(vector3_type) randVec) + { + ray_type ray; + ray.origin = camPos; + + vector4_type tmp = NDC; + // apply stochastic reconstruction filter + const float gaussianFilterCutoff = 2.5; + const float truncation = nbl::hlsl::exp(-0.5 * gaussianFilterCutoff * gaussianFilterCutoff); + vec2 remappedRand = randVec.xy; + remappedRand.x *= 1.0 - truncation; + remappedRand.x += truncation; + tmp.xy += pixOffsetParam * nbl::hlsl::boxMullerTransform(remappedRand, 1.5); + // for depth of field we could do another stochastic point-pick + tmp = invMVP * tmp; + ray.direction = nbl::hlsl::normalize(tmp.xyz / tmp.w - camPos); + + // #if POLYGON_METHOD==2 + // ray._immutable.normalAtOrigin = vec3(0.0,0.0,0.0); + // ray._immutable.wasBSDFAtOrigin = false; + // #endif + + ray.payload.accumulation = (vector3_type)0.0; + ray.payload.otherTechniqueHeuristic = 0.0; // needed for direct eye-light paths + ray.payload.throughput = (vector3_type)1.0; + // #ifdef KILL_DIFFUSE_SPECULAR_PATHS + // ray._payload.hasDiffuse = false; + // #endif + + return ray; + } + + vector2_type pixOffsetParam; + vector3_type camPos; + vector4_type NDC; + matrix4x4_type invMVP; +}; + +} +} +} +} + +#endif \ No newline at end of file From af35393db518deca935259cab7c414dbad44be20 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 5 Feb 2025 14:16:08 +0700 Subject: [PATCH 04/53] intersection logic --- .../app_resources/hlsl/common.hlsl | 95 +++++++++++++++++++ .../app_resources/hlsl/intersector.hlsl | 39 +++++++- .../app_resources/hlsl/pathtracer.hlsl | 9 ++ 3 files changed, 142 insertions(+), 1 deletion(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index 694defc08..56a4cace7 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -1,6 +1,10 @@ #ifndef _NBL_HLSL_EXT_PATHTRACING_COMMON_INCLUDED_ #define _NBL_HLSL_EXT_PATHTRACING_COMMON_INCLUDED_ +#include +#include +#include + namespace nbl { namespace hlsl @@ -42,6 +46,97 @@ struct Ray Payload payload; }; +enum PTIntersectionType : uint16_t +{ + PIT_NONE = 0, + PIT_SPHERE, + PIT_TRIANGLE, + PIT_RECTANGLE +}; + +// TODO: check if this works for ambiguous arrays of Intersection +// unsure if calling correct method +struct IIntersection +{ + PTIntersectionType type = PIT_NONE; +}; + +template +struct Intersection : IIntersection +{ + PTIntersectionType type = PIT_NONE; +}; + +template<> +struct Intersection : IIntersection +{ + static Intersection create(NBL_CONST_REF_ARG(float32_t3) position, float32_t radius, uint32_t bsdfID, uint32_t lightID) + { + Intersection retval; + retval.type = PIT_SPHERE; + retval.position = position; + retval.radius2 = radius * radius; + retval.bsdfLightIDs = spirv::bitFieldInsert(bsdfID, lightID, 16, 16); + return retval; + } + + // return intersection distance if found, nan otherwise + float intersect(NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(float32_t3) direction) + { + float32_t3 relOrigin = origin - position; + float relOriginLen2 = nbl::hlsl::dot(relOrigin, relOrigin); + + float dirDotRelOrigin = nbl::hlsl::dot(direction, relOrigin); + float det = radius2 - relOriginLen2 + dirDotRelOrigin * dirDotRelOrigin; + + // do some speculative math here + float detsqrt = nbl::hlsl::sqrt(det); + return -dirDotRelOrigin + (relOriginLen2 > radius2 ? (-detsqrt) : detsqrt); + } + + float32_t3 getNormal(NBL_CONST_REF_ARG(float32_t3) hitPosition) + { + const float radiusRcp = spirv::inverseSqrt(radius2); + return (hitPosition - position) * radiusRcp; + } + + float getSolidAngle(NBL_CONST_REF_ARG(float32_t3) origin) + { + float32_t3 dist = position - origin; + float cosThetaMax = nbl::hlsl::sqrt(1.0 - radius2 / nbl::hlsl::dot(dist, dist)); + return 2.0 * numbers::pi * (1.0 - cosThetaMax); + } + + // should this be in material system? + float deferredPdf(Light light, Ray ray) + { + return 1.0 / getSolidAngle(ray.origin); + } + + float generate_and_pdf() + { + // TODO + } + + float32_t3 generate_and + + float32_t3 position; + float32_t radius2; + uint32_t bsdfLightIDs; +}; + +template<> +struct Intersection : IIntersection +{ + +}; + +template<> +struct Intersection : IIntersection +{ + +}; + } } } diff --git a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl index 5d12d6d18..b2b3d0d2d 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl @@ -1,6 +1,9 @@ #ifndef _NBL_HLSL_EXT_INTERSECTOR_INCLUDED_ #define _NBL_HLSL_EXT_INTERSECTOR_INCLUDED_ +#include "common.hlsl" +#include + namespace nbl { namespace hlsl @@ -11,12 +14,46 @@ namespace Intersector { // ray query method +// ray query struct holds AS info +// pass in address to vertex/index buffers? // ray tracing pipeline method +// does everything in traceray in ex 30 +template struct Procedural { - + using scalar_type = typename Ray::scalar_type; + using ray_type = Ray; + + static int traceRay(NBL_REF_ARG(ray_type) ray, IIntersection objects[32], int objCount) + { + const bool anyHit = ray.intersectionT != numeric_limits::max; + + int objectID = -1; + for (int i = 0; i < objCount; i++) + { + float t; + if (objects[i].type == PIT_SPHERE) // we don't know what type of intersection it is so cast, there has to be a better way to do this + { + Intersection sphere = (Intersection)objects[i]; + t = sphere.intersect(ray.origin, ray.direction); + } + // TODO: other types + + bool closerIntersection = t > 0.0 && t < ray.intersectionT; + + ray.intersectionT = closerIntersection ? t : ray.intersectionT; + objectID = closerIntersection ? i : objectID; + + // allowing early out results in a performance regression, WTF!? + //if (anyHit && closerIntersection) + //break; + } + return objectID; + } + + // TODO? traceray with vertex/index buffer }; } diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index 9d2e8c260..f28dc621b 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -22,6 +22,15 @@ struct Unidirectional /* PathGuider pathGuider, */ NextEventEstimator nee) {} + + // closest hit + + // Li + MaterialSystem::measure_t getMeasure() + { + // loop through bounces, do closest hit + // return ray.payload.accumulation --> color + } }; } From 5a5fbfe55aa4cf062c562f19507ba30de085b7a6 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 6 Feb 2025 11:24:28 +0700 Subject: [PATCH 05/53] changes to intersection logic --- .../app_resources/hlsl/common.hlsl | 115 ++++++++++++++--- .../app_resources/hlsl/intersector.hlsl | 120 ++++++++++++++++-- 2 files changed, 208 insertions(+), 27 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index 56a4cace7..84933edfb 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -46,34 +46,22 @@ struct Ray Payload payload; }; -enum PTIntersectionType : uint16_t +enum ProceduralIntersectionType : uint16_t { - PIT_NONE = 0, PIT_SPHERE, PIT_TRIANGLE, PIT_RECTANGLE }; -// TODO: check if this works for ambiguous arrays of Intersection -// unsure if calling correct method -struct IIntersection -{ - PTIntersectionType type = PIT_NONE; -}; - -template -struct Intersection : IIntersection -{ - PTIntersectionType type = PIT_NONE; -}; +template +struct Intersection; template<> -struct Intersection : IIntersection +struct Intersection { static Intersection create(NBL_CONST_REF_ARG(float32_t3) position, float32_t radius, uint32_t bsdfID, uint32_t lightID) { Intersection retval; - retval.type = PIT_SPHERE; retval.position = position; retval.radius2 = radius * radius; retval.bsdfLightIDs = spirv::bitFieldInsert(bsdfID, lightID, 16, 16); @@ -118,7 +106,7 @@ struct Intersection : IIntersection // TODO } - float32_t3 generate_and + NBL_CONSTEXPR_STATIC_INLINE uint32_t ObjSize = 5; float32_t3 position; float32_t radius2; @@ -126,15 +114,104 @@ struct Intersection : IIntersection }; template<> -struct Intersection : IIntersection +struct Intersection { + static Intersection create(NBL_CONST_REF_ARG(float32_t3) vertex0, NBL_CONST_REF_ARG(float32_t3) vertex1, NBL_CONST_REF_ARG(float32_t3) vertex2, uint32_t bsdfID, uint32_t lightID) + { + Intersection retval; + retval.vertex0 = vertex0; + retval.vertex1 = vertex1; + retval.vertex2 = vertex2; + retval.bsdfLightIDs = spirv::bitFieldInsert(bsdfID, lightID, 16, 16); + return retval; + } + + float intersect(NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(float32_t3) direction) + { + const float32_t3 edges[2] = { vertex1 - vertex0, vertex2 - vertex0 }; + + const float32_t3 h = nbl::hlsl::cross(direction, edges[1]); + const float a = nbl::hlsl::dot(edges[0], h); + + const float32_t3 relOrigin = origin - vertex0; + + const float u = nbl::hlsl::dot(relOrigin, h) / a; + + const float32_t3 q = nbl::hlsl::cross(relOrigin, edges[0]); + const float v = nbl::hlsl::dot(direction, q) / a; + + const float t = nbl::hlsl::dot(edges[1], q) / a; + + const bool intersection = t > 0.f && u >= 0.f && v >= 0.f && (u + v) <= 1.f; + return intersection ? t : numeric_limits::infinity; + } + + float32_t3 getNormalTimesArea() + { + const float32_t3 edges[2] = { vertex1 - vertex0, vertex2 - vertex0 }; + return nbl::hlsl::cross(edges[0], edges[1]) * 0.5f; + } + + NBL_CONSTEXPR_STATIC_INLINE uint32_t ObjSize = 10; + float32_t3 vertex0; + float32_t3 vertex1; + float32_t3 vertex2; + uint32_t bsdfLightIDs; }; template<> -struct Intersection : IIntersection +struct Intersection { + static Intersection create(NBL_CONST_REF_ARG(float32_t3) offset, NBL_CONST_REF_ARG(float32_t3) edge0, NBL_CONST_REF_ARG(float32_t3) edge1, uint32_t bsdfID, uint32_t lightID) + { + Intersection retval; + retval.offset = offset; + retval.edge0 = edge0; + retval.edge1 = edge1; + retval.bsdfLightIDs = spirv::bitFieldInsert(bsdfID, lightID, 16, 16); + return retval; + } + + float intersect(NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(float32_t3) direction) + { + const float32_t3 h = nbl::hlsl::cross(direction, edge1); + const float a = nbl::hlsl::dot(edge0, h); + + const float32_t3 relOrigin = origin - offset; + + const float u = nbl::hlsl::dot(relOrigin,h)/a; + + const float32_t3 q = nbl::hlsl::cross(relOrigin, edge0); + const float v = nbl::hlsl::dot(direction, q) / a; + + const float t = nbl::hlsl::dot(edge1, q) / a; + const bool intersection = t > 0.f && u >= 0.f && v >= 0.f && u <= 1.f && v <= 1.f; + return intersection ? t : numeric_limits::infinity; + } + + float32_t3 getNormalTimesArea() + { + return nbl::hlsl::cross(edge0, edge1); + } + + void getNormalBasis(NBL_REF_ARG(float32_t3x3) basis, NBL_REF_ARG(float32_t2) extents) + { + extents = float32_t2(nbl::hlsl::length(edge0), nbl::hlsl::length(edge1)); + basis[0] = edge0 / extents[0]; + basis[1] = edge1 / extents[1]; + basis[2] = normalize(cross(basis[0],basis[1])); + + basis = nbl::hlsl::transpose(basis); // TODO: double check transpose + } + + NBL_CONSTEXPR_STATIC_INLINE uint32_t ObjSize = 10; + + float32_t3 offset; + float32_t3 edge0; + float32_t3 edge1; + uint32_t bsdfLightIDs; }; } diff --git a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl index b2b3d0d2d..d4b87196d 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl @@ -19,27 +19,71 @@ namespace Intersector // ray tracing pipeline method -// does everything in traceray in ex 30 +// procedural data store: [obj count] [intersect type] [obj1] [obj2] [...] + +struct IntersectData +{ + enum class Mode : uint32_t + { + RAY_QUERY, + RAY_TRACING, + PROCEDURAL + }; + + NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 128; + + uint32_t mode : 1; + unit32_t unused : 31; // possible space for flags + uint32_t data[DataSize]; +}; + template -struct Procedural +struct Comprehensive { using scalar_type = typename Ray::scalar_type; using ray_type = Ray; - static int traceRay(NBL_REF_ARG(ray_type) ray, IIntersection objects[32], int objCount) + static int traceProcedural(NBL_REF_ARG(ray_type) ray, NBL_REF_ARG(IntersectData) intersect) { const bool anyHit = ray.intersectionT != numeric_limits::max; + const uint32_t objCount = intersect.data[0]; + const ProceduralIntersectionType type = intersect.data[1]; int objectID = -1; for (int i = 0; i < objCount; i++) { float t; - if (objects[i].type == PIT_SPHERE) // we don't know what type of intersection it is so cast, there has to be a better way to do this + switch (type) { - Intersection sphere = (Intersection)objects[i]; - t = sphere.intersect(ray.origin, ray.direction); + case PIT_SPHERE: + { + float32_t3 position = float32_t3(asfloat(intersect.data[2 + i * Intersection::ObjSize]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 1]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 2])); + Intersection sphere = Intersection::create(position, asfloat(intersect.data[2 + i * Intersection::ObjSize + 3]), intersect.data[2 + i * Intersection::ObjSize + 4]); + t = sphere.intersect(ray.origin, ray.direction); + } + break; + case PIT_TRIANGLE: + { + float32_t3 vertex0 = float32_t3(asfloat(intersect.data[2 + i * Intersection::ObjSize]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 1]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 2])); + float32_t3 vertex1 = float32_t3(asfloat(intersect.data[2 + i * Intersection::ObjSize + 3]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 4]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 5])); + float32_t3 vertex2 = float32_t3(asfloat(intersect.data[2 + i * Intersection::ObjSize + 6]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 7]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 8])); + Intersection tri = Intersection::create(vertex0, vertex1, vertex2, intersect.data[2 + i * Intersection::ObjSize + 9]); + t = tri.intersect(ray.origin, ray.direction); + } + break; + case PIT_RECTANGLE: + { + float32_t3 offset = float32_t3(asfloat(intersect.data[2 + i * Intersection::ObjSize]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 1]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 2])); + float32_t3 edge0 = float32_t3(asfloat(intersect.data[2 + i * Intersection::ObjSize + 3]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 4]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 5])); + float32_t3 edge1 = float32_t3(asfloat(intersect.data[2 + i * Intersection::ObjSize + 6]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 7]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 8])); + Intersection rect = Intersection::create(offset, edge0, edge1, intersect.data[2 + i * Intersection::ObjSize + 9]); + t = rect.intersect(ray.origin, ray.direction); + } + break; + default: + t = numeric_limits::infinity; + break; } - // TODO: other types bool closerIntersection = t > 0.0 && t < ray.intersectionT; @@ -53,9 +97,69 @@ struct Procedural return objectID; } - // TODO? traceray with vertex/index buffer + static int traceRay(NBL_REF_ARG(ray_type) ray, NBL_REF_ARG(IntersectData) intersect) + { + const IntersectData::Mode mode = intersect.mode; + switch (mode) + { + case IntersectData::Mode::RAY_QUERY: + { + // TODO: do ray query stuff + } + break; + case IntersectData::Mode::RAY_TRACING: + { + // TODO: do ray tracing stuff + } + break; + case IntersectData::Mode::PROCEDURAL: + { + return traceProcedural(ray, intersect); + } + break; + default: + return -1; + } + } }; +// does everything in traceray in ex 30 +// template +// struct Procedural +// { +// using scalar_type = typename Ray::scalar_type; +// using ray_type = Ray; + +// static int traceRay(NBL_REF_ARG(ray_type) ray, IIntersection objects[32], int objCount) +// { +// const bool anyHit = ray.intersectionT != numeric_limits::max; + +// int objectID = -1; +// for (int i = 0; i < objCount; i++) +// { +// float t; +// if (objects[i].type == PIT_SPHERE) // we don't know what type of intersection it is so cast, there has to be a better way to do this +// { +// Intersection sphere = (Intersection)objects[i]; +// t = sphere.intersect(ray.origin, ray.direction); +// } +// // TODO: other types + +// bool closerIntersection = t > 0.0 && t < ray.intersectionT; + +// ray.intersectionT = closerIntersection ? t : ray.intersectionT; +// objectID = closerIntersection ? i : objectID; + +// // allowing early out results in a performance regression, WTF!? +// //if (anyHit && closerIntersection) +// //break; +// } +// return objectID; +// } + +// // TODO? traceray with vertex/index buffer +// }; + } } } From 85e67ad0c4012d7d8d2014489327036d89b0bf57 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 6 Feb 2025 16:51:56 +0700 Subject: [PATCH 06/53] completed material system? --- .../app_resources/hlsl/material_system.hlsl | 119 ++++++++++++++++++ .../app_resources/hlsl/pathtracer.hlsl | 2 +- 2 files changed, 120 insertions(+), 1 deletion(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl index 6f635ab68..1f13198fa 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl @@ -1,6 +1,9 @@ #ifndef _NBL_HLSL_EXT_MATERIAL_SYSTEM_INCLUDED_ #define _NBL_HLSL_EXT_MATERIAL_SYSTEM_INCLUDED_ +#include +#include + namespace nbl { namespace hlsl @@ -10,7 +13,123 @@ namespace ext namespace MaterialSystem { +struct Material +{ + enum class Type : uint32_t + { + DIFFUSE, + CONDUCTOR, + DIELECTRIC + }; + + NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 32; + + uint32_t type : 1; + unit32_t unused : 31; // possible space for flags + uint32_t data[DataSize]; +}; + +template +struct System +{ + using this_t = System; + using scalar_type = typename DiffuseBxDF::scalar_type; // types should be same across all 3 bxdfs + using vector2_type = vector; + using vector3_type = vector; + using measure_type = typename DiffuseBxDF::spectral_type; + using quotient_pdf_type = typename DiffuseBxDF::quotient_pdf_type; + using anisotropic_type = typename DiffuseBxDF::anisotropic_type; + using anisocache_type = typename ConductorBxDF::anisocache_type; + using params_t = SBxDFParams; + + static this_t create(NBL_CONST_REF_ARG(SBxDFCreationParams) diffuseParams, NBL_CONST_REF_ARG(SBxDFCreationParams) conductorParams, NBL_CONST_REF_ARG(SBxDFCreationParams) dielectricParams) + { + diffuseBxDF = DiffuseBxDF::create(diffuseParams); + conductorBxDF = DiffuseBxDF::create(conductorParams); + dielectricBxDF = DiffuseBxDF::create(dielectricParams); + } + + static measure_type eval(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(params_t) params) + { + switch(material.type) + { + case DIFFUSE: + { + return (measure_type)diffuseBxDF.eval(params); + } + break; + case CONDUCTOR: + { + return conductorBxDF.eval(params); + } + break; + case DIELECTRIC: + { + return dielectricBxDF.eval(params); + } + break; + default: + return (measure_type)0.0; + } + } + + static vector3_type generate(NBL_CONST_REF_ARG(Material) material, anisotropic_type interaction, vector2_type u, NBL_REF_ARG(anisocache_type) cache) + { + switch(material.type) + { + case DIFFUSE: + { + return diffuseBxDF.generate(interaction, u); + } + break; + case CONDUCTOR: + { + return conductorBxDF.generate(interaction, u, cache); + } + break; + case DIELECTRIC: + { + return dielectricBxDF.generate(interaction, u, cache); + } + break; + default: + return (vector3_type)numeric_limits::infinity; + } + } + + static quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(params_t) params) + { + const float minimumProjVectorLen = 0.00000001; + if (params.NdotV > minimumProjVectorLen && params.NdotL > minimumProjVectorLen) + { + switch(material.type) + { + case DIFFUSE: + { + return diffuseBxDF.quotient_and_pdf(params); + } + break; + case CONDUCTOR: + { + return conductorBxDF.quotient_and_pdf(params); + } + break; + case DIELECTRIC: + { + return dielectricBxDF.quotient_and_pdf(params); + } + break; + default: + return quotient_pdf_type::create((measure_type)0.0, numeric_limits::infinity); + } + } + return quotient_pdf_type::create((measure_type)0.0, numeric_limits::infinity); + } + DiffuseBxDF diffuseBxDF; + ConductorBxDF conductorBxDF; + DielectricBxDF dielectricBxDF; +}; } } diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index f28dc621b..9ca0f77e4 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -26,7 +26,7 @@ struct Unidirectional // closest hit // Li - MaterialSystem::measure_t getMeasure() + MaterialSystem::measure_type getMeasure() { // loop through bounces, do closest hit // return ray.payload.accumulation --> color From 2c500b1e06e3e83b2a427bf0aa1ef27878467e0b Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 7 Feb 2025 14:37:56 +0700 Subject: [PATCH 07/53] sphere nee stuff --- .../app_resources/hlsl/common.hlsl | 62 +++++++++++++------ .../app_resources/hlsl/intersector.hlsl | 30 ++++----- 2 files changed, 59 insertions(+), 33 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index 84933edfb..2b627523f 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -46,22 +46,22 @@ struct Ray Payload payload; }; -enum ProceduralIntersectionType : uint16_t +enum ProceduralShapeType : uint16_t { - PIT_SPHERE, - PIT_TRIANGLE, - PIT_RECTANGLE + PST_SPHERE, + PST_TRIANGLE, + PST_RECTANGLE }; -template -struct Intersection; +template +struct Shape; template<> -struct Intersection +struct Shape { - static Intersection create(NBL_CONST_REF_ARG(float32_t3) position, float32_t radius, uint32_t bsdfID, uint32_t lightID) + static Shape create(NBL_CONST_REF_ARG(float32_t3) position, float32_t radius, uint32_t bsdfID, uint32_t lightID) { - Intersection retval; + Shape retval; retval.position = position; retval.radius2 = radius * radius; retval.bsdfLightIDs = spirv::bitFieldInsert(bsdfID, lightID, 16, 16); @@ -95,15 +95,41 @@ struct Intersection return 2.0 * numbers::pi * (1.0 - cosThetaMax); } - // should this be in material system? float deferredPdf(Light light, Ray ray) { return 1.0 / getSolidAngle(ray.origin); } - float generate_and_pdf() + template + float generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, float32_t3 xi, uint32_t objectID) { - // TODO + float32_t3 Z = position - origin; + const float distanceSQ = nbl::hlsl::dot(Z,Z); + const float cosThetaMax2 = 1.0 - radius2 / distanceSQ; + if (cosThetaMax2 > 0.0) + { + const float rcpDistance = 1.0 / nbl::hlsl::sqrt(distanceSQ); + Z *= rcpDistance; + + const float cosThetaMax = nbl::hlsl::sqrt(cosThetaMax2); + const float cosTheta = nbl::hlsl::mix(1.0, cosThetaMax, xi.x); + + vec3 L = Z * cosTheta; + + const float cosTheta2 = cosTheta * cosTheta; + const float sinTheta = nbl::hlsl::sqrt(1.0 - cosTheta2); + float sinPhi, cosPhi; + math::sincos(2.0 * numbers::pi * xi.y - numbers::pi, sinPhi, cosPhi); + float32_t2x3 XY = math::frisvad(Z); + + L += (XY[0] * cosPhi + XY[1] * sinPhi) * sinTheta; + + newRayMaxT = (cosTheta - nbl::hlsl::sqrt(cosTheta2 - cosThetaMax2)) / rcpDistance; + pdf = 1.0 / (2.0 * numbers::pi * (1.0 - cosThetaMax)); + return L; + } + pdf = 0.0; + return float32_t3(0.0,0.0,0.0); } NBL_CONSTEXPR_STATIC_INLINE uint32_t ObjSize = 5; @@ -114,11 +140,11 @@ struct Intersection }; template<> -struct Intersection +struct Shape { - static Intersection create(NBL_CONST_REF_ARG(float32_t3) vertex0, NBL_CONST_REF_ARG(float32_t3) vertex1, NBL_CONST_REF_ARG(float32_t3) vertex2, uint32_t bsdfID, uint32_t lightID) + static Shape create(NBL_CONST_REF_ARG(float32_t3) vertex0, NBL_CONST_REF_ARG(float32_t3) vertex1, NBL_CONST_REF_ARG(float32_t3) vertex2, uint32_t bsdfID, uint32_t lightID) { - Intersection retval; + Shape retval; retval.vertex0 = vertex0; retval.vertex1 = vertex1; retval.vertex2 = vertex2; @@ -161,11 +187,11 @@ struct Intersection }; template<> -struct Intersection +struct Shape { - static Intersection create(NBL_CONST_REF_ARG(float32_t3) offset, NBL_CONST_REF_ARG(float32_t3) edge0, NBL_CONST_REF_ARG(float32_t3) edge1, uint32_t bsdfID, uint32_t lightID) + static Shape create(NBL_CONST_REF_ARG(float32_t3) offset, NBL_CONST_REF_ARG(float32_t3) edge0, NBL_CONST_REF_ARG(float32_t3) edge1, uint32_t bsdfID, uint32_t lightID) { - Intersection retval; + Shape retval; retval.offset = offset; retval.edge0 = edge0; retval.edge1 = edge1; diff --git a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl index d4b87196d..a694082fe 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl @@ -55,28 +55,28 @@ struct Comprehensive float t; switch (type) { - case PIT_SPHERE: + case PST_SPHERE: { - float32_t3 position = float32_t3(asfloat(intersect.data[2 + i * Intersection::ObjSize]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 1]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 2])); - Intersection sphere = Intersection::create(position, asfloat(intersect.data[2 + i * Intersection::ObjSize + 3]), intersect.data[2 + i * Intersection::ObjSize + 4]); + float32_t3 position = float32_t3(asfloat(intersect.data[2 + i * Shape::ObjSize]), asfloat(intersect.data[2 + i * Shape::ObjSize + 1]), asfloat(intersect.data[2 + i * Shape::ObjSize + 2])); + Shape sphere = Shape::create(position, asfloat(intersect.data[2 + i * Shape::ObjSize + 3]), intersect.data[2 + i * Shape::ObjSize + 4]); t = sphere.intersect(ray.origin, ray.direction); } break; - case PIT_TRIANGLE: + case PST_TRIANGLE: { - float32_t3 vertex0 = float32_t3(asfloat(intersect.data[2 + i * Intersection::ObjSize]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 1]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 2])); - float32_t3 vertex1 = float32_t3(asfloat(intersect.data[2 + i * Intersection::ObjSize + 3]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 4]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 5])); - float32_t3 vertex2 = float32_t3(asfloat(intersect.data[2 + i * Intersection::ObjSize + 6]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 7]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 8])); - Intersection tri = Intersection::create(vertex0, vertex1, vertex2, intersect.data[2 + i * Intersection::ObjSize + 9]); + float32_t3 vertex0 = float32_t3(asfloat(intersect.data[2 + i * Shape::ObjSize]), asfloat(intersect.data[2 + i * Shape::ObjSize + 1]), asfloat(intersect.data[2 + i * Shape::ObjSize + 2])); + float32_t3 vertex1 = float32_t3(asfloat(intersect.data[2 + i * Shape::ObjSize + 3]), asfloat(intersect.data[2 + i * Shape::ObjSize + 4]), asfloat(intersect.data[2 + i * Shape::ObjSize + 5])); + float32_t3 vertex2 = float32_t3(asfloat(intersect.data[2 + i * Shape::ObjSize + 6]), asfloat(intersect.data[2 + i * Shape::ObjSize + 7]), asfloat(intersect.data[2 + i * Shape::ObjSize + 8])); + Shape tri = Shape::create(vertex0, vertex1, vertex2, intersect.data[2 + i * Shape::ObjSize + 9]); t = tri.intersect(ray.origin, ray.direction); } break; - case PIT_RECTANGLE: + case PST_RECTANGLE: { - float32_t3 offset = float32_t3(asfloat(intersect.data[2 + i * Intersection::ObjSize]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 1]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 2])); - float32_t3 edge0 = float32_t3(asfloat(intersect.data[2 + i * Intersection::ObjSize + 3]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 4]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 5])); - float32_t3 edge1 = float32_t3(asfloat(intersect.data[2 + i * Intersection::ObjSize + 6]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 7]), asfloat(intersect.data[2 + i * Intersection::ObjSize + 8])); - Intersection rect = Intersection::create(offset, edge0, edge1, intersect.data[2 + i * Intersection::ObjSize + 9]); + float32_t3 offset = float32_t3(asfloat(intersect.data[2 + i * Shape::ObjSize]), asfloat(intersect.data[2 + i * Shape::ObjSize + 1]), asfloat(intersect.data[2 + i * Shape::ObjSize + 2])); + float32_t3 edge0 = float32_t3(asfloat(intersect.data[2 + i * Shape::ObjSize + 3]), asfloat(intersect.data[2 + i * Shape::ObjSize + 4]), asfloat(intersect.data[2 + i * Shape::ObjSize + 5])); + float32_t3 edge1 = float32_t3(asfloat(intersect.data[2 + i * Shape::ObjSize + 6]), asfloat(intersect.data[2 + i * Shape::ObjSize + 7]), asfloat(intersect.data[2 + i * Shape::ObjSize + 8])); + Shape rect = Shape::create(offset, edge0, edge1, intersect.data[2 + i * Shape::ObjSize + 9]); t = rect.intersect(ray.origin, ray.direction); } break; @@ -138,9 +138,9 @@ struct Comprehensive // for (int i = 0; i < objCount; i++) // { // float t; -// if (objects[i].type == PIT_SPHERE) // we don't know what type of intersection it is so cast, there has to be a better way to do this +// if (objects[i].type == PST_SPHERE) // we don't know what type of intersection it is so cast, there has to be a better way to do this // { -// Intersection sphere = (Intersection)objects[i]; +// Shape sphere = (Shape)objects[i]; // t = sphere.intersect(ray.origin, ray.direction); // } // // TODO: other types From e6a99165c1b153977192f9722381fc24f566c9ca Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 10 Feb 2025 16:58:50 +0700 Subject: [PATCH 08/53] triangle sampling --- .../app_resources/hlsl/common.hlsl | 109 +++++++++++++++++- 1 file changed, 108 insertions(+), 1 deletion(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index 2b627523f..dfc500beb 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -4,6 +4,11 @@ #include #include #include +#include +#include +#include +#include +//#include namespace nbl { @@ -53,6 +58,13 @@ enum ProceduralShapeType : uint16_t PST_RECTANGLE }; +enum PTPolygonMethod : uint16_t +{ + PPM_AREA, + PPM_SOLID_ANGLE, + PPM_APPROX_PROJECTED_SOLID_ANGLE +}; + template struct Shape; @@ -95,7 +107,7 @@ struct Shape return 2.0 * numbers::pi * (1.0 - cosThetaMax); } - float deferredPdf(Light light, Ray ray) + float deferredPdf(NBL_CONST_REF_ARG(Light light), NBL_CONST_REF_ARG(Ray) ray) { return 1.0 / getSolidAngle(ray.origin); } @@ -149,6 +161,7 @@ struct Shape retval.vertex1 = vertex1; retval.vertex2 = vertex2; retval.bsdfLightIDs = spirv::bitFieldInsert(bsdfID, lightID, 16, 16); + retval.polygonMethod = PPM_SOLID_ANGLE; return retval; } @@ -178,12 +191,104 @@ struct Shape return nbl::hlsl::cross(edges[0], edges[1]) * 0.5f; } + float deferredPdf(NBL_CONST_REF_ARG(Light light), NBL_CONST_REF_ARG(Ray) ray) + { + const float32_t3 L = ray.direction; + switch (polygonMethod) + { + case PPM_AREA: + { + const float dist = ray.intersectionT; + return dist * dist / nbl::hlsl::abs(nbl::hlsl::dot(getNormalTimesArea()), L); + } + break; + case PPM_SOLID_ANGLE: + { + shapes::SphericalTriangle st = shapes::SphericalTriangle::create(vertex0, vertex1, vertex2, ray.origin); + const float rcpProb = st.solidAngleOfTriangle(); + // if `rcpProb` is NAN then the triangle's solid angle was close to 0.0 + return rcpProb > numeric_limits::min ? (1.0 / rcpProb) : numeric_limits::max; + } + break; + case PPM_APPROX_PROJECTED_SOLID_ANGLE: + { + shapes::SphericalTriangle st = shapes::SphericalTriangle::create(vertex0, vertex1, vertex2, ray.origin); + const float pdf = st.projectedSolidAngleOfTriangle(ray.normalAtOrigin, ray.wasBSDFAtOrigin, 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::max ? pdf : 0.0; + } + break; + default: + return 0.0; + } + } + + template + float32_t3 generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, float32_t3 xi, uint32_t objectID) + { + switch(polygonMethod) + { + case PPM_AREA: + { + const float32_t3 edge0 = vertex1 - vertex0; + const float32_t3 edge1 = vertex2 - vertex0; + const float sqrtU = nbl::hlsl::sqrt(xi.x); + float32_t3 pnt = vertex0 + edge0 * (1.0 - sqrtU) + edge1 * sqrtU * xi.y; + float32_t3 L = pnt - origin; + + const float distanceSq = nbl::hlsl::dot(L,L); + const float rcpDistance = 1.0 / nbl::hlsl::sqrt(distanceSq); + L *= rcpDistance; + + pdf = distanceSq / nbl::hlsl::abs(nbl::hlsl::dot(nbl::hlsl::cross(edge0, edge1) * 0.5f, L)); + newRayMaxT = 1.0 / rcpDistance; + return L; + } + break; + case PPM_SOLID_ANGLE: + { + float rcpPdf; + + shapes::SphericalTriangle st = shapes::SphericalTriangle::create(vertex0, vertex1, vertex2, ray.origin); + sampling::SphericalTriangle sst = sampling::SphericalTriangle::create(st); + + const float32_t3 L = sst.generate(rcpPdf, xi.xy); + + pdf = rcpPdf > numeric_limits::min ? (1.0 / rcpPdf) : 0.0; + + const float32_t3 N = getNormalTimesArea(); + newRayMaxT = nbl::hlsl::dot(N, vertex0 - origin) / nbl::hlsl::dot(N, L); + return L; + } + break; + case PPM_APPROX_PROJECTED_SOLID_ANGLE: + { + float rcpPdf; + + shapes::SphericalTriangle st = shapes::SphericalTriangle::create(vertex0, vertex1, vertex2, ray.origin); + sampling::ProjectedSphericalTriangle sst = sampling::ProjectedSphericalTriangle::create(st); + + const float32_t3 L = sst.generate(rcpPdf, interaction.N, isBSDF, xi.xy); + + pdf = rcpPdf > numeric_limits::min ? (1.0 / rcpPdf) : 0.0; + + const float32_t3 N = getNormalTimesArea(); + newRayMaxT = nbl::hlsl::dot(N, vertex0 - origin) / nbl::hlsl::dot(N, L); + return L; + } + break; + default: + return (float32_t3)0.0; + } + } + NBL_CONSTEXPR_STATIC_INLINE uint32_t ObjSize = 10; float32_t3 vertex0; float32_t3 vertex1; float32_t3 vertex2; uint32_t bsdfLightIDs; + PTPolygonMethod polygonMethod; }; template<> @@ -196,6 +301,7 @@ struct Shape retval.edge0 = edge0; retval.edge1 = edge1; retval.bsdfLightIDs = spirv::bitFieldInsert(bsdfID, lightID, 16, 16); + retval.polygonMethod = PPM_SOLID_ANGLE; return retval; } @@ -238,6 +344,7 @@ struct Shape float32_t3 edge0; float32_t3 edge1; uint32_t bsdfLightIDs; + PTPolygonMethod polygonMethod; }; } From 6dc25eb438bed7c9a729da6520b930d571410d20 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 11 Feb 2025 14:04:11 +0700 Subject: [PATCH 09/53] rectangle sampling --- .../app_resources/hlsl/common.hlsl | 102 +++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index dfc500beb..9295b459b 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -51,6 +51,13 @@ struct Ray Payload payload; }; +template +struct Light +{ + Spectrum radiance; + uint32_t objectID; +}; + enum ProceduralShapeType : uint16_t { PST_SPHERE, @@ -191,7 +198,7 @@ struct Shape return nbl::hlsl::cross(edges[0], edges[1]) * 0.5f; } - float deferredPdf(NBL_CONST_REF_ARG(Light light), NBL_CONST_REF_ARG(Ray) ray) + float deferredPdf(NBL_CONST_REF_ARG(Light light), NBL_CONST_REF_ARG(Ray) ray) { const float32_t3 L = ray.direction; switch (polygonMethod) @@ -338,6 +345,99 @@ struct Shape basis = nbl::hlsl::transpose(basis); // TODO: double check transpose } + float deferredPdf(NBL_CONST_REF_ARG(Light light), NBL_CONST_REF_ARG(Ray) ray) + { + switch (polygonMethod) + { + case PPM_AREA: + { + const float dist = ray.intersectionT; + return dist * dist / nbl::hlsl::abs(nbl::hlsl::dot(getNormalTimesArea(), L)); + } + break; + // #ifdef TRIANGLE_REFERENCE ? + case PPM_SOLID_ANGLE: + { + float pdf; + float32_t3x3 rectNormalBasis; + float32_t2 rectExtents; + getNormalBasis(rectNormalBasis, rectExtents); + shapes::SphericalRectangle sphR0 = shapes::SphericalRectangle::create(ray.origin, offset, rectNormalBasis); + float solidAngle = sphR0.solidAngleOfRectangle(rectExtents); + if (solidAngle > numeric_limits::min) + pdf = 1.f / solidAngle; + else + pdf = numeric_limits::infinity; + return pdf; + } + break; + case PPM_APPROX_PROJECTED_SOLID_ANGLE: + { + return numeric_limits::infinity; + } + break; + default: + return numeric_limits::infinity; + } + } + + template + float32_t3 generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, float32_t3 xi, uint32_t objectID) + { + const float32_t3 N = getNormalTimesArea(); + const float32_t3 origin2origin = offset - origin; + + switch (polygonMethod) + { + case PPM_AREA: + { + float32_t3 L = origin2origin + edge0 * xi.x + edge1 * xi.y; + const float distSq = nbl::hlsl::dot(L, L); + const float rcpDist = 1.0 / nbl::hlsl::sqrt(distSq); + L *= rcpDist; + pdf = distSq / nbl::hlsl::abs(nbl::hlsl::dot(N, L)); + newRayMaxT = 1.0 / rcpDist; + return L; + } + break; + // #ifdef TRIANGLE_REFERENCE ? + case PPM_SOLID_ANGLE: + { + float pdf; + float32_t3x3 rectNormalBasis; + float32_t2 rectExtents; + getNormalBasis(rectNormalBasis, rectExtents); + shapes::SphericalRectangle sphR0 = shapes::SphericalRectangle::create(origin, offset, rectNormalBasis); + float32_t3 L = (float32_t3)0.0; + float solidAngle = sphR0.solidAngleOfRectangle(rectExtents); + + sampling::SphericalRectangle ssph = sampling::SphericalRectangle::create(sphR0); + float32_t2 sphUv = ssph.generate(rectExtents, xi.xy, solidAngle); + if (solidAngle > numeric_limits::min) + { + float32_t3 sph_sample = sphUv[0] * edge0 + sphUv[1] * edge1 + offset; + L = nbl::hlsl::normalize(sph_sample - origin); + pdf = 1.f / solidAngle; + } + else + pdf = numeric_limits::infinity; + + newRayMaxT = nbl::hlsl::dot(N, origin2origin) / nbl::hlsl::dot(N, L); + return L; + } + break; + case PPM_APPROX_PROJECTED_SOLID_ANGLE: + { + pdf = numeric_limits::infinity; + return (float32_t3)0.0; + } + break; + default: + pdf = numeric_limits::infinity; + return (float32_t3)0.0; + } + } + NBL_CONSTEXPR_STATIC_INLINE uint32_t ObjSize = 10; float32_t3 offset; From 22bd6f970c3954049a899181085a8df06b64fbe7 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 12 Feb 2025 11:30:50 +0700 Subject: [PATCH 10/53] nee stuff --- .../app_resources/hlsl/common.hlsl | 35 +++- .../app_resources/hlsl/intersector.hlsl | 2 +- .../hlsl/next_event_estimator.hlsl | 171 ++++++++++++++++++ 3 files changed, 203 insertions(+), 5 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index 9295b459b..e5940aab0 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -54,10 +54,34 @@ struct Ray template struct Light { - Spectrum radiance; + using spectral_type = Spectrum; + + spectral_type radiance; uint32_t objectID; }; +template +struct Tolerance +{ + NBL_CONSTEXPR_STATIC_INLINE float INTERSECTION_ERROR_BOUND_LOG2 = -8.0; + + static T __common(uint32_t depth) + { + float depthRcp = 1.0 / float(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); + } +} + enum ProceduralShapeType : uint16_t { PST_SPHERE, @@ -114,7 +138,8 @@ struct Shape return 2.0 * numbers::pi * (1.0 - cosThetaMax); } - float deferredPdf(NBL_CONST_REF_ARG(Light light), NBL_CONST_REF_ARG(Ray) ray) + template + float deferredPdf(NBL_CONST_REF_ARG(Light) light, NBL_CONST_REF_ARG(Ray) ray) { return 1.0 / getSolidAngle(ray.origin); } @@ -198,7 +223,8 @@ struct Shape return nbl::hlsl::cross(edges[0], edges[1]) * 0.5f; } - float deferredPdf(NBL_CONST_REF_ARG(Light light), NBL_CONST_REF_ARG(Ray) ray) + template + float deferredPdf(NBL_CONST_REF_ARG(Light) light, NBL_CONST_REF_ARG(Ray) ray) { const float32_t3 L = ray.direction; switch (polygonMethod) @@ -345,7 +371,8 @@ struct Shape basis = nbl::hlsl::transpose(basis); // TODO: double check transpose } - float deferredPdf(NBL_CONST_REF_ARG(Light light), NBL_CONST_REF_ARG(Ray) ray) + template + float deferredPdf(NBL_CONST_REF_ARG(Light light), NBL_CONST_REF_ARG(Ray) ray) { switch (polygonMethod) { diff --git a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl index a694082fe..919816019 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl @@ -47,7 +47,7 @@ struct Comprehensive { const bool anyHit = ray.intersectionT != numeric_limits::max; const uint32_t objCount = intersect.data[0]; - const ProceduralIntersectionType type = intersect.data[1]; + const ProceduralShapeType type = intersect.data[1]; int objectID = -1; for (int i = 0; i < objCount; i++) diff --git a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl index 1afa8d12e..5d96ae13e 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl @@ -1,6 +1,8 @@ #ifndef _NBL_HLSL_EXT_NEXT_EVENT_ESTIMATOR_INCLUDED_ #define _NBL_HLSL_EXT_NEXT_EVENT_ESTIMATOR_INCLUDED_ +#include "common.hlsl" + namespace nbl { namespace hlsl @@ -10,7 +12,176 @@ namespace ext namespace NextEventEstimator { +// procedural data store: [light count] [intersect type] [obj] + +struct Event +{ + enum class Mode : uint32_t + { + RAY_QUERY, + RAY_TRACING, + PROCEDURAL + }; + + NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 128; + + uint32_t mode : 1; + unit32_t unused : 31; // possible space for flags + uint32_t data[DataSize]; +}; + +template +struct Estimator +{ + using scalar_type = typename Ray::scalar_type; + using ray_type = Ray; + using light_type = Light; + using spectral_type = typename Light::spectral_type; + using interaction_type = Aniso; + using quotient_pdf_type = quotient_and_pdf; + using sample_type = LightSample; + + static spectral_type proceduralDeferredEvalAndPdf(NBL_REF_ARG(scalar_type) pdf, NBL_CONST_REF_ARG(light_type) light, NBL_CONST_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(Event) event) + { + const uint32_t lightCount = event.data[0]; + const ProceduralShapeType type = event.data[1]; + + pdf = 1.0 / lightCount; + switch (type) + { + case PST_SPHERE: + { + float32_t3 position = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize]), asfloat(intersect.data[2 + Shape::ObjSize + 1]), asfloat(intersect.data[2 + Shape::ObjSize + 2])); + Shape sphere = Shape::create(position, asfloat(intersect.data[2 + Shape::ObjSize + 3]), intersect.data[2 + Shape::ObjSize + 4]); + pdf *= sphere.template deferredPdf(light, ray); + } + break; + case PST_TRIANGLE: + { + float32_t3 vertex0 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize]), asfloat(intersect.data[2 + Shape::ObjSize + 1]), asfloat(intersect.data[2 + Shape::ObjSize + 2])); + float32_t3 vertex1 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 3]), asfloat(intersect.data[2 + Shape::ObjSize + 4]), asfloat(intersect.data[2 + Shape::ObjSize + 5])); + float32_t3 vertex2 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 6]), asfloat(intersect.data[2 + Shape::ObjSize + 7]), asfloat(intersect.data[2 + Shape::ObjSize + 8])); + Shape tri = Shape::create(vertex0, vertex1, vertex2, intersect.data[2 + Shape::ObjSize + 9]); + pdf *= tri.template deferredPdf(light, ray); + } + break; + case PST_RECTANGLE: + { + float32_t3 offset = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize]), asfloat(intersect.data[2 + Shape::ObjSize + 1]), asfloat(intersect.data[2 + Shape::ObjSize + 2])); + float32_t3 edge0 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 3]), asfloat(intersect.data[2 + Shape::ObjSize + 4]), asfloat(intersect.data[2 + Shape::ObjSize + 5])); + float32_t3 edge1 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 6]), asfloat(intersect.data[2 + Shape::ObjSize + 7]), asfloat(intersect.data[2 + Shape::ObjSize + 8])); + Shape rect = Shape::create(offset, edge0, edge1, intersect.data[2 + Shape::ObjSize + 9]); + pdf *= rect.template deferredPdf(light, ray); + } + break; + default: + pdf = numeric_limits::infinity; + break; + } + + return light.radiance; + } + + static spectral_type deferredEvalAndPdf(NBL_REF_ARG(scalar_type) pdf, NBL_CONST_REF_ARG(light_type) light, NBL_CONST_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(Event) event) + { + const Event::Mode mode = event.mode; + switch (mode) + { + case Event::Mode::RAY_QUERY: + { + // TODO: do ray query stuff + } + break; + case Event::Mode::RAY_TRACING: + { + // TODO: do ray tracing stuff + } + break; + case Event::Mode::PROCEDURAL: + { + return proceduralDeferredEvalAndPdf(pdf, light, ray, event); + } + break; + default: + return (spectral_type)0.0; + } + } + + static sample_type procedural_generate_and_quotient_and_pdf(NBL_REF_ARG(quotient_pdf_type) quotient_pdf, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(interaction_type) interaction, bool isBSDF, NBL_CONST_REF_ARG(vector3_type) xi, unit32_t depth, NBL_CONST_REF_ARG(Event) event) + { + const uint32_t lightCount = event.data[0]; + const ProceduralShapeType type = event.data[1]; + + sample_type L; + scalar_type pdf; + switch (type) + { + case PST_SPHERE: + { + float32_t3 position = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize]), asfloat(intersect.data[2 + Shape::ObjSize + 1]), asfloat(intersect.data[2 + Shape::ObjSize + 2])); + Shape sphere = Shape::create(position, asfloat(intersect.data[2 + Shape::ObjSize + 3]), intersect.data[2 + Shape::ObjSize + 4]); + L = sphere.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi, objectID); + } + break; + case PST_TRIANGLE: + { + float32_t3 vertex0 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize]), asfloat(intersect.data[2 + Shape::ObjSize + 1]), asfloat(intersect.data[2 + Shape::ObjSize + 2])); + float32_t3 vertex1 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 3]), asfloat(intersect.data[2 + Shape::ObjSize + 4]), asfloat(intersect.data[2 + Shape::ObjSize + 5])); + float32_t3 vertex2 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 6]), asfloat(intersect.data[2 + Shape::ObjSize + 7]), asfloat(intersect.data[2 + Shape::ObjSize + 8])); + Shape tri = Shape::create(vertex0, vertex1, vertex2, intersect.data[2 + Shape::ObjSize + 9]); + L = tri.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi, objectID); + } + break; + case PST_RECTANGLE: + { + float32_t3 offset = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize]), asfloat(intersect.data[2 + Shape::ObjSize + 1]), asfloat(intersect.data[2 + Shape::ObjSize + 2])); + float32_t3 edge0 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 3]), asfloat(intersect.data[2 + Shape::ObjSize + 4]), asfloat(intersect.data[2 + Shape::ObjSize + 5])); + float32_t3 edge1 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 6]), asfloat(intersect.data[2 + Shape::ObjSize + 7]), asfloat(intersect.data[2 + Shape::ObjSize + 8])); + Shape rect = Shape::create(offset, edge0, edge1, intersect.data[2 + Shape::ObjSize + 9]); + L = rect.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi, objectID); + } + break; + default: + pdf = numeric_limits::infinity; + break; + } + + newRayMaxT *= Tolerance::getEnd(depth); + pdf *= 1.0 / lightCount; + spectral_type quo = light.radiance / pdf; + quotient_pdf = quotient_pdf_type::create(quo, pdf); + + return L; + } + static sample_type generate_and_quotient_and_pdf(NBL_REF_ARG(quotient_pdf_type) quotient_pdf, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(interaction_type) interaction, bool isBSDF, NBL_CONST_REF_ARG(vector3_type) xi, unit32_t depth, NBL_CONST_REF_ARG(Event) event) + { + const Event::Mode mode = event.mode; + switch (mode) + { + case Event::Mode::RAY_QUERY: + { + // TODO: do ray query stuff + } + break; + case Event::Mode::RAY_TRACING: + { + // TODO: do ray tracing stuff + } + break; + case Event::Mode::PROCEDURAL: + { + return procedural_generate_and_quotient_and_pdf(newRayMaxT, origin, interaction, isBSDF, xi, depth, event); + } + break; + default: + { + sample_type L; + return L; + } + } + } +}; } } From 3bb858b45166be7b8c4b48a3d465697c7a6aadc8 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 13 Feb 2025 17:01:20 +0700 Subject: [PATCH 11/53] scene representation, getmeasure for pt --- .../app_resources/hlsl/common.hlsl | 21 +++ .../app_resources/hlsl/intersector.hlsl | 72 ++++++++- .../app_resources/hlsl/pathtracer.hlsl | 144 ++++++++++++++++-- 3 files changed, 227 insertions(+), 10 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index e5940aab0..7289d508d 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -474,6 +474,27 @@ struct Shape PTPolygonMethod polygonMethod; }; +struct Scene +{ + NBL_CONSTEXPR_STATIC_INLINE uint32_t maxSphereCount = 25; + NBL_CONSTEXPR_STATIC_INLINE uint32_t maxTriangleCount = 12; + NBL_CONSTEXPR_STATIC_INLINE uint32_t maxRectangleCount = 12; + + Shape spheres[maxSphereCount]; + Shape triangles[maxTriangleCount]; + Shape rectangles[maxRectangleCount]; + + uint32_t sphereCount; + uint32_t triangleCount; + uint32_t rectangleCount; + + Light lights[]; + // Material materials[]; + // + obj count for each + + // AS ases; +}; + } } } diff --git a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl index 919816019..23706402a 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl @@ -23,6 +23,76 @@ namespace Intersector struct IntersectData { + static IntersectData encode(uint32_t mode, ProceduralShapeType type, NBL_CONST_REF_ARG(Scene) scene) + { + IntersectData retval; + retval.mode = mode; + + uint32_t objCount = (type == PST_SPHERE) ? scene.sphereCount : + (type == PST_TRIANGLE) ? scene.triangleCount : + (type == PST_RECTANGLE) ? scene.rectangleCount : + -1; + retval.data[0] = objCount; + retval.data[1] = type; + + switch (type) + { + case PST_SPHERE: + { + for (int i = 0; i < objCount; i++) + { + Shape sphere = scene.spheres[i]; + retval.data[2 + i * Shape::ObjSize] = asuint(sphere.position.x); + retval.data[2 + i * Shape::ObjSize + 1] = asuint(sphere.position.y); + retval.data[2 + i * Shape::ObjSize + 2] = asuint(sphere.position.z); + retval.data[2 + i * Shape::ObjSize + 3] = asuint(sphere.radius); + retval.data[2 + i * Shape::ObjSize + 4] = sphere.bsdfLightIDs; + } + } + break; + case PST_TRIANGLE: + { + for (int i = 0; i < objCount; i++) + { + Shape tri = scene.triangles[i]; + retval.data[2 + i * Shape::ObjSize] = asuint(tri.vertex0.x); + retval.data[2 + i * Shape::ObjSize + 1] = asuint(tri.vertex0.y); + retval.data[2 + i * Shape::ObjSize + 2] = asuint(tri.vertex0.z); + retval.data[2 + i * Shape::ObjSize + 3] = asuint(tri.vertex1.x); + retval.data[2 + i * Shape::ObjSize + 4] = asuint(tri.vertex1.y); + retval.data[2 + i * Shape::ObjSize + 5] = asuint(tri.vertex1.z); + retval.data[2 + i * Shape::ObjSize + 6] = asuint(tri.vertex2.x); + retval.data[2 + i * Shape::ObjSize + 7] = asuint(tri.vertex2.y); + retval.data[2 + i * Shape::ObjSize + 8] = asuint(tri.vertex2.z); + retval.data[2 + i * Shape::ObjSize + 9] = tri.bsdfLightIDs; + } + } + break; + case PST_RECTANGLE: + { + for (int i = 0; i < objCount; i++) + { + Shape rect = scene.rectangles[i]; + retval.data[2 + i * Shape::ObjSize] = asuint(rect.offset.x); + retval.data[2 + i * Shape::ObjSize + 1] = asuint(rect.offset.y); + retval.data[2 + i * Shape::ObjSize + 2] = asuint(rect.offset.z); + retval.data[2 + i * Shape::ObjSize + 3] = asuint(rect.edge0.x); + retval.data[2 + i * Shape::ObjSize + 4] = asuint(rect.edge0.y); + retval.data[2 + i * Shape::ObjSize + 5] = asuint(rect.edge0.z); + retval.data[2 + i * Shape::ObjSize + 6] = asuint(rect.edge1.x); + retval.data[2 + i * Shape::ObjSize + 7] = asuint(rect.edge1.y); + retval.data[2 + i * Shape::ObjSize + 8] = asuint(rect.edge1.z); + retval.data[2 + i * Shape::ObjSize + 9] = rect.bsdfLightIDs; + } + } + break; + default: + // for ASes + break; + } + return retval; + } + enum class Mode : uint32_t { RAY_QUERY, @@ -49,7 +119,7 @@ struct Comprehensive const uint32_t objCount = intersect.data[0]; const ProceduralShapeType type = intersect.data[1]; - int objectID = -1; + int objectID = ray.objectID; for (int i = 0; i < objCount; i++) { float t; diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index 9ca0f77e4..06950b825 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -10,27 +10,153 @@ namespace ext namespace PathTracer { +template +struct PathTracerCreationParams +{ + // rng gen + uint32_t2 rngState; + + // ray gen + vector pixOffsetParam; + vector camPos; + vector NDC; + matrix invMVP; + + // mat + BxDFCreation diffuseParams; + BxDFCreation conductorParams; + BxDFCreation dielectricParams; +}; + template 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 scalar_type = typename MaterialSystem::scalar_type; + using vector3_type = vector; + using measure_type = typename MaterialSystem::measure_type; + using ray_type = typename RayGen::ray_type; - static this_t create(RandGen randGen, - RayGen rayGen, - Intersector intersector, - MaterialSystem materialSystem, - /* PathGuider pathGuider, */ - NextEventEstimator nee) - {} + // static this_t create(RandGen randGen, + // RayGen rayGen, + // Intersector intersector, + // MaterialSystem materialSystem, + // /* PathGuider pathGuider, */ + // NextEventEstimator nee) + // {} - // closest hit + static this_t create(NBL_CONST_REF_ARG(PathTracerCreationParams) params) + { + this_t retval; + retval.randGen = randgen_type::create(params.rngState); + retval.rayGen = raygen_type::create(params.pixOffsetParam, params.camPos, params.NDC, params.invMVP); + retval.materialSystem = material_system_type::create(diffuseParams, conductorParams, dielectricParams); + return retval; + } + + // TODO: get working, what is sampleSequence stuff + vector3_type rand3d(uint32_t protoDimension, uint32_t _sample) + { + uint32_t address = spirv::bitfieldInsert(protoDimension, _sample, MAX_DEPTH_LOG2, MAX_SAMPLES_LOG2); + unit32_t3 seqVal = texelFetch(sampleSequence, int(address) + i).xyz; + seqVal ^= unit32_t3(randGen(), randGen(), randGen()); + return vector3_type(seqVal) * asfloat(0x2f800004u); + } + + bool closestHitProgram(unit32_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray) + { + const uint32_t objectID = ray.objectID; + const vector3_type intersection = ray.origin + ray.direction * ray.intersectionT; + + uint32_t bsdfLightIDs; + } + + 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 - MaterialSystem::measure_type getMeasure() + measure_type getMeasure(uint32_t numSamples, uint32_t depth, NBL_CONST_REF_ARG(Scene) scene) { // loop through bounces, do closest hit // return ray.payload.accumulation --> color + + // TODO: not hardcode this, pass value from somewhere?, where to get objects? + Intersector::IntersectData data; + + measure_type Li = (measure_type)0.0; + scalar_type meanLumaSq = 0.0; + for (uint32_t i = 0; i < numSamples; i++) + { + vector3_type uvw = rand3d(0u, i); + ray_type ray = rayGen.generate(uvw); + + // bounces + bool hit = true; + bool rayAlive = true; + for (int d = 1; d <= depth && hit && rayAlive; d += 2) + { + ray.intersectionT = numeric_limits::max; + ray.objectID = -1; // start with no intersect + + // prodedural shapes + if (scene.sphereCount > 0) + { + data = Intersector::IntersectData::encode(Intersector::IntersectData::Mode::PROCEDURAL, PST_SPHERE, scene); + ray.objectID = intersector.traceRay(ray, data); + } + + if (scene.triangleCount > 0) + { + data = Intersector::IntersectData::encode(Intersector::IntersectData::Mode::PROCEDURAL, PST_TRIANGLE, scene); + ray.objectID = intersector.traceRay(ray, data); + } + + if (scene.rectangleCount > 0) + { + data = Intersector::IntersectData::encode(Intersector::IntersectData::Mode::PROCEDURAL, PST_RECTANGLE, scene); + ray.objectID = intersector.traceRay(ray, data); + } + + // TODO: trace AS + + hit = ray.objectID != -1; + if (hit) + rayAlive = closestHitProgram(d, i, ray); + } + if (!hit) + missProgram(ray); + + spectral_type accumulation = ray.payload.accumulation; + scalar_type rcpSampleSize = 1.0 / (i + 1); + Li += (accumulation - Li) * rcpSampleSize; + + // TODO: visualize high variance + } + + return Li; } + + randgen_type randGen; + raygen_type rayGen; + intersector_type intersector; + material_system_type materialSystem; + nee_type nee; }; } From f5adbf6494a28624db0b6204f34b0235a8687c3c Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 14 Feb 2025 16:35:04 +0700 Subject: [PATCH 12/53] moved scene rep out, some closest hit stuff --- .../app_resources/hlsl/common.hlsl | 68 +++---- .../app_resources/hlsl/intersector.hlsl | 84 +------- .../hlsl/next_event_estimator.hlsl | 8 +- .../app_resources/hlsl/pathtracer.hlsl | 60 +++++- .../app_resources/hlsl/scene.hlsl | 190 ++++++++++++++++++ 5 files changed, 289 insertions(+), 121 deletions(-) create mode 100644 31_HLSLPathTracer/app_resources/hlsl/scene.hlsl diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index 7289d508d..00d35a2a9 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -8,7 +8,7 @@ #include #include #include -//#include +#include namespace nbl { @@ -32,6 +32,20 @@ struct Payload // #endif }; +enum ProceduralShapeType : uint16_t +{ + PST_SPHERE, + PST_TRIANGLE, + PST_RECTANGLE +}; + +struct ObjectID +{ + uint32_t id; + uint32_t mode; + ProceduralShapeType shapeType; +}; + template struct Ray { @@ -46,7 +60,7 @@ struct Ray // mutable scalar_type intersectionT; - uint32_t objectID; + ObjectID objectID; Payload payload; }; @@ -56,10 +70,24 @@ struct Light { using spectral_type = Spectrum; + NBL_CONSTEXPR_STATIC_INLINE uint32_t INVALID_ID = 0xffffu; + spectral_type radiance; - uint32_t objectID; + ObjectID objectID; }; +template +struct BxDFNode +{ + using spectral_type = Spectrum; + using params_type = bxdf::SBxDFCreationParams; + + NBL_CONSTEXPR_STATIC_INLINE uint32_t INVALID_ID = 0xffffu; + + params_type params; + ObjectID objectID; +} + template struct Tolerance { @@ -82,13 +110,6 @@ struct Tolerance } } -enum ProceduralShapeType : uint16_t -{ - PST_SPHERE, - PST_TRIANGLE, - PST_RECTANGLE -}; - enum PTPolygonMethod : uint16_t { PPM_AREA, @@ -145,7 +166,7 @@ struct Shape } template - float generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, float32_t3 xi, uint32_t objectID) + float generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, float32_t3 xi) { float32_t3 Z = position - origin; const float distanceSQ = nbl::hlsl::dot(Z,Z); @@ -257,7 +278,7 @@ struct Shape } template - float32_t3 generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, float32_t3 xi, uint32_t objectID) + float32_t3 generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, float32_t3 xi) { switch(polygonMethod) { @@ -409,7 +430,7 @@ struct Shape } template - float32_t3 generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, float32_t3 xi, uint32_t objectID) + float32_t3 generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, float32_t3 xi) { const float32_t3 N = getNormalTimesArea(); const float32_t3 origin2origin = offset - origin; @@ -474,27 +495,6 @@ struct Shape PTPolygonMethod polygonMethod; }; -struct Scene -{ - NBL_CONSTEXPR_STATIC_INLINE uint32_t maxSphereCount = 25; - NBL_CONSTEXPR_STATIC_INLINE uint32_t maxTriangleCount = 12; - NBL_CONSTEXPR_STATIC_INLINE uint32_t maxRectangleCount = 12; - - Shape spheres[maxSphereCount]; - Shape triangles[maxTriangleCount]; - Shape rectangles[maxRectangleCount]; - - uint32_t sphereCount; - uint32_t triangleCount; - uint32_t rectangleCount; - - Light lights[]; - // Material materials[]; - // + obj count for each - - // AS ases; -}; - } } } diff --git a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl index 23706402a..b2d858ef6 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl @@ -23,76 +23,6 @@ namespace Intersector struct IntersectData { - static IntersectData encode(uint32_t mode, ProceduralShapeType type, NBL_CONST_REF_ARG(Scene) scene) - { - IntersectData retval; - retval.mode = mode; - - uint32_t objCount = (type == PST_SPHERE) ? scene.sphereCount : - (type == PST_TRIANGLE) ? scene.triangleCount : - (type == PST_RECTANGLE) ? scene.rectangleCount : - -1; - retval.data[0] = objCount; - retval.data[1] = type; - - switch (type) - { - case PST_SPHERE: - { - for (int i = 0; i < objCount; i++) - { - Shape sphere = scene.spheres[i]; - retval.data[2 + i * Shape::ObjSize] = asuint(sphere.position.x); - retval.data[2 + i * Shape::ObjSize + 1] = asuint(sphere.position.y); - retval.data[2 + i * Shape::ObjSize + 2] = asuint(sphere.position.z); - retval.data[2 + i * Shape::ObjSize + 3] = asuint(sphere.radius); - retval.data[2 + i * Shape::ObjSize + 4] = sphere.bsdfLightIDs; - } - } - break; - case PST_TRIANGLE: - { - for (int i = 0; i < objCount; i++) - { - Shape tri = scene.triangles[i]; - retval.data[2 + i * Shape::ObjSize] = asuint(tri.vertex0.x); - retval.data[2 + i * Shape::ObjSize + 1] = asuint(tri.vertex0.y); - retval.data[2 + i * Shape::ObjSize + 2] = asuint(tri.vertex0.z); - retval.data[2 + i * Shape::ObjSize + 3] = asuint(tri.vertex1.x); - retval.data[2 + i * Shape::ObjSize + 4] = asuint(tri.vertex1.y); - retval.data[2 + i * Shape::ObjSize + 5] = asuint(tri.vertex1.z); - retval.data[2 + i * Shape::ObjSize + 6] = asuint(tri.vertex2.x); - retval.data[2 + i * Shape::ObjSize + 7] = asuint(tri.vertex2.y); - retval.data[2 + i * Shape::ObjSize + 8] = asuint(tri.vertex2.z); - retval.data[2 + i * Shape::ObjSize + 9] = tri.bsdfLightIDs; - } - } - break; - case PST_RECTANGLE: - { - for (int i = 0; i < objCount; i++) - { - Shape rect = scene.rectangles[i]; - retval.data[2 + i * Shape::ObjSize] = asuint(rect.offset.x); - retval.data[2 + i * Shape::ObjSize + 1] = asuint(rect.offset.y); - retval.data[2 + i * Shape::ObjSize + 2] = asuint(rect.offset.z); - retval.data[2 + i * Shape::ObjSize + 3] = asuint(rect.edge0.x); - retval.data[2 + i * Shape::ObjSize + 4] = asuint(rect.edge0.y); - retval.data[2 + i * Shape::ObjSize + 5] = asuint(rect.edge0.z); - retval.data[2 + i * Shape::ObjSize + 6] = asuint(rect.edge1.x); - retval.data[2 + i * Shape::ObjSize + 7] = asuint(rect.edge1.y); - retval.data[2 + i * Shape::ObjSize + 8] = asuint(rect.edge1.z); - retval.data[2 + i * Shape::ObjSize + 9] = rect.bsdfLightIDs; - } - } - break; - default: - // for ASes - break; - } - return retval; - } - enum class Mode : uint32_t { RAY_QUERY, @@ -113,13 +43,15 @@ struct Comprehensive using scalar_type = typename Ray::scalar_type; using ray_type = Ray; - static int traceProcedural(NBL_REF_ARG(ray_type) ray, NBL_REF_ARG(IntersectData) intersect) + static ObjectID traceProcedural(NBL_REF_ARG(ray_type) ray, NBL_REF_ARG(IntersectData) intersect) { const bool anyHit = ray.intersectionT != numeric_limits::max; const uint32_t objCount = intersect.data[0]; const ProceduralShapeType type = intersect.data[1]; - int objectID = ray.objectID; + ObjectID objectID = ray.objectID; + objectID.mode = IntersectData::Mode::PROCEDURAL; + objectID.type = type; for (int i = 0; i < objCount; i++) { float t; @@ -152,13 +84,13 @@ struct Comprehensive break; default: t = numeric_limits::infinity; - break; + break; } bool closerIntersection = t > 0.0 && t < ray.intersectionT; ray.intersectionT = closerIntersection ? t : ray.intersectionT; - objectID = closerIntersection ? i : objectID; + objectID.id = closerIntersection ? i : objectID.id; // allowing early out results in a performance regression, WTF!? //if (anyHit && closerIntersection) @@ -167,7 +99,7 @@ struct Comprehensive return objectID; } - static int traceRay(NBL_REF_ARG(ray_type) ray, NBL_REF_ARG(IntersectData) intersect) + static ObjectID traceRay(NBL_REF_ARG(ray_type) ray, NBL_REF_ARG(IntersectData) intersect) { const IntersectData::Mode mode = intersect.mode; switch (mode) @@ -188,7 +120,7 @@ struct Comprehensive } break; default: - return -1; + return ObjectID(-1, IntersectData::Mode::PROCEDURAL, PST_SPHERE); } } }; diff --git a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl index 5d96ae13e..74cf00926 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl @@ -23,7 +23,7 @@ struct Event PROCEDURAL }; - NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 128; + NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 16; uint32_t mode : 1; unit32_t unused : 31; // possible space for flags @@ -120,7 +120,7 @@ struct Estimator { float32_t3 position = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize]), asfloat(intersect.data[2 + Shape::ObjSize + 1]), asfloat(intersect.data[2 + Shape::ObjSize + 2])); Shape sphere = Shape::create(position, asfloat(intersect.data[2 + Shape::ObjSize + 3]), intersect.data[2 + Shape::ObjSize + 4]); - L = sphere.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi, objectID); + L = sphere.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); } break; case PST_TRIANGLE: @@ -129,7 +129,7 @@ struct Estimator float32_t3 vertex1 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 3]), asfloat(intersect.data[2 + Shape::ObjSize + 4]), asfloat(intersect.data[2 + Shape::ObjSize + 5])); float32_t3 vertex2 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 6]), asfloat(intersect.data[2 + Shape::ObjSize + 7]), asfloat(intersect.data[2 + Shape::ObjSize + 8])); Shape tri = Shape::create(vertex0, vertex1, vertex2, intersect.data[2 + Shape::ObjSize + 9]); - L = tri.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi, objectID); + L = tri.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); } break; case PST_RECTANGLE: @@ -138,7 +138,7 @@ struct Estimator float32_t3 edge0 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 3]), asfloat(intersect.data[2 + Shape::ObjSize + 4]), asfloat(intersect.data[2 + Shape::ObjSize + 5])); float32_t3 edge1 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 6]), asfloat(intersect.data[2 + Shape::ObjSize + 7]), asfloat(intersect.data[2 + Shape::ObjSize + 8])); Shape rect = Shape::create(offset, edge0, edge1, intersect.data[2 + Shape::ObjSize + 9]); - L = rect.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi, objectID); + L = rect.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); } break; default: diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index 06950b825..80a342a86 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -42,6 +42,10 @@ struct Unidirectional using vector3_type = vector; using measure_type = typename MaterialSystem::measure_type; using ray_type = typename RayGen::ray_type; + using light_type = Light; + using bxdfnode_type = BxDFNode; + using anisotropic_type = typename MaterialSystem::anisotropic_type; + using isotropic_type = typename anisotropic_type::isotropic_type; // static this_t create(RandGen randGen, // RayGen rayGen, @@ -69,12 +73,54 @@ struct Unidirectional return vector3_type(seqVal) * asfloat(0x2f800004u); } - bool closestHitProgram(unit32_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray) + // TODO: probably will only work with procedural shapes, do the other ones + bool closestHitProgram(unit32_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(Scene) scene) { const uint32_t objectID = ray.objectID; const vector3_type intersection = ray.origin + ray.direction * ray.intersectionT; uint32_t bsdfLightIDs; + anisotropic_type interaction; + switch (objectID.mode) + { + // TODO + case Intersector::IntersectData::Mode::RAY_QUERY: + case Intersector::IntersectData::Mode::RAY_TRACING: + break; + case Intersector::IntersectData::Mode::PROCEDURAL: + { + bsdfLightIDs = scene.getBsdfLightIDs(objectID.id); + vector3_type N = scene.getNormal(objectID.id) + N = nbl::hlsl::normalize(N); + typename isotropic_type::ray_dir_info_type V; + V.direction = nbl::hlsl::normalize(-ray.direction); + isotropic_type iso = isotropic_type::create(V, N); + interaction = anisotropic_type::create(iso); + } + break; + default: + break; + } + + vector3_type throughput = ray.payload.throughput; + + // emissive + const uint32_t lightID = spirv::bitfieldExtract(bsdfLightIDs, 16, 16); + if (lightID != light_type::INVALID_ID) + { + float pdf; + ray.payload.accumulation += nee.deferredEvalAndPdf(pdf, lights[lightID], ray, scene.toNextEvent(lightID)) * throughput / (1.0 + pdf * pdf * ray.payload.otherTechniqueHeuristic); + } + + const uint32_t bsdfID = spirv::bitfieldExtract(bsdfLightIDs, 0, 16); + if (bsdfID == bxdfnode_type::INVALID_ID) + return false; + + // TODO: ifdef kill diffuse specular paths + + // sample lights + + // sample BSDF } void missProgram(NBL_REF_ARG(ray_type) ray) @@ -112,32 +158,32 @@ struct Unidirectional for (int d = 1; d <= depth && hit && rayAlive; d += 2) { ray.intersectionT = numeric_limits::max; - ray.objectID = -1; // start with no intersect + ray.objectID.id = -1; // start with no intersect // prodedural shapes if (scene.sphereCount > 0) { - data = Intersector::IntersectData::encode(Intersector::IntersectData::Mode::PROCEDURAL, PST_SPHERE, scene); + data = scene.toIntersectData(Intersector::IntersectData::Mode::PROCEDURAL, PST_SPHERE); ray.objectID = intersector.traceRay(ray, data); } if (scene.triangleCount > 0) { - data = Intersector::IntersectData::encode(Intersector::IntersectData::Mode::PROCEDURAL, PST_TRIANGLE, scene); + data = scene.toIntersectData(Intersector::IntersectData::Mode::PROCEDURAL, PST_TRIANGLE); ray.objectID = intersector.traceRay(ray, data); } if (scene.rectangleCount > 0) { - data = Intersector::IntersectData::encode(Intersector::IntersectData::Mode::PROCEDURAL, PST_RECTANGLE, scene); + data = scene.toIntersectData(Intersector::IntersectData::Mode::PROCEDURAL, PST_RECTANGLE); ray.objectID = intersector.traceRay(ray, data); } // TODO: trace AS - hit = ray.objectID != -1; + hit = ray.objectID.id != -1; if (hit) - rayAlive = closestHitProgram(d, i, ray); + rayAlive = closestHitProgram(d, i, ray, scene); } if (!hit) missProgram(ray); diff --git a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl new file mode 100644 index 000000000..ea173e1a7 --- /dev/null +++ b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl @@ -0,0 +1,190 @@ +#ifndef _NBL_HLSL_EXT_PATHTRACING_SCENE_INCLUDED_ +#define _NBL_HLSL_EXT_PATHTRACING_SCENE_INCLUDED_ + +#include "common.hlsl" +#include "material_system.hlsl" +#include "next_event_estimator.hlsl" +#include "intersector.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace ext +{ + +struct Scene +{ + NBL_CONSTEXPR_STATIC_INLINE uint32_t maxSphereCount = 25; + NBL_CONSTEXPR_STATIC_INLINE uint32_t maxTriangleCount = 12; + NBL_CONSTEXPR_STATIC_INLINE uint32_t maxRectangleCount = 12; + + Shape spheres[maxSphereCount]; + Shape triangles[maxTriangleCount]; + Shape rectangles[maxRectangleCount]; + + uint32_t sphereCount; + uint32_t triangleCount; + uint32_t rectangleCount; + + NBL_CONSTEXPR_STATIC_INLINE uint32_t maxLightCount = 4; + + Light lights[maxLightCount]; + uint32_t lightCount; + // Material materials[]; + // + obj count for each + + // AS ases; + + Intersector::IntersectData toIntersectData(uint32_t mode, ProceduralShapeType type) + { + Intersector::IntersectData retval; + retval.mode = mode; + + uint32_t objCount = (type == PST_SPHERE) ? sphereCount : + (type == PST_TRIANGLE) ? triangleCount : + (type == PST_RECTANGLE) ? rectangleCount : + -1; + retval.data[0] = objCount; + retval.data[1] = type; + + switch (type) + { + case PST_SPHERE: + { + for (int i = 0; i < objCount; i++) + { + Shape sphere = spheres[i]; + retval.data[2 + i * Shape::ObjSize] = asuint(sphere.position.x); + retval.data[2 + i * Shape::ObjSize + 1] = asuint(sphere.position.y); + retval.data[2 + i * Shape::ObjSize + 2] = asuint(sphere.position.z); + retval.data[2 + i * Shape::ObjSize + 3] = asuint(sphere.radius); + retval.data[2 + i * Shape::ObjSize + 4] = sphere.bsdfLightIDs; + } + } + break; + case PST_TRIANGLE: + { + for (int i = 0; i < objCount; i++) + { + Shape tri = triangles[i]; + retval.data[2 + i * Shape::ObjSize] = asuint(tri.vertex0.x); + retval.data[2 + i * Shape::ObjSize + 1] = asuint(tri.vertex0.y); + retval.data[2 + i * Shape::ObjSize + 2] = asuint(tri.vertex0.z); + retval.data[2 + i * Shape::ObjSize + 3] = asuint(tri.vertex1.x); + retval.data[2 + i * Shape::ObjSize + 4] = asuint(tri.vertex1.y); + retval.data[2 + i * Shape::ObjSize + 5] = asuint(tri.vertex1.z); + retval.data[2 + i * Shape::ObjSize + 6] = asuint(tri.vertex2.x); + retval.data[2 + i * Shape::ObjSize + 7] = asuint(tri.vertex2.y); + retval.data[2 + i * Shape::ObjSize + 8] = asuint(tri.vertex2.z); + retval.data[2 + i * Shape::ObjSize + 9] = tri.bsdfLightIDs; + } + } + break; + case PST_RECTANGLE: + { + for (int i = 0; i < objCount; i++) + { + Shape rect = rectangles[i]; + retval.data[2 + i * Shape::ObjSize] = asuint(rect.offset.x); + retval.data[2 + i * Shape::ObjSize + 1] = asuint(rect.offset.y); + retval.data[2 + i * Shape::ObjSize + 2] = asuint(rect.offset.z); + retval.data[2 + i * Shape::ObjSize + 3] = asuint(rect.edge0.x); + retval.data[2 + i * Shape::ObjSize + 4] = asuint(rect.edge0.y); + retval.data[2 + i * Shape::ObjSize + 5] = asuint(rect.edge0.z); + retval.data[2 + i * Shape::ObjSize + 6] = asuint(rect.edge1.x); + retval.data[2 + i * Shape::ObjSize + 7] = asuint(rect.edge1.y); + retval.data[2 + i * Shape::ObjSize + 8] = asuint(rect.edge1.z); + retval.data[2 + i * Shape::ObjSize + 9] = rect.bsdfLightIDs; + } + } + break; + default: + // for ASes + break; + } + return retval; + } + + NextEventEstimator::Event toNextEvent(uint32_t lightID) + { + NextEventEstimator::Event retval; + + ObjectID objectID = lights[lightID].objectID; + retval.mode = objectID.mode; + + retval.data[0] = lightCount; + retval.data[1] = objectID.type; + + uint32_t id = objectID.id; + switch (type) + { + case PST_SPHERE: + { + Shape sphere = spheres[id]; + retval.data[2 + Shape::ObjSize] = asuint(sphere.position.x); + retval.data[2 + Shape::ObjSize + 1] = asuint(sphere.position.y); + retval.data[2 + Shape::ObjSize + 2] = asuint(sphere.position.z); + retval.data[2 + Shape::ObjSize + 3] = asuint(sphere.radius); + retval.data[2 + Shape::ObjSize + 4] = sphere.bsdfLightIDs; + } + break; + case PST_TRIANGLE: + { + Shape tri = triangles[id]; + retval.data[2 + Shape::ObjSize] = asuint(tri.vertex0.x); + retval.data[2 + Shape::ObjSize + 1] = asuint(tri.vertex0.y); + retval.data[2 + Shape::ObjSize + 2] = asuint(tri.vertex0.z); + retval.data[2 + Shape::ObjSize + 3] = asuint(tri.vertex1.x); + retval.data[2 + Shape::ObjSize + 4] = asuint(tri.vertex1.y); + retval.data[2 + Shape::ObjSize + 5] = asuint(tri.vertex1.z); + retval.data[2 + Shape::ObjSize + 6] = asuint(tri.vertex2.x); + retval.data[2 + Shape::ObjSize + 7] = asuint(tri.vertex2.y); + retval.data[2 + Shape::ObjSize + 8] = asuint(tri.vertex2.z); + retval.data[2 + Shape::ObjSize + 9] = tri.bsdfLightIDs; + } + break; + case PST_RECTANGLE: + { + Shape rect = rectangles[id]; + retval.data[2 + Shape::ObjSize] = asuint(rect.offset.x); + retval.data[2 + Shape::ObjSize + 1] = asuint(rect.offset.y); + retval.data[2 + Shape::ObjSize + 2] = asuint(rect.offset.z); + retval.data[2 + Shape::ObjSize + 3] = asuint(rect.edge0.x); + retval.data[2 + Shape::ObjSize + 4] = asuint(rect.edge0.y); + retval.data[2 + Shape::ObjSize + 5] = asuint(rect.edge0.z); + retval.data[2 + Shape::ObjSize + 6] = asuint(rect.edge1.x); + retval.data[2 + Shape::ObjSize + 7] = asuint(rect.edge1.y); + retval.data[2 + Shape::ObjSize + 8] = asuint(rect.edge1.z); + retval.data[2 + Shape::ObjSize + 9] = rect.bsdfLightIDs; + } + break; + default: + // for ASes + break; + } + return retval; + } + + // TODO: get these to work with AS types as well + uint32_t getBsdfLightIDs(uint32_t id) + { + return (objectID.type == PST_SPHERE) ? spheres[id].bsdfLightIDs : + (objectID.type == PST_TRIANGLE) ? triangles[id].bsdfLightIDs : + (objectID.type == PST_RECTANGLE) ? rectangles[id].bsdfLightIDs : -1; + } + + float32_t3 getNormal(uint32_t id, NBL_CONST_REF_ARG(float32_t3) intersection) + { + return (objectID.type == PST_SPHERE) ? scene.spheres[id].getNormal(intersection) : + (objectID.type == PST_TRIANGLE) ? scene.triangles[id].getNormalTimesArea() : + (objectID.type == PST_RECTANGLE) ? scene.rectangles[id].getNormalTimesArea() : + (float32_t3)0.0; + } +}; + +} +} +} + +#endif From 159d1533e8d82e3c5e82165e8b79ea67c0f23111 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 17 Feb 2025 16:58:03 +0700 Subject: [PATCH 13/53] sample light part of closest hit --- .../app_resources/hlsl/common.hlsl | 1 + .../app_resources/hlsl/intersector.hlsl | 2 +- .../app_resources/hlsl/material_system.hlsl | 8 +- .../hlsl/next_event_estimator.hlsl | 2 +- .../app_resources/hlsl/pathtracer.hlsl | 132 ++++++++++++++++-- .../app_resources/hlsl/scene.hlsl | 7 +- 6 files changed, 137 insertions(+), 15 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index 00d35a2a9..7d29dabd4 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -84,6 +84,7 @@ struct BxDFNode NBL_CONSTEXPR_STATIC_INLINE uint32_t INVALID_ID = 0xffffu; + uint32_t materialType; params_type params; ObjectID objectID; } diff --git a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl index b2d858ef6..60aa7143b 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl @@ -23,7 +23,7 @@ namespace Intersector struct IntersectData { - enum class Mode : uint32_t + enum Mode : uint32_t // enum class? { RAY_QUERY, RAY_TRACING, diff --git a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl index 1f13198fa..b89bfbd40 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl @@ -15,7 +15,7 @@ namespace MaterialSystem struct Material { - enum class Type : uint32_t + enum Type : uint32_t // enum class? { DIFFUSE, CONDUCTOR, @@ -29,7 +29,7 @@ struct Material uint32_t data[DataSize]; }; -template +template // NOTE: these bxdfs should match the ones in Scene BxDFNode struct System { using this_t = System; @@ -42,6 +42,10 @@ struct System using anisocache_type = typename ConductorBxDF::anisocache_type; using params_t = SBxDFParams; + using diffuse_op_type = DiffuseBxDF; + using conductor_op_type = ConductorBxDF; + using dielectric_op_type = DielectricBxDF; + static this_t create(NBL_CONST_REF_ARG(SBxDFCreationParams) diffuseParams, NBL_CONST_REF_ARG(SBxDFCreationParams) conductorParams, NBL_CONST_REF_ARG(SBxDFCreationParams) dielectricParams) { diffuseBxDF = DiffuseBxDF::create(diffuseParams); diff --git a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl index 74cf00926..c6380094d 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl @@ -16,7 +16,7 @@ namespace NextEventEstimator struct Event { - enum class Mode : uint32_t + enum Mode : uint32_t // enum class? { RAY_QUERY, RAY_TRACING, diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index 80a342a86..e4638703a 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -1,6 +1,16 @@ #ifndef _NBL_HLSL_EXT_PATHTRACER_INCLUDED_ #define _NBL_HLSL_EXT_PATHTRACER_INCLUDED_ +#include +#include +#include + +#include "rand_gen.hlsl" +#include "ray_gen.hlsl" +#include "intersector.hlsl" +#include "material_system.hlsl" +#include "next_event_estimator.hlsl" + namespace nbl { namespace hlsl @@ -41,11 +51,20 @@ struct Unidirectional using scalar_type = typename MaterialSystem::scalar_type; using vector3_type = vector; using measure_type = typename MaterialSystem::measure_type; + using sample_type = typename NextEventEstimator::sample_type; using ray_type = typename RayGen::ray_type; using light_type = Light; using bxdfnode_type = BxDFNode; using anisotropic_type = typename MaterialSystem::anisotropic_type; using isotropic_type = typename anisotropic_type::isotropic_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 params_type = typename MaterialSystem::params_t; + + 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; // static this_t create(RandGen randGen, // RayGen rayGen, @@ -73,6 +92,11 @@ struct Unidirectional return vector3_type(seqVal) * asfloat(0x2f800004u); } + scalar_type getLuma(NBL_CONST_REF_ARG(vector3_type) col) + { + return nbl::hlsl::dot(nbl::hlsl::transpose(colorspace::scRGBtoXYZ)[1], col); + } + // TODO: probably will only work with procedural shapes, do the other ones bool closestHitProgram(unit32_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(Scene) scene) { @@ -81,21 +105,22 @@ struct Unidirectional uint32_t bsdfLightIDs; anisotropic_type interaction; + isotropic_type iso_interaction; switch (objectID.mode) { // TODO - case Intersector::IntersectData::Mode::RAY_QUERY: - case Intersector::IntersectData::Mode::RAY_TRACING: + case ext::Intersector::IntersectData::Mode::RAY_QUERY: + case ext::Intersector::IntersectData::Mode::RAY_TRACING: break; - case Intersector::IntersectData::Mode::PROCEDURAL: + case ext::Intersector::IntersectData::Mode::PROCEDURAL: { bsdfLightIDs = scene.getBsdfLightIDs(objectID.id); vector3_type N = scene.getNormal(objectID.id) N = nbl::hlsl::normalize(N); typename isotropic_type::ray_dir_info_type V; V.direction = nbl::hlsl::normalize(-ray.direction); - isotropic_type iso = isotropic_type::create(V, N); - interaction = anisotropic_type::create(iso); + isotropic_type iso_interaction = isotropic_type::create(V, N); + interaction = anisotropic_type::create(iso_interaction); } break; default: @@ -116,9 +141,98 @@ struct Unidirectional if (bsdfID == bxdfnode_type::INVALID_ID) return false; + BxDFNode bxdf = scene.bxdfs[bsdfID]; + // TODO: ifdef kill diffuse specular paths + const bool isBSDF = (bxdf.materialType == ext::MaterialSystem::Material::DIFFUSE) ? bxdf_traits::type == BT_BSDF : + (bxdf.materialType == ext::MaterialSystem::Material::CONDUCTOR) ? bxdf_traits::type == BT_BSDF : + bxdf_traits::type == BT_BSDF; + + vector3_type eps0 = rand3d(depth, _sample); + vector3_type eps1 = rand3d(depth, _sample); + vector3_type eps2 = rand3d(depth, _sample); + + // thresholds + const scalar_type bsdfPdfThreshold = 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 = nbl::hlsl::transpose(colorspace::sRGBtoXYZ)[1] * throughput; // TODO: this only works if spectral_type is dim 3 + const scalar_type monochromeEta = nbl::hlsl::dot(throughputCIE_Y, BSDFNode_getEta(bsdf)[0]) / (throughputCIE_Y.r + throughputCIE_Y.g + throughputCIE_Y.b); // TODO: fix getEta, what is real eta + // sample lights + const scalar_type neeProbability = 1.0;// BSDFNode_getNEEProb(bsdf); + scalar_type rcpChoiceProb; + if (!math::partitionRandVariable(neeProbability, eps0.z, rcpChoiceProb) && depth < 2u) + { + quotient_pdf_type neeContrib_pdf; + scalar_type t; + sample_type nee_sample = nee.generate_and_quotient_and_pdf( + neeContrib_pdf, t, + intersection, interaction, + isBSDF, eps0, depth + ); + + // We don't allow non watertight transmitters in this renderer + bool validPath = nee_sample.NdotL > numeric_limits::min; + // but if we allowed non-watertight transmitters (single water surface), it would make sense just to apply this line by itself + anisocache_type _cache; + validPath = validPath && anisocache_type::compute(_cache, interaction, nee_sample, monochromeEta); + + if (neeContrib_pdf.pdf < numeric_limits::max) + { + if (nbl::hlsl::any(isnan(nee_sample.L))) + ray.payload.accumulation += vector3_type(1000.f, 0.f, 0.f); + else if (nbl::hlsl::all((vector3_type)69.f == nee_sample.L)) + ray.payload.accumulation += vector3_type(0.f, 1000.f, 0.f); + else if (validPath) + { + ext::MaterialSystem::Material material; + material.type = bxdf.materialType; + params_type params; + + // TODO: does not yet account for smooth dielectric + if (!isBSDF && bxdf.materialType == ext::MaterialSystem::Material::DIFFUSE) + { + params = params_type::template create(nee_sample, iso_interaction, bxdf::BCM_MAX); + } + else if (!isBSDF && bxdf.materialType != ext::MaterialSystem::Material::DIFFUSE) + { + if (bxdf.params.is_aniso) + params = params_type::template create(nee_sample, interaction, _cache, bxdf::BCM_MAX); + else + { + isocache = (iso_cache)_cache; + params = params_type::template create(nee_sample, iso_interaction, isocache, bxdf::BCM_MAX); + } + } + else if (isBSDF && bxdf.materialType == ext::MaterialSystem::Material::DIFFUSE) + { + params = params_type::template create(nee_sample, iso_interaction, bxdf::BCM_ABS); + } + else if (isBSDF && bxdf.materialType != ext::MaterialSystem::Material::DIFFUSE) + { + if (bxdf.params.is_aniso) + params = params_type::template create(nee_sample, interaction, _cache, bxdf::BCM_ABS); + else + { + isocache = (iso_cache)_cache; + params = params_type::template create(nee_sample, iso_interaction, isocache, bxdf::BCM_ABS); + } + } + + quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(material, params) * throughput; + neeContrib_pdf.quotient *= 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 + + if (bsdf_quotient_pdf.pdf < numeric_limits::max && getLuma(neeContrib_pdf.quotient) > lumaContributionThreshold && traceRay(t,intersection+nee_sample.L*t*getStartTolerance(depth),nee_sample.L)==-1) + ray._payload.accumulation += neeContrib_pdf.quotient; + } + } + } // sample BSDF } @@ -143,7 +257,7 @@ struct Unidirectional // return ray.payload.accumulation --> color // TODO: not hardcode this, pass value from somewhere?, where to get objects? - Intersector::IntersectData data; + ext::Intersector::IntersectData data; measure_type Li = (measure_type)0.0; scalar_type meanLumaSq = 0.0; @@ -163,19 +277,19 @@ struct Unidirectional // prodedural shapes if (scene.sphereCount > 0) { - data = scene.toIntersectData(Intersector::IntersectData::Mode::PROCEDURAL, PST_SPHERE); + data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_SPHERE); ray.objectID = intersector.traceRay(ray, data); } if (scene.triangleCount > 0) { - data = scene.toIntersectData(Intersector::IntersectData::Mode::PROCEDURAL, PST_TRIANGLE); + data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_TRIANGLE); ray.objectID = intersector.traceRay(ray, data); } if (scene.rectangleCount > 0) { - data = scene.toIntersectData(Intersector::IntersectData::Mode::PROCEDURAL, PST_RECTANGLE); + data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_RECTANGLE); ray.objectID = intersector.traceRay(ray, data); } diff --git a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl index ea173e1a7..fe4dea8b3 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl @@ -31,8 +31,11 @@ struct Scene Light lights[maxLightCount]; uint32_t lightCount; - // Material materials[]; - // + obj count for each + + NBL_CONSTEXPR_STATIC_INLINE uint32_t maxBxdfCount = 16; // TODO: limit change? + + BxDFNode bxdfs[maxBxdfCount]; + uint32_t bxdfCount; // AS ases; From a7350db7d7e422fa5086982b3327103c06cfbe44 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 18 Feb 2025 15:23:52 +0700 Subject: [PATCH 14/53] fix bugs, reorganize traceRay --- .../app_resources/hlsl/intersector.hlsl | 32 +++++++++++++ .../app_resources/hlsl/material_system.hlsl | 16 +++++-- .../app_resources/hlsl/pathtracer.hlsl | 47 ++++++------------- .../app_resources/hlsl/scene.hlsl | 8 +++- 4 files changed, 65 insertions(+), 38 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl index 60aa7143b..cf2d3ae7c 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl @@ -123,6 +123,38 @@ struct Comprehensive return ObjectID(-1, IntersectData::Mode::PROCEDURAL, PST_SPHERE); } } + + template + static ObjectID traceRay(NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(Scene) scene) + { + IntersectData data; + + ObjectID objectID; + objectID.id = -1; // start with no intersect + + // prodedural shapes + if (scene.sphereCount > 0) + { + data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_SPHERE); + objectID = intersector.traceRay(ray, data); + } + + if (scene.triangleCount > 0) + { + data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_TRIANGLE); + objectID = intersector.traceRay(ray, data); + } + + if (scene.rectangleCount > 0) + { + data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_RECTANGLE); + objectID = intersector.traceRay(ray, data); + } + + // TODO: trace AS + + return objectID; + } }; // does everything in traceray in ex 30 diff --git a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl index b89bfbd40..1d5587443 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl @@ -41,6 +41,7 @@ struct System using anisotropic_type = typename DiffuseBxDF::anisotropic_type; using anisocache_type = typename ConductorBxDF::anisocache_type; using params_t = SBxDFParams; + using create_params_t = SBxDFCreationParams; using diffuse_op_type = DiffuseBxDF; using conductor_op_type = ConductorBxDF; @@ -53,22 +54,25 @@ struct System dielectricBxDF = DiffuseBxDF::create(dielectricParams); } - static measure_type eval(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(params_t) params) + static measure_type eval(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, NBL_CONST_REF_ARG(params_t) params) { switch(material.type) { case DIFFUSE: { + diffuseBxDF.init(cparams); return (measure_type)diffuseBxDF.eval(params); } break; case CONDUCTOR: { + conductorBxDF.init(cparams); return conductorBxDF.eval(params); } break; case DIELECTRIC: { + dielectricBxDF.init(cparams); return dielectricBxDF.eval(params); } break; @@ -77,22 +81,25 @@ struct System } } - static vector3_type generate(NBL_CONST_REF_ARG(Material) material, anisotropic_type interaction, vector2_type u, NBL_REF_ARG(anisocache_type) cache) + static vector3_type generate(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, anisotropic_type interaction, vector2_type u, NBL_REF_ARG(anisocache_type) cache) { switch(material.type) { case DIFFUSE: { + diffuseBxDF.init(cparams); return diffuseBxDF.generate(interaction, u); } break; case CONDUCTOR: { + conductorBxDF.init(cparams); return conductorBxDF.generate(interaction, u, cache); } break; case DIELECTRIC: { + dielectricBxDF.init(cparams); return dielectricBxDF.generate(interaction, u, cache); } break; @@ -101,7 +108,7 @@ struct System } } - static quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(params_t) params) + static quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, NBL_CONST_REF_ARG(params_t) params) { const float minimumProjVectorLen = 0.00000001; if (params.NdotV > minimumProjVectorLen && params.NdotL > minimumProjVectorLen) @@ -110,16 +117,19 @@ struct System { case DIFFUSE: { + diffuseBxDF.init(cparams); return diffuseBxDF.quotient_and_pdf(params); } break; case CONDUCTOR: { + conductorBxDF.init(cparams); return conductorBxDF.quotient_and_pdf(params); } break; case DIELECTRIC: { + dielectricBxDF.init(cparams); return dielectricBxDF.quotient_and_pdf(params); } break; diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index e4638703a..8d8d9a201 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -61,6 +61,7 @@ struct Unidirectional using isocache_type = typename anisocache_type::isocache_type; using quotient_pdf_type = typename NextEventEstimator::quotient_pdf_type; using params_type = typename MaterialSystem::params_t; + using scene_type = Scene; using diffuse_op_type = typename MaterialSystem::diffuse_op_type; using conductor_op_type = typename MaterialSystem::conductor_op_type; @@ -98,7 +99,7 @@ struct Unidirectional } // TODO: probably will only work with procedural shapes, do the other ones - bool closestHitProgram(unit32_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(Scene) scene) + bool closestHitProgram(unit32_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(scene_type) scene) { const uint32_t objectID = ray.objectID; const vector3_type intersection = ray.origin + ray.direction * ray.intersectionT; @@ -157,7 +158,8 @@ struct Unidirectional const scalar_type bsdfPdfThreshold = 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 = nbl::hlsl::transpose(colorspace::sRGBtoXYZ)[1] * throughput; // TODO: this only works if spectral_type is dim 3 - const scalar_type monochromeEta = nbl::hlsl::dot(throughputCIE_Y, BSDFNode_getEta(bsdf)[0]) / (throughputCIE_Y.r + throughputCIE_Y.g + throughputCIE_Y.b); // TODO: fix getEta, what is real eta + const measure_type eta = bxdf.params.ior0 / bxdf.params.ior1; // assume it's real, not imaginary? + const scalar_type monochromeEta = nbl::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); @@ -177,6 +179,8 @@ struct Unidirectional // but if we allowed non-watertight transmitters (single water surface), it would make sense just to apply this line by itself anisocache_type _cache; validPath = validPath && anisocache_type::compute(_cache, interaction, nee_sample, monochromeEta); + bxdf.params.A = nbl::hlsl::max(bxdf.params.A, vector(0,0)); + bxdf.params.eta = monochromeEta; if (neeContrib_pdf.pdf < numeric_limits::max) { @@ -220,7 +224,7 @@ struct Unidirectional } } - quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(material, params) * throughput; + quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(material, bxdf.params, params) * throughput; neeContrib_pdf.quotient *= bsdf_quotient_pdf.quotient; const scalar_type otherGenOverChoice = bsdf_quotient_pdf.pdf * rcpChoiceProb; const scalar_type otherGenOverLightAndChoice = otherGenOverChoice / bsdf_quotient_pdf.pdf; @@ -228,7 +232,11 @@ struct Unidirectional // TODO: ifdef NEE only - if (bsdf_quotient_pdf.pdf < numeric_limits::max && getLuma(neeContrib_pdf.quotient) > lumaContributionThreshold && traceRay(t,intersection+nee_sample.L*t*getStartTolerance(depth),nee_sample.L)==-1) + ray_type nee_ray; + nee_ray.origin = intersection + nee_sample.L * t * Tolerance::getStart(depth); + nee_ray.direction = nee_sample.L; + nee_ray.intersectionT = t; + if (bsdf_quotient_pdf.pdf < numeric_limits::max && getLuma(neeContrib_pdf.quotient) > lumaContributionThreshold && intersector.traceRay(nee_ray, scene).id == -1) ray._payload.accumulation += neeContrib_pdf.quotient; } } @@ -251,14 +259,8 @@ struct Unidirectional } // Li - measure_type getMeasure(uint32_t numSamples, uint32_t depth, NBL_CONST_REF_ARG(Scene) scene) + measure_type getMeasure(uint32_t numSamples, uint32_t depth, NBL_CONST_REF_ARG(scene_type) scene) { - // loop through bounces, do closest hit - // return ray.payload.accumulation --> color - - // TODO: not hardcode this, pass value from somewhere?, where to get objects? - ext::Intersector::IntersectData data; - measure_type Li = (measure_type)0.0; scalar_type meanLumaSq = 0.0; for (uint32_t i = 0; i < numSamples; i++) @@ -272,28 +274,7 @@ struct Unidirectional for (int d = 1; d <= depth && hit && rayAlive; d += 2) { ray.intersectionT = numeric_limits::max; - ray.objectID.id = -1; // start with no intersect - - // prodedural shapes - if (scene.sphereCount > 0) - { - data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_SPHERE); - ray.objectID = intersector.traceRay(ray, data); - } - - if (scene.triangleCount > 0) - { - data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_TRIANGLE); - ray.objectID = intersector.traceRay(ray, data); - } - - if (scene.rectangleCount > 0) - { - data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_RECTANGLE); - ray.objectID = intersector.traceRay(ray, data); - } - - // TODO: trace AS + ray.objectID = intersector.traceRay(ray, scene); hit = ray.objectID.id != -1; if (hit) diff --git a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl index fe4dea8b3..cbc9d153c 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl @@ -13,8 +13,12 @@ namespace hlsl namespace ext { +template struct Scene { + using light_type = Light; + using bxdfnode_type = BxdfNode; + NBL_CONSTEXPR_STATIC_INLINE uint32_t maxSphereCount = 25; NBL_CONSTEXPR_STATIC_INLINE uint32_t maxTriangleCount = 12; NBL_CONSTEXPR_STATIC_INLINE uint32_t maxRectangleCount = 12; @@ -29,12 +33,12 @@ struct Scene NBL_CONSTEXPR_STATIC_INLINE uint32_t maxLightCount = 4; - Light lights[maxLightCount]; + light_type lights[maxLightCount]; uint32_t lightCount; NBL_CONSTEXPR_STATIC_INLINE uint32_t maxBxdfCount = 16; // TODO: limit change? - BxDFNode bxdfs[maxBxdfCount]; + bxdfnode_type bxdfs[maxBxdfCount]; uint32_t bxdfCount; // AS ases; From 8a4e0a94aab11c6eb0072ca0044db26ffe433a91 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 18 Feb 2025 16:45:20 +0700 Subject: [PATCH 15/53] sample bsdf in closest hit --- .../app_resources/hlsl/material_system.hlsl | 6 +- .../app_resources/hlsl/pathtracer.hlsl | 95 ++++++++++++++++--- 2 files changed, 86 insertions(+), 15 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl index 1d5587443..038bd578a 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl @@ -81,20 +81,20 @@ struct System } } - static vector3_type generate(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, anisotropic_type interaction, vector2_type u, NBL_REF_ARG(anisocache_type) cache) + static vector3_type generate(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, anisotropic_type interaction, NBL_CONST_REF_ARG(vector3_type) u, NBL_REF_ARG(anisocache_type) cache) { switch(material.type) { case DIFFUSE: { diffuseBxDF.init(cparams); - return diffuseBxDF.generate(interaction, u); + return diffuseBxDF.generate(interaction, u.xy); } break; case CONDUCTOR: { conductorBxDF.init(cparams); - return conductorBxDF.generate(interaction, u, cache); + return conductorBxDF.generate(interaction, u.xy, cache); } break; case DIELECTRIC: diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index 8d8d9a201..e20ef705b 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -61,6 +61,7 @@ struct Unidirectional using isocache_type = typename anisocache_type::isocache_type; using quotient_pdf_type = typename NextEventEstimator::quotient_pdf_type; using params_type = typename MaterialSystem::params_t; + using create_params_type = typename MaterialSystem::create_params_t; using scene_type = Scene; using diffuse_op_type = typename MaterialSystem::diffuse_op_type; @@ -75,17 +76,17 @@ struct Unidirectional // NextEventEstimator nee) // {} - static this_t create(NBL_CONST_REF_ARG(PathTracerCreationParams) params) + static this_t create(NBL_CONST_REF_ARG(PathTracerCreationParams) params, Buffer samplerSequence) { this_t retval; retval.randGen = randgen_type::create(params.rngState); retval.rayGen = raygen_type::create(params.pixOffsetParam, params.camPos, params.NDC, params.invMVP); retval.materialSystem = material_system_type::create(diffuseParams, conductorParams, dielectricParams); + retval.samplerSequence = samplerSequence; return retval; } - // TODO: get working, what is sampleSequence stuff - vector3_type rand3d(uint32_t protoDimension, uint32_t _sample) + vector3_type rand3d(uint32_t protoDimension, uint32_t _sample, uint32_t i) { uint32_t address = spirv::bitfieldInsert(protoDimension, _sample, MAX_DEPTH_LOG2, MAX_SAMPLES_LOG2); unit32_t3 seqVal = texelFetch(sampleSequence, int(address) + i).xyz; @@ -150,19 +151,18 @@ struct Unidirectional (bxdf.materialType == ext::MaterialSystem::Material::CONDUCTOR) ? bxdf_traits::type == BT_BSDF : bxdf_traits::type == BT_BSDF; - vector3_type eps0 = rand3d(depth, _sample); - vector3_type eps1 = rand3d(depth, _sample); - vector3_type eps2 = rand3d(depth, _sample); + vector3_type eps0 = rand3d(depth, _sample, 0u); + vector3_type eps1 = rand3d(depth, _sample, 1u); // thresholds - const scalar_type bsdfPdfThreshold = 0.0001; + 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 = nbl::hlsl::transpose(colorspace::sRGBtoXYZ)[1] * throughput; // TODO: this only works if spectral_type is dim 3 const measure_type eta = bxdf.params.ior0 / bxdf.params.ior1; // assume it's real, not imaginary? const scalar_type monochromeEta = nbl::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 = 1.0; // BSDFNode_getNEEProb(bsdf); scalar_type rcpChoiceProb; if (!math::partitionRandVariable(neeProbability, eps0.z, rcpChoiceProb) && depth < 2u) { @@ -184,9 +184,9 @@ struct Unidirectional if (neeContrib_pdf.pdf < numeric_limits::max) { - if (nbl::hlsl::any(isnan(nee_sample.L))) + if (nbl::hlsl::any(isnan(nee_sample.L.direction))) ray.payload.accumulation += vector3_type(1000.f, 0.f, 0.f); - else if (nbl::hlsl::all((vector3_type)69.f == nee_sample.L)) + else if (nbl::hlsl::all((vector3_type)69.f == nee_sample.L.direction)) ray.payload.accumulation += vector3_type(0.f, 1000.f, 0.f); else if (validPath) { @@ -233,8 +233,8 @@ struct Unidirectional // TODO: ifdef NEE only ray_type nee_ray; - nee_ray.origin = intersection + nee_sample.L * t * Tolerance::getStart(depth); - nee_ray.direction = nee_sample.L; + nee_ray.origin = intersection + nee_sample.L.direction * t * Tolerance::getStart(depth); + nee_ray.direction = nee_sample.L.direction; nee_ray.intersectionT = t; if (bsdf_quotient_pdf.pdf < numeric_limits::max && getLuma(neeContrib_pdf.quotient) > lumaContributionThreshold && intersector.traceRay(nee_ray, scene).id == -1) ray._payload.accumulation += neeContrib_pdf.quotient; @@ -243,6 +243,70 @@ struct Unidirectional } // sample BSDF + scalar_type bxdfPdf; + vector3_type bxdfSample; + { + ext::MaterialSystem::Material material; + material.type = bxdf.materialType; + + anisocache_type _cache; + sample_type bsdf_sample = materialSystem.generate(material, bxdf.params, interaction, eps1, _cache); + + // TODO: does not yet account for smooth dielectric + params_type params; + if (!isBSDF && bxdf.materialType == ext::MaterialSystem::Material::DIFFUSE) + { + params = params_type::template create(bsdf_sample, iso_interaction, bxdf::BCM_MAX); + } + else if (!isBSDF && bxdf.materialType != ext::MaterialSystem::Material::DIFFUSE) + { + if (bxdf.params.is_aniso) + params = params_type::template create(bsdf_sample, interaction, _cache, bxdf::BCM_MAX); + else + { + isocache = (iso_cache)_cache; + params = params_type::template create(bsdf_sample, iso_interaction, isocache, bxdf::BCM_MAX); + } + } + else if (isBSDF && bxdf.materialType == ext::MaterialSystem::Material::DIFFUSE) + { + params = params_type::template create(bsdf_sample, iso_interaction, bxdf::BCM_ABS); + } + else if (isBSDF && bxdf.materialType != ext::MaterialSystem::Material::DIFFUSE) + { + if (bxdf.params.is_aniso) + params = params_type::template create(bsdf_sample, interaction, _cache, bxdf::BCM_ABS); + else + { + isocache = (iso_cache)_cache; + params = params_type::template create(bsdf_sample, iso_interaction, isocache, bxdf::BCM_ABS); + } + } + + // the value of the bsdf divided by the probability of the sample being generated + throughput *= materialSystem.quotient_and_pdf(material, bxdf.params, params); + bxdfSample = bsdf_sample.L.direction; + } + + // additional threshold + const float lumaThroughputThreshold = lumaContributionThreshold; + if (bxdfPdf > bxdfPdfThreshold && getLuma(throughput) > lumaThroughputThreshold) + { + ray.payload.throughput = throughput; + ray.payload.otherTechniqueHeuristic = neeProbability / bxdfPdf; // numerically stable, don't touch + ray.payload.otherTechniqueHeuristic *= ray.payload.otherTechniqueHeuristic; + + // trace new ray + ray.origin = intersection + bsdfSampleL * (1.0/*kSceneSize*/) * Tolerance::getStart(depth); + ray.direction = bxdfSample; + // #if POLYGON_METHOD==2 + // ray._immutable.normalAtOrigin = interaction.isotropic.N; + // ray._immutable.wasBSDFAtOrigin = isBSDF; + // #endif + return true; + } + + return false; } void missProgram(NBL_REF_ARG(ray_type) ray) @@ -288,16 +352,23 @@ struct Unidirectional Li += (accumulation - Li) * rcpSampleSize; // TODO: visualize high variance + + // TODO: russian roulette early exit? } return Li; } + 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; intersector_type intersector; material_system_type materialSystem; nee_type nee; + + Buffer samplerSequence; }; } From 72104b8b192a447bf8bdce09b1826f4150ce1d6a Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 19 Feb 2025 16:19:54 +0700 Subject: [PATCH 16/53] set up path tracer render shader --- .../app_resources/hlsl/common.hlsl | 13 +- .../hlsl/next_event_estimator.hlsl | 6 +- .../app_resources/hlsl/pathtracer.hlsl | 6 +- .../app_resources/hlsl/rand_gen.hlsl | 4 +- .../app_resources/hlsl/render.comp.hlsl | 171 ++++++++++++++++++ 5 files changed, 185 insertions(+), 15 deletions(-) create mode 100644 31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index 7d29dabd4..cc92a33ba 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -86,7 +86,6 @@ struct BxDFNode uint32_t materialType; params_type params; - ObjectID objectID; } template @@ -160,8 +159,8 @@ struct Shape return 2.0 * numbers::pi * (1.0 - cosThetaMax); } - template - float deferredPdf(NBL_CONST_REF_ARG(Light) light, NBL_CONST_REF_ARG(Ray) ray) + template + float deferredPdf(NBL_CONST_REF_ARG(Ray) ray) { return 1.0 / getSolidAngle(ray.origin); } @@ -245,8 +244,8 @@ struct Shape return nbl::hlsl::cross(edges[0], edges[1]) * 0.5f; } - template - float deferredPdf(NBL_CONST_REF_ARG(Light) light, NBL_CONST_REF_ARG(Ray) ray) + template + float deferredPdf(NBL_CONST_REF_ARG(Ray) ray) { const float32_t3 L = ray.direction; switch (polygonMethod) @@ -393,8 +392,8 @@ struct Shape basis = nbl::hlsl::transpose(basis); // TODO: double check transpose } - template - float deferredPdf(NBL_CONST_REF_ARG(Light light), NBL_CONST_REF_ARG(Ray) ray) + template + float deferredPdf(NBL_CONST_REF_ARG(Ray) ray) { switch (polygonMethod) { diff --git a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl index c6380094d..86c26a152 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl @@ -53,7 +53,7 @@ struct Estimator { float32_t3 position = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize]), asfloat(intersect.data[2 + Shape::ObjSize + 1]), asfloat(intersect.data[2 + Shape::ObjSize + 2])); Shape sphere = Shape::create(position, asfloat(intersect.data[2 + Shape::ObjSize + 3]), intersect.data[2 + Shape::ObjSize + 4]); - pdf *= sphere.template deferredPdf(light, ray); + pdf *= sphere.template deferredPdf(ray); } break; case PST_TRIANGLE: @@ -62,7 +62,7 @@ struct Estimator float32_t3 vertex1 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 3]), asfloat(intersect.data[2 + Shape::ObjSize + 4]), asfloat(intersect.data[2 + Shape::ObjSize + 5])); float32_t3 vertex2 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 6]), asfloat(intersect.data[2 + Shape::ObjSize + 7]), asfloat(intersect.data[2 + Shape::ObjSize + 8])); Shape tri = Shape::create(vertex0, vertex1, vertex2, intersect.data[2 + Shape::ObjSize + 9]); - pdf *= tri.template deferredPdf(light, ray); + pdf *= tri.template deferredPdf(ray); } break; case PST_RECTANGLE: @@ -71,7 +71,7 @@ struct Estimator float32_t3 edge0 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 3]), asfloat(intersect.data[2 + Shape::ObjSize + 4]), asfloat(intersect.data[2 + Shape::ObjSize + 5])); float32_t3 edge1 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 6]), asfloat(intersect.data[2 + Shape::ObjSize + 7]), asfloat(intersect.data[2 + Shape::ObjSize + 8])); Shape rect = Shape::create(offset, edge0, edge1, intersect.data[2 + Shape::ObjSize + 9]); - pdf *= rect.template deferredPdf(light, ray); + pdf *= rect.template deferredPdf(ray); } break; default: diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index e20ef705b..350e5e404 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -90,7 +90,7 @@ struct Unidirectional { uint32_t address = spirv::bitfieldInsert(protoDimension, _sample, MAX_DEPTH_LOG2, MAX_SAMPLES_LOG2); unit32_t3 seqVal = texelFetch(sampleSequence, int(address) + i).xyz; - seqVal ^= unit32_t3(randGen(), randGen(), randGen()); + seqVal ^= randGen(); return vector3_type(seqVal) * asfloat(0x2f800004u); } @@ -147,8 +147,8 @@ struct Unidirectional // TODO: ifdef kill diffuse specular paths - const bool isBSDF = (bxdf.materialType == ext::MaterialSystem::Material::DIFFUSE) ? bxdf_traits::type == BT_BSDF : - (bxdf.materialType == ext::MaterialSystem::Material::CONDUCTOR) ? bxdf_traits::type == BT_BSDF : + const bool isBSDF = (bxdf.materialType == ext::MaterialSystem::Material::Type::DIFFUSE) ? bxdf_traits::type == BT_BSDF : + (bxdf.materialType == ext::MaterialSystem::Material::Type::CONDUCTOR) ? bxdf_traits::type == BT_BSDF : bxdf_traits::type == BT_BSDF; vector3_type eps0 = rand3d(depth, _sample, 0u); diff --git a/31_HLSLPathTracer/app_resources/hlsl/rand_gen.hlsl b/31_HLSLPathTracer/app_resources/hlsl/rand_gen.hlsl index 949c2064b..30125c687 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/rand_gen.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/rand_gen.hlsl @@ -22,9 +22,9 @@ struct Uniform3D return retval; } - float32_t3 operator()() + uint32_t3 operator()() { - return float32_t3(uint32_t3(rng(), rng(), rng())); + return uint32_t3(rng(), rng(), rng()); } rng_type rng; diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl new file mode 100644 index 000000000..306188fd0 --- /dev/null +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -0,0 +1,171 @@ +#include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include "nbl/builtin/hlsl/glsl_compat/core.hlsl" +#include "nbl/builtin/hlsl/random/pcg.hlsl" + +#include "pathtracer.hlsl" + +// add these defines (one at a time) using -D argument to dxc +// #define SPHERE_LIGHT +// #define TRIANGLE_LIGHT +// #define RECTANGLE_LIGHT + +#ifdef SPHERE_LIGHT +#define SPHERE_COUNT 9 +#define LIGHT_TYPE PST_SPHERE +#else +#define SPHERE_COUNT 8 +#endif + +using namespace nbl::hlsl; + +NBL_CONSTEXPR uint32_t WorkgroupSize = 32; + +struct SPushConstants +{ + float32_t4x4 invMVP; + int sampleCount; + int depth; +}; + +[[vk::push_constant]] SPushConstants pc; + +[[vk::combinedImageSampler]][[vk::binding(0, 2)]] Texture2D envMap; // unused +[[vk::combinedImageSampler]][[vk::binding(0, 2)]] SamplerState envSampler + +[[vk::binding(1, 2)]] Buffer sampleSequence; + +[[vk::combinedImageSampler]][[vk::binding(2, 2)]] Texture2D scramblebuf; // unused +[[vk::combinedImageSampler]][[vk::binding(2, 2)]] SamplerState scrambleSampler; + +[[vk::binding(0, 0)]] RWTexture2D outImage; + +int32_t2 getCoordinates() +{ + return int32_t2(glsl::gl_GlobalInvocationID.xy); +} + +float32_t2 getTexCoords() +{ + uint32_t width, height; + outImage.GetDimensions(width, height); + int32_t2 iCoords = getCoordinates(); + return float32_t2(float(iCoords.x) / width, 1.0 - float(iCoords.y) / height); +} + +using ray_dir_info_t = bxdf::ray_dir_info::SBasic; +using iso_interaction = bxdf::surface_interactions::SIsotropic; +using aniso_interaction = bxdf::surface_interactions::SAnisotropic; +using sample_t = bxdf::SLightSample; +using iso_cache = bxdf::SIsotropicMicrofacetCache; +using aniso_cache = bxdf::SAnisotropicMicrofacetCache; +using quotient_pdf_t = bxdf::quotient_and_pdf; +using spectral_t = vector; +using params_t = bxdf::SBxDFParams; +using create_params_t = SBxDFCreationParams; + +using diffuse_bxdf_type = bxdf::reflection::SOrenNayarBxDF; +using conductor_bxdf_type = bxdf::reflection::SGGXBxDF; +using dielectric_bxdf_type = bxdf::transmission::SGGXDielectricBxDF; + +using ray_type = ext::Ray; +using light_type = ext::Light; +using bxdfnode_type = ext::BxDFNode; +using randgen_type = ext::RandGen::Uniform3D; +using raygen_type = ext::RayGen::Basic; +using intersector_type = ext::Intersector::Comprehensive; +using material_system_type = ext::MaterialSystem::System; +using nee_type = ext::NextEventEstimator::Estimator; +using pathtracer_type = ext::PathTracer::Unidirectional; + +Shape spheres[SPHERE_COUNT] = { + Shape::create(float3(0.0, -100.5, -1.0), 100.0, 0u, light_type::INVALID_ID), + Shape::create(float3(2.0, 0.0, -1.0), 0.5, 1u, light_type::INVALID_ID), + Shape::create(float3(0.0, 0.0, -1.0), 0.5, 2u, light_type::INVALID_ID), + Shape::create(float3(-2.0, 0.0, -1.0), 0.5, 3u, light_type::INVALID_ID), + Shape::create(float3(2.0, 0.0, 1.0), 0.5, 4u, light_type::INVALID_ID), + Shape::create(float3(0.0, 0.0, 1.0), 0.5, 4u, light_type::INVALID_ID), + Shape::create(float3(-2.0, 0.0, 1.0), 0.5, 5u, light_type::INVALID_ID), + Shape::create(float3(0.5, 1.0, 0.5), 0.5, 6u, light_type::INVALID_ID) +#ifdef SPHERE_LIGHT + ,Shape::create(float3(-1.5, 1.5, 0.0), 0.3, bxdfnode_type::INVALID_ID, 0u) +#endif +}; + +#ifdef TRIANGLE_LIGHT +#define LIGHT_TYPE PST_TRIANGLE +#define TRIANGLE_COUNT 1 +Shape triangles[TRIANGLE_COUNT] = { + Shape::create(float3(-1.8,0.35,0.3) * 10.0, float3(-1.2,0.35,0.0) * 10.0, float3(-1.5,0.8,-0.3) * 10.0, bxdfnode_type::INVALID_ID, 0u) +}; +#endif + +#ifdef RECTANGLE_LIGHT +#define LIGHT_TYPE PST_RECTANGLE +#define RECTANGLE_COUNT 1 +Shape rectangles[RECTANGLE_COUNT] = { + Shape::create(float3(-3.8,0.35,1.3), normalize(float3(2,0,-1))*7.0, normalize(float3(2,-5,4))*0.1, bxdfnode_type::INVALID_ID, 0u) +}; +#endif + +#define LIGHT_COUNT 1 +light_type lights[LIGHT_COUNT] = { + light_type(spectral_t(30.0,25.0,15.0), ext::ObjectID(8u, ext::NextEventEstimator::Event::Mode::PROCEDURAL, LIGHT_TYPE)) +}; + +#define BSDF_COUNT 7 +bxdfnode_type bsdfs[BSDF_COUNT] = { + bxdfnode_type(ext::MaterialSystem::Material::Type::DIFFUSE, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(1.25,1.25,1.25))), + bxdfnode_type(ext::MaterialSystem::Material::Type::DIFFUSE, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(1.25,2.5,2.5))), + bxdfnode_type(ext::MaterialSystem::Material::Type::DIFFUSE, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(2.5,1.25,2.5))), + bxdfnode_type(ext::MaterialSystem::Material::Type::CONDUCTOR, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(0.98,0.98,0.77))), + bxdfnode_type(ext::MaterialSystem::Material::Type::CONDUCTOR, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(0.98,0.77,0.98))), + bxdfnode_type(ext::MaterialSystem::Material::Type::CONDUCTOR, create_params_t(false, float2(0.15,0.15), spectral_t(1,1,1), spectral_t(0.98,0.77,0.98))), + bxdfnode_type(ext::MaterialSystem::Material::Type::DIELECTRIC, create_params_t(false, float2(0.0625,0.0625), spectral_t(1,1,1), spectral_t(0.71,0.69,0.67))) +}; + +[numthreads(WorkgroupGridDim, WorkgroupGridDim, 1)] +void main(uint32_t3 threadID : SV_DispatchThreadID) +{ + uint32_t width, height; + outImage.GetDimensions(width, height); + const int32_t2 coords = getCoordinates(); + float32_t2 texCoord = float32_t2(coords) / float32_t2(width, height); + texCoord.y = 1.0 - texCoord.y; + + if (false == (all((int32_t2)0 < coords)) && all(int32_t2(width, height) < coords)) { + return; + } + + if (((pc.depth - 1) >> MAX_DEPTH_LOG2) > 0 || ((pc.sampleCount - 1) >> MAX_SAMPLES_LOG2) > 0) + { + float32_t4 pixelCol = float32_t4(1.0,0.0,0.0,1.0); + outImage[coords] = pixelCol; + return; + } + + int flatIdx = glsl::gl_GlobalInvocationID.y * glsl::gl_NumWorkGroups.x * WorkgroupSize + glsl::gl_GlobalInvocationID.x; + PCG32x2 pcg = PCG32x2::construct(flatIdx); // replaces scramblebuf? + + // set up path tracer + const PathTracerCreationParams ptCreateParams; + ptCreateParams.rngState = pcg(); + + uint2 scrambleDim; + scramblebuf.GetDimensions(scrambleDim.x, scrambleDim.y); + ptCreateParams.pixOffsetParam = (float2)1.0 / float2(scrambleDim); + + float4 NDC = float4(texCoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); + { + vec4 tmp = mul(pc.invMVP, NDC); + ptCreateParams.camPos = tmp.xyz / tmp.w; + NDC.z = 1.0; + } + + ptCreateParams.NDC = NDC; + ptCreateParams.invMVP = pc.invMVP; + + pathtracer_type pathtracer = pathtracer_type::create(ptCreateParams, samplerSequence); + + // set up scene (can do as global var?) + Scene scene; +} From 202c645b6a43906589457bed95154c4f98785e67 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 20 Feb 2025 11:08:58 +0700 Subject: [PATCH 17/53] finish render shader --- .../app_resources/hlsl/render.comp.hlsl | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index 306188fd0..7beccd322 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -112,8 +112,8 @@ light_type lights[LIGHT_COUNT] = { light_type(spectral_t(30.0,25.0,15.0), ext::ObjectID(8u, ext::NextEventEstimator::Event::Mode::PROCEDURAL, LIGHT_TYPE)) }; -#define BSDF_COUNT 7 -bxdfnode_type bsdfs[BSDF_COUNT] = { +#define BXDF_COUNT 7 +bxdfnode_type bxdfs[BXDF_COUNT] = { bxdfnode_type(ext::MaterialSystem::Material::Type::DIFFUSE, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(1.25,1.25,1.25))), bxdfnode_type(ext::MaterialSystem::Material::Type::DIFFUSE, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(1.25,2.5,2.5))), bxdfnode_type(ext::MaterialSystem::Material::Type::DIFFUSE, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(2.5,1.25,2.5))), @@ -168,4 +168,31 @@ void main(uint32_t3 threadID : SV_DispatchThreadID) // set up scene (can do as global var?) Scene scene; + scene.sphereCount = SPHERE_COUNT; + for (uint32_t i = 0; i < SPHERE_COUNT; i++) + scene.spheres[i] = spheres[i]; +#ifdef TRIANGLE_LIGHT + scene.triangleCount = TRIANGLE_COUNT; + for (uint32_t i = 0; i < TRIANGLE_COUNT; i++) + scene.triangles[i] = triangles[i]; +#else + scene.triangleCount = 0; +#endif +#ifdef RECTANGLE_LIGHT + scene.rectangleCount = RECTANGLE_COUNT; + for (uint32_t i = 0; i < RECTANGLE_COUNT; i++) + scene.rectangles[i] = rectangles[i]; +#else + scene.rectangleCount = 0; +#endif + scene.lightCount = LIGHT_COUNT; + for (uint32_t i = 0; i < LIGHT_COUNT; i++) + scene.lights[i] = lights[i]; + scene.bxdfCount = BXDF_COUNT; + for (uint32_t i = 0; i < BXDF_COUNT; i++) + scene.bxdfs[i] = bxdfs[i]; + + float32_t3 color = pathtracer.getMeasure(pc.sampleCount, pc.depth, scene); + float32_t4 pixCol = float32_t4(color, 1.0); + outImage[coords] = pixCol; } From 2f77555ce484c2f8ecb390e68fc3f4c830b23ef7 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 20 Feb 2025 16:55:07 +0700 Subject: [PATCH 18/53] hlsl path tracer initial, bug fixes --- .../app_resources/hlsl/common.hlsl | 24 ++--- .../app_resources/hlsl/intersector.hlsl | 4 +- .../app_resources/hlsl/material_system.hlsl | 3 +- .../hlsl/next_event_estimator.hlsl | 2 +- .../app_resources/hlsl/pathtracer.hlsl | 2 +- .../app_resources/hlsl/rand_gen.hlsl | 2 +- .../app_resources/hlsl/ray_gen.hlsl | 6 +- .../app_resources/hlsl/render.comp.hlsl | 9 +- 31_HLSLPathTracer/main.cpp | 94 ++++++++++++++++--- 9 files changed, 109 insertions(+), 37 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index cc92a33ba..938e3ca22 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -86,7 +86,7 @@ struct BxDFNode uint32_t materialType; params_type params; -} +}; template struct Tolerance @@ -108,7 +108,7 @@ struct Tolerance { return 1.0 - nbl::hlsl::exp2(__common(depth) + 1.0); } -} +}; enum PTPolygonMethod : uint16_t { @@ -166,7 +166,7 @@ struct Shape } template - float generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, float32_t3 xi) + float32_t3 generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, float32_t3 xi) { float32_t3 Z = position - origin; const float distanceSQ = nbl::hlsl::dot(Z,Z); @@ -179,7 +179,7 @@ struct Shape const float cosThetaMax = nbl::hlsl::sqrt(cosThetaMax2); const float cosTheta = nbl::hlsl::mix(1.0, cosThetaMax, xi.x); - vec3 L = Z * cosTheta; + float32_t3 L = Z * cosTheta; const float cosTheta2 = cosTheta * cosTheta; const float sinTheta = nbl::hlsl::sqrt(1.0 - cosTheta2); @@ -253,7 +253,8 @@ struct Shape case PPM_AREA: { const float dist = ray.intersectionT; - return dist * dist / nbl::hlsl::abs(nbl::hlsl::dot(getNormalTimesArea()), L); + const float32_t3 L = ray.direction; + return dist * dist / nbl::hlsl::abs(nbl::hlsl::dot(getNormalTimesArea(), L)); } break; case PPM_SOLID_ANGLE: @@ -303,7 +304,7 @@ struct Shape { float rcpPdf; - shapes::SphericalTriangle st = shapes::SphericalTriangle::create(vertex0, vertex1, vertex2, ray.origin); + shapes::SphericalTriangle st = shapes::SphericalTriangle::create(vertex0, vertex1, vertex2, origin); sampling::SphericalTriangle sst = sampling::SphericalTriangle::create(st); const float32_t3 L = sst.generate(rcpPdf, xi.xy); @@ -319,7 +320,7 @@ struct Shape { float rcpPdf; - shapes::SphericalTriangle st = shapes::SphericalTriangle::create(vertex0, vertex1, vertex2, ray.origin); + shapes::SphericalTriangle st = shapes::SphericalTriangle::create(vertex0, vertex1, vertex2, origin); sampling::ProjectedSphericalTriangle sst = sampling::ProjectedSphericalTriangle::create(st); const float32_t3 L = sst.generate(rcpPdf, interaction.N, isBSDF, xi.xy); @@ -348,9 +349,9 @@ struct Shape template<> struct Shape { - static Shape create(NBL_CONST_REF_ARG(float32_t3) offset, NBL_CONST_REF_ARG(float32_t3) edge0, NBL_CONST_REF_ARG(float32_t3) edge1, uint32_t bsdfID, uint32_t lightID) + static Shape create(NBL_CONST_REF_ARG(float32_t3) offset, NBL_CONST_REF_ARG(float32_t3) edge0, NBL_CONST_REF_ARG(float32_t3) edge1, uint32_t bsdfID, uint32_t lightID) { - Shape retval; + Shape retval; retval.offset = offset; retval.edge0 = edge0; retval.edge1 = edge1; @@ -389,7 +390,7 @@ struct Shape basis[1] = edge1 / extents[1]; basis[2] = normalize(cross(basis[0],basis[1])); - basis = nbl::hlsl::transpose(basis); // TODO: double check transpose + basis = nbl::hlsl::transpose(basis); // TODO: double check transpose } template @@ -400,6 +401,7 @@ struct Shape case PPM_AREA: { const float dist = ray.intersectionT; + const float32_t3 L = ray.direction; return dist * dist / nbl::hlsl::abs(nbl::hlsl::dot(getNormalTimesArea(), L)); } break; @@ -499,4 +501,4 @@ struct Shape } } -#endif \ No newline at end of file +#endif diff --git a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl index cf2d3ae7c..5151ea9c0 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl @@ -33,7 +33,7 @@ struct IntersectData NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 128; uint32_t mode : 1; - unit32_t unused : 31; // possible space for flags + uint32_t unused : 31; // possible space for flags uint32_t data[DataSize]; }; @@ -199,4 +199,4 @@ struct Comprehensive } } -#endif \ No newline at end of file +#endif diff --git a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl index 038bd578a..687c41dc0 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl @@ -3,6 +3,7 @@ #include #include +#include namespace nbl { @@ -150,4 +151,4 @@ struct System } } -#endif \ No newline at end of file +#endif diff --git a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl index 86c26a152..5695efc0d 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl @@ -188,4 +188,4 @@ struct Estimator } } -#endif \ No newline at end of file +#endif diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index 350e5e404..b14c9baae 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -376,4 +376,4 @@ struct Unidirectional } } -#endif \ No newline at end of file +#endif diff --git a/31_HLSLPathTracer/app_resources/hlsl/rand_gen.hlsl b/31_HLSLPathTracer/app_resources/hlsl/rand_gen.hlsl index 30125c687..4f5302fea 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/rand_gen.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/rand_gen.hlsl @@ -35,4 +35,4 @@ struct Uniform3D } } -#endif \ No newline at end of file +#endif diff --git a/31_HLSLPathTracer/app_resources/hlsl/ray_gen.hlsl b/31_HLSLPathTracer/app_resources/hlsl/ray_gen.hlsl index 467ef2bd4..dcb695fbe 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/ray_gen.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/ray_gen.hlsl @@ -1,6 +1,8 @@ #ifndef _NBL_HLSL_EXT_RAYGEN_INCLUDED_ #define _NBL_HLSL_EXT_RAYGEN_INCLUDED_ +#include + #include "common.hlsl" namespace nbl @@ -43,7 +45,7 @@ struct Basic // apply stochastic reconstruction filter const float gaussianFilterCutoff = 2.5; const float truncation = nbl::hlsl::exp(-0.5 * gaussianFilterCutoff * gaussianFilterCutoff); - vec2 remappedRand = randVec.xy; + vector2_type remappedRand = randVec.xy; remappedRand.x *= 1.0 - truncation; remappedRand.x += truncation; tmp.xy += pixOffsetParam * nbl::hlsl::boxMullerTransform(remappedRand, 1.5); @@ -77,4 +79,4 @@ struct Basic } } -#endif \ No newline at end of file +#endif diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index 7beccd322..1c8c15ec4 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -2,6 +2,9 @@ #include "nbl/builtin/hlsl/glsl_compat/core.hlsl" #include "nbl/builtin/hlsl/random/pcg.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection.hlsl" +#include "nbl/builtin/hlsl/bxdf/transmission.hlsl" + #include "pathtracer.hlsl" // add these defines (one at a time) using -D argument to dxc @@ -30,7 +33,7 @@ struct SPushConstants [[vk::push_constant]] SPushConstants pc; [[vk::combinedImageSampler]][[vk::binding(0, 2)]] Texture2D envMap; // unused -[[vk::combinedImageSampler]][[vk::binding(0, 2)]] SamplerState envSampler +[[vk::combinedImageSampler]][[vk::binding(0, 2)]] SamplerState envSampler; [[vk::binding(1, 2)]] Buffer sampleSequence; @@ -41,7 +44,7 @@ struct SPushConstants int32_t2 getCoordinates() { - return int32_t2(glsl::gl_GlobalInvocationID.xy); + return int32_t2(glsl::gl_GlobalInvocationID().xy); } float32_t2 getTexCoords() @@ -143,7 +146,7 @@ void main(uint32_t3 threadID : SV_DispatchThreadID) return; } - int flatIdx = glsl::gl_GlobalInvocationID.y * glsl::gl_NumWorkGroups.x * WorkgroupSize + glsl::gl_GlobalInvocationID.x; + int flatIdx = glsl::gl_GlobalInvocationID().y * glsl::gl_NumWorkGroups().x * WorkgroupSize + glsl::gl_GlobalInvocationID().x; PCG32x2 pcg = PCG32x2::construct(flatIdx); // replaces scramblebuf? // set up path tracer diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index 018468e46..13aa59823 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -37,6 +37,14 @@ class ComputeShaderPathtracer final : public examples::SimpleWindowedApplication ELG_COUNT }; + enum E_RENDER_MODE : uint8_t + { + ERM_GLSL, + ERM_HLSL, + ERM_CHECKERED, + ERM_COUNT + }; + constexpr static inline uint32_t2 WindowDimensions = { 1280, 720 }; constexpr static inline uint32_t MaxFramesInFlight = 5; constexpr static inline clock_t::duration DisplayImageDuration = std::chrono::milliseconds(900); @@ -49,7 +57,9 @@ class ComputeShaderPathtracer final : public examples::SimpleWindowedApplication constexpr static inline uint8_t MaxUITextureCount = 1u; static inline std::string DefaultImagePathsFile = "envmap/envmap_0.exr"; static inline std::string OwenSamplerFilePath = "owen_sampler_buffer.bin"; - static inline std::array PTShaderPaths = { "app_resources/glsl/litBySphere.comp", "app_resources/glsl/litByTriangle.comp", "app_resources/glsl/litByRectangle.comp" }; + static inline std::array PTGLSLShaderPaths = { "app_resources/glsl/litBySphere.comp", "app_resources/glsl/litByTriangle.comp", "app_resources/glsl/litByRectangle.comp" }; + static inline std::string PTHLSLShaderPath = "app_resources/hlsl/render.comp.hlsl"; + static inline std::array PTHLSLShaderVariants = { "SPHERE_LIGHT", "TRIANGLE_LIGHT", "RECTANGLE_LIGHT" }; static inline std::string PresentShaderPath = "app_resources/hlsl/present.frag.hlsl"; const char* shaderNames[E_LIGHT_GEOMETRY::ELG_COUNT] = { @@ -301,7 +311,7 @@ class ComputeShaderPathtracer final : public examples::SimpleWindowedApplication m_presentDescriptorSet = presentDSPool->createDescriptorSet(gpuPresentDescriptorSetLayout); // Create Shaders - auto loadAndCompileShader = [&](std::string pathToShader) + auto loadAndCompileGLSLShader = [&](const std::string& pathToShader) -> smart_refctd_ptr { IAssetLoader::SAssetLoadParams lp = {}; lp.workingDirectory = localInputCWD; @@ -328,10 +338,46 @@ class ComputeShaderPathtracer final : public examples::SimpleWindowedApplication return shader; }; + auto loadAndCompileHLSLShader = [&](const std::string& pathToShader, const std::string& defineMacro) -> smart_refctd_ptr + { + IAssetLoader::SAssetLoadParams lp = {}; + lp.workingDirectory = localInputCWD; + auto assetBundle = m_assetMgr->getAsset(pathToShader, lp); + const auto assets = assetBundle.getContents(); + if (assets.empty()) + { + m_logger->log("Could not load shader: ", ILogger::ELL_ERROR, pathToShader); + std::exit(-1); + } + + auto source = IAsset::castDown(assets[0]); + // The down-cast should not fail! + assert(source); + + auto compiler = make_smart_refctd_ptr(smart_refctd_ptr(m_system)); + CHLSLCompiler::SOptions options = {}; + options.stage = IShader::E_SHADER_STAGE::ESS_COMPUTE; // should be compute + options.targetSpirvVersion = m_device->getPhysicalDevice()->getLimits().spirvVersion; + options.spirvOptimizer = nullptr; +#ifndef _NBL_DEBUG + ISPIRVOptimizer::E_OPTIMIZER_PASS optPasses = ISPIRVOptimizer::EOP_STRIP_DEBUG_INFO; + auto opt = make_smart_refctd_ptr(std::span(&optPasses, 1)); + options.spirvOptimizer = opt.get(); +#endif + options.debugInfoFlags |= IShaderCompiler::E_DEBUG_INFO_FLAGS::EDIF_SOURCE_BIT; + options.preprocessorOptions.sourceIdentifier = source->getFilepathHint(); + options.preprocessorOptions.logger = m_logger.get(); + options.preprocessorOptions.includeFinder = compiler->getDefaultIncludeFinder(); + + //std::string dxcOptionStr[] = { "-D" + defineMacro }; + //options.dxcOptions = std::span(dxcOptionStr); + + source = compiler->compileToSPIRV((const char*)source->getContent()->getPointer(), options); + }; + // Create compute pipelines { for (int index = 0; index < E_LIGHT_GEOMETRY::ELG_COUNT; index++) { - auto ptShader = loadAndCompileShader(PTShaderPaths[index]); const nbl::asset::SPushConstantRange pcRange = { .stageFlags = IShader::E_SHADER_STAGE::ESS_COMPUTE, .offset = 0, @@ -348,15 +394,31 @@ class ComputeShaderPathtracer final : public examples::SimpleWindowedApplication return logFail("Failed to create Pathtracing pipeline layout"); } - IGPUComputePipeline::SCreationParams params = {}; - params.layout = ptPipelineLayout.get(); - params.shader.shader = ptShader.get(); - params.shader.entryPoint = "main"; - params.shader.entries = nullptr; - params.shader.requireFullSubgroups = true; - params.shader.requiredSubgroupSize = static_cast(5); - if (!m_device->createComputePipelines(nullptr, { ¶ms, 1 }, m_PTPipelines.data() + index)) { - return logFail("Failed to create compute pipeline!\n"); + { + auto ptShader = loadAndCompileGLSLShader(PTGLSLShaderPaths[index]); + + IGPUComputePipeline::SCreationParams params = {}; + params.layout = ptPipelineLayout.get(); + params.shader.shader = ptShader.get(); + params.shader.entryPoint = "main"; + params.shader.entries = nullptr; + params.shader.requireFullSubgroups = true; + params.shader.requiredSubgroupSize = static_cast(5); + if (!m_device->createComputePipelines(nullptr, { ¶ms, 1 }, m_PTGLSLPipelines.data() + index)) + return logFail("Failed to create GLSL compute pipeline!\n"); + } + { + auto ptShader = loadAndCompileHLSLShader(PTHLSLShaderPath, PTHLSLShaderVariants[index]); + + IGPUComputePipeline::SCreationParams params = {}; + params.layout = ptPipelineLayout.get(); + params.shader.shader = ptShader.get(); + params.shader.entryPoint = "main"; + params.shader.entries = nullptr; + params.shader.requireFullSubgroups = true; + params.shader.requiredSubgroupSize = static_cast(5); + if (!m_device->createComputePipelines(nullptr, { ¶ms, 1 }, m_PTHLSLPipelines.data() + index)) + return logFail("Failed to create HLSL compute pipeline!\n"); } } } @@ -369,7 +431,7 @@ class ComputeShaderPathtracer final : public examples::SimpleWindowedApplication return logFail("Failed to create Full Screen Triangle protopipeline or load its vertex shader!"); // Load Fragment Shader - auto fragmentShader = loadAndCompileShader(PresentShaderPath); + auto fragmentShader = loadAndCompileGLSLShader(PresentShaderPath); if (!fragmentShader) return logFail("Failed to Load and Compile Fragment Shader: lumaMeterShader!"); @@ -985,7 +1047,7 @@ class ComputeShaderPathtracer final : public examples::SimpleWindowedApplication // cube envmap handle { - auto pipeline = m_PTPipelines[PTPipline].get(); + auto pipeline = renderMode == E_RENDER_MODE::ERM_HLSL ? m_PTHLSLPipelines[PTPipline].get() : m_PTGLSLPipelines[PTPipline].get(); cmdbuf->bindComputePipeline(pipeline); cmdbuf->bindDescriptorSets(EPBP_COMPUTE, pipeline->getLayout(), 0u, 1u, &m_descriptorSet0.get()); cmdbuf->bindDescriptorSets(EPBP_COMPUTE, pipeline->getLayout(), 2u, 1u, &m_descriptorSet2.get()); @@ -1220,7 +1282,8 @@ class ComputeShaderPathtracer final : public examples::SimpleWindowedApplication // gpu resources smart_refctd_ptr m_cmdPool; - std::array, E_LIGHT_GEOMETRY::ELG_COUNT> m_PTPipelines; + std::array, E_LIGHT_GEOMETRY::ELG_COUNT> m_PTGLSLPipelines; + std::array, E_LIGHT_GEOMETRY::ELG_COUNT> m_PTHLSLPipelines; smart_refctd_ptr m_presentPipeline; uint64_t m_realFrameIx = 0; std::array, MaxFramesInFlight> m_cmdBufs; @@ -1269,6 +1332,7 @@ class ComputeShaderPathtracer final : public examples::SimpleWindowedApplication float camYAngle = 165.f / 180.f * 3.14159f; float camXAngle = 32.f / 180.f * 3.14159f; int PTPipline = E_LIGHT_GEOMETRY::ELG_SPHERE; + int renderMode = E_RENDER_MODE::ERM_GLSL; int spp = 32; int depth = 3; From 99aed4777c208c5acc4e66bb7ea8dc48f814c8d0 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 21 Feb 2025 14:16:11 +0700 Subject: [PATCH 19/53] fix shader bugs --- .../app_resources/hlsl/common.hlsl | 30 +++++++-- .../app_resources/hlsl/intersector.hlsl | 33 +++++----- .../app_resources/hlsl/material_system.hlsl | 40 ++++++------ .../hlsl/next_event_estimator.hlsl | 61 ++++++++++--------- .../app_resources/hlsl/pathtracer.hlsl | 9 +-- .../app_resources/hlsl/render.comp.hlsl | 4 ++ .../app_resources/hlsl/scene.hlsl | 50 +++++++-------- 31_HLSLPathTracer/main.cpp | 4 +- 8 files changed, 131 insertions(+), 100 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index 938e3ca22..1b0aac72f 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -123,15 +123,21 @@ struct Shape; template<> struct Shape { - static Shape create(NBL_CONST_REF_ARG(float32_t3) position, float32_t radius, uint32_t bsdfID, uint32_t lightID) + static Shape create(NBL_CONST_REF_ARG(float32_t3) position, float32_t radius, uint32_t bsdfLightIDs) { Shape retval; retval.position = position; retval.radius2 = radius * radius; - retval.bsdfLightIDs = spirv::bitFieldInsert(bsdfID, lightID, 16, 16); + retval.bsdfLightIDs = bsdfLightIDs; return retval; } + static Shape create(NBL_CONST_REF_ARG(float32_t3) position, float32_t radius, uint32_t bsdfID, uint32_t lightID) + { + uint32_t bsdfLightIDs = spirv::bitFieldInsert(bsdfID, lightID, 16, 16); + return create(position, radius, bsdfLightIDs); + } + // return intersection distance if found, nan otherwise float intersect(NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(float32_t3) direction) { @@ -207,17 +213,23 @@ struct Shape template<> struct Shape { - static Shape create(NBL_CONST_REF_ARG(float32_t3) vertex0, NBL_CONST_REF_ARG(float32_t3) vertex1, NBL_CONST_REF_ARG(float32_t3) vertex2, uint32_t bsdfID, uint32_t lightID) + static Shape create(NBL_CONST_REF_ARG(float32_t3) vertex0, NBL_CONST_REF_ARG(float32_t3) vertex1, NBL_CONST_REF_ARG(float32_t3) vertex2, uint32_t bsdfLightIDs) { Shape retval; retval.vertex0 = vertex0; retval.vertex1 = vertex1; retval.vertex2 = vertex2; - retval.bsdfLightIDs = spirv::bitFieldInsert(bsdfID, lightID, 16, 16); + retval.bsdfLightIDs = bsdfLightIDs; retval.polygonMethod = PPM_SOLID_ANGLE; return retval; } + static Shape create(NBL_CONST_REF_ARG(float32_t3) vertex0, NBL_CONST_REF_ARG(float32_t3) vertex1, NBL_CONST_REF_ARG(float32_t3) vertex2, uint32_t bsdfID, uint32_t lightID) + { + uint32_t bsdfLightIDs = spirv::bitFieldInsert(bsdfID, lightID, 16, 16); + return create(vertex0, vertex1, vertex2, bsdfLightIDs); + } + float intersect(NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(float32_t3) direction) { const float32_t3 edges[2] = { vertex1 - vertex0, vertex2 - vertex0 }; @@ -349,17 +361,23 @@ struct Shape template<> struct Shape { - static Shape create(NBL_CONST_REF_ARG(float32_t3) offset, NBL_CONST_REF_ARG(float32_t3) edge0, NBL_CONST_REF_ARG(float32_t3) edge1, uint32_t bsdfID, uint32_t lightID) + static Shape create(NBL_CONST_REF_ARG(float32_t3) offset, NBL_CONST_REF_ARG(float32_t3) edge0, NBL_CONST_REF_ARG(float32_t3) edge1, uint32_t bsdfLightIDs) { Shape retval; retval.offset = offset; retval.edge0 = edge0; retval.edge1 = edge1; - retval.bsdfLightIDs = spirv::bitFieldInsert(bsdfID, lightID, 16, 16); + retval.bsdfLightIDs = bsdfLightIDs; retval.polygonMethod = PPM_SOLID_ANGLE; return retval; } + static Shape create(NBL_CONST_REF_ARG(float32_t3) offset, NBL_CONST_REF_ARG(float32_t3) edge0, NBL_CONST_REF_ARG(float32_t3) edge1, uint32_t bsdfID, uint32_t lightID) + { + uint32_t bsdfLightIDs = spirv::bitFieldInsert(bsdfID, lightID, 16, 16); + return create(offset, edge0, edge1, bsdfLightIDs); + } + float intersect(NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(float32_t3) direction) { const float32_t3 h = nbl::hlsl::cross(direction, edge1); diff --git a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl index 5151ea9c0..0bb6cb31c 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl @@ -41,17 +41,18 @@ template struct Comprehensive { using scalar_type = typename Ray::scalar_type; + using vector3_type = vector; using ray_type = Ray; static ObjectID traceProcedural(NBL_REF_ARG(ray_type) ray, NBL_REF_ARG(IntersectData) intersect) { const bool anyHit = ray.intersectionT != numeric_limits::max; const uint32_t objCount = intersect.data[0]; - const ProceduralShapeType type = intersect.data[1]; + const ProceduralShapeType type = (ProceduralShapeType)intersect.data[1]; ObjectID objectID = ray.objectID; objectID.mode = IntersectData::Mode::PROCEDURAL; - objectID.type = type; + objectID.shapeType = type; for (int i = 0; i < objCount; i++) { float t; @@ -59,25 +60,25 @@ struct Comprehensive { case PST_SPHERE: { - float32_t3 position = float32_t3(asfloat(intersect.data[2 + i * Shape::ObjSize]), asfloat(intersect.data[2 + i * Shape::ObjSize + 1]), asfloat(intersect.data[2 + i * Shape::ObjSize + 2])); + vector3_type position = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize]), asfloat(intersect.data[2 + i * Shape::ObjSize + 1]), asfloat(intersect.data[2 + i * Shape::ObjSize + 2])); Shape sphere = Shape::create(position, asfloat(intersect.data[2 + i * Shape::ObjSize + 3]), intersect.data[2 + i * Shape::ObjSize + 4]); t = sphere.intersect(ray.origin, ray.direction); } break; case PST_TRIANGLE: { - float32_t3 vertex0 = float32_t3(asfloat(intersect.data[2 + i * Shape::ObjSize]), asfloat(intersect.data[2 + i * Shape::ObjSize + 1]), asfloat(intersect.data[2 + i * Shape::ObjSize + 2])); - float32_t3 vertex1 = float32_t3(asfloat(intersect.data[2 + i * Shape::ObjSize + 3]), asfloat(intersect.data[2 + i * Shape::ObjSize + 4]), asfloat(intersect.data[2 + i * Shape::ObjSize + 5])); - float32_t3 vertex2 = float32_t3(asfloat(intersect.data[2 + i * Shape::ObjSize + 6]), asfloat(intersect.data[2 + i * Shape::ObjSize + 7]), asfloat(intersect.data[2 + i * Shape::ObjSize + 8])); + vector3_type vertex0 = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize]), asfloat(intersect.data[2 + i * Shape::ObjSize + 1]), asfloat(intersect.data[2 + i * Shape::ObjSize + 2])); + vector3_type vertex1 = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize + 3]), asfloat(intersect.data[2 + i * Shape::ObjSize + 4]), asfloat(intersect.data[2 + i * Shape::ObjSize + 5])); + vector3_type vertex2 = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize + 6]), asfloat(intersect.data[2 + i * Shape::ObjSize + 7]), asfloat(intersect.data[2 + i * Shape::ObjSize + 8])); Shape tri = Shape::create(vertex0, vertex1, vertex2, intersect.data[2 + i * Shape::ObjSize + 9]); t = tri.intersect(ray.origin, ray.direction); } break; case PST_RECTANGLE: { - float32_t3 offset = float32_t3(asfloat(intersect.data[2 + i * Shape::ObjSize]), asfloat(intersect.data[2 + i * Shape::ObjSize + 1]), asfloat(intersect.data[2 + i * Shape::ObjSize + 2])); - float32_t3 edge0 = float32_t3(asfloat(intersect.data[2 + i * Shape::ObjSize + 3]), asfloat(intersect.data[2 + i * Shape::ObjSize + 4]), asfloat(intersect.data[2 + i * Shape::ObjSize + 5])); - float32_t3 edge1 = float32_t3(asfloat(intersect.data[2 + i * Shape::ObjSize + 6]), asfloat(intersect.data[2 + i * Shape::ObjSize + 7]), asfloat(intersect.data[2 + i * Shape::ObjSize + 8])); + vector3_type offset = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize]), asfloat(intersect.data[2 + i * Shape::ObjSize + 1]), asfloat(intersect.data[2 + i * Shape::ObjSize + 2])); + vector3_type edge0 = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize + 3]), asfloat(intersect.data[2 + i * Shape::ObjSize + 4]), asfloat(intersect.data[2 + i * Shape::ObjSize + 5])); + vector3_type edge1 = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize + 6]), asfloat(intersect.data[2 + i * Shape::ObjSize + 7]), asfloat(intersect.data[2 + i * Shape::ObjSize + 8])); Shape rect = Shape::create(offset, edge0, edge1, intersect.data[2 + i * Shape::ObjSize + 9]); t = rect.intersect(ray.origin, ray.direction); } @@ -101,7 +102,7 @@ struct Comprehensive static ObjectID traceRay(NBL_REF_ARG(ray_type) ray, NBL_REF_ARG(IntersectData) intersect) { - const IntersectData::Mode mode = intersect.mode; + const IntersectData::Mode mode = (IntersectData::Mode)intersect.mode; switch (mode) { case IntersectData::Mode::RAY_QUERY: @@ -120,7 +121,11 @@ struct Comprehensive } break; default: - return ObjectID(-1, IntersectData::Mode::PROCEDURAL, PST_SPHERE); + { + ObjectID objID; + objID.id = -1; + return objID; + } } } @@ -136,19 +141,19 @@ struct Comprehensive if (scene.sphereCount > 0) { data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_SPHERE); - objectID = intersector.traceRay(ray, data); + objectID = traceRay(ray, data); } if (scene.triangleCount > 0) { data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_TRIANGLE); - objectID = intersector.traceRay(ray, data); + objectID = traceRay(ray, data); } if (scene.rectangleCount > 0) { data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_RECTANGLE); - objectID = intersector.traceRay(ray, data); + objectID = traceRay(ray, data); } // TODO: trace AS diff --git a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl index 687c41dc0..9d638c232 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl @@ -26,7 +26,7 @@ struct Material NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 32; uint32_t type : 1; - unit32_t unused : 31; // possible space for flags + uint32_t unused : 31; // possible space for flags uint32_t data[DataSize]; }; @@ -41,37 +41,39 @@ struct System using quotient_pdf_type = typename DiffuseBxDF::quotient_pdf_type; using anisotropic_type = typename DiffuseBxDF::anisotropic_type; using anisocache_type = typename ConductorBxDF::anisocache_type; - using params_t = SBxDFParams; - using create_params_t = SBxDFCreationParams; + using params_t = bxdf::SBxDFParams; + using create_params_t = bxdf::SBxDFCreationParams; using diffuse_op_type = DiffuseBxDF; using conductor_op_type = ConductorBxDF; using dielectric_op_type = DielectricBxDF; - static this_t create(NBL_CONST_REF_ARG(SBxDFCreationParams) diffuseParams, NBL_CONST_REF_ARG(SBxDFCreationParams) conductorParams, NBL_CONST_REF_ARG(SBxDFCreationParams) dielectricParams) + static this_t create(NBL_CONST_REF_ARG(create_params_t) diffuseParams, NBL_CONST_REF_ARG(create_params_t) conductorParams, NBL_CONST_REF_ARG(create_params_t) dielectricParams) { - diffuseBxDF = DiffuseBxDF::create(diffuseParams); - conductorBxDF = DiffuseBxDF::create(conductorParams); - dielectricBxDF = DiffuseBxDF::create(dielectricParams); + this_t retval; + retval.diffuseBxDF = DiffuseBxDF::create(diffuseParams); + retval.conductorBxDF = DiffuseBxDF::create(conductorParams); + retval.dielectricBxDF = DiffuseBxDF::create(dielectricParams); + return retval; } - static measure_type eval(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, NBL_CONST_REF_ARG(params_t) params) + measure_type eval(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, NBL_CONST_REF_ARG(params_t) params) { switch(material.type) { - case DIFFUSE: + case Material::Type::DIFFUSE: { diffuseBxDF.init(cparams); return (measure_type)diffuseBxDF.eval(params); } break; - case CONDUCTOR: + case Material::Type::CONDUCTOR: { conductorBxDF.init(cparams); return conductorBxDF.eval(params); } break; - case DIELECTRIC: + case Material::Type::DIELECTRIC: { dielectricBxDF.init(cparams); return dielectricBxDF.eval(params); @@ -82,23 +84,23 @@ struct System } } - static vector3_type generate(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, anisotropic_type interaction, NBL_CONST_REF_ARG(vector3_type) u, NBL_REF_ARG(anisocache_type) cache) + vector3_type generate(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, anisotropic_type interaction, NBL_CONST_REF_ARG(vector3_type) u, NBL_REF_ARG(anisocache_type) cache) { switch(material.type) { - case DIFFUSE: + case Material::Type::DIFFUSE: { diffuseBxDF.init(cparams); return diffuseBxDF.generate(interaction, u.xy); } break; - case CONDUCTOR: + case Material::Type::CONDUCTOR: { conductorBxDF.init(cparams); return conductorBxDF.generate(interaction, u.xy, cache); } break; - case DIELECTRIC: + case Material::Type::DIELECTRIC: { dielectricBxDF.init(cparams); return dielectricBxDF.generate(interaction, u, cache); @@ -109,26 +111,26 @@ struct System } } - static quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, NBL_CONST_REF_ARG(params_t) params) + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, NBL_CONST_REF_ARG(params_t) params) { const float minimumProjVectorLen = 0.00000001; if (params.NdotV > minimumProjVectorLen && params.NdotL > minimumProjVectorLen) { switch(material.type) { - case DIFFUSE: + case Material::Type::DIFFUSE: { diffuseBxDF.init(cparams); return diffuseBxDF.quotient_and_pdf(params); } break; - case CONDUCTOR: + case Material::Type::CONDUCTOR: { conductorBxDF.init(cparams); return conductorBxDF.quotient_and_pdf(params); } break; - case DIELECTRIC: + case Material::Type::DIELECTRIC: { dielectricBxDF.init(cparams); return dielectricBxDF.quotient_and_pdf(params); diff --git a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl index 5695efc0d..c7573fbb3 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl @@ -12,7 +12,7 @@ namespace ext namespace NextEventEstimator { -// procedural data store: [light count] [intersect type] [obj] +// procedural data store: [light count] [event type] [obj] struct Event { @@ -26,7 +26,7 @@ struct Event NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 16; uint32_t mode : 1; - unit32_t unused : 31; // possible space for flags + uint32_t unused : 31; // possible space for flags uint32_t data[DataSize]; }; @@ -34,43 +34,44 @@ template struct Estimator { using scalar_type = typename Ray::scalar_type; + using vector3_type = vector; using ray_type = Ray; using light_type = Light; using spectral_type = typename Light::spectral_type; using interaction_type = Aniso; - using quotient_pdf_type = quotient_and_pdf; + using quotient_pdf_type = bxdf::quotient_and_pdf; using sample_type = LightSample; static spectral_type proceduralDeferredEvalAndPdf(NBL_REF_ARG(scalar_type) pdf, NBL_CONST_REF_ARG(light_type) light, NBL_CONST_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(Event) event) { const uint32_t lightCount = event.data[0]; - const ProceduralShapeType type = event.data[1]; + const ProceduralShapeType type = (ProceduralShapeType)event.data[1]; pdf = 1.0 / lightCount; switch (type) { case PST_SPHERE: { - float32_t3 position = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize]), asfloat(intersect.data[2 + Shape::ObjSize + 1]), asfloat(intersect.data[2 + Shape::ObjSize + 2])); - Shape sphere = Shape::create(position, asfloat(intersect.data[2 + Shape::ObjSize + 3]), intersect.data[2 + Shape::ObjSize + 4]); + vector3_type position = vector3_type(asfloat(event.data[2]), asfloat(event.data[3]), asfloat(event.data[4])); + Shape sphere = Shape::create(position, asfloat(event.data[5]), event.data[6]); pdf *= sphere.template deferredPdf(ray); } break; case PST_TRIANGLE: { - float32_t3 vertex0 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize]), asfloat(intersect.data[2 + Shape::ObjSize + 1]), asfloat(intersect.data[2 + Shape::ObjSize + 2])); - float32_t3 vertex1 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 3]), asfloat(intersect.data[2 + Shape::ObjSize + 4]), asfloat(intersect.data[2 + Shape::ObjSize + 5])); - float32_t3 vertex2 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 6]), asfloat(intersect.data[2 + Shape::ObjSize + 7]), asfloat(intersect.data[2 + Shape::ObjSize + 8])); - Shape tri = Shape::create(vertex0, vertex1, vertex2, intersect.data[2 + Shape::ObjSize + 9]); + vector3_type vertex0 = vector3_type(asfloat(event.data[2]), asfloat(event.data[3]), asfloat(event.data[4])); + vector3_type vertex1 = vector3_type(asfloat(event.data[5]), asfloat(event.data[6]), asfloat(event.data[7])); + vector3_type vertex2 = vector3_type(asfloat(event.data[8]), asfloat(event.data[9]), asfloat(event.data[10])); + Shape tri = Shape::create(vertex0, vertex1, vertex2, event.data[11]); pdf *= tri.template deferredPdf(ray); } break; case PST_RECTANGLE: { - float32_t3 offset = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize]), asfloat(intersect.data[2 + Shape::ObjSize + 1]), asfloat(intersect.data[2 + Shape::ObjSize + 2])); - float32_t3 edge0 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 3]), asfloat(intersect.data[2 + Shape::ObjSize + 4]), asfloat(intersect.data[2 + Shape::ObjSize + 5])); - float32_t3 edge1 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 6]), asfloat(intersect.data[2 + Shape::ObjSize + 7]), asfloat(intersect.data[2 + Shape::ObjSize + 8])); - Shape rect = Shape::create(offset, edge0, edge1, intersect.data[2 + Shape::ObjSize + 9]); + vector3_type offset = vector3_type(asfloat(event.data[2]), asfloat(event.data[3]), asfloat(event.data[4])); + vector3_type edge0 = vector3_type(asfloat(event.data[5]), asfloat(event.data[6]), asfloat(event.data[7])); + vector3_type edge1 = vector3_type(asfloat(event.data[8]), asfloat(event.data[9]), asfloat(event.data[10])); + Shape rect = Shape::create(offset, edge0, edge1, event.data[11]); pdf *= rect.template deferredPdf(ray); } break; @@ -84,7 +85,7 @@ struct Estimator static spectral_type deferredEvalAndPdf(NBL_REF_ARG(scalar_type) pdf, NBL_CONST_REF_ARG(light_type) light, NBL_CONST_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(Event) event) { - const Event::Mode mode = event.mode; + const Event::Mode mode = (Event::Mode)event.mode; switch (mode) { case Event::Mode::RAY_QUERY: @@ -107,10 +108,10 @@ struct Estimator } } - static sample_type procedural_generate_and_quotient_and_pdf(NBL_REF_ARG(quotient_pdf_type) quotient_pdf, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(interaction_type) interaction, bool isBSDF, NBL_CONST_REF_ARG(vector3_type) xi, unit32_t depth, NBL_CONST_REF_ARG(Event) event) + static sample_type procedural_generate_and_quotient_and_pdf(NBL_REF_ARG(quotient_pdf_type) quotient_pdf, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(light_type) light, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(interaction_type) interaction, bool isBSDF, NBL_CONST_REF_ARG(vector3_type) xi, uint32_t depth, NBL_CONST_REF_ARG(Event) event) { const uint32_t lightCount = event.data[0]; - const ProceduralShapeType type = event.data[1]; + const ProceduralShapeType type = (ProceduralShapeType)event.data[1]; sample_type L; scalar_type pdf; @@ -118,26 +119,26 @@ struct Estimator { case PST_SPHERE: { - float32_t3 position = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize]), asfloat(intersect.data[2 + Shape::ObjSize + 1]), asfloat(intersect.data[2 + Shape::ObjSize + 2])); - Shape sphere = Shape::create(position, asfloat(intersect.data[2 + Shape::ObjSize + 3]), intersect.data[2 + Shape::ObjSize + 4]); + vector3_type position = vector3_type(asfloat(event.data[2 + Shape::ObjSize]), asfloat(event.data[2 + Shape::ObjSize + 1]), asfloat(event.data[2 + Shape::ObjSize + 2])); + Shape sphere = Shape::create(position, asfloat(event.data[2 + Shape::ObjSize + 3]), event.data[2 + Shape::ObjSize + 4]); L = sphere.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); } break; case PST_TRIANGLE: { - float32_t3 vertex0 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize]), asfloat(intersect.data[2 + Shape::ObjSize + 1]), asfloat(intersect.data[2 + Shape::ObjSize + 2])); - float32_t3 vertex1 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 3]), asfloat(intersect.data[2 + Shape::ObjSize + 4]), asfloat(intersect.data[2 + Shape::ObjSize + 5])); - float32_t3 vertex2 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 6]), asfloat(intersect.data[2 + Shape::ObjSize + 7]), asfloat(intersect.data[2 + Shape::ObjSize + 8])); - Shape tri = Shape::create(vertex0, vertex1, vertex2, intersect.data[2 + Shape::ObjSize + 9]); + vector3_type vertex0 = vector3_type(asfloat(event.data[2 + Shape::ObjSize]), asfloat(event.data[2 + Shape::ObjSize + 1]), asfloat(event.data[2 + Shape::ObjSize + 2])); + vector3_type vertex1 = vector3_type(asfloat(event.data[2 + Shape::ObjSize + 3]), asfloat(event.data[2 + Shape::ObjSize + 4]), asfloat(event.data[2 + Shape::ObjSize + 5])); + vector3_type vertex2 = vector3_type(asfloat(event.data[2 + Shape::ObjSize + 6]), asfloat(event.data[2 + Shape::ObjSize + 7]), asfloat(event.data[2 + Shape::ObjSize + 8])); + Shape tri = Shape::create(vertex0, vertex1, vertex2, event.data[2 + Shape::ObjSize + 9]); L = tri.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); } break; case PST_RECTANGLE: { - float32_t3 offset = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize]), asfloat(intersect.data[2 + Shape::ObjSize + 1]), asfloat(intersect.data[2 + Shape::ObjSize + 2])); - float32_t3 edge0 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 3]), asfloat(intersect.data[2 + Shape::ObjSize + 4]), asfloat(intersect.data[2 + Shape::ObjSize + 5])); - float32_t3 edge1 = float32_t3(asfloat(intersect.data[2 + Shape::ObjSize + 6]), asfloat(intersect.data[2 + Shape::ObjSize + 7]), asfloat(intersect.data[2 + Shape::ObjSize + 8])); - Shape rect = Shape::create(offset, edge0, edge1, intersect.data[2 + Shape::ObjSize + 9]); + vector3_type offset = vector3_type(asfloat(event.data[2 + Shape::ObjSize]), asfloat(event.data[2 + Shape::ObjSize + 1]), asfloat(event.data[2 + Shape::ObjSize + 2])); + vector3_type edge0 = vector3_type(asfloat(event.data[2 + Shape::ObjSize + 3]), asfloat(event.data[2 + Shape::ObjSize + 4]), asfloat(event.data[2 + Shape::ObjSize + 5])); + vector3_type edge1 = vector3_type(asfloat(event.data[2 + Shape::ObjSize + 6]), asfloat(event.data[2 + Shape::ObjSize + 7]), asfloat(event.data[2 + Shape::ObjSize + 8])); + Shape rect = Shape::create(offset, edge0, edge1, event.data[2 + Shape::ObjSize + 9]); L = rect.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); } break; @@ -154,9 +155,9 @@ struct Estimator return L; } - static sample_type generate_and_quotient_and_pdf(NBL_REF_ARG(quotient_pdf_type) quotient_pdf, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(interaction_type) interaction, bool isBSDF, NBL_CONST_REF_ARG(vector3_type) xi, unit32_t depth, NBL_CONST_REF_ARG(Event) event) + static sample_type generate_and_quotient_and_pdf(NBL_REF_ARG(quotient_pdf_type) quotient_pdf, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(light_type) light, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(interaction_type) interaction, bool isBSDF, NBL_CONST_REF_ARG(vector3_type) xi, uint32_t depth, NBL_CONST_REF_ARG(Event) event) { - const Event::Mode mode = event.mode; + const Event::Mode mode = (Event::Mode)event.mode; switch (mode) { case Event::Mode::RAY_QUERY: @@ -171,7 +172,7 @@ struct Estimator break; case Event::Mode::PROCEDURAL: { - return procedural_generate_and_quotient_and_pdf(newRayMaxT, origin, interaction, isBSDF, xi, depth, event); + return procedural_generate_and_quotient_and_pdf(quotient_pdf, newRayMaxT, light, origin, interaction, isBSDF, xi, depth, event); } break; default: diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index b14c9baae..a740ec388 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -10,6 +10,7 @@ #include "intersector.hlsl" #include "material_system.hlsl" #include "next_event_estimator.hlsl" +#include "scene.hlsl" namespace nbl { @@ -170,8 +171,8 @@ struct Unidirectional scalar_type t; sample_type nee_sample = nee.generate_and_quotient_and_pdf( neeContrib_pdf, t, - intersection, interaction, - isBSDF, eps0, depth + lights[lightID], intersection, interaction, + isBSDF, eps0, depth, scene.toNextEvent(lightID) ); // We don't allow non watertight transmitters in this renderer @@ -236,7 +237,7 @@ struct Unidirectional nee_ray.origin = intersection + nee_sample.L.direction * t * Tolerance::getStart(depth); nee_ray.direction = nee_sample.L.direction; nee_ray.intersectionT = t; - if (bsdf_quotient_pdf.pdf < numeric_limits::max && getLuma(neeContrib_pdf.quotient) > lumaContributionThreshold && intersector.traceRay(nee_ray, scene).id == -1) + if (bsdf_quotient_pdf.pdf < numeric_limits::max && getLuma(neeContrib_pdf.quotient) > lumaContributionThreshold && intersector::traceRay(nee_ray, scene).id == -1) ray._payload.accumulation += neeContrib_pdf.quotient; } } @@ -338,7 +339,7 @@ struct Unidirectional for (int d = 1; d <= depth && hit && rayAlive; d += 2) { ray.intersectionT = numeric_limits::max; - ray.objectID = intersector.traceRay(ray, scene); + ray.objectID = intersector::traceRay(ray, scene); hit = ray.objectID.id != -1; if (hit) diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index 1c8c15ec4..f9558c3d1 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -167,6 +167,10 @@ void main(uint32_t3 threadID : SV_DispatchThreadID) ptCreateParams.NDC = NDC; ptCreateParams.invMVP = pc.invMVP; + ptCreateParams.diffuseParams = bxdfs[0].params; + ptCreateParams.conductorParams = bxdfs[3].params; + ptCreateParams.dielectricParams = bxdfs[6].params; + pathtracer_type pathtracer = pathtracer_type::create(ptCreateParams, samplerSequence); // set up scene (can do as global var?) diff --git a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl index cbc9d153c..fc10d906c 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl @@ -129,41 +129,41 @@ struct Scene case PST_SPHERE: { Shape sphere = spheres[id]; - retval.data[2 + Shape::ObjSize] = asuint(sphere.position.x); - retval.data[2 + Shape::ObjSize + 1] = asuint(sphere.position.y); - retval.data[2 + Shape::ObjSize + 2] = asuint(sphere.position.z); - retval.data[2 + Shape::ObjSize + 3] = asuint(sphere.radius); - retval.data[2 + Shape::ObjSize + 4] = sphere.bsdfLightIDs; + retval.data[2] = asuint(sphere.position.x); + retval.data[3] = asuint(sphere.position.y); + retval.data[4] = asuint(sphere.position.z); + retval.data[5] = asuint(sphere.radius); + retval.data[6] = sphere.bsdfLightIDs; } break; case PST_TRIANGLE: { Shape tri = triangles[id]; - retval.data[2 + Shape::ObjSize] = asuint(tri.vertex0.x); - retval.data[2 + Shape::ObjSize + 1] = asuint(tri.vertex0.y); - retval.data[2 + Shape::ObjSize + 2] = asuint(tri.vertex0.z); - retval.data[2 + Shape::ObjSize + 3] = asuint(tri.vertex1.x); - retval.data[2 + Shape::ObjSize + 4] = asuint(tri.vertex1.y); - retval.data[2 + Shape::ObjSize + 5] = asuint(tri.vertex1.z); - retval.data[2 + Shape::ObjSize + 6] = asuint(tri.vertex2.x); - retval.data[2 + Shape::ObjSize + 7] = asuint(tri.vertex2.y); - retval.data[2 + Shape::ObjSize + 8] = asuint(tri.vertex2.z); - retval.data[2 + Shape::ObjSize + 9] = tri.bsdfLightIDs; + retval.data[2] = asuint(tri.vertex0.x); + retval.data[3] = asuint(tri.vertex0.y); + retval.data[4] = asuint(tri.vertex0.z); + retval.data[5] = asuint(tri.vertex1.x); + retval.data[6] = asuint(tri.vertex1.y); + retval.data[7] = asuint(tri.vertex1.z); + retval.data[8] = asuint(tri.vertex2.x); + retval.data[9] = asuint(tri.vertex2.y); + retval.data[10] = asuint(tri.vertex2.z); + retval.data[11] = tri.bsdfLightIDs; } break; case PST_RECTANGLE: { Shape rect = rectangles[id]; - retval.data[2 + Shape::ObjSize] = asuint(rect.offset.x); - retval.data[2 + Shape::ObjSize + 1] = asuint(rect.offset.y); - retval.data[2 + Shape::ObjSize + 2] = asuint(rect.offset.z); - retval.data[2 + Shape::ObjSize + 3] = asuint(rect.edge0.x); - retval.data[2 + Shape::ObjSize + 4] = asuint(rect.edge0.y); - retval.data[2 + Shape::ObjSize + 5] = asuint(rect.edge0.z); - retval.data[2 + Shape::ObjSize + 6] = asuint(rect.edge1.x); - retval.data[2 + Shape::ObjSize + 7] = asuint(rect.edge1.y); - retval.data[2 + Shape::ObjSize + 8] = asuint(rect.edge1.z); - retval.data[2 + Shape::ObjSize + 9] = rect.bsdfLightIDs; + retval.data[2] = asuint(rect.offset.x); + retval.data[3] = asuint(rect.offset.y); + retval.data[4] = asuint(rect.offset.z); + retval.data[5] = asuint(rect.edge0.x); + retval.data[6] = asuint(rect.edge0.y); + retval.data[7] = asuint(rect.edge0.z); + retval.data[8] = asuint(rect.edge1.x); + retval.data[9] = asuint(rect.edge1.y); + retval.data[10] = asuint(rect.edge1.z); + retval.data[11] = rect.bsdfLightIDs; } break; default: diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index 13aa59823..5aff6bde7 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -369,8 +369,8 @@ class ComputeShaderPathtracer final : public examples::SimpleWindowedApplication options.preprocessorOptions.logger = m_logger.get(); options.preprocessorOptions.includeFinder = compiler->getDefaultIncludeFinder(); - //std::string dxcOptionStr[] = { "-D" + defineMacro }; - //options.dxcOptions = std::span(dxcOptionStr); + std::string dxcOptionStr[] = { "-D" + defineMacro }; + options.dxcOptions = std::span(dxcOptionStr); source = compiler->compileToSPIRV((const char*)source->getContent()->getPointer(), options); }; From a802a97943bd9e17187a306f8058c21d2774678b Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 21 Feb 2025 16:57:15 +0700 Subject: [PATCH 20/53] bug fixes #3 --- .../app_resources/hlsl/common.hlsl | 7 ++++--- .../hlsl/next_event_estimator.hlsl | 20 +++++++++---------- .../app_resources/hlsl/pathtracer.hlsl | 16 +++++++-------- .../app_resources/hlsl/render.comp.hlsl | 10 +++++----- .../app_resources/hlsl/scene.hlsl | 18 ++++++++--------- 5 files changed, 36 insertions(+), 35 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index 1b0aac72f..f12b72b5d 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -134,7 +135,7 @@ struct Shape static Shape create(NBL_CONST_REF_ARG(float32_t3) position, float32_t radius, uint32_t bsdfID, uint32_t lightID) { - uint32_t bsdfLightIDs = spirv::bitFieldInsert(bsdfID, lightID, 16, 16); + uint32_t bsdfLightIDs = glsl::bitfieldInsert(bsdfID, lightID, 16, 16); return create(position, radius, bsdfLightIDs); } @@ -226,7 +227,7 @@ struct Shape static Shape create(NBL_CONST_REF_ARG(float32_t3) vertex0, NBL_CONST_REF_ARG(float32_t3) vertex1, NBL_CONST_REF_ARG(float32_t3) vertex2, uint32_t bsdfID, uint32_t lightID) { - uint32_t bsdfLightIDs = spirv::bitFieldInsert(bsdfID, lightID, 16, 16); + uint32_t bsdfLightIDs = glsl::bitfieldInsert(bsdfID, lightID, 16, 16); return create(vertex0, vertex1, vertex2, bsdfLightIDs); } @@ -374,7 +375,7 @@ struct Shape static Shape create(NBL_CONST_REF_ARG(float32_t3) offset, NBL_CONST_REF_ARG(float32_t3) edge0, NBL_CONST_REF_ARG(float32_t3) edge1, uint32_t bsdfID, uint32_t lightID) { - uint32_t bsdfLightIDs = spirv::bitFieldInsert(bsdfID, lightID, 16, 16); + uint32_t bsdfLightIDs = glsl::bitfieldInsert(bsdfID, lightID, 16, 16); return create(offset, edge0, edge1, bsdfLightIDs); } diff --git a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl index c7573fbb3..32a7b7476 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl @@ -119,26 +119,26 @@ struct Estimator { case PST_SPHERE: { - vector3_type position = vector3_type(asfloat(event.data[2 + Shape::ObjSize]), asfloat(event.data[2 + Shape::ObjSize + 1]), asfloat(event.data[2 + Shape::ObjSize + 2])); - Shape sphere = Shape::create(position, asfloat(event.data[2 + Shape::ObjSize + 3]), event.data[2 + Shape::ObjSize + 4]); + vector3_type position = vector3_type(asfloat(event.data[2]), asfloat(event.data[3]), asfloat(event.data[4])); + Shape sphere = Shape::create(position, asfloat(event.data[5]), event.data[6]); L = sphere.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); } break; case PST_TRIANGLE: { - vector3_type vertex0 = vector3_type(asfloat(event.data[2 + Shape::ObjSize]), asfloat(event.data[2 + Shape::ObjSize + 1]), asfloat(event.data[2 + Shape::ObjSize + 2])); - vector3_type vertex1 = vector3_type(asfloat(event.data[2 + Shape::ObjSize + 3]), asfloat(event.data[2 + Shape::ObjSize + 4]), asfloat(event.data[2 + Shape::ObjSize + 5])); - vector3_type vertex2 = vector3_type(asfloat(event.data[2 + Shape::ObjSize + 6]), asfloat(event.data[2 + Shape::ObjSize + 7]), asfloat(event.data[2 + Shape::ObjSize + 8])); - Shape tri = Shape::create(vertex0, vertex1, vertex2, event.data[2 + Shape::ObjSize + 9]); + vector3_type vertex0 = vector3_type(asfloat(event.data[2]), asfloat(event.data[3]), asfloat(event.data[4])); + vector3_type vertex1 = vector3_type(asfloat(event.data[5]), asfloat(event.data[6]), asfloat(event.data[7])); + vector3_type vertex2 = vector3_type(asfloat(event.data[8]), asfloat(event.data[9]), asfloat(event.data[10])); + Shape tri = Shape::create(vertex0, vertex1, vertex2, event.data[11]); L = tri.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); } break; case PST_RECTANGLE: { - vector3_type offset = vector3_type(asfloat(event.data[2 + Shape::ObjSize]), asfloat(event.data[2 + Shape::ObjSize + 1]), asfloat(event.data[2 + Shape::ObjSize + 2])); - vector3_type edge0 = vector3_type(asfloat(event.data[2 + Shape::ObjSize + 3]), asfloat(event.data[2 + Shape::ObjSize + 4]), asfloat(event.data[2 + Shape::ObjSize + 5])); - vector3_type edge1 = vector3_type(asfloat(event.data[2 + Shape::ObjSize + 6]), asfloat(event.data[2 + Shape::ObjSize + 7]), asfloat(event.data[2 + Shape::ObjSize + 8])); - Shape rect = Shape::create(offset, edge0, edge1, event.data[2 + Shape::ObjSize + 9]); + vector3_type offset = vector3_type(asfloat(event.data[2]), asfloat(event.data[3]), asfloat(event.data[4])); + vector3_type edge0 = vector3_type(asfloat(event.data[5]), asfloat(event.data[6]), asfloat(event.data[7])); + vector3_type edge1 = vector3_type(asfloat(event.data[8]), asfloat(event.data[9]), asfloat(event.data[10])); + Shape rect = Shape::create(offset, edge0, edge1, event.data[11]); L = rect.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); } break; diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index a740ec388..c47f24753 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -82,15 +82,15 @@ struct Unidirectional this_t retval; retval.randGen = randgen_type::create(params.rngState); retval.rayGen = raygen_type::create(params.pixOffsetParam, params.camPos, params.NDC, params.invMVP); - retval.materialSystem = material_system_type::create(diffuseParams, conductorParams, dielectricParams); + retval.materialSystem = material_system_type::create(params.diffuseParams, params.conductorParams, params.dielectricParams); retval.samplerSequence = samplerSequence; return retval; } vector3_type rand3d(uint32_t protoDimension, uint32_t _sample, uint32_t i) { - uint32_t address = spirv::bitfieldInsert(protoDimension, _sample, MAX_DEPTH_LOG2, MAX_SAMPLES_LOG2); - unit32_t3 seqVal = texelFetch(sampleSequence, int(address) + i).xyz; + uint32_t address = glsl::bitfieldInsert(protoDimension, _sample, MAX_DEPTH_LOG2, MAX_SAMPLES_LOG2); + uint32_t3 seqVal = texelFetch(sampleSequence, int(address) + i).xyz; seqVal ^= randGen(); return vector3_type(seqVal) * asfloat(0x2f800004u); } @@ -101,7 +101,7 @@ struct Unidirectional } // TODO: probably will only work with procedural shapes, do the other ones - bool closestHitProgram(unit32_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(scene_type) scene) { const uint32_t objectID = ray.objectID; const vector3_type intersection = ray.origin + ray.direction * ray.intersectionT; @@ -117,8 +117,8 @@ struct Unidirectional break; case ext::Intersector::IntersectData::Mode::PROCEDURAL: { - bsdfLightIDs = scene.getBsdfLightIDs(objectID.id); - vector3_type N = scene.getNormal(objectID.id) + bsdfLightIDs = scene.getBsdfLightIDs(objectID); + vector3_type N = scene.getNormal(objectID); N = nbl::hlsl::normalize(N); typename isotropic_type::ray_dir_info_type V; V.direction = nbl::hlsl::normalize(-ray.direction); @@ -133,14 +133,14 @@ struct Unidirectional vector3_type throughput = ray.payload.throughput; // emissive - const uint32_t lightID = spirv::bitfieldExtract(bsdfLightIDs, 16, 16); + const uint32_t lightID = glsl::bitfieldExtract(bsdfLightIDs, 16, 16); if (lightID != light_type::INVALID_ID) { float pdf; ray.payload.accumulation += nee.deferredEvalAndPdf(pdf, lights[lightID], ray, scene.toNextEvent(lightID)) * throughput / (1.0 + pdf * pdf * ray.payload.otherTechniqueHeuristic); } - const uint32_t bsdfID = spirv::bitfieldExtract(bsdfLightIDs, 0, 16); + const uint32_t bsdfID = glsl::bitfieldExtract(bsdfLightIDs, 0, 16); if (bsdfID == bxdfnode_type::INVALID_ID) return false; diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index f9558c3d1..4143b973d 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -80,7 +80,7 @@ using material_system_type = ext::MaterialSystem::System; using pathtracer_type = ext::PathTracer::Unidirectional; -Shape spheres[SPHERE_COUNT] = { +static const Shape spheres[SPHERE_COUNT] = { Shape::create(float3(0.0, -100.5, -1.0), 100.0, 0u, light_type::INVALID_ID), Shape::create(float3(2.0, 0.0, -1.0), 0.5, 1u, light_type::INVALID_ID), Shape::create(float3(0.0, 0.0, -1.0), 0.5, 2u, light_type::INVALID_ID), @@ -97,7 +97,7 @@ Shape spheres[SPHERE_COUNT] = { #ifdef TRIANGLE_LIGHT #define LIGHT_TYPE PST_TRIANGLE #define TRIANGLE_COUNT 1 -Shape triangles[TRIANGLE_COUNT] = { +static const Shape triangles[TRIANGLE_COUNT] = { Shape::create(float3(-1.8,0.35,0.3) * 10.0, float3(-1.2,0.35,0.0) * 10.0, float3(-1.5,0.8,-0.3) * 10.0, bxdfnode_type::INVALID_ID, 0u) }; #endif @@ -105,18 +105,18 @@ Shape triangles[TRIANGLE_COUNT] = { #ifdef RECTANGLE_LIGHT #define LIGHT_TYPE PST_RECTANGLE #define RECTANGLE_COUNT 1 -Shape rectangles[RECTANGLE_COUNT] = { +static const Shape rectangles[RECTANGLE_COUNT] = { Shape::create(float3(-3.8,0.35,1.3), normalize(float3(2,0,-1))*7.0, normalize(float3(2,-5,4))*0.1, bxdfnode_type::INVALID_ID, 0u) }; #endif #define LIGHT_COUNT 1 -light_type lights[LIGHT_COUNT] = { +static const light_type lights[LIGHT_COUNT] = { light_type(spectral_t(30.0,25.0,15.0), ext::ObjectID(8u, ext::NextEventEstimator::Event::Mode::PROCEDURAL, LIGHT_TYPE)) }; #define BXDF_COUNT 7 -bxdfnode_type bxdfs[BXDF_COUNT] = { +static const bxdfnode_type bxdfs[BXDF_COUNT] = { bxdfnode_type(ext::MaterialSystem::Material::Type::DIFFUSE, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(1.25,1.25,1.25))), bxdfnode_type(ext::MaterialSystem::Material::Type::DIFFUSE, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(1.25,2.5,2.5))), bxdfnode_type(ext::MaterialSystem::Material::Type::DIFFUSE, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(2.5,1.25,2.5))), diff --git a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl index fc10d906c..88940c54d 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl @@ -65,7 +65,7 @@ struct Scene retval.data[2 + i * Shape::ObjSize] = asuint(sphere.position.x); retval.data[2 + i * Shape::ObjSize + 1] = asuint(sphere.position.y); retval.data[2 + i * Shape::ObjSize + 2] = asuint(sphere.position.z); - retval.data[2 + i * Shape::ObjSize + 3] = asuint(sphere.radius); + retval.data[2 + i * Shape::ObjSize + 3] = asuint(sphere.radius2); retval.data[2 + i * Shape::ObjSize + 4] = sphere.bsdfLightIDs; } } @@ -174,18 +174,18 @@ struct Scene } // TODO: get these to work with AS types as well - uint32_t getBsdfLightIDs(uint32_t id) + uint32_t getBsdfLightIDs(NBL_CONST_REF_ARG(ObjectID) objectID) { - return (objectID.type == PST_SPHERE) ? spheres[id].bsdfLightIDs : - (objectID.type == PST_TRIANGLE) ? triangles[id].bsdfLightIDs : - (objectID.type == PST_RECTANGLE) ? rectangles[id].bsdfLightIDs : -1; + return (objectID.type == PST_SPHERE) ? spheres[objectID.id].bsdfLightIDs : + (objectID.type == PST_TRIANGLE) ? triangles[objectID.id].bsdfLightIDs : + (objectID.type == PST_RECTANGLE) ? rectangles[objectID.id].bsdfLightIDs : -1; } - float32_t3 getNormal(uint32_t id, NBL_CONST_REF_ARG(float32_t3) intersection) + float32_t3 getNormal(NBL_CONST_REF_ARG(ObjectID) objectID, NBL_CONST_REF_ARG(float32_t3) intersection) { - return (objectID.type == PST_SPHERE) ? scene.spheres[id].getNormal(intersection) : - (objectID.type == PST_TRIANGLE) ? scene.triangles[id].getNormalTimesArea() : - (objectID.type == PST_RECTANGLE) ? scene.rectangles[id].getNormalTimesArea() : + return (objectID.type == PST_SPHERE) ? scene.spheres[objectID.id].getNormal(intersection) : + (objectID.type == PST_TRIANGLE) ? scene.triangles[objectID.id].getNormalTimesArea() : + (objectID.type == PST_RECTANGLE) ? scene.rectangles[objectID.id].getNormalTimesArea() : (float32_t3)0.0; } }; From eed47e73c53be25cb9be67924ca0d075897b64bc Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 24 Feb 2025 10:41:08 +0700 Subject: [PATCH 21/53] fix include when embed resources off --- 31_HLSLPathTracer/app_resources/glsl/litByRectangle.comp | 2 +- 31_HLSLPathTracer/app_resources/glsl/litBySphere.comp | 2 +- 31_HLSLPathTracer/app_resources/glsl/litByTriangle.comp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/glsl/litByRectangle.comp b/31_HLSLPathTracer/app_resources/glsl/litByRectangle.comp index 300cef559..d898655c4 100644 --- a/31_HLSLPathTracer/app_resources/glsl/litByRectangle.comp +++ b/31_HLSLPathTracer/app_resources/glsl/litByRectangle.comp @@ -7,7 +7,7 @@ #define SPHERE_COUNT 8 #define POLYGON_METHOD 1 // 0 area sampling, 1 solid angle sampling, 2 approximate projected solid angle sampling -#include "common.glsl" +#include "app_resources/glsl/common.glsl" #define RECTANGLE_COUNT 1 const vec3 edge0 = normalize(vec3(2,0,-1)); diff --git a/31_HLSLPathTracer/app_resources/glsl/litBySphere.comp b/31_HLSLPathTracer/app_resources/glsl/litBySphere.comp index bd1a48575..c8ebb9f08 100644 --- a/31_HLSLPathTracer/app_resources/glsl/litBySphere.comp +++ b/31_HLSLPathTracer/app_resources/glsl/litBySphere.comp @@ -6,7 +6,7 @@ #extension GL_GOOGLE_include_directive : require #define SPHERE_COUNT 9 -#include "common.glsl" +#include "app_resources/glsl/common.glsl" void traceRay_extraShape(inout int objectID, inout float intersectionT, in vec3 origin, in vec3 direction) diff --git a/31_HLSLPathTracer/app_resources/glsl/litByTriangle.comp b/31_HLSLPathTracer/app_resources/glsl/litByTriangle.comp index ba23c82e5..36fe522f2 100644 --- a/31_HLSLPathTracer/app_resources/glsl/litByTriangle.comp +++ b/31_HLSLPathTracer/app_resources/glsl/litByTriangle.comp @@ -7,7 +7,7 @@ #define SPHERE_COUNT 8 #define POLYGON_METHOD 1 // 0 area sampling, 1 solid angle sampling, 2 approximate projected solid angle sampling -#include "common.glsl" +#include "app_resources/glsl/common.glsl" #define TRIANGLE_COUNT 1 Triangle triangles[TRIANGLE_COUNT] = { From 6e26dae254d190ea66e812fa0789e958716edacc Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 24 Feb 2025 17:11:42 +0700 Subject: [PATCH 22/53] fixed more bugs #4 --- .../app_resources/hlsl/common.hlsl | 2 +- .../app_resources/hlsl/pathtracer.hlsl | 35 +-- .../app_resources/hlsl/render.comp.hlsl | 273 +++++++++--------- .../app_resources/hlsl/scene.hlsl | 18 +- 4 files changed, 165 insertions(+), 163 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index f12b72b5d..cd2310fbf 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -184,7 +184,7 @@ struct Shape Z *= rcpDistance; const float cosThetaMax = nbl::hlsl::sqrt(cosThetaMax2); - const float cosTheta = nbl::hlsl::mix(1.0, cosThetaMax, xi.x); + const float cosTheta = nbl::hlsl::mix(1.0, cosThetaMax, xi.x); float32_t3 L = Z * cosTheta; diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index c47f24753..f1237006c 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -4,6 +4,7 @@ #include #include #include +#include #include "rand_gen.hlsl" #include "ray_gen.hlsl" @@ -77,13 +78,13 @@ struct Unidirectional // NextEventEstimator nee) // {} - static this_t create(NBL_CONST_REF_ARG(PathTracerCreationParams) params, Buffer samplerSequence) + static this_t create(NBL_CONST_REF_ARG(PathTracerCreationParams) params, Buffer sampleSequence) { this_t retval; retval.randGen = randgen_type::create(params.rngState); retval.rayGen = raygen_type::create(params.pixOffsetParam, params.camPos, params.NDC, params.invMVP); retval.materialSystem = material_system_type::create(params.diffuseParams, params.conductorParams, params.dielectricParams); - retval.samplerSequence = samplerSequence; + retval.sampleSequence = sampleSequence; return retval; } @@ -103,13 +104,14 @@ struct Unidirectional // TODO: probably will only work with procedural shapes, do the other ones bool closestHitProgram(uint32_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(scene_type) scene) { - const uint32_t objectID = ray.objectID; + const ObjectID objectID = ray.objectID; const vector3_type intersection = ray.origin + ray.direction * ray.intersectionT; uint32_t bsdfLightIDs; anisotropic_type interaction; isotropic_type iso_interaction; - switch (objectID.mode) + ext::Intersector::IntersectData::Mode mode = (ext::Intersector::IntersectData::Mode)objectID.mode; + switch (mode) { // TODO case ext::Intersector::IntersectData::Mode::RAY_QUERY: @@ -137,14 +139,14 @@ struct Unidirectional if (lightID != light_type::INVALID_ID) { float pdf; - ray.payload.accumulation += nee.deferredEvalAndPdf(pdf, lights[lightID], ray, scene.toNextEvent(lightID)) * throughput / (1.0 + pdf * pdf * ray.payload.otherTechniqueHeuristic); + ray.payload.accumulation += nee.deferredEvalAndPdf(pdf, scene.lights[lightID], ray, scene.toNextEvent(lightID)) * 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 bxdf = scene.bxdfs[bsdfID]; + bxdfnode_type bxdf = scene.bxdfs[bsdfID]; // TODO: ifdef kill diffuse specular paths @@ -171,7 +173,7 @@ struct Unidirectional scalar_type t; sample_type nee_sample = nee.generate_and_quotient_and_pdf( neeContrib_pdf, t, - lights[lightID], intersection, interaction, + scene.lights[lightID], intersection, interaction, isBSDF, eps0, depth, scene.toNextEvent(lightID) ); @@ -206,7 +208,7 @@ struct Unidirectional params = params_type::template create(nee_sample, interaction, _cache, bxdf::BCM_MAX); else { - isocache = (iso_cache)_cache; + isocache_type isocache = (isocache_type)_cache; params = params_type::template create(nee_sample, iso_interaction, isocache, bxdf::BCM_MAX); } } @@ -220,7 +222,7 @@ struct Unidirectional params = params_type::template create(nee_sample, interaction, _cache, bxdf::BCM_ABS); else { - isocache = (iso_cache)_cache; + isocache_type isocache = (isocache_type)_cache; params = params_type::template create(nee_sample, iso_interaction, isocache, bxdf::BCM_ABS); } } @@ -237,7 +239,7 @@ struct Unidirectional nee_ray.origin = intersection + nee_sample.L.direction * t * Tolerance::getStart(depth); nee_ray.direction = nee_sample.L.direction; nee_ray.intersectionT = t; - if (bsdf_quotient_pdf.pdf < numeric_limits::max && getLuma(neeContrib_pdf.quotient) > lumaContributionThreshold && intersector::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).id == -1) ray._payload.accumulation += neeContrib_pdf.quotient; } } @@ -265,7 +267,7 @@ struct Unidirectional params = params_type::template create(bsdf_sample, interaction, _cache, bxdf::BCM_MAX); else { - isocache = (iso_cache)_cache; + isocache_type isocache = (isocache_type)_cache; params = params_type::template create(bsdf_sample, iso_interaction, isocache, bxdf::BCM_MAX); } } @@ -279,7 +281,7 @@ struct Unidirectional params = params_type::template create(bsdf_sample, interaction, _cache, bxdf::BCM_ABS); else { - isocache = (iso_cache)_cache; + isocache_type isocache = (isocache_type)_cache; params = params_type::template create(bsdf_sample, iso_interaction, isocache, bxdf::BCM_ABS); } } @@ -298,7 +300,7 @@ struct Unidirectional ray.payload.otherTechniqueHeuristic *= ray.payload.otherTechniqueHeuristic; // trace new ray - ray.origin = intersection + bsdfSampleL * (1.0/*kSceneSize*/) * Tolerance::getStart(depth); + ray.origin = intersection + bxdfSample * (1.0/*kSceneSize*/) * Tolerance::getStart(depth); ray.direction = bxdfSample; // #if POLYGON_METHOD==2 // ray._immutable.normalAtOrigin = interaction.isotropic.N; @@ -339,7 +341,7 @@ struct Unidirectional for (int d = 1; d <= depth && hit && rayAlive; d += 2) { ray.intersectionT = numeric_limits::max; - ray.objectID = intersector::traceRay(ray, scene); + ray.objectID = intersector_type::traceRay(ray, scene); hit = ray.objectID.id != -1; if (hit) @@ -348,7 +350,7 @@ struct Unidirectional if (!hit) missProgram(ray); - spectral_type accumulation = ray.payload.accumulation; + measure_type accumulation = ray.payload.accumulation; scalar_type rcpSampleSize = 1.0 / (i + 1); Li += (accumulation - Li) * rcpSampleSize; @@ -365,11 +367,10 @@ struct Unidirectional randgen_type randGen; raygen_type rayGen; - intersector_type intersector; material_system_type materialSystem; nee_type nee; - Buffer samplerSequence; + Buffer sampleSequence; }; } diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index 4143b973d..cc64de33c 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -1,6 +1,7 @@ #include "nbl/builtin/hlsl/cpp_compat.hlsl" #include "nbl/builtin/hlsl/glsl_compat/core.hlsl" #include "nbl/builtin/hlsl/random/pcg.hlsl" +#include "nbl/builtin/hlsl/random/xoroshiro.hlsl" #include "nbl/builtin/hlsl/bxdf/reflection.hlsl" #include "nbl/builtin/hlsl/bxdf/transmission.hlsl" @@ -40,7 +41,7 @@ struct SPushConstants [[vk::combinedImageSampler]][[vk::binding(2, 2)]] Texture2D scramblebuf; // unused [[vk::combinedImageSampler]][[vk::binding(2, 2)]] SamplerState scrambleSampler; -[[vk::binding(0, 0)]] RWTexture2D outImage; +[[vk::image_format("rgba16f")]][[vk::binding(0, 0)]] RWTexture2D outImage; int32_t2 getCoordinates() { @@ -64,142 +65,142 @@ using aniso_cache = bxdf::SAnisotropicMicrofacetCache; using quotient_pdf_t = bxdf::quotient_and_pdf; using spectral_t = vector; using params_t = bxdf::SBxDFParams; -using create_params_t = SBxDFCreationParams; - -using diffuse_bxdf_type = bxdf::reflection::SOrenNayarBxDF; -using conductor_bxdf_type = bxdf::reflection::SGGXBxDF; -using dielectric_bxdf_type = bxdf::transmission::SGGXDielectricBxDF; - -using ray_type = ext::Ray; -using light_type = ext::Light; -using bxdfnode_type = ext::BxDFNode; -using randgen_type = ext::RandGen::Uniform3D; -using raygen_type = ext::RayGen::Basic; -using intersector_type = ext::Intersector::Comprehensive; -using material_system_type = ext::MaterialSystem::System; -using nee_type = ext::NextEventEstimator::Estimator; -using pathtracer_type = ext::PathTracer::Unidirectional; - -static const Shape spheres[SPHERE_COUNT] = { - Shape::create(float3(0.0, -100.5, -1.0), 100.0, 0u, light_type::INVALID_ID), - Shape::create(float3(2.0, 0.0, -1.0), 0.5, 1u, light_type::INVALID_ID), - Shape::create(float3(0.0, 0.0, -1.0), 0.5, 2u, light_type::INVALID_ID), - Shape::create(float3(-2.0, 0.0, -1.0), 0.5, 3u, light_type::INVALID_ID), - Shape::create(float3(2.0, 0.0, 1.0), 0.5, 4u, light_type::INVALID_ID), - Shape::create(float3(0.0, 0.0, 1.0), 0.5, 4u, light_type::INVALID_ID), - Shape::create(float3(-2.0, 0.0, 1.0), 0.5, 5u, light_type::INVALID_ID), - Shape::create(float3(0.5, 1.0, 0.5), 0.5, 6u, light_type::INVALID_ID) -#ifdef SPHERE_LIGHT - ,Shape::create(float3(-1.5, 1.5, 0.0), 0.3, bxdfnode_type::INVALID_ID, 0u) -#endif -}; - -#ifdef TRIANGLE_LIGHT -#define LIGHT_TYPE PST_TRIANGLE -#define TRIANGLE_COUNT 1 -static const Shape triangles[TRIANGLE_COUNT] = { - Shape::create(float3(-1.8,0.35,0.3) * 10.0, float3(-1.2,0.35,0.0) * 10.0, float3(-1.5,0.8,-0.3) * 10.0, bxdfnode_type::INVALID_ID, 0u) -}; -#endif - -#ifdef RECTANGLE_LIGHT -#define LIGHT_TYPE PST_RECTANGLE -#define RECTANGLE_COUNT 1 -static const Shape rectangles[RECTANGLE_COUNT] = { - Shape::create(float3(-3.8,0.35,1.3), normalize(float3(2,0,-1))*7.0, normalize(float3(2,-5,4))*0.1, bxdfnode_type::INVALID_ID, 0u) -}; -#endif - -#define LIGHT_COUNT 1 -static const light_type lights[LIGHT_COUNT] = { - light_type(spectral_t(30.0,25.0,15.0), ext::ObjectID(8u, ext::NextEventEstimator::Event::Mode::PROCEDURAL, LIGHT_TYPE)) -}; - -#define BXDF_COUNT 7 -static const bxdfnode_type bxdfs[BXDF_COUNT] = { - bxdfnode_type(ext::MaterialSystem::Material::Type::DIFFUSE, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(1.25,1.25,1.25))), - bxdfnode_type(ext::MaterialSystem::Material::Type::DIFFUSE, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(1.25,2.5,2.5))), - bxdfnode_type(ext::MaterialSystem::Material::Type::DIFFUSE, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(2.5,1.25,2.5))), - bxdfnode_type(ext::MaterialSystem::Material::Type::CONDUCTOR, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(0.98,0.98,0.77))), - bxdfnode_type(ext::MaterialSystem::Material::Type::CONDUCTOR, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(0.98,0.77,0.98))), - bxdfnode_type(ext::MaterialSystem::Material::Type::CONDUCTOR, create_params_t(false, float2(0.15,0.15), spectral_t(1,1,1), spectral_t(0.98,0.77,0.98))), - bxdfnode_type(ext::MaterialSystem::Material::Type::DIELECTRIC, create_params_t(false, float2(0.0625,0.0625), spectral_t(1,1,1), spectral_t(0.71,0.69,0.67))) -}; - -[numthreads(WorkgroupGridDim, WorkgroupGridDim, 1)] +using create_params_t = bxdf::SBxDFCreationParams; + +// using diffuse_bxdf_type = bxdf::reflection::SOrenNayarBxDF; +// using conductor_bxdf_type = bxdf::reflection::SGGXBxDF; +// using dielectric_bxdf_type = bxdf::transmission::SGGXDielectricBxDF; + +// using ray_type = ext::Ray; +// using light_type = ext::Light; +// using bxdfnode_type = ext::BxDFNode; +// using randgen_type = ext::RandGen::Uniform3D; +// using raygen_type = ext::RayGen::Basic; +// using intersector_type = ext::Intersector::Comprehensive; +// using material_system_type = ext::MaterialSystem::System; +// using nee_type = ext::NextEventEstimator::Estimator; +// using pathtracer_type = ext::PathTracer::Unidirectional; + +// static const Shape spheres[SPHERE_COUNT] = { +// Shape::create(float3(0.0, -100.5, -1.0), 100.0, 0u, light_type::INVALID_ID), +// Shape::create(float3(2.0, 0.0, -1.0), 0.5, 1u, light_type::INVALID_ID), +// Shape::create(float3(0.0, 0.0, -1.0), 0.5, 2u, light_type::INVALID_ID), +// Shape::create(float3(-2.0, 0.0, -1.0), 0.5, 3u, light_type::INVALID_ID), +// Shape::create(float3(2.0, 0.0, 1.0), 0.5, 4u, light_type::INVALID_ID), +// Shape::create(float3(0.0, 0.0, 1.0), 0.5, 4u, light_type::INVALID_ID), +// Shape::create(float3(-2.0, 0.0, 1.0), 0.5, 5u, light_type::INVALID_ID), +// Shape::create(float3(0.5, 1.0, 0.5), 0.5, 6u, light_type::INVALID_ID) +// #ifdef SPHERE_LIGHT +// ,Shape::create(float3(-1.5, 1.5, 0.0), 0.3, bxdfnode_type::INVALID_ID, 0u) +// #endif +// }; + +// #ifdef TRIANGLE_LIGHT +// #define LIGHT_TYPE PST_TRIANGLE +// #define TRIANGLE_COUNT 1 +// static const Shape triangles[TRIANGLE_COUNT] = { +// Shape::create(float3(-1.8,0.35,0.3) * 10.0, float3(-1.2,0.35,0.0) * 10.0, float3(-1.5,0.8,-0.3) * 10.0, bxdfnode_type::INVALID_ID, 0u) +// }; +// #endif + +// #ifdef RECTANGLE_LIGHT +// #define LIGHT_TYPE PST_RECTANGLE +// #define RECTANGLE_COUNT 1 +// static const Shape rectangles[RECTANGLE_COUNT] = { +// Shape::create(float3(-3.8,0.35,1.3), normalize(float3(2,0,-1))*7.0, normalize(float3(2,-5,4))*0.1, bxdfnode_type::INVALID_ID, 0u) +// }; +// #endif + +// #define LIGHT_COUNT 1 +// static const light_type lights[LIGHT_COUNT] = { +// light_type(spectral_t(30.0,25.0,15.0), ext::ObjectID(8u, ext::NextEventEstimator::Event::Mode::PROCEDURAL, LIGHT_TYPE)) +// }; + +// #define BXDF_COUNT 7 +// static const bxdfnode_type bxdfs[BXDF_COUNT] = { +// bxdfnode_type(ext::MaterialSystem::Material::Type::DIFFUSE, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(1.25,1.25,1.25))), +// bxdfnode_type(ext::MaterialSystem::Material::Type::DIFFUSE, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(1.25,2.5,2.5))), +// bxdfnode_type(ext::MaterialSystem::Material::Type::DIFFUSE, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(2.5,1.25,2.5))), +// bxdfnode_type(ext::MaterialSystem::Material::Type::CONDUCTOR, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(0.98,0.98,0.77))), +// bxdfnode_type(ext::MaterialSystem::Material::Type::CONDUCTOR, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(0.98,0.77,0.98))), +// bxdfnode_type(ext::MaterialSystem::Material::Type::CONDUCTOR, create_params_t(false, float2(0.15,0.15), spectral_t(1,1,1), spectral_t(0.98,0.77,0.98))), +// bxdfnode_type(ext::MaterialSystem::Material::Type::DIELECTRIC, create_params_t(false, float2(0.0625,0.0625), spectral_t(1,1,1), spectral_t(0.71,0.69,0.67))) +// }; + +[numthreads(WorkgroupSize, WorkgroupSize, 1)] void main(uint32_t3 threadID : SV_DispatchThreadID) { - uint32_t width, height; - outImage.GetDimensions(width, height); - const int32_t2 coords = getCoordinates(); - float32_t2 texCoord = float32_t2(coords) / float32_t2(width, height); - texCoord.y = 1.0 - texCoord.y; - - if (false == (all((int32_t2)0 < coords)) && all(int32_t2(width, height) < coords)) { - return; - } - - if (((pc.depth - 1) >> MAX_DEPTH_LOG2) > 0 || ((pc.sampleCount - 1) >> MAX_SAMPLES_LOG2) > 0) - { - float32_t4 pixelCol = float32_t4(1.0,0.0,0.0,1.0); - outImage[coords] = pixelCol; - return; - } - - int flatIdx = glsl::gl_GlobalInvocationID().y * glsl::gl_NumWorkGroups().x * WorkgroupSize + glsl::gl_GlobalInvocationID().x; - PCG32x2 pcg = PCG32x2::construct(flatIdx); // replaces scramblebuf? - - // set up path tracer - const PathTracerCreationParams ptCreateParams; - ptCreateParams.rngState = pcg(); - - uint2 scrambleDim; - scramblebuf.GetDimensions(scrambleDim.x, scrambleDim.y); - ptCreateParams.pixOffsetParam = (float2)1.0 / float2(scrambleDim); - - float4 NDC = float4(texCoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); - { - vec4 tmp = mul(pc.invMVP, NDC); - ptCreateParams.camPos = tmp.xyz / tmp.w; - NDC.z = 1.0; - } +// uint32_t width, height; +// outImage.GetDimensions(width, height); +// const int32_t2 coords = getCoordinates(); +// float32_t2 texCoord = float32_t2(coords) / float32_t2(width, height); +// texCoord.y = 1.0 - texCoord.y; + +// if (false == (all((int32_t2)0 < coords)) && all(int32_t2(width, height) < coords)) { +// return; +// } + +// if (((pc.depth - 1) >> MAX_DEPTH_LOG2) > 0 || ((pc.sampleCount - 1) >> MAX_SAMPLES_LOG2) > 0) +// { +// float32_t4 pixelCol = float32_t4(1.0,0.0,0.0,1.0); +// outImage[coords] = pixelCol; +// return; +// } + +// int flatIdx = glsl::gl_GlobalInvocationID().y * glsl::gl_NumWorkGroups().x * WorkgroupSize + glsl::gl_GlobalInvocationID().x; +// PCG32x2 pcg = PCG32x2::construct(flatIdx); // replaces scramblebuf? + +// // set up path tracer +// const PathTracerCreationParams ptCreateParams; +// ptCreateParams.rngState = pcg(); + +// uint2 scrambleDim; +// scramblebuf.GetDimensions(scrambleDim.x, scrambleDim.y); +// ptCreateParams.pixOffsetParam = (float2)1.0 / float2(scrambleDim); + +// float4 NDC = float4(texCoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); +// { +// vec4 tmp = mul(pc.invMVP, NDC); +// ptCreateParams.camPos = tmp.xyz / tmp.w; +// NDC.z = 1.0; +// } - ptCreateParams.NDC = NDC; - ptCreateParams.invMVP = pc.invMVP; - - ptCreateParams.diffuseParams = bxdfs[0].params; - ptCreateParams.conductorParams = bxdfs[3].params; - ptCreateParams.dielectricParams = bxdfs[6].params; - - pathtracer_type pathtracer = pathtracer_type::create(ptCreateParams, samplerSequence); - - // set up scene (can do as global var?) - Scene scene; - scene.sphereCount = SPHERE_COUNT; - for (uint32_t i = 0; i < SPHERE_COUNT; i++) - scene.spheres[i] = spheres[i]; -#ifdef TRIANGLE_LIGHT - scene.triangleCount = TRIANGLE_COUNT; - for (uint32_t i = 0; i < TRIANGLE_COUNT; i++) - scene.triangles[i] = triangles[i]; -#else - scene.triangleCount = 0; -#endif -#ifdef RECTANGLE_LIGHT - scene.rectangleCount = RECTANGLE_COUNT; - for (uint32_t i = 0; i < RECTANGLE_COUNT; i++) - scene.rectangles[i] = rectangles[i]; -#else - scene.rectangleCount = 0; -#endif - scene.lightCount = LIGHT_COUNT; - for (uint32_t i = 0; i < LIGHT_COUNT; i++) - scene.lights[i] = lights[i]; - scene.bxdfCount = BXDF_COUNT; - for (uint32_t i = 0; i < BXDF_COUNT; i++) - scene.bxdfs[i] = bxdfs[i]; - - float32_t3 color = pathtracer.getMeasure(pc.sampleCount, pc.depth, scene); - float32_t4 pixCol = float32_t4(color, 1.0); - outImage[coords] = pixCol; +// ptCreateParams.NDC = NDC; +// ptCreateParams.invMVP = pc.invMVP; + +// ptCreateParams.diffuseParams = bxdfs[0].params; +// ptCreateParams.conductorParams = bxdfs[3].params; +// ptCreateParams.dielectricParams = bxdfs[6].params; + +// pathtracer_type pathtracer = pathtracer_type::create(ptCreateParams, sampleSequence); + +// // set up scene (can do as global var?) +// Scene scene; +// scene.sphereCount = SPHERE_COUNT; +// for (uint32_t i = 0; i < SPHERE_COUNT; i++) +// scene.spheres[i] = spheres[i]; +// #ifdef TRIANGLE_LIGHT +// scene.triangleCount = TRIANGLE_COUNT; +// for (uint32_t i = 0; i < TRIANGLE_COUNT; i++) +// scene.triangles[i] = triangles[i]; +// #else +// scene.triangleCount = 0; +// #endif +// #ifdef RECTANGLE_LIGHT +// scene.rectangleCount = RECTANGLE_COUNT; +// for (uint32_t i = 0; i < RECTANGLE_COUNT; i++) +// scene.rectangles[i] = rectangles[i]; +// #else +// scene.rectangleCount = 0; +// #endif +// scene.lightCount = LIGHT_COUNT; +// for (uint32_t i = 0; i < LIGHT_COUNT; i++) +// scene.lights[i] = lights[i]; +// scene.bxdfCount = BXDF_COUNT; +// for (uint32_t i = 0; i < BXDF_COUNT; i++) +// scene.bxdfs[i] = bxdfs[i]; + +// float32_t3 color = pathtracer.getMeasure(pc.sampleCount, pc.depth, scene); +// float32_t4 pixCol = float32_t4(color, 1.0); +// outImage[coords] = pixCol; } diff --git a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl index 88940c54d..ed0c612f1 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl @@ -121,10 +121,10 @@ struct Scene retval.mode = objectID.mode; retval.data[0] = lightCount; - retval.data[1] = objectID.type; + retval.data[1] = objectID.shapeType; uint32_t id = objectID.id; - switch (type) + switch (objectID.shapeType) { case PST_SPHERE: { @@ -132,7 +132,7 @@ struct Scene retval.data[2] = asuint(sphere.position.x); retval.data[3] = asuint(sphere.position.y); retval.data[4] = asuint(sphere.position.z); - retval.data[5] = asuint(sphere.radius); + retval.data[5] = asuint(sphere.radius2); retval.data[6] = sphere.bsdfLightIDs; } break; @@ -176,16 +176,16 @@ struct Scene // TODO: get these to work with AS types as well uint32_t getBsdfLightIDs(NBL_CONST_REF_ARG(ObjectID) objectID) { - return (objectID.type == PST_SPHERE) ? spheres[objectID.id].bsdfLightIDs : - (objectID.type == PST_TRIANGLE) ? triangles[objectID.id].bsdfLightIDs : - (objectID.type == PST_RECTANGLE) ? rectangles[objectID.id].bsdfLightIDs : -1; + return (objectID.shapeType == PST_SPHERE) ? spheres[objectID.id].bsdfLightIDs : + (objectID.shapeType == PST_TRIANGLE) ? triangles[objectID.id].bsdfLightIDs : + (objectID.shapeType == PST_RECTANGLE) ? rectangles[objectID.id].bsdfLightIDs : -1; } float32_t3 getNormal(NBL_CONST_REF_ARG(ObjectID) objectID, NBL_CONST_REF_ARG(float32_t3) intersection) { - return (objectID.type == PST_SPHERE) ? scene.spheres[objectID.id].getNormal(intersection) : - (objectID.type == PST_TRIANGLE) ? scene.triangles[objectID.id].getNormalTimesArea() : - (objectID.type == PST_RECTANGLE) ? scene.rectangles[objectID.id].getNormalTimesArea() : + return (objectID.shapeType == PST_SPHERE) ? spheres[objectID.id].getNormal(intersection) : + (objectID.shapeType == PST_TRIANGLE) ? triangles[objectID.id].getNormalTimesArea() : + (objectID.shapeType == PST_RECTANGLE) ? rectangles[objectID.id].getNormalTimesArea() : (float32_t3)0.0; } }; From da661c08d50eb60b8e95fe4a0028aac653a10c4b Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 25 Feb 2025 12:12:38 +0700 Subject: [PATCH 23/53] fix compile hlsl shader bug --- 31_HLSLPathTracer/main.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index 5aff6bde7..4a2c1110b 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -331,7 +331,7 @@ class ComputeShaderPathtracer final : public examples::SimpleWindowedApplication auto shader = m_device->createShader(source.get()); if (!shader) { - m_logger->log("Shader creationed failed: %s!", ILogger::ELL_ERROR, pathToShader); + m_logger->log("GLSL shader creationed failed: %s!", ILogger::ELL_ERROR, pathToShader); std::exit(-1); } @@ -373,6 +373,15 @@ class ComputeShaderPathtracer final : public examples::SimpleWindowedApplication options.dxcOptions = std::span(dxcOptionStr); source = compiler->compileToSPIRV((const char*)source->getContent()->getPointer(), options); + + auto shader = m_device->createShader(source.get()); + if (!shader) + { + m_logger->log("HLSL shader creationed failed: %s!", ILogger::ELL_ERROR, pathToShader); + std::exit(-1); + } + + return shader; }; // Create compute pipelines From f97757bffcc28ad208a10dfb485214b8d9e1fdd1 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 25 Feb 2025 16:55:48 +0700 Subject: [PATCH 24/53] more bug fixes #5 --- .../app_resources/hlsl/common.hlsl | 68 +++++ .../app_resources/hlsl/intersector.hlsl | 42 +-- .../app_resources/hlsl/material_system.hlsl | 6 +- .../hlsl/next_event_estimator.hlsl | 18 -- .../app_resources/hlsl/pathtracer.hlsl | 6 +- .../app_resources/hlsl/ray_gen.hlsl | 2 +- .../app_resources/hlsl/render.comp.hlsl | 266 +++++++++--------- .../app_resources/hlsl/scene.hlsl | 3 - 31_HLSLPathTracer/main.cpp | 6 +- 9 files changed, 222 insertions(+), 195 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index cd2310fbf..a264fabd5 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -42,6 +42,15 @@ enum ProceduralShapeType : uint16_t struct ObjectID { + static ObjectID create(uint32_t id, uint32_t mode, ProceduralShapeType shapeType) + { + ObjectID retval; + retval.id = id; + retval.mode = mode; + retval.shapeType = shapeType; + return retval; + } + uint32_t id; uint32_t mode; ProceduralShapeType shapeType; @@ -85,6 +94,17 @@ struct BxDFNode NBL_CONSTEXPR_STATIC_INLINE uint32_t INVALID_ID = 0xffffu; + static BxDFNode create(uint32_t materialType, bool isAniso, NBL_CONST_REF_ARG(float32_t2) A, NBL_CONST_REF_ARG(spectral_type) ior0, NBL_CONST_REF_ARG(spectral_type) ior1) + { + BxDFNode retval; + retval.materialType = materialType; + retval.params.is_aniso = isAniso; + retval.params.A = A; + retval.params.ior0 = ior0; + retval.params.ior1 = ior1; + return retval; + } + uint32_t materialType; params_type params; }; @@ -118,6 +138,54 @@ enum PTPolygonMethod : uint16_t PPM_APPROX_PROJECTED_SOLID_ANGLE }; +namespace Intersector +{ +// ray query method +// ray query struct holds AS info +// pass in address to vertex/index buffers? + +// ray tracing pipeline method + +// procedural data store: [obj count] [intersect type] [obj1] [obj2] [...] + +struct IntersectData +{ + enum Mode : uint32_t // enum class? + { + RAY_QUERY, + RAY_TRACING, + PROCEDURAL + }; + + NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 128; + + uint32_t mode : 1; + uint32_t unused : 31; // possible space for flags + uint32_t data[DataSize]; +}; +} + +namespace NextEventEstimator +{ +// procedural data store: [light count] [event type] [obj] + +struct Event +{ + enum Mode : uint32_t // enum class? + { + RAY_QUERY, + RAY_TRACING, + PROCEDURAL + }; + + NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 16; + + uint32_t mode : 1; + uint32_t unused : 31; // possible space for flags + uint32_t data[DataSize]; +}; +} + template struct Shape; diff --git a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl index 0bb6cb31c..880ae1169 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl @@ -2,6 +2,7 @@ #define _NBL_HLSL_EXT_INTERSECTOR_INCLUDED_ #include "common.hlsl" +#include "scene.hlsl" #include namespace nbl @@ -13,38 +14,18 @@ namespace ext namespace Intersector { -// ray query method -// ray query struct holds AS info -// pass in address to vertex/index buffers? - -// ray tracing pipeline method - -// procedural data store: [obj count] [intersect type] [obj1] [obj2] [...] - -struct IntersectData -{ - enum Mode : uint32_t // enum class? - { - RAY_QUERY, - RAY_TRACING, - PROCEDURAL - }; - - NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 128; - - uint32_t mode : 1; - uint32_t unused : 31; // possible space for flags - uint32_t data[DataSize]; -}; - -template +template struct Comprehensive { using scalar_type = typename Ray::scalar_type; using vector3_type = vector; using ray_type = Ray; - static ObjectID traceProcedural(NBL_REF_ARG(ray_type) ray, NBL_REF_ARG(IntersectData) intersect) + using light_type = Light; + using bxdfnode_type = BxdfNode; + using scene_type = Scene; + + static ObjectID traceProcedural(NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(IntersectData) intersect) { const bool anyHit = ray.intersectionT != numeric_limits::max; const uint32_t objCount = intersect.data[0]; @@ -100,7 +81,7 @@ struct Comprehensive return objectID; } - static ObjectID traceRay(NBL_REF_ARG(ray_type) ray, NBL_REF_ARG(IntersectData) intersect) + static ObjectID traceRay(NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(IntersectData) intersect) { const IntersectData::Mode mode = (IntersectData::Mode)intersect.mode; switch (mode) @@ -122,15 +103,12 @@ struct Comprehensive break; default: { - ObjectID objID; - objID.id = -1; - return objID; + return ObjectID::create(-1, 0, PST_SPHERE); } } } - template - static ObjectID traceRay(NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(Scene) scene) + static ObjectID traceRay(NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(scene_type) scene) { IntersectData data; diff --git a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl index 9d638c232..16f8dcabf 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl @@ -51,9 +51,9 @@ struct System static this_t create(NBL_CONST_REF_ARG(create_params_t) diffuseParams, NBL_CONST_REF_ARG(create_params_t) conductorParams, NBL_CONST_REF_ARG(create_params_t) dielectricParams) { this_t retval; - retval.diffuseBxDF = DiffuseBxDF::create(diffuseParams); - retval.conductorBxDF = DiffuseBxDF::create(conductorParams); - retval.dielectricBxDF = DiffuseBxDF::create(dielectricParams); + retval.diffuseBxDF = diffuse_op_type::create(diffuseParams); + retval.conductorBxDF = conductor_op_type::create(conductorParams); + retval.dielectricBxDF = dielectric_op_type::create(dielectricParams); return retval; } diff --git a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl index 32a7b7476..f0eeb0885 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl @@ -12,24 +12,6 @@ namespace ext namespace NextEventEstimator { -// procedural data store: [light count] [event type] [obj] - -struct Event -{ - enum Mode : uint32_t // enum class? - { - RAY_QUERY, - RAY_TRACING, - PROCEDURAL - }; - - NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 16; - - uint32_t mode : 1; - uint32_t unused : 31; // possible space for flags - uint32_t data[DataSize]; -}; - template struct Estimator { diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index f1237006c..460744940 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -91,7 +91,7 @@ struct Unidirectional vector3_type rand3d(uint32_t protoDimension, uint32_t _sample, uint32_t i) { uint32_t address = glsl::bitfieldInsert(protoDimension, _sample, MAX_DEPTH_LOG2, MAX_SAMPLES_LOG2); - uint32_t3 seqVal = texelFetch(sampleSequence, int(address) + i).xyz; + uint32_t3 seqVal = sampleSequence[address + i].xyz; seqVal ^= randGen(); return vector3_type(seqVal) * asfloat(0x2f800004u); } @@ -120,7 +120,7 @@ struct Unidirectional case ext::Intersector::IntersectData::Mode::PROCEDURAL: { bsdfLightIDs = scene.getBsdfLightIDs(objectID); - vector3_type N = scene.getNormal(objectID); + vector3_type N = scene.getNormal(objectID, intersection); N = nbl::hlsl::normalize(N); typename isotropic_type::ray_dir_info_type V; V.direction = nbl::hlsl::normalize(-ray.direction); @@ -332,7 +332,7 @@ struct Unidirectional scalar_type meanLumaSq = 0.0; for (uint32_t i = 0; i < numSamples; i++) { - vector3_type uvw = rand3d(0u, i); + vector3_type uvw = rand3d(0u, i, randGen.rng()); // TODO: take from scramblebuf? ray_type ray = rayGen.generate(uvw); // bounces diff --git a/31_HLSLPathTracer/app_resources/hlsl/ray_gen.hlsl b/31_HLSLPathTracer/app_resources/hlsl/ray_gen.hlsl index dcb695fbe..0759b1cd3 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/ray_gen.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/ray_gen.hlsl @@ -50,7 +50,7 @@ struct Basic remappedRand.x += truncation; tmp.xy += pixOffsetParam * nbl::hlsl::boxMullerTransform(remappedRand, 1.5); // for depth of field we could do another stochastic point-pick - tmp = invMVP * tmp; + tmp = nbl::hlsl::mul(invMVP, tmp); ray.direction = nbl::hlsl::normalize(tmp.xyz / tmp.w - camPos); // #if POLYGON_METHOD==2 diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index cc64de33c..5be6adf78 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -15,7 +15,7 @@ #ifdef SPHERE_LIGHT #define SPHERE_COUNT 9 -#define LIGHT_TYPE PST_SPHERE +#define LIGHT_TYPE ext::PST_SPHERE #else #define SPHERE_COUNT 8 #endif @@ -23,6 +23,8 @@ using namespace nbl::hlsl; NBL_CONSTEXPR uint32_t WorkgroupSize = 32; +NBL_CONSTEXPR uint32_t MAX_DEPTH_LOG2 = 4; +NBL_CONSTEXPR uint32_t MAX_SAMPLES_LOG2 = 10; struct SPushConstants { @@ -67,140 +69,140 @@ using spectral_t = vector; using params_t = bxdf::SBxDFParams; using create_params_t = bxdf::SBxDFCreationParams; -// using diffuse_bxdf_type = bxdf::reflection::SOrenNayarBxDF; -// using conductor_bxdf_type = bxdf::reflection::SGGXBxDF; -// using dielectric_bxdf_type = bxdf::transmission::SGGXDielectricBxDF; - -// using ray_type = ext::Ray; -// using light_type = ext::Light; -// using bxdfnode_type = ext::BxDFNode; -// using randgen_type = ext::RandGen::Uniform3D; -// using raygen_type = ext::RayGen::Basic; -// using intersector_type = ext::Intersector::Comprehensive; -// using material_system_type = ext::MaterialSystem::System; -// using nee_type = ext::NextEventEstimator::Estimator; -// using pathtracer_type = ext::PathTracer::Unidirectional; - -// static const Shape spheres[SPHERE_COUNT] = { -// Shape::create(float3(0.0, -100.5, -1.0), 100.0, 0u, light_type::INVALID_ID), -// Shape::create(float3(2.0, 0.0, -1.0), 0.5, 1u, light_type::INVALID_ID), -// Shape::create(float3(0.0, 0.0, -1.0), 0.5, 2u, light_type::INVALID_ID), -// Shape::create(float3(-2.0, 0.0, -1.0), 0.5, 3u, light_type::INVALID_ID), -// Shape::create(float3(2.0, 0.0, 1.0), 0.5, 4u, light_type::INVALID_ID), -// Shape::create(float3(0.0, 0.0, 1.0), 0.5, 4u, light_type::INVALID_ID), -// Shape::create(float3(-2.0, 0.0, 1.0), 0.5, 5u, light_type::INVALID_ID), -// Shape::create(float3(0.5, 1.0, 0.5), 0.5, 6u, light_type::INVALID_ID) -// #ifdef SPHERE_LIGHT -// ,Shape::create(float3(-1.5, 1.5, 0.0), 0.3, bxdfnode_type::INVALID_ID, 0u) -// #endif -// }; - -// #ifdef TRIANGLE_LIGHT -// #define LIGHT_TYPE PST_TRIANGLE -// #define TRIANGLE_COUNT 1 -// static const Shape triangles[TRIANGLE_COUNT] = { -// Shape::create(float3(-1.8,0.35,0.3) * 10.0, float3(-1.2,0.35,0.0) * 10.0, float3(-1.5,0.8,-0.3) * 10.0, bxdfnode_type::INVALID_ID, 0u) -// }; -// #endif - -// #ifdef RECTANGLE_LIGHT -// #define LIGHT_TYPE PST_RECTANGLE -// #define RECTANGLE_COUNT 1 -// static const Shape rectangles[RECTANGLE_COUNT] = { -// Shape::create(float3(-3.8,0.35,1.3), normalize(float3(2,0,-1))*7.0, normalize(float3(2,-5,4))*0.1, bxdfnode_type::INVALID_ID, 0u) -// }; -// #endif - -// #define LIGHT_COUNT 1 -// static const light_type lights[LIGHT_COUNT] = { -// light_type(spectral_t(30.0,25.0,15.0), ext::ObjectID(8u, ext::NextEventEstimator::Event::Mode::PROCEDURAL, LIGHT_TYPE)) -// }; - -// #define BXDF_COUNT 7 -// static const bxdfnode_type bxdfs[BXDF_COUNT] = { -// bxdfnode_type(ext::MaterialSystem::Material::Type::DIFFUSE, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(1.25,1.25,1.25))), -// bxdfnode_type(ext::MaterialSystem::Material::Type::DIFFUSE, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(1.25,2.5,2.5))), -// bxdfnode_type(ext::MaterialSystem::Material::Type::DIFFUSE, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(2.5,1.25,2.5))), -// bxdfnode_type(ext::MaterialSystem::Material::Type::CONDUCTOR, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(0.98,0.98,0.77))), -// bxdfnode_type(ext::MaterialSystem::Material::Type::CONDUCTOR, create_params_t(false, float2(0,0), spectral_t(1,1,1), spectral_t(0.98,0.77,0.98))), -// bxdfnode_type(ext::MaterialSystem::Material::Type::CONDUCTOR, create_params_t(false, float2(0.15,0.15), spectral_t(1,1,1), spectral_t(0.98,0.77,0.98))), -// bxdfnode_type(ext::MaterialSystem::Material::Type::DIELECTRIC, create_params_t(false, float2(0.0625,0.0625), spectral_t(1,1,1), spectral_t(0.71,0.69,0.67))) -// }; +using diffuse_bxdf_type = bxdf::reflection::SOrenNayarBxDF; +using conductor_bxdf_type = bxdf::reflection::SGGXBxDF; +using dielectric_bxdf_type = bxdf::transmission::SGGXDielectricBxDF; + +using ray_type = ext::Ray; +using light_type = ext::Light; +using bxdfnode_type = ext::BxDFNode; +using randgen_type = ext::RandGen::Uniform3D; +using raygen_type = ext::RayGen::Basic; +using intersector_type = ext::Intersector::Comprehensive; +using material_system_type = ext::MaterialSystem::System; +using nee_type = ext::NextEventEstimator::Estimator; +using pathtracer_type = ext::PathTracer::Unidirectional; + +static const ext::Shape spheres[SPHERE_COUNT] = { + ext::Shape::create(float3(0.0, -100.5, -1.0), 100.0, 0u, light_type::INVALID_ID), + ext::Shape::create(float3(2.0, 0.0, -1.0), 0.5, 1u, light_type::INVALID_ID), + ext::Shape::create(float3(0.0, 0.0, -1.0), 0.5, 2u, light_type::INVALID_ID), + ext::Shape::create(float3(-2.0, 0.0, -1.0), 0.5, 3u, light_type::INVALID_ID), + ext::Shape::create(float3(2.0, 0.0, 1.0), 0.5, 4u, light_type::INVALID_ID), + ext::Shape::create(float3(0.0, 0.0, 1.0), 0.5, 4u, light_type::INVALID_ID), + ext::Shape::create(float3(-2.0, 0.0, 1.0), 0.5, 5u, light_type::INVALID_ID), + ext::Shape::create(float3(0.5, 1.0, 0.5), 0.5, 6u, light_type::INVALID_ID) +#ifdef SPHERE_LIGHT + ,ext::Shape::create(float3(-1.5, 1.5, 0.0), 0.3, bxdfnode_type::INVALID_ID, 0u) +#endif +}; + +#ifdef TRIANGLE_LIGHT +#define LIGHT_TYPE ext::PST_TRIANGLE +#define TRIANGLE_COUNT 1 +static const ext::Shape triangles[TRIANGLE_COUNT] = { + ext::Shape::create(float3(-1.8,0.35,0.3) * 10.0, float3(-1.2,0.35,0.0) * 10.0, float3(-1.5,0.8,-0.3) * 10.0, bxdfnode_type::INVALID_ID, 0u) +}; +#endif + +#ifdef RECTANGLE_LIGHT +#define LIGHT_TYPE ext::PST_RECTANGLE +#define RECTANGLE_COUNT 1 +static const ext::Shape rectangles[RECTANGLE_COUNT] = { + ext::Shape::create(float3(-3.8,0.35,1.3), normalize(float3(2,0,-1))*7.0, normalize(float3(2,-5,4))*0.1, bxdfnode_type::INVALID_ID, 0u) +}; +#endif + +#define LIGHT_COUNT 1 +static const light_type lights[LIGHT_COUNT] = { + light_type(spectral_t(30.0,25.0,15.0), ext::ObjectID(8u, ext::NextEventEstimator::Event::Mode::PROCEDURAL, LIGHT_TYPE)) +}; + +#define BXDF_COUNT 7 +static const bxdfnode_type bxdfs[BXDF_COUNT] = { + bxdfnode_type::create(ext::MaterialSystem::Material::Type::DIFFUSE, false, float2(0,0), spectral_t(1,1,1), spectral_t(1.25,1.25,1.25)), + bxdfnode_type::create(ext::MaterialSystem::Material::Type::DIFFUSE, false, float2(0,0), spectral_t(1,1,1), spectral_t(1.25,2.5,2.5)), + bxdfnode_type::create(ext::MaterialSystem::Material::Type::DIFFUSE, false, float2(0,0), spectral_t(1,1,1), spectral_t(2.5,1.25,2.5)), + bxdfnode_type::create(ext::MaterialSystem::Material::Type::CONDUCTOR, false, float2(0,0), spectral_t(1,1,1), spectral_t(0.98,0.98,0.77)), + bxdfnode_type::create(ext::MaterialSystem::Material::Type::CONDUCTOR, false, float2(0,0), spectral_t(1,1,1), spectral_t(0.98,0.77,0.98)), + bxdfnode_type::create(ext::MaterialSystem::Material::Type::CONDUCTOR, false, float2(0.15,0.15), spectral_t(1,1,1), spectral_t(0.98,0.77,0.98)), + bxdfnode_type::create(ext::MaterialSystem::Material::Type::DIELECTRIC, false, float2(0.0625,0.0625), spectral_t(1,1,1), spectral_t(0.71,0.69,0.67)) +}; [numthreads(WorkgroupSize, WorkgroupSize, 1)] void main(uint32_t3 threadID : SV_DispatchThreadID) { -// uint32_t width, height; -// outImage.GetDimensions(width, height); -// const int32_t2 coords = getCoordinates(); -// float32_t2 texCoord = float32_t2(coords) / float32_t2(width, height); -// texCoord.y = 1.0 - texCoord.y; - -// if (false == (all((int32_t2)0 < coords)) && all(int32_t2(width, height) < coords)) { -// return; -// } - -// if (((pc.depth - 1) >> MAX_DEPTH_LOG2) > 0 || ((pc.sampleCount - 1) >> MAX_SAMPLES_LOG2) > 0) -// { -// float32_t4 pixelCol = float32_t4(1.0,0.0,0.0,1.0); -// outImage[coords] = pixelCol; -// return; -// } - -// int flatIdx = glsl::gl_GlobalInvocationID().y * glsl::gl_NumWorkGroups().x * WorkgroupSize + glsl::gl_GlobalInvocationID().x; -// PCG32x2 pcg = PCG32x2::construct(flatIdx); // replaces scramblebuf? - -// // set up path tracer -// const PathTracerCreationParams ptCreateParams; -// ptCreateParams.rngState = pcg(); - -// uint2 scrambleDim; -// scramblebuf.GetDimensions(scrambleDim.x, scrambleDim.y); -// ptCreateParams.pixOffsetParam = (float2)1.0 / float2(scrambleDim); - -// float4 NDC = float4(texCoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); -// { -// vec4 tmp = mul(pc.invMVP, NDC); -// ptCreateParams.camPos = tmp.xyz / tmp.w; -// NDC.z = 1.0; -// } + uint32_t width, height; + outImage.GetDimensions(width, height); + const int32_t2 coords = getCoordinates(); + float32_t2 texCoord = float32_t2(coords) / float32_t2(width, height); + texCoord.y = 1.0 - texCoord.y; + + if (false == (all((int32_t2)0 < coords)) && all(int32_t2(width, height) < coords)) { + return; + } + + if (((pc.depth - 1) >> MAX_DEPTH_LOG2) > 0 || ((pc.sampleCount - 1) >> MAX_SAMPLES_LOG2) > 0) + { + float32_t4 pixelCol = float32_t4(1.0,0.0,0.0,1.0); + outImage[coords] = pixelCol; + return; + } + + int flatIdx = glsl::gl_GlobalInvocationID().y * glsl::gl_NumWorkGroups().x * WorkgroupSize + glsl::gl_GlobalInvocationID().x; + PCG32x2 pcg = PCG32x2::construct(flatIdx); // replaces scramblebuf? + + // set up path tracer + ext::PathTracer::PathTracerCreationParams ptCreateParams; + ptCreateParams.rngState = pcg(); + + uint2 scrambleDim; + scramblebuf.GetDimensions(scrambleDim.x, scrambleDim.y); + ptCreateParams.pixOffsetParam = (float2)1.0 / float2(scrambleDim); + + float4 NDC = float4(texCoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); + { + float4 tmp = mul(pc.invMVP, NDC); + ptCreateParams.camPos = tmp.xyz / tmp.w; + NDC.z = 1.0; + } -// ptCreateParams.NDC = NDC; -// ptCreateParams.invMVP = pc.invMVP; - -// ptCreateParams.diffuseParams = bxdfs[0].params; -// ptCreateParams.conductorParams = bxdfs[3].params; -// ptCreateParams.dielectricParams = bxdfs[6].params; - -// pathtracer_type pathtracer = pathtracer_type::create(ptCreateParams, sampleSequence); - -// // set up scene (can do as global var?) -// Scene scene; -// scene.sphereCount = SPHERE_COUNT; -// for (uint32_t i = 0; i < SPHERE_COUNT; i++) -// scene.spheres[i] = spheres[i]; -// #ifdef TRIANGLE_LIGHT -// scene.triangleCount = TRIANGLE_COUNT; -// for (uint32_t i = 0; i < TRIANGLE_COUNT; i++) -// scene.triangles[i] = triangles[i]; -// #else -// scene.triangleCount = 0; -// #endif -// #ifdef RECTANGLE_LIGHT -// scene.rectangleCount = RECTANGLE_COUNT; -// for (uint32_t i = 0; i < RECTANGLE_COUNT; i++) -// scene.rectangles[i] = rectangles[i]; -// #else -// scene.rectangleCount = 0; -// #endif -// scene.lightCount = LIGHT_COUNT; -// for (uint32_t i = 0; i < LIGHT_COUNT; i++) -// scene.lights[i] = lights[i]; -// scene.bxdfCount = BXDF_COUNT; -// for (uint32_t i = 0; i < BXDF_COUNT; i++) -// scene.bxdfs[i] = bxdfs[i]; - -// float32_t3 color = pathtracer.getMeasure(pc.sampleCount, pc.depth, scene); -// float32_t4 pixCol = float32_t4(color, 1.0); -// outImage[coords] = pixCol; + ptCreateParams.NDC = NDC; + ptCreateParams.invMVP = pc.invMVP; + + ptCreateParams.diffuseParams = bxdfs[0].params; + ptCreateParams.conductorParams = bxdfs[3].params; + ptCreateParams.dielectricParams = bxdfs[6].params; + + pathtracer_type pathtracer = pathtracer_type::create(ptCreateParams, sampleSequence); + + // set up scene (can do as global var?) + ext::Scene scene; + scene.sphereCount = SPHERE_COUNT; + for (uint32_t i = 0; i < SPHERE_COUNT; i++) + scene.spheres[i] = spheres[i]; +#ifdef TRIANGLE_LIGHT + scene.triangleCount = TRIANGLE_COUNT; + for (uint32_t i = 0; i < TRIANGLE_COUNT; i++) + scene.triangles[i] = triangles[i]; +#else + scene.triangleCount = 0; +#endif +#ifdef RECTANGLE_LIGHT + scene.rectangleCount = RECTANGLE_COUNT; + for (uint32_t i = 0; i < RECTANGLE_COUNT; i++) + scene.rectangles[i] = rectangles[i]; +#else + scene.rectangleCount = 0; +#endif + scene.lightCount = LIGHT_COUNT; + for (uint32_t i = 0; i < LIGHT_COUNT; i++) + scene.lights[i] = lights[i]; + scene.bxdfCount = BXDF_COUNT; + for (uint32_t i = 0; i < BXDF_COUNT; i++) + scene.bxdfs[i] = bxdfs[i]; + + float32_t3 color = pathtracer.getMeasure(pc.sampleCount, pc.depth, scene); + float32_t4 pixCol = float32_t4(color, 1.0); + outImage[coords] = pixCol; } diff --git a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl index ed0c612f1..48be039a7 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl @@ -2,9 +2,6 @@ #define _NBL_HLSL_EXT_PATHTRACING_SCENE_INCLUDED_ #include "common.hlsl" -#include "material_system.hlsl" -#include "next_event_estimator.hlsl" -#include "intersector.hlsl" namespace nbl { diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index 4a2c1110b..4bb260b09 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -368,9 +368,9 @@ class ComputeShaderPathtracer final : public examples::SimpleWindowedApplication options.preprocessorOptions.sourceIdentifier = source->getFilepathHint(); options.preprocessorOptions.logger = m_logger.get(); options.preprocessorOptions.includeFinder = compiler->getDefaultIncludeFinder(); - - std::string dxcOptionStr[] = { "-D" + defineMacro }; - options.dxcOptions = std::span(dxcOptionStr); + + const IShaderCompiler::SMacroDefinition variantDefine = { defineMacro, "" }; + options.preprocessorOptions.extraDefines = { &variantDefine, &variantDefine + 1 }; source = compiler->compileToSPIRV((const char*)source->getContent()->getPointer(), options); From 8e759f24d5b386291660f50af1c04efbff3eff08 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 26 Feb 2025 16:53:31 +0700 Subject: [PATCH 25/53] more bug fixes #6 --- .../app_resources/hlsl/common.hlsl | 28 +++++++++++++------ .../app_resources/hlsl/intersector.hlsl | 11 ++++---- .../app_resources/hlsl/material_system.hlsl | 5 ++-- .../hlsl/next_event_estimator.hlsl | 15 ++++++++-- .../app_resources/hlsl/pathtracer.hlsl | 16 ++++++----- .../app_resources/hlsl/render.comp.hlsl | 4 +-- .../app_resources/hlsl/scene.hlsl | 4 +-- 7 files changed, 54 insertions(+), 29 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index a264fabd5..913225f8b 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -66,7 +66,10 @@ struct Ray // immutable vector3_type origin; vector3_type direction; + // TODO: polygon method == 2 stuff + vector3_type normalAtOrigin; + bool wasBSDFAtOrigin; // mutable scalar_type intersectionT; @@ -82,6 +85,14 @@ struct Light NBL_CONSTEXPR_STATIC_INLINE uint32_t INVALID_ID = 0xffffu; + static Light create(NBL_CONST_REF_ARG(spectral_type) radiance, NBL_CONST_REF_ARG(ObjectID) objectID) + { + Light retval; + retval.radiance = radiance; + retval.objectID = objectID; + return retval; + } + spectral_type radiance; ObjectID objectID; }; @@ -250,7 +261,7 @@ struct Shape { const float rcpDistance = 1.0 / nbl::hlsl::sqrt(distanceSQ); Z *= rcpDistance; - + const float cosThetaMax = nbl::hlsl::sqrt(cosThetaMax2); const float cosTheta = nbl::hlsl::mix(1.0, cosThetaMax, xi.x); @@ -261,9 +272,9 @@ struct Shape float sinPhi, cosPhi; math::sincos(2.0 * numbers::pi * xi.y - numbers::pi, sinPhi, cosPhi); float32_t2x3 XY = math::frisvad(Z); - + L += (XY[0] * cosPhi + XY[1] * sinPhi) * sinTheta; - + newRayMaxT = (cosTheta - nbl::hlsl::sqrt(cosTheta2 - cosThetaMax2)) / rcpDistance; pdf = 1.0 / (2.0 * numbers::pi * (1.0 - cosThetaMax)); return L; @@ -342,14 +353,15 @@ struct Shape { shapes::SphericalTriangle st = shapes::SphericalTriangle::create(vertex0, vertex1, vertex2, ray.origin); const float rcpProb = st.solidAngleOfTriangle(); - // if `rcpProb` is NAN then the triangle's solid angle was close to 0.0 + // if `rcpProb` is NAN then the triangle's solid angle was close to 0.0 return rcpProb > numeric_limits::min ? (1.0 / rcpProb) : numeric_limits::max; } break; case PPM_APPROX_PROJECTED_SOLID_ANGLE: { shapes::SphericalTriangle st = shapes::SphericalTriangle::create(vertex0, vertex1, vertex2, ray.origin); - const float pdf = st.projectedSolidAngleOfTriangle(ray.normalAtOrigin, ray.wasBSDFAtOrigin, L); + sampling::ProjectedSphericalTriangle pst = sampling::ProjectedSphericalTriangle::create(st); + const float pdf = pst.pdf(ray.normalAtOrigin, ray.wasBSDFAtOrigin, 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::max ? pdf : 0.0; } @@ -371,11 +383,11 @@ struct Shape const float sqrtU = nbl::hlsl::sqrt(xi.x); float32_t3 pnt = vertex0 + edge0 * (1.0 - sqrtU) + edge1 * sqrtU * xi.y; float32_t3 L = pnt - origin; - + const float distanceSq = nbl::hlsl::dot(L,L); const float rcpDistance = 1.0 / nbl::hlsl::sqrt(distanceSq); L *= rcpDistance; - + pdf = distanceSq / nbl::hlsl::abs(nbl::hlsl::dot(nbl::hlsl::cross(edge0, edge1) * 0.5f, L)); newRayMaxT = 1.0 / rcpDistance; return L; @@ -403,7 +415,7 @@ struct Shape shapes::SphericalTriangle st = shapes::SphericalTriangle::create(vertex0, vertex1, vertex2, origin); sampling::ProjectedSphericalTriangle sst = sampling::ProjectedSphericalTriangle::create(st); - + const float32_t3 L = sst.generate(rcpPdf, interaction.N, isBSDF, xi.xy); pdf = rcpPdf > numeric_limits::min ? (1.0 / rcpPdf) : 0.0; diff --git a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl index 880ae1169..525af5525 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl @@ -68,12 +68,12 @@ struct Comprehensive t = numeric_limits::infinity; break; } - + bool closerIntersection = t > 0.0 && t < ray.intersectionT; ray.intersectionT = closerIntersection ? t : ray.intersectionT; objectID.id = closerIntersection ? i : objectID.id; - + // allowing early out results in a performance regression, WTF!? //if (anyHit && closerIntersection) //break; @@ -106,6 +106,7 @@ struct Comprehensive return ObjectID::create(-1, 0, PST_SPHERE); } } + return ObjectID::create(-1, 0, PST_SPHERE); } static ObjectID traceRay(NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(scene_type) scene) @@ -114,7 +115,7 @@ struct Comprehensive ObjectID objectID; objectID.id = -1; // start with no intersect - + // prodedural shapes if (scene.sphereCount > 0) { @@ -161,12 +162,12 @@ struct Comprehensive // t = sphere.intersect(ray.origin, ray.direction); // } // // TODO: other types - + // bool closerIntersection = t > 0.0 && t < ray.intersectionT; // ray.intersectionT = closerIntersection ? t : ray.intersectionT; // objectID = closerIntersection ? i : objectID; - + // // allowing early out results in a performance regression, WTF!? // //if (anyHit && closerIntersection) // //break; diff --git a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl index 16f8dcabf..1a613080f 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl @@ -38,6 +38,7 @@ struct System using vector2_type = vector; using vector3_type = vector; using measure_type = typename DiffuseBxDF::spectral_type; + using sample_type = typename DiffuseBxDF::sample_type; using quotient_pdf_type = typename DiffuseBxDF::quotient_pdf_type; using anisotropic_type = typename DiffuseBxDF::anisotropic_type; using anisocache_type = typename ConductorBxDF::anisocache_type; @@ -84,7 +85,7 @@ struct System } } - vector3_type generate(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, anisotropic_type interaction, NBL_CONST_REF_ARG(vector3_type) u, NBL_REF_ARG(anisocache_type) cache) + sample_type generate(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, anisotropic_type interaction, NBL_CONST_REF_ARG(vector3_type) u, NBL_REF_ARG(anisocache_type) cache) { switch(material.type) { @@ -107,7 +108,7 @@ struct System } break; default: - return (vector3_type)numeric_limits::infinity; + return (sample_type)numeric_limits::infinity; } } diff --git a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl index f0eeb0885..15dbf3a9b 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl @@ -103,7 +103,10 @@ struct Estimator { vector3_type position = vector3_type(asfloat(event.data[2]), asfloat(event.data[3]), asfloat(event.data[4])); Shape sphere = Shape::create(position, asfloat(event.data[5]), event.data[6]); - L = sphere.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); + const vector3_type sampleL = sphere.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); + const vector3_type V = interaction.V.getDirection(); + const scalar_type VdotL = nbl::hlsl::dot(V, sampleL); + L = sample_type::create(sampleL,VdotL,interaction.T,interaction.B,interaction.N); } break; case PST_TRIANGLE: @@ -112,7 +115,10 @@ struct Estimator vector3_type vertex1 = vector3_type(asfloat(event.data[5]), asfloat(event.data[6]), asfloat(event.data[7])); vector3_type vertex2 = vector3_type(asfloat(event.data[8]), asfloat(event.data[9]), asfloat(event.data[10])); Shape tri = Shape::create(vertex0, vertex1, vertex2, event.data[11]); - L = tri.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); + const vector3_type sampleL = tri.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); + const vector3_type V = interaction.V.getDirection(); + const scalar_type VdotL = nbl::hlsl::dot(V, sampleL); + L = sample_type::create(sampleL,VdotL,interaction.T,interaction.B,interaction.N); } break; case PST_RECTANGLE: @@ -121,7 +127,10 @@ struct Estimator vector3_type edge0 = vector3_type(asfloat(event.data[5]), asfloat(event.data[6]), asfloat(event.data[7])); vector3_type edge1 = vector3_type(asfloat(event.data[8]), asfloat(event.data[9]), asfloat(event.data[10])); Shape rect = Shape::create(offset, edge0, edge1, event.data[11]); - L = rect.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); + const vector3_type sampleL = rect.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); + const vector3_type V = interaction.V.getDirection(); + const scalar_type VdotL = nbl::hlsl::dot(V, sampleL); + L = sample_type::create(sampleL,VdotL,interaction.T,interaction.B,interaction.N); } break; default: diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index 460744940..62398a58e 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -227,11 +227,12 @@ struct Unidirectional } } - quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(material, bxdf.params, params) * throughput; + quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(material, bxdf.params, params); + bsdf_quotient_pdf.quotient *= throughput; neeContrib_pdf.quotient *= 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 *= otherGenOverChoice / (1.f + otherGenOverLightAndChoice * otherGenOverLightAndChoice); // balance heuristic // TODO: ifdef NEE only @@ -240,7 +241,7 @@ struct Unidirectional nee_ray.direction = nee_sample.L.direction; 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; + ray.payload.accumulation += neeContrib_pdf.quotient; } } } @@ -256,7 +257,7 @@ struct Unidirectional sample_type bsdf_sample = materialSystem.generate(material, bxdf.params, interaction, eps1, _cache); // TODO: does not yet account for smooth dielectric - params_type params; + params_type params; if (!isBSDF && bxdf.materialType == ext::MaterialSystem::Material::DIFFUSE) { params = params_type::template create(bsdf_sample, iso_interaction, bxdf::BCM_MAX); @@ -287,7 +288,8 @@ struct Unidirectional } // the value of the bsdf divided by the probability of the sample being generated - throughput *= materialSystem.quotient_and_pdf(material, bxdf.params, params); + quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(material, bxdf.params, params); + throughput *= bsdf_quotient_pdf.quotient; bxdfSample = bsdf_sample.L.direction; } @@ -298,7 +300,7 @@ struct Unidirectional ray.payload.throughput = throughput; ray.payload.otherTechniqueHeuristic = neeProbability / bxdfPdf; // numerically stable, don't touch ray.payload.otherTechniqueHeuristic *= ray.payload.otherTechniqueHeuristic; - + // trace new ray ray.origin = intersection + bxdfSample * (1.0/*kSceneSize*/) * Tolerance::getStart(depth); ray.direction = bxdfSample; @@ -314,7 +316,7 @@ struct Unidirectional void missProgram(NBL_REF_ARG(ray_type) ray) { - vector3_type finalContribution = ray.payload.throughput; + vector3_type finalContribution = ray.payload.throughput; // #ifdef USE_ENVMAP // vec2 uv = SampleSphericalMap(_immutable.direction); // finalContribution *= textureLod(envMap, uv, 0.0).rgb; diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index 5be6adf78..360d085a6 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -115,7 +115,7 @@ static const ext::Shape rectangles[RECTANGLE_COUNT] = { #define LIGHT_COUNT 1 static const light_type lights[LIGHT_COUNT] = { - light_type(spectral_t(30.0,25.0,15.0), ext::ObjectID(8u, ext::NextEventEstimator::Event::Mode::PROCEDURAL, LIGHT_TYPE)) + light_type::create(spectral_t(30.0,25.0,15.0), ext::ObjectID::create(8u, ext::NextEventEstimator::Event::Mode::PROCEDURAL, LIGHT_TYPE)) }; #define BXDF_COUNT 7 @@ -166,7 +166,7 @@ void main(uint32_t3 threadID : SV_DispatchThreadID) ptCreateParams.camPos = tmp.xyz / tmp.w; NDC.z = 1.0; } - + ptCreateParams.NDC = NDC; ptCreateParams.invMVP = pc.invMVP; diff --git a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl index 48be039a7..79b66dbfb 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl @@ -32,7 +32,7 @@ struct Scene light_type lights[maxLightCount]; uint32_t lightCount; - + NBL_CONSTEXPR_STATIC_INLINE uint32_t maxBxdfCount = 16; // TODO: limit change? bxdfnode_type bxdfs[maxBxdfCount]; @@ -51,7 +51,7 @@ struct Scene -1; retval.data[0] = objCount; retval.data[1] = type; - + switch (type) { case PST_SPHERE: From b1831d983d2f8d8df7641d44d8cde857c6977a2c Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 4 Mar 2025 16:56:49 +0700 Subject: [PATCH 26/53] refactor to use new frisvad --- 31_HLSLPathTracer/app_resources/hlsl/common.hlsl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index 913225f8b..2482806e2 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -271,9 +271,10 @@ struct Shape const float sinTheta = nbl::hlsl::sqrt(1.0 - cosTheta2); float sinPhi, cosPhi; math::sincos(2.0 * numbers::pi * xi.y - numbers::pi, sinPhi, cosPhi); - float32_t2x3 XY = math::frisvad(Z); + float32_t3 X, Y; + math::frisvad(Z, X, Y); - L += (XY[0] * cosPhi + XY[1] * sinPhi) * sinTheta; + L += (X * cosPhi + Y * sinPhi) * sinTheta; newRayMaxT = (cosTheta - nbl::hlsl::sqrt(cosTheta2 - cosThetaMax2)) / rcpDistance; pdf = 1.0 / (2.0 * numbers::pi * (1.0 - cosThetaMax)); From cb5662a63b6d24dbdc620aeb3606582dc51a4f9f Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 7 Mar 2025 10:44:08 +0700 Subject: [PATCH 27/53] fix bugs again --- .../app_resources/hlsl/common.hlsl | 4 ++-- .../app_resources/hlsl/material_system.hlsl | 17 ++++++++++---- .../hlsl/next_event_estimator.hlsl | 23 +++++++++++++------ .../app_resources/hlsl/pathtracer.hlsl | 7 +++--- 31_HLSLPathTracer/main.cpp | 6 ++--- 5 files changed, 38 insertions(+), 19 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index 2482806e2..244a92107 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -66,7 +66,7 @@ struct Ray // immutable vector3_type origin; vector3_type direction; - + // TODO: polygon method == 2 stuff vector3_type normalAtOrigin; bool wasBSDFAtOrigin; @@ -417,7 +417,7 @@ struct Shape shapes::SphericalTriangle st = shapes::SphericalTriangle::create(vertex0, vertex1, vertex2, origin); sampling::ProjectedSphericalTriangle sst = sampling::ProjectedSphericalTriangle::create(st); - const float32_t3 L = sst.generate(rcpPdf, interaction.N, isBSDF, xi.xy); + const float32_t3 L = sst.generate(rcpPdf, interaction.isotropic.N, isBSDF, xi.xy); pdf = rcpPdf > numeric_limits::min ? (1.0 / rcpPdf) : 0.0; diff --git a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl index 1a613080f..09236c85e 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl @@ -39,6 +39,7 @@ struct System using vector3_type = vector; using measure_type = typename DiffuseBxDF::spectral_type; using sample_type = typename DiffuseBxDF::sample_type; + using ray_dir_info_type = typename sample_type::ray_dir_info_type; using quotient_pdf_type = typename DiffuseBxDF::quotient_pdf_type; using anisotropic_type = typename DiffuseBxDF::anisotropic_type; using anisocache_type = typename ConductorBxDF::anisocache_type; @@ -85,7 +86,7 @@ struct System } } - sample_type generate(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, anisotropic_type interaction, NBL_CONST_REF_ARG(vector3_type) u, NBL_REF_ARG(anisocache_type) cache) + sample_type generate(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, NBL_CONST_REF_ARG(anisotropic_type) interaction, NBL_CONST_REF_ARG(vector3_type) u, NBL_REF_ARG(anisocache_type) _cache) { switch(material.type) { @@ -98,18 +99,26 @@ struct System case Material::Type::CONDUCTOR: { conductorBxDF.init(cparams); - return conductorBxDF.generate(interaction, u.xy, cache); + return conductorBxDF.generate(interaction, u.xy, _cache); } break; case Material::Type::DIELECTRIC: { dielectricBxDF.init(cparams); - return dielectricBxDF.generate(interaction, u, cache); + return dielectricBxDF.generate(interaction, u, _cache); } break; default: - return (sample_type)numeric_limits::infinity; + { + ray_dir_info_type L; + L.direction = (vector3_type)0; + return sample_type::create(L, 0, (vector3_type)0); + } } + + ray_dir_info_type L; + L.direction = (vector3_type)0; + return sample_type::create(L, 0, (vector3_type)0); } quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, NBL_CONST_REF_ARG(params_t) params) diff --git a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl index 15dbf3a9b..38a5fae15 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl @@ -23,6 +23,7 @@ struct Estimator using interaction_type = Aniso; using quotient_pdf_type = bxdf::quotient_and_pdf; using sample_type = LightSample; + using ray_dir_info_type = typename sample_type::ray_dir_info_type; static spectral_type proceduralDeferredEvalAndPdf(NBL_REF_ARG(scalar_type) pdf, NBL_CONST_REF_ARG(light_type) light, NBL_CONST_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(Event) event) { @@ -88,6 +89,7 @@ struct Estimator default: return (spectral_type)0.0; } + return (spectral_type)0.0; } static sample_type procedural_generate_and_quotient_and_pdf(NBL_REF_ARG(quotient_pdf_type) quotient_pdf, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(light_type) light, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(interaction_type) interaction, bool isBSDF, NBL_CONST_REF_ARG(vector3_type) xi, uint32_t depth, NBL_CONST_REF_ARG(Event) event) @@ -104,9 +106,11 @@ struct Estimator vector3_type position = vector3_type(asfloat(event.data[2]), asfloat(event.data[3]), asfloat(event.data[4])); Shape sphere = Shape::create(position, asfloat(event.data[5]), event.data[6]); const vector3_type sampleL = sphere.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); - const vector3_type V = interaction.V.getDirection(); + const vector3_type V = interaction.isotropic.V.getDirection(); const scalar_type VdotL = nbl::hlsl::dot(V, sampleL); - L = sample_type::create(sampleL,VdotL,interaction.T,interaction.B,interaction.N); + ray_dir_info_type rayL; + rayL.direction = sampleL; + L = sample_type::create(rayL,VdotL,interaction.T,interaction.B,interaction.isotropic.N); } break; case PST_TRIANGLE: @@ -116,9 +120,11 @@ struct Estimator vector3_type vertex2 = vector3_type(asfloat(event.data[8]), asfloat(event.data[9]), asfloat(event.data[10])); Shape tri = Shape::create(vertex0, vertex1, vertex2, event.data[11]); const vector3_type sampleL = tri.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); - const vector3_type V = interaction.V.getDirection(); + const vector3_type V = interaction.isotropic.V.getDirection(); const scalar_type VdotL = nbl::hlsl::dot(V, sampleL); - L = sample_type::create(sampleL,VdotL,interaction.T,interaction.B,interaction.N); + ray_dir_info_type rayL; + rayL.direction = sampleL; + L = sample_type::create(rayL,VdotL,interaction.T,interaction.B,interaction.isotropic.N); } break; case PST_RECTANGLE: @@ -128,9 +134,11 @@ struct Estimator vector3_type edge1 = vector3_type(asfloat(event.data[8]), asfloat(event.data[9]), asfloat(event.data[10])); Shape rect = Shape::create(offset, edge0, edge1, event.data[11]); const vector3_type sampleL = rect.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); - const vector3_type V = interaction.V.getDirection(); + const vector3_type V = interaction.isotropic.V.getDirection(); const scalar_type VdotL = nbl::hlsl::dot(V, sampleL); - L = sample_type::create(sampleL,VdotL,interaction.T,interaction.B,interaction.N); + ray_dir_info_type rayL; + rayL.direction = sampleL; + L = sample_type::create(rayL,VdotL,interaction.T,interaction.B,interaction.isotropic.N); } break; default: @@ -149,6 +157,7 @@ struct Estimator static sample_type generate_and_quotient_and_pdf(NBL_REF_ARG(quotient_pdf_type) quotient_pdf, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(light_type) light, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(interaction_type) interaction, bool isBSDF, NBL_CONST_REF_ARG(vector3_type) xi, uint32_t depth, NBL_CONST_REF_ARG(Event) event) { const Event::Mode mode = (Event::Mode)event.mode; + sample_type L; switch (mode) { case Event::Mode::RAY_QUERY: @@ -168,10 +177,10 @@ struct Estimator break; default: { - sample_type L; return L; } } + return L; } }; diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index 62398a58e..ba683d443 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -54,6 +54,7 @@ struct Unidirectional using vector3_type = vector; using measure_type = typename MaterialSystem::measure_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 light_type = Light; using bxdfnode_type = BxDFNode; @@ -181,7 +182,7 @@ struct Unidirectional bool validPath = nee_sample.NdotL > numeric_limits::min; // but if we allowed non-watertight transmitters (single water surface), it would make sense just to apply this line by itself anisocache_type _cache; - validPath = validPath && anisocache_type::compute(_cache, interaction, nee_sample, monochromeEta); + validPath = validPath && anisocache_type::template compute(_cache, interaction, nee_sample, monochromeEta); bxdf.params.A = nbl::hlsl::max(bxdf.params.A, vector(0,0)); bxdf.params.eta = monochromeEta; @@ -268,7 +269,7 @@ struct Unidirectional params = params_type::template create(bsdf_sample, interaction, _cache, bxdf::BCM_MAX); else { - isocache_type isocache = (isocache_type)_cache; + isocache_type isocache = _cache.iso_cache; params = params_type::template create(bsdf_sample, iso_interaction, isocache, bxdf::BCM_MAX); } } @@ -282,7 +283,7 @@ struct Unidirectional params = params_type::template create(bsdf_sample, interaction, _cache, bxdf::BCM_ABS); else { - isocache_type isocache = (isocache_type)_cache; + isocache_type isocache = _cache.iso_cache; params = params_type::template create(bsdf_sample, iso_interaction, isocache, bxdf::BCM_ABS); } } diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index 4bb260b09..036fcdb79 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -23,7 +23,7 @@ struct PTPushConstant { // TODO: Add a QueryPool for timestamping once its ready // TODO: Do buffer creation using assConv -class ComputeShaderPathtracer final : public examples::SimpleWindowedApplication, public application_templates::MonoAssetManagerAndBuiltinResourceApplication +class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, public application_templates::MonoAssetManagerAndBuiltinResourceApplication { using device_base_t = examples::SimpleWindowedApplication; using asset_base_t = application_templates::MonoAssetManagerAndBuiltinResourceApplication; @@ -69,7 +69,7 @@ class ComputeShaderPathtracer final : public examples::SimpleWindowedApplication }; public: - inline ComputeShaderPathtracer(const path& _localInputCWD, const path& _localOutputCWD, const path& _sharedInputCWD, const path& _sharedOutputCWD) + inline HLSLComputePathtracer(const path& _localInputCWD, const path& _localOutputCWD, const path& _sharedInputCWD, const path& _sharedOutputCWD) : IApplicationFramework(_localInputCWD, _localOutputCWD, _sharedInputCWD, _sharedOutputCWD) {} inline bool isComputeOnly() const override { return false; } @@ -1349,4 +1349,4 @@ class ComputeShaderPathtracer final : public examples::SimpleWindowedApplication IGPUCommandBuffer::SClearColorValue clearColor = { .float32 = {0.f,0.f,0.f,1.f} }; }; -NBL_MAIN_FUNC(ComputeShaderPathtracer) +NBL_MAIN_FUNC(HLSLComputePathtracer) From 8eaa71463bfc1cf1cda0002e67f0f67e1d3a4ba5 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 7 Mar 2025 16:58:44 +0700 Subject: [PATCH 28/53] fix intersector, no use intersectdata --- .../app_resources/hlsl/common.hlsl | 82 +++++----- .../app_resources/hlsl/intersector.hlsl | 105 ++++++++++--- .../app_resources/hlsl/pathtracer.hlsl | 18 ++- .../app_resources/hlsl/render.comp.hlsl | 2 +- .../app_resources/hlsl/scene.hlsl | 140 +++++++++--------- 31_HLSLPathTracer/main.cpp | 2 +- 6 files changed, 207 insertions(+), 142 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index 244a92107..0fd595bca 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -203,11 +203,11 @@ struct Shape; template<> struct Shape { - static Shape create(NBL_CONST_REF_ARG(float32_t3) position, float32_t radius, uint32_t bsdfLightIDs) + static Shape create(NBL_CONST_REF_ARG(float32_t3) position, float32_t radius2, uint32_t bsdfLightIDs) { Shape retval; retval.position = position; - retval.radius2 = radius * radius; + retval.radius2 = radius2; retval.bsdfLightIDs = bsdfLightIDs; return retval; } @@ -215,20 +215,20 @@ struct Shape static Shape create(NBL_CONST_REF_ARG(float32_t3) position, float32_t radius, uint32_t bsdfID, uint32_t lightID) { uint32_t bsdfLightIDs = glsl::bitfieldInsert(bsdfID, lightID, 16, 16); - return create(position, radius, bsdfLightIDs); + return create(position, radius * radius, bsdfLightIDs); } // return intersection distance if found, nan otherwise float intersect(NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(float32_t3) direction) { float32_t3 relOrigin = origin - position; - float relOriginLen2 = nbl::hlsl::dot(relOrigin, relOrigin); + float relOriginLen2 = hlsl::dot(relOrigin, relOrigin); - float dirDotRelOrigin = nbl::hlsl::dot(direction, relOrigin); + float dirDotRelOrigin = hlsl::dot(direction, relOrigin); float det = radius2 - relOriginLen2 + dirDotRelOrigin * dirDotRelOrigin; // do some speculative math here - float detsqrt = nbl::hlsl::sqrt(det); + float detsqrt = hlsl::sqrt(det); return -dirDotRelOrigin + (relOriginLen2 > radius2 ? (-detsqrt) : detsqrt); } @@ -241,7 +241,7 @@ struct Shape float getSolidAngle(NBL_CONST_REF_ARG(float32_t3) origin) { float32_t3 dist = position - origin; - float cosThetaMax = nbl::hlsl::sqrt(1.0 - radius2 / nbl::hlsl::dot(dist, dist)); + float cosThetaMax = hlsl::sqrt(1.0 - radius2 / hlsl::dot(dist, dist)); return 2.0 * numbers::pi * (1.0 - cosThetaMax); } @@ -255,28 +255,28 @@ struct Shape float32_t3 generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, float32_t3 xi) { float32_t3 Z = position - origin; - const float distanceSQ = nbl::hlsl::dot(Z,Z); + const float distanceSQ = hlsl::dot(Z,Z); const float cosThetaMax2 = 1.0 - radius2 / distanceSQ; if (cosThetaMax2 > 0.0) { - const float rcpDistance = 1.0 / nbl::hlsl::sqrt(distanceSQ); + const float rcpDistance = 1.0 / hlsl::sqrt(distanceSQ); Z *= rcpDistance; - const float cosThetaMax = nbl::hlsl::sqrt(cosThetaMax2); + const float cosThetaMax = hlsl::sqrt(cosThetaMax2); const float cosTheta = nbl::hlsl::mix(1.0, cosThetaMax, xi.x); float32_t3 L = Z * cosTheta; const float cosTheta2 = cosTheta * cosTheta; - const float sinTheta = nbl::hlsl::sqrt(1.0 - cosTheta2); + const float sinTheta = hlsl::sqrt(1.0 - cosTheta2); float sinPhi, cosPhi; - math::sincos(2.0 * numbers::pi * xi.y - numbers::pi, sinPhi, cosPhi); + math::sincos(2.0 * numbers::pi * xi.y - numbers::pi, sinPhi, cosPhi); float32_t3 X, Y; math::frisvad(Z, X, Y); L += (X * cosPhi + Y * sinPhi) * sinTheta; - newRayMaxT = (cosTheta - nbl::hlsl::sqrt(cosTheta2 - cosThetaMax2)) / rcpDistance; + newRayMaxT = (cosTheta - hlsl::sqrt(cosTheta2 - cosThetaMax2)) / rcpDistance; pdf = 1.0 / (2.0 * numbers::pi * (1.0 - cosThetaMax)); return L; } @@ -315,26 +315,26 @@ struct Shape { const float32_t3 edges[2] = { vertex1 - vertex0, vertex2 - vertex0 }; - const float32_t3 h = nbl::hlsl::cross(direction, edges[1]); - const float a = nbl::hlsl::dot(edges[0], h); + const float32_t3 h = hlsl::cross(direction, edges[1]); + const float a = hlsl::dot(edges[0], h); const float32_t3 relOrigin = origin - vertex0; - const float u = nbl::hlsl::dot(relOrigin, h) / a; + const float u = hlsl::dot(relOrigin, h) / a; - const float32_t3 q = nbl::hlsl::cross(relOrigin, edges[0]); - const float v = nbl::hlsl::dot(direction, q) / a; + const float32_t3 q = hlsl::cross(relOrigin, edges[0]); + const float v = hlsl::dot(direction, q) / a; - const float t = nbl::hlsl::dot(edges[1], q) / a; + const float t = hlsl::dot(edges[1], q) / a; const bool intersection = t > 0.f && u >= 0.f && v >= 0.f && (u + v) <= 1.f; - return intersection ? t : numeric_limits::infinity; + return intersection ? t : bit_cast(numeric_limits::infinity); } float32_t3 getNormalTimesArea() { const float32_t3 edges[2] = { vertex1 - vertex0, vertex2 - vertex0 }; - return nbl::hlsl::cross(edges[0], edges[1]) * 0.5f; + return hlsl::cross(edges[0], edges[1]) * 0.5f; } template @@ -347,7 +347,7 @@ struct Shape { const float dist = ray.intersectionT; const float32_t3 L = ray.direction; - return dist * dist / nbl::hlsl::abs(nbl::hlsl::dot(getNormalTimesArea(), L)); + return dist * dist / hlsl::abs(hlsl::dot(getNormalTimesArea(), L)); } break; case PPM_SOLID_ANGLE: @@ -381,15 +381,15 @@ struct Shape { const float32_t3 edge0 = vertex1 - vertex0; const float32_t3 edge1 = vertex2 - vertex0; - const float sqrtU = nbl::hlsl::sqrt(xi.x); + const float sqrtU = hlsl::sqrt(xi.x); float32_t3 pnt = vertex0 + edge0 * (1.0 - sqrtU) + edge1 * sqrtU * xi.y; float32_t3 L = pnt - origin; - const float distanceSq = nbl::hlsl::dot(L,L); - const float rcpDistance = 1.0 / nbl::hlsl::sqrt(distanceSq); + const float distanceSq = hlsl::dot(L,L); + const float rcpDistance = 1.0 / hlsl::sqrt(distanceSq); L *= rcpDistance; - pdf = distanceSq / nbl::hlsl::abs(nbl::hlsl::dot(nbl::hlsl::cross(edge0, edge1) * 0.5f, L)); + pdf = distanceSq / hlsl::abs(hlsl::dot(hlsl::cross(edge0, edge1) * 0.5f, L)); newRayMaxT = 1.0 / rcpDistance; return L; } @@ -406,7 +406,7 @@ struct Shape pdf = rcpPdf > numeric_limits::min ? (1.0 / rcpPdf) : 0.0; const float32_t3 N = getNormalTimesArea(); - newRayMaxT = nbl::hlsl::dot(N, vertex0 - origin) / nbl::hlsl::dot(N, L); + newRayMaxT = hlsl::dot(N, vertex0 - origin) / hlsl::dot(N, L); return L; } break; @@ -422,7 +422,7 @@ struct Shape pdf = rcpPdf > numeric_limits::min ? (1.0 / rcpPdf) : 0.0; const float32_t3 N = getNormalTimesArea(); - newRayMaxT = nbl::hlsl::dot(N, vertex0 - origin) / nbl::hlsl::dot(N, L); + newRayMaxT = hlsl::dot(N, vertex0 - origin) / hlsl::dot(N, L); return L; } break; @@ -462,25 +462,25 @@ struct Shape float intersect(NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(float32_t3) direction) { - const float32_t3 h = nbl::hlsl::cross(direction, edge1); - const float a = nbl::hlsl::dot(edge0, h); + const float32_t3 h = hlsl::cross(direction, edge1); + const float a = hlsl::dot(edge0, h); const float32_t3 relOrigin = origin - offset; - const float u = nbl::hlsl::dot(relOrigin,h)/a; + const float u = hlsl::dot(relOrigin,h)/a; - const float32_t3 q = nbl::hlsl::cross(relOrigin, edge0); - const float v = nbl::hlsl::dot(direction, q) / a; + const float32_t3 q = hlsl::cross(relOrigin, edge0); + const float v = hlsl::dot(direction, q) / a; - const float t = nbl::hlsl::dot(edge1, q) / a; + const float t = hlsl::dot(edge1, q) / a; const bool intersection = t > 0.f && u >= 0.f && v >= 0.f && u <= 1.f && v <= 1.f; - return intersection ? t : numeric_limits::infinity; + return intersection ? t : bit_cast(numeric_limits::infinity); } float32_t3 getNormalTimesArea() { - return nbl::hlsl::cross(edge0, edge1); + return hlsl::cross(edge0, edge1); } void getNormalBasis(NBL_REF_ARG(float32_t3x3) basis, NBL_REF_ARG(float32_t2) extents) @@ -502,7 +502,7 @@ struct Shape { const float dist = ray.intersectionT; const float32_t3 L = ray.direction; - return dist * dist / nbl::hlsl::abs(nbl::hlsl::dot(getNormalTimesArea(), L)); + return dist * dist / hlsl::abs(hlsl::dot(getNormalTimesArea(), L)); } break; // #ifdef TRIANGLE_REFERENCE ? @@ -542,10 +542,10 @@ struct Shape case PPM_AREA: { float32_t3 L = origin2origin + edge0 * xi.x + edge1 * xi.y; - const float distSq = nbl::hlsl::dot(L, L); - const float rcpDist = 1.0 / nbl::hlsl::sqrt(distSq); + const float distSq = hlsl::dot(L, L); + const float rcpDist = 1.0 / hlsl::sqrt(distSq); L *= rcpDist; - pdf = distSq / nbl::hlsl::abs(nbl::hlsl::dot(N, L)); + pdf = distSq / hlsl::abs(hlsl::dot(N, L)); newRayMaxT = 1.0 / rcpDist; return L; } @@ -572,7 +572,7 @@ struct Shape else pdf = numeric_limits::infinity; - newRayMaxT = nbl::hlsl::dot(N, origin2origin) / nbl::hlsl::dot(N, L); + newRayMaxT = hlsl::dot(N, origin2origin) / hlsl::dot(N, L); return L; } break; diff --git a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl index 525af5525..68ea75dd3 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl @@ -25,6 +25,62 @@ struct Comprehensive using bxdfnode_type = BxdfNode; using scene_type = Scene; + static ObjectID traceRay(NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(scene_type) scene) + { + ObjectID objectID; + objectID.id = -1; + + // prodedural shapes + for (int i = 0; i < scene.sphereCount; i++) + { + float t = scene.spheres[i].intersect(ray.origin, ray.direction); + + bool closerIntersection = t > 0.0 && t < ray.intersectionT; + + if (closerIntersection) + { + ray.intersectionT = t; + objectID.id = i; + objectID.mode = IntersectData::Mode::PROCEDURAL; + objectID.shapeType = PST_SPHERE; + } + } + for (int i = 0; i < scene.triangleCount; i++) + { + float t = scene.triangles[i].intersect(ray.origin, ray.direction); + + bool closerIntersection = t > 0.0 && t < ray.intersectionT; + + if (closerIntersection) + { + ray.intersectionT = t; + objectID.id = i; + objectID.mode = IntersectData::Mode::PROCEDURAL; + objectID.shapeType = PST_TRIANGLE; + } + } + for (int i = 0; i < scene.rectangleCount; i++) + { + float t = scene.rectangles[i].intersect(ray.origin, ray.direction); + + bool closerIntersection = t > 0.0 && t < ray.intersectionT; + + if (closerIntersection) + { + ray.intersectionT = t; + objectID.id = i; + objectID.mode = IntersectData::Mode::PROCEDURAL; + objectID.shapeType = PST_TRIANGLE; + } + } + + // TODO: trace AS + + return objectID; + } + + // note for future consideration: still need to encode to IntersectData? + // obsolete? static ObjectID traceProcedural(NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(IntersectData) intersect) { const bool anyHit = ray.intersectionT != numeric_limits::max; @@ -81,6 +137,7 @@ struct Comprehensive return objectID; } + // obsolete? static ObjectID traceRay(NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(IntersectData) intersect) { const IntersectData::Mode mode = (IntersectData::Mode)intersect.mode; @@ -109,36 +166,36 @@ struct Comprehensive return ObjectID::create(-1, 0, PST_SPHERE); } - static ObjectID traceRay(NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(scene_type) scene) - { - IntersectData data; + // static ObjectID traceRay(NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(scene_type) scene) + // { + // IntersectData data; - ObjectID objectID; - objectID.id = -1; // start with no intersect + // ObjectID objectID; + // objectID.id = -1; // start with no intersect - // prodedural shapes - if (scene.sphereCount > 0) - { - data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_SPHERE); - objectID = traceRay(ray, data); - } + // // prodedural shapes + // if (scene.sphereCount > 0) + // { + // data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_SPHERE); + // objectID = traceRay(ray, data); + // } - if (scene.triangleCount > 0) - { - data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_TRIANGLE); - objectID = traceRay(ray, data); - } + // if (scene.triangleCount > 0) + // { + // data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_TRIANGLE); + // objectID = traceRay(ray, data); + // } - if (scene.rectangleCount > 0) - { - data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_RECTANGLE); - objectID = traceRay(ray, data); - } + // if (scene.rectangleCount > 0) + // { + // data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_RECTANGLE); + // objectID = traceRay(ray, data); + // } - // TODO: trace AS + // // TODO: trace AS - return objectID; - } + // return objectID; + // } }; // does everything in traceray in ex 30 diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index ba683d443..6b49bc758 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -79,7 +79,7 @@ struct Unidirectional // NextEventEstimator nee) // {} - static this_t create(NBL_CONST_REF_ARG(PathTracerCreationParams) params, Buffer sampleSequence) + static this_t create(NBL_CONST_REF_ARG(PathTracerCreationParams) params, Buffer sampleSequence) { this_t retval; retval.randGen = randgen_type::create(params.rngState); @@ -341,15 +341,21 @@ struct Unidirectional // bounces bool hit = true; bool rayAlive = true; - for (int d = 1; d <= depth && hit && rayAlive; d += 2) - { + // TODO for (int d = 1; d <= depth && hit && rayAlive; d += 2) + // TODO { ray.intersectionT = numeric_limits::max; + ray.objectID.id = -1; ray.objectID = intersector_type::traceRay(ray, scene); hit = ray.objectID.id != -1; if (hit) - rayAlive = closestHitProgram(d, i, ray, scene); - } + { + float pp = float(ray.objectID.id) / 10.0; + ray.payload.accumulation = measure_type(pp, 1.0-pp, 0.3); + // TODO rayAlive = closestHitProgram(1, i, ray, scene); + } + + // TODO } if (!hit) missProgram(ray); @@ -373,7 +379,7 @@ struct Unidirectional material_system_type materialSystem; nee_type nee; - Buffer sampleSequence; + Buffer sampleSequence; }; } diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index 360d085a6..f23d042a8 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -38,7 +38,7 @@ struct SPushConstants [[vk::combinedImageSampler]][[vk::binding(0, 2)]] Texture2D envMap; // unused [[vk::combinedImageSampler]][[vk::binding(0, 2)]] SamplerState envSampler; -[[vk::binding(1, 2)]] Buffer sampleSequence; +[[vk::binding(1, 2)]] Buffer sampleSequence; [[vk::combinedImageSampler]][[vk::binding(2, 2)]] Texture2D scramblebuf; // unused [[vk::combinedImageSampler]][[vk::binding(2, 2)]] SamplerState scrambleSampler; diff --git a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl index 79b66dbfb..1c17e2531 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl @@ -40,75 +40,77 @@ struct Scene // AS ases; - Intersector::IntersectData toIntersectData(uint32_t mode, ProceduralShapeType type) - { - Intersector::IntersectData retval; - retval.mode = mode; - - uint32_t objCount = (type == PST_SPHERE) ? sphereCount : - (type == PST_TRIANGLE) ? triangleCount : - (type == PST_RECTANGLE) ? rectangleCount : - -1; - retval.data[0] = objCount; - retval.data[1] = type; - - switch (type) - { - case PST_SPHERE: - { - for (int i = 0; i < objCount; i++) - { - Shape sphere = spheres[i]; - retval.data[2 + i * Shape::ObjSize] = asuint(sphere.position.x); - retval.data[2 + i * Shape::ObjSize + 1] = asuint(sphere.position.y); - retval.data[2 + i * Shape::ObjSize + 2] = asuint(sphere.position.z); - retval.data[2 + i * Shape::ObjSize + 3] = asuint(sphere.radius2); - retval.data[2 + i * Shape::ObjSize + 4] = sphere.bsdfLightIDs; - } - } - break; - case PST_TRIANGLE: - { - for (int i = 0; i < objCount; i++) - { - Shape tri = triangles[i]; - retval.data[2 + i * Shape::ObjSize] = asuint(tri.vertex0.x); - retval.data[2 + i * Shape::ObjSize + 1] = asuint(tri.vertex0.y); - retval.data[2 + i * Shape::ObjSize + 2] = asuint(tri.vertex0.z); - retval.data[2 + i * Shape::ObjSize + 3] = asuint(tri.vertex1.x); - retval.data[2 + i * Shape::ObjSize + 4] = asuint(tri.vertex1.y); - retval.data[2 + i * Shape::ObjSize + 5] = asuint(tri.vertex1.z); - retval.data[2 + i * Shape::ObjSize + 6] = asuint(tri.vertex2.x); - retval.data[2 + i * Shape::ObjSize + 7] = asuint(tri.vertex2.y); - retval.data[2 + i * Shape::ObjSize + 8] = asuint(tri.vertex2.z); - retval.data[2 + i * Shape::ObjSize + 9] = tri.bsdfLightIDs; - } - } - break; - case PST_RECTANGLE: - { - for (int i = 0; i < objCount; i++) - { - Shape rect = rectangles[i]; - retval.data[2 + i * Shape::ObjSize] = asuint(rect.offset.x); - retval.data[2 + i * Shape::ObjSize + 1] = asuint(rect.offset.y); - retval.data[2 + i * Shape::ObjSize + 2] = asuint(rect.offset.z); - retval.data[2 + i * Shape::ObjSize + 3] = asuint(rect.edge0.x); - retval.data[2 + i * Shape::ObjSize + 4] = asuint(rect.edge0.y); - retval.data[2 + i * Shape::ObjSize + 5] = asuint(rect.edge0.z); - retval.data[2 + i * Shape::ObjSize + 6] = asuint(rect.edge1.x); - retval.data[2 + i * Shape::ObjSize + 7] = asuint(rect.edge1.y); - retval.data[2 + i * Shape::ObjSize + 8] = asuint(rect.edge1.z); - retval.data[2 + i * Shape::ObjSize + 9] = rect.bsdfLightIDs; - } - } - break; - default: - // for ASes - break; - } - return retval; - } + // obsolete? + // Intersector::IntersectData toIntersectData(uint32_t mode, ProceduralShapeType type) + // { + // Intersector::IntersectData retval; + // retval.mode = mode; + + // uint32_t objCount = (type == PST_SPHERE) ? sphereCount : + // (type == PST_TRIANGLE) ? triangleCount : + // (type == PST_RECTANGLE) ? rectangleCount : + // -1; + // retval.data[0] = objCount; + // retval.data[1] = type; + + // switch (type) + // { + // case PST_SPHERE: + // { + // for (int i = 0; i < objCount; i++) + // { + // Shape sphere = spheres[i]; + // uint32_t3 uintPos = bit_cast(sphere.position); + // retval.data[2 + i * Shape::ObjSize] = uintPos.x; + // retval.data[2 + i * Shape::ObjSize + 1] = uintPos.y; + // retval.data[2 + i * Shape::ObjSize + 2] = uintPos.z; + // retval.data[2 + i * Shape::ObjSize + 3] = bit_cast(sphere.radius2); + // retval.data[2 + i * Shape::ObjSize + 4] = sphere.bsdfLightIDs; + // } + // } + // break; + // case PST_TRIANGLE: + // { + // for (int i = 0; i < objCount; i++) + // { + // Shape tri = triangles[i]; + // retval.data[2 + i * Shape::ObjSize] = asuint(tri.vertex0.x); + // retval.data[2 + i * Shape::ObjSize + 1] = asuint(tri.vertex0.y); + // retval.data[2 + i * Shape::ObjSize + 2] = asuint(tri.vertex0.z); + // retval.data[2 + i * Shape::ObjSize + 3] = asuint(tri.vertex1.x); + // retval.data[2 + i * Shape::ObjSize + 4] = asuint(tri.vertex1.y); + // retval.data[2 + i * Shape::ObjSize + 5] = asuint(tri.vertex1.z); + // retval.data[2 + i * Shape::ObjSize + 6] = asuint(tri.vertex2.x); + // retval.data[2 + i * Shape::ObjSize + 7] = asuint(tri.vertex2.y); + // retval.data[2 + i * Shape::ObjSize + 8] = asuint(tri.vertex2.z); + // retval.data[2 + i * Shape::ObjSize + 9] = tri.bsdfLightIDs; + // } + // } + // break; + // case PST_RECTANGLE: + // { + // for (int i = 0; i < objCount; i++) + // { + // Shape rect = rectangles[i]; + // retval.data[2 + i * Shape::ObjSize] = asuint(rect.offset.x); + // retval.data[2 + i * Shape::ObjSize + 1] = asuint(rect.offset.y); + // retval.data[2 + i * Shape::ObjSize + 2] = asuint(rect.offset.z); + // retval.data[2 + i * Shape::ObjSize + 3] = asuint(rect.edge0.x); + // retval.data[2 + i * Shape::ObjSize + 4] = asuint(rect.edge0.y); + // retval.data[2 + i * Shape::ObjSize + 5] = asuint(rect.edge0.z); + // retval.data[2 + i * Shape::ObjSize + 6] = asuint(rect.edge1.x); + // retval.data[2 + i * Shape::ObjSize + 7] = asuint(rect.edge1.y); + // retval.data[2 + i * Shape::ObjSize + 8] = asuint(rect.edge1.z); + // retval.data[2 + i * Shape::ObjSize + 9] = rect.bsdfLightIDs; + // } + // } + // break; + // default: + // // for ASes + // break; + // } + // return retval; + // } NextEventEstimator::Event toNextEvent(uint32_t lightID) { diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index 036fcdb79..8da32083e 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -1341,7 +1341,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, float camYAngle = 165.f / 180.f * 3.14159f; float camXAngle = 32.f / 180.f * 3.14159f; int PTPipline = E_LIGHT_GEOMETRY::ELG_SPHERE; - int renderMode = E_RENDER_MODE::ERM_GLSL; + int renderMode = E_RENDER_MODE::ERM_HLSL; int spp = 32; int depth = 3; From 4d3e04698b85f31910d8d28b82925fe2b641adae Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 10 Mar 2025 14:55:22 +0700 Subject: [PATCH 29/53] removed intersectdata usage, fix emissive bug --- .../app_resources/hlsl/common.hlsl | 76 ++++--- .../app_resources/hlsl/intersector.hlsl | 207 +++++++----------- .../app_resources/hlsl/material_system.hlsl | 6 +- .../hlsl/next_event_estimator.hlsl | 40 ++-- .../app_resources/hlsl/pathtracer.hlsl | 38 ++-- .../app_resources/hlsl/render.comp.hlsl | 8 +- .../app_resources/hlsl/scene.hlsl | 51 +++-- 7 files changed, 214 insertions(+), 212 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index 0fd595bca..f67716060 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -105,17 +105,33 @@ struct BxDFNode NBL_CONSTEXPR_STATIC_INLINE uint32_t INVALID_ID = 0xffffu; + // for diffuse bxdfs + static BxDFNode create(uint32_t materialType, bool isAniso, NBL_CONST_REF_ARG(float32_t2) A, NBL_CONST_REF_ARG(spectral_type) albedo) + { + BxDFNode retval; + retval.albedo = albedo; + retval.materialType = materialType; + retval.params.is_aniso = isAniso; + retval.params.A = hlsl::max(A, 1e-4); + retval.params.ior0 = (spectral_type)1.0; + retval.params.ior1 = (spectral_type)1.0; + return retval; + } + + // for conductor + dielectric static BxDFNode create(uint32_t materialType, bool isAniso, NBL_CONST_REF_ARG(float32_t2) A, NBL_CONST_REF_ARG(spectral_type) ior0, NBL_CONST_REF_ARG(spectral_type) ior1) { BxDFNode retval; + retval.albedo = (spectral_type)1.0; retval.materialType = materialType; retval.params.is_aniso = isAniso; - retval.params.A = A; + retval.params.A = hlsl::max(A, 1e-4); retval.params.ior0 = ior0; retval.params.ior1 = ior1; return retval; } + spectral_type albedo; uint32_t materialType; params_type params; }; @@ -149,32 +165,39 @@ enum PTPolygonMethod : uint16_t PPM_APPROX_PROJECTED_SOLID_ANGLE }; -namespace Intersector -{ -// ray query method -// ray query struct holds AS info -// pass in address to vertex/index buffers? +// namespace Intersector +// { +// // ray query method +// // ray query struct holds AS info +// // pass in address to vertex/index buffers? -// ray tracing pipeline method +// // ray tracing pipeline method -// procedural data store: [obj count] [intersect type] [obj1] [obj2] [...] +// // procedural data store: [obj count] [intersect type] [obj1] [obj2] [...] -struct IntersectData -{ - enum Mode : uint32_t // enum class? - { - RAY_QUERY, - RAY_TRACING, - PROCEDURAL - }; +// struct IntersectData +// { +// enum Mode : uint32_t // enum class? +// { +// RAY_QUERY, +// RAY_TRACING, +// PROCEDURAL +// }; - NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 128; +// NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 128; - uint32_t mode : 1; - uint32_t unused : 31; // possible space for flags - uint32_t data[DataSize]; +// uint32_t mode : 2; +// uint32_t unused : 30; // possible space for flags +// uint32_t data[DataSize]; +// }; +// } + +enum IntersectMode : uint32_t +{ + IM_RAY_QUERY, + IM_RAY_TRACING, + IM_PROCEDURAL }; -} namespace NextEventEstimator { @@ -182,17 +205,10 @@ namespace NextEventEstimator struct Event { - enum Mode : uint32_t // enum class? - { - RAY_QUERY, - RAY_TRACING, - PROCEDURAL - }; - NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 16; - uint32_t mode : 1; - uint32_t unused : 31; // possible space for flags + uint32_t mode : 2; + uint32_t unused : 30; // possible space for flags uint32_t data[DataSize]; }; } diff --git a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl index 68ea75dd3..03a45f866 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl @@ -41,7 +41,7 @@ struct Comprehensive { ray.intersectionT = t; objectID.id = i; - objectID.mode = IntersectData::Mode::PROCEDURAL; + objectID.mode = IM_PROCEDURAL; objectID.shapeType = PST_SPHERE; } } @@ -55,7 +55,7 @@ struct Comprehensive { ray.intersectionT = t; objectID.id = i; - objectID.mode = IntersectData::Mode::PROCEDURAL; + objectID.mode = IM_PROCEDURAL; objectID.shapeType = PST_TRIANGLE; } } @@ -69,7 +69,7 @@ struct Comprehensive { ray.intersectionT = t; objectID.id = i; - objectID.mode = IntersectData::Mode::PROCEDURAL; + objectID.mode = IM_PROCEDURAL; objectID.shapeType = PST_TRIANGLE; } } @@ -81,90 +81,90 @@ struct Comprehensive // note for future consideration: still need to encode to IntersectData? // obsolete? - static ObjectID traceProcedural(NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(IntersectData) intersect) - { - const bool anyHit = ray.intersectionT != numeric_limits::max; - const uint32_t objCount = intersect.data[0]; - const ProceduralShapeType type = (ProceduralShapeType)intersect.data[1]; - - ObjectID objectID = ray.objectID; - objectID.mode = IntersectData::Mode::PROCEDURAL; - objectID.shapeType = type; - for (int i = 0; i < objCount; i++) - { - float t; - switch (type) - { - case PST_SPHERE: - { - vector3_type position = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize]), asfloat(intersect.data[2 + i * Shape::ObjSize + 1]), asfloat(intersect.data[2 + i * Shape::ObjSize + 2])); - Shape sphere = Shape::create(position, asfloat(intersect.data[2 + i * Shape::ObjSize + 3]), intersect.data[2 + i * Shape::ObjSize + 4]); - t = sphere.intersect(ray.origin, ray.direction); - } - break; - case PST_TRIANGLE: - { - vector3_type vertex0 = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize]), asfloat(intersect.data[2 + i * Shape::ObjSize + 1]), asfloat(intersect.data[2 + i * Shape::ObjSize + 2])); - vector3_type vertex1 = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize + 3]), asfloat(intersect.data[2 + i * Shape::ObjSize + 4]), asfloat(intersect.data[2 + i * Shape::ObjSize + 5])); - vector3_type vertex2 = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize + 6]), asfloat(intersect.data[2 + i * Shape::ObjSize + 7]), asfloat(intersect.data[2 + i * Shape::ObjSize + 8])); - Shape tri = Shape::create(vertex0, vertex1, vertex2, intersect.data[2 + i * Shape::ObjSize + 9]); - t = tri.intersect(ray.origin, ray.direction); - } - break; - case PST_RECTANGLE: - { - vector3_type offset = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize]), asfloat(intersect.data[2 + i * Shape::ObjSize + 1]), asfloat(intersect.data[2 + i * Shape::ObjSize + 2])); - vector3_type edge0 = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize + 3]), asfloat(intersect.data[2 + i * Shape::ObjSize + 4]), asfloat(intersect.data[2 + i * Shape::ObjSize + 5])); - vector3_type edge1 = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize + 6]), asfloat(intersect.data[2 + i * Shape::ObjSize + 7]), asfloat(intersect.data[2 + i * Shape::ObjSize + 8])); - Shape rect = Shape::create(offset, edge0, edge1, intersect.data[2 + i * Shape::ObjSize + 9]); - t = rect.intersect(ray.origin, ray.direction); - } - break; - default: - t = numeric_limits::infinity; - break; - } - - bool closerIntersection = t > 0.0 && t < ray.intersectionT; - - ray.intersectionT = closerIntersection ? t : ray.intersectionT; - objectID.id = closerIntersection ? i : objectID.id; - - // allowing early out results in a performance regression, WTF!? - //if (anyHit && closerIntersection) - //break; - } - return objectID; - } + // static ObjectID traceProcedural(NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(IntersectData) intersect) + // { + // const bool anyHit = ray.intersectionT != numeric_limits::max; + // const uint32_t objCount = intersect.data[0]; + // const ProceduralShapeType type = (ProceduralShapeType)intersect.data[1]; + + // ObjectID objectID = ray.objectID; + // objectID.mode = IM_PROCEDURAL; + // objectID.shapeType = type; + // for (int i = 0; i < objCount; i++) + // { + // float t; + // switch (type) + // { + // case PST_SPHERE: + // { + // vector3_type position = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize]), asfloat(intersect.data[2 + i * Shape::ObjSize + 1]), asfloat(intersect.data[2 + i * Shape::ObjSize + 2])); + // Shape sphere = Shape::create(position, asfloat(intersect.data[2 + i * Shape::ObjSize + 3]), intersect.data[2 + i * Shape::ObjSize + 4]); + // t = sphere.intersect(ray.origin, ray.direction); + // } + // break; + // case PST_TRIANGLE: + // { + // vector3_type vertex0 = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize]), asfloat(intersect.data[2 + i * Shape::ObjSize + 1]), asfloat(intersect.data[2 + i * Shape::ObjSize + 2])); + // vector3_type vertex1 = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize + 3]), asfloat(intersect.data[2 + i * Shape::ObjSize + 4]), asfloat(intersect.data[2 + i * Shape::ObjSize + 5])); + // vector3_type vertex2 = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize + 6]), asfloat(intersect.data[2 + i * Shape::ObjSize + 7]), asfloat(intersect.data[2 + i * Shape::ObjSize + 8])); + // Shape tri = Shape::create(vertex0, vertex1, vertex2, intersect.data[2 + i * Shape::ObjSize + 9]); + // t = tri.intersect(ray.origin, ray.direction); + // } + // break; + // case PST_RECTANGLE: + // { + // vector3_type offset = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize]), asfloat(intersect.data[2 + i * Shape::ObjSize + 1]), asfloat(intersect.data[2 + i * Shape::ObjSize + 2])); + // vector3_type edge0 = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize + 3]), asfloat(intersect.data[2 + i * Shape::ObjSize + 4]), asfloat(intersect.data[2 + i * Shape::ObjSize + 5])); + // vector3_type edge1 = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize + 6]), asfloat(intersect.data[2 + i * Shape::ObjSize + 7]), asfloat(intersect.data[2 + i * Shape::ObjSize + 8])); + // Shape rect = Shape::create(offset, edge0, edge1, intersect.data[2 + i * Shape::ObjSize + 9]); + // t = rect.intersect(ray.origin, ray.direction); + // } + // break; + // default: + // t = numeric_limits::infinity; + // break; + // } + + // bool closerIntersection = t > 0.0 && t < ray.intersectionT; + + // ray.intersectionT = closerIntersection ? t : ray.intersectionT; + // objectID.id = closerIntersection ? i : objectID.id; + + // // allowing early out results in a performance regression, WTF!? + // //if (anyHit && closerIntersection) + // //break; + // } + // return objectID; + // } // obsolete? - static ObjectID traceRay(NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(IntersectData) intersect) - { - const IntersectData::Mode mode = (IntersectData::Mode)intersect.mode; - switch (mode) - { - case IntersectData::Mode::RAY_QUERY: - { - // TODO: do ray query stuff - } - break; - case IntersectData::Mode::RAY_TRACING: - { - // TODO: do ray tracing stuff - } - break; - case IntersectData::Mode::PROCEDURAL: - { - return traceProcedural(ray, intersect); - } - break; - default: - { - return ObjectID::create(-1, 0, PST_SPHERE); - } - } - return ObjectID::create(-1, 0, PST_SPHERE); - } + // static ObjectID traceRay(NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(IntersectData) intersect) + // { + // const uint32_t mode = intersect.mode; + // switch (mode) + // { + // case IM_RAY_QUERY: + // { + // // TODO: do ray query stuff + // } + // break; + // case IM_RAY_TRACING: + // { + // // TODO: do ray tracing stuff + // } + // break; + // case IM_PROCEDURAL: + // { + // return traceProcedural(ray, intersect); + // } + // break; + // default: + // { + // return ObjectID::create(-1, 0, PST_SPHERE); + // } + // } + // return ObjectID::create(-1, 0, PST_SPHERE); + // } // static ObjectID traceRay(NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(scene_type) scene) // { @@ -198,43 +198,6 @@ struct Comprehensive // } }; -// does everything in traceray in ex 30 -// template -// struct Procedural -// { -// using scalar_type = typename Ray::scalar_type; -// using ray_type = Ray; - -// static int traceRay(NBL_REF_ARG(ray_type) ray, IIntersection objects[32], int objCount) -// { -// const bool anyHit = ray.intersectionT != numeric_limits::max; - -// int objectID = -1; -// for (int i = 0; i < objCount; i++) -// { -// float t; -// if (objects[i].type == PST_SPHERE) // we don't know what type of intersection it is so cast, there has to be a better way to do this -// { -// Shape sphere = (Shape)objects[i]; -// t = sphere.intersect(ray.origin, ray.direction); -// } -// // TODO: other types - -// bool closerIntersection = t > 0.0 && t < ray.intersectionT; - -// ray.intersectionT = closerIntersection ? t : ray.intersectionT; -// objectID = closerIntersection ? i : objectID; - -// // allowing early out results in a performance regression, WTF!? -// //if (anyHit && closerIntersection) -// //break; -// } -// return objectID; -// } - -// // TODO? traceray with vertex/index buffer -// }; - } } } diff --git a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl index 09236c85e..7fb153791 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl @@ -25,8 +25,8 @@ struct Material NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 32; - uint32_t type : 1; - uint32_t unused : 31; // possible space for flags + uint32_t type : 2; + uint32_t unused : 30; // possible space for flags uint32_t data[DataSize]; }; @@ -66,7 +66,7 @@ struct System case Material::Type::DIFFUSE: { diffuseBxDF.init(cparams); - return (measure_type)diffuseBxDF.eval(params); + return cparams.albedo * (measure_type)diffuseBxDF.eval(params); } break; case Material::Type::CONDUCTOR: diff --git a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl index 38a5fae15..949db8456 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl @@ -35,16 +35,28 @@ struct Estimator { case PST_SPHERE: { - vector3_type position = vector3_type(asfloat(event.data[2]), asfloat(event.data[3]), asfloat(event.data[4])); - Shape sphere = Shape::create(position, asfloat(event.data[5]), event.data[6]); + const vector3_type position = vector3_type( + bit_cast(event.data[2]), + bit_cast(event.data[3]), + bit_cast(event.data[4])); + Shape sphere = Shape::create(position, bit_cast(event.data[5]), event.data[6]); pdf *= sphere.template deferredPdf(ray); } break; case PST_TRIANGLE: { - vector3_type vertex0 = vector3_type(asfloat(event.data[2]), asfloat(event.data[3]), asfloat(event.data[4])); - vector3_type vertex1 = vector3_type(asfloat(event.data[5]), asfloat(event.data[6]), asfloat(event.data[7])); - vector3_type vertex2 = vector3_type(asfloat(event.data[8]), asfloat(event.data[9]), asfloat(event.data[10])); + const vector3_type vertex0 = vector3_type( + bit_cast(event.data[2]), + bit_cast(event.data[3]), + bit_cast(event.data[4])); + const vector3_type vertex1 = vector3_type( + bit_cast(event.data[5]), + bit_cast(event.data[6]), + bit_cast(event.data[7])); + const vector3_type vertex2 = vector3_type( + bit_cast(event.data[8]), + bit_cast(event.data[9]), + bit_cast(event.data[10])); Shape tri = Shape::create(vertex0, vertex1, vertex2, event.data[11]); pdf *= tri.template deferredPdf(ray); } @@ -59,7 +71,7 @@ struct Estimator } break; default: - pdf = numeric_limits::infinity; + pdf = bit_cast(numeric_limits::infinity); break; } @@ -68,20 +80,20 @@ struct Estimator static spectral_type deferredEvalAndPdf(NBL_REF_ARG(scalar_type) pdf, NBL_CONST_REF_ARG(light_type) light, NBL_CONST_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(Event) event) { - const Event::Mode mode = (Event::Mode)event.mode; + const uint32_t mode = event.mode; switch (mode) { - case Event::Mode::RAY_QUERY: + case IM_RAY_QUERY: { // TODO: do ray query stuff } break; - case Event::Mode::RAY_TRACING: + case IM_RAY_TRACING: { // TODO: do ray tracing stuff } break; - case Event::Mode::PROCEDURAL: + case IM_PROCEDURAL: { return proceduralDeferredEvalAndPdf(pdf, light, ray, event); } @@ -156,21 +168,21 @@ struct Estimator static sample_type generate_and_quotient_and_pdf(NBL_REF_ARG(quotient_pdf_type) quotient_pdf, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(light_type) light, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(interaction_type) interaction, bool isBSDF, NBL_CONST_REF_ARG(vector3_type) xi, uint32_t depth, NBL_CONST_REF_ARG(Event) event) { - const Event::Mode mode = (Event::Mode)event.mode; + const uint32_t mode = event.mode; sample_type L; switch (mode) { - case Event::Mode::RAY_QUERY: + case IM_RAY_QUERY: { // TODO: do ray query stuff } break; - case Event::Mode::RAY_TRACING: + case IM_RAY_TRACING: { // TODO: do ray tracing stuff } break; - case Event::Mode::PROCEDURAL: + case IM_PROCEDURAL: { return procedural_generate_and_quotient_and_pdf(quotient_pdf, newRayMaxT, light, origin, interaction, isBSDF, xi, depth, event); } diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index 6b49bc758..df4792a9c 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -111,14 +111,14 @@ struct Unidirectional uint32_t bsdfLightIDs; anisotropic_type interaction; isotropic_type iso_interaction; - ext::Intersector::IntersectData::Mode mode = (ext::Intersector::IntersectData::Mode)objectID.mode; + uint32_t mode = objectID.mode; switch (mode) { // TODO - case ext::Intersector::IntersectData::Mode::RAY_QUERY: - case ext::Intersector::IntersectData::Mode::RAY_TRACING: + case IM_RAY_QUERY: + case IM_RAY_TRACING: break; - case ext::Intersector::IntersectData::Mode::PROCEDURAL: + case IM_PROCEDURAL: { bsdfLightIDs = scene.getBsdfLightIDs(objectID); vector3_type N = scene.getNormal(objectID, intersection); @@ -139,10 +139,12 @@ 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.lights[lightID], ray, scene.toNextEvent(lightID)) * throughput / (1.0 + pdf * pdf * ray.payload.otherTechniqueHeuristic); + float _pdf; + ray.payload.accumulation += nee.deferredEvalAndPdf(_pdf, scene.lights[lightID], ray, scene.toNextEvent(lightID)) * throughput / (1.0 + _pdf * _pdf * ray.payload.otherTechniqueHeuristic); } + return false; // emissive only + const uint32_t bsdfID = glsl::bitfieldExtract(bsdfLightIDs, 0, 16); if (bsdfID == bxdfnode_type::INVALID_ID) return false; @@ -209,7 +211,7 @@ struct Unidirectional params = params_type::template create(nee_sample, interaction, _cache, bxdf::BCM_MAX); else { - isocache_type isocache = (isocache_type)_cache; + isocache_type isocache = _cache.iso_cache; params = params_type::template create(nee_sample, iso_interaction, isocache, bxdf::BCM_MAX); } } @@ -223,7 +225,7 @@ struct Unidirectional params = params_type::template create(nee_sample, interaction, _cache, bxdf::BCM_ABS); else { - isocache_type isocache = (isocache_type)_cache; + isocache_type isocache = _cache.iso_cache; params = params_type::template create(nee_sample, iso_interaction, isocache, bxdf::BCM_ABS); } } @@ -232,10 +234,11 @@ struct Unidirectional bsdf_quotient_pdf.quotient *= throughput; neeContrib_pdf.quotient *= 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 + // 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.L.direction * t * Tolerance::getStart(depth); @@ -247,6 +250,8 @@ struct Unidirectional } } + return false; // NEE only + // sample BSDF scalar_type bxdfPdf; vector3_type bxdfSample; @@ -341,21 +346,20 @@ struct Unidirectional // bounces bool hit = true; bool rayAlive = true; - // TODO for (int d = 1; d <= depth && hit && rayAlive; d += 2) - // TODO { + for (int d = 1; d <= depth && hit && rayAlive; d += 2) + { ray.intersectionT = numeric_limits::max; - ray.objectID.id = -1; ray.objectID = intersector_type::traceRay(ray, scene); hit = ray.objectID.id != -1; if (hit) { - float pp = float(ray.objectID.id) / 10.0; - ray.payload.accumulation = measure_type(pp, 1.0-pp, 0.3); - // TODO rayAlive = closestHitProgram(1, i, ray, scene); + // float pp = float(ray.objectID.id) / 10.0; + // ray.payload.accumulation = measure_type(pp, 1.0-pp, 0.3); + rayAlive = closestHitProgram(1, i, ray, scene); } - // TODO } + } if (!hit) missProgram(ray); diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index f23d042a8..e25961b56 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -115,14 +115,14 @@ static const ext::Shape rectangles[RECTANGLE_COUNT] = { #define LIGHT_COUNT 1 static const light_type lights[LIGHT_COUNT] = { - light_type::create(spectral_t(30.0,25.0,15.0), ext::ObjectID::create(8u, ext::NextEventEstimator::Event::Mode::PROCEDURAL, LIGHT_TYPE)) + light_type::create(spectral_t(30.0,25.0,15.0), ext::ObjectID::create(8u, ext::IntersectMode::IM_PROCEDURAL, LIGHT_TYPE)) }; #define BXDF_COUNT 7 static const bxdfnode_type bxdfs[BXDF_COUNT] = { - bxdfnode_type::create(ext::MaterialSystem::Material::Type::DIFFUSE, false, float2(0,0), spectral_t(1,1,1), spectral_t(1.25,1.25,1.25)), - bxdfnode_type::create(ext::MaterialSystem::Material::Type::DIFFUSE, false, float2(0,0), spectral_t(1,1,1), spectral_t(1.25,2.5,2.5)), - bxdfnode_type::create(ext::MaterialSystem::Material::Type::DIFFUSE, false, float2(0,0), spectral_t(1,1,1), spectral_t(2.5,1.25,2.5)), + bxdfnode_type::create(ext::MaterialSystem::Material::Type::DIFFUSE, false, float2(0,0), spectral_t(0.8,0.8,0.8)), + bxdfnode_type::create(ext::MaterialSystem::Material::Type::DIFFUSE, false, float2(0,0), spectral_t(0.8,0.4,0.4)), + bxdfnode_type::create(ext::MaterialSystem::Material::Type::DIFFUSE, false, float2(0,0), spectral_t(0.4,0.8,0.4)), bxdfnode_type::create(ext::MaterialSystem::Material::Type::CONDUCTOR, false, float2(0,0), spectral_t(1,1,1), spectral_t(0.98,0.98,0.77)), bxdfnode_type::create(ext::MaterialSystem::Material::Type::CONDUCTOR, false, float2(0,0), spectral_t(1,1,1), spectral_t(0.98,0.77,0.98)), bxdfnode_type::create(ext::MaterialSystem::Material::Type::CONDUCTOR, false, float2(0.15,0.15), spectral_t(1,1,1), spectral_t(0.98,0.77,0.98)), diff --git a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl index 1c17e2531..5b4178ec4 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl @@ -128,40 +128,47 @@ struct Scene case PST_SPHERE: { Shape sphere = spheres[id]; - retval.data[2] = asuint(sphere.position.x); - retval.data[3] = asuint(sphere.position.y); - retval.data[4] = asuint(sphere.position.z); - retval.data[5] = asuint(sphere.radius2); + uint32_t3 position = bit_cast(sphere.position); + retval.data[2] = position.x; + retval.data[3] = position.y; + retval.data[4] = position.z; + retval.data[5] = bit_cast(sphere.radius2); retval.data[6] = sphere.bsdfLightIDs; } break; case PST_TRIANGLE: { Shape tri = triangles[id]; - retval.data[2] = asuint(tri.vertex0.x); - retval.data[3] = asuint(tri.vertex0.y); - retval.data[4] = asuint(tri.vertex0.z); - retval.data[5] = asuint(tri.vertex1.x); - retval.data[6] = asuint(tri.vertex1.y); - retval.data[7] = asuint(tri.vertex1.z); - retval.data[8] = asuint(tri.vertex2.x); - retval.data[9] = asuint(tri.vertex2.y); - retval.data[10] = asuint(tri.vertex2.z); + uint32_t3 vertex = bit_cast(tri.vertex0); + retval.data[2] = vertex.x; + retval.data[3] = vertex.y; + retval.data[4] = vertex.z; + vertex = bit_cast(tri.vertex1); + retval.data[5] = vertex.x; + retval.data[6] = vertex.y; + retval.data[7] = vertex.z; + vertex = bit_cast(tri.vertex2); + retval.data[8] = vertex.x; + retval.data[9] = vertex.y; + retval.data[10] = vertex.z; retval.data[11] = tri.bsdfLightIDs; } break; case PST_RECTANGLE: { Shape rect = rectangles[id]; - retval.data[2] = asuint(rect.offset.x); - retval.data[3] = asuint(rect.offset.y); - retval.data[4] = asuint(rect.offset.z); - retval.data[5] = asuint(rect.edge0.x); - retval.data[6] = asuint(rect.edge0.y); - retval.data[7] = asuint(rect.edge0.z); - retval.data[8] = asuint(rect.edge1.x); - retval.data[9] = asuint(rect.edge1.y); - retval.data[10] = asuint(rect.edge1.z); + uint32_t3 tmp = bit_cast(rect.offset); + retval.data[2] = tmp.x; + retval.data[3] = tmp.y; + retval.data[4] = tmp.z; + tmp = bit_cast(rect.edge0); + retval.data[5] = tmp.x; + retval.data[6] = tmp.y; + retval.data[7] = tmp.z; + tmp = bit_cast(rect.edge1); + retval.data[8] = tmp.x; + retval.data[9] = tmp.y; + retval.data[10] = tmp.z; retval.data[11] = rect.bsdfLightIDs; } break; From e7d4670fca8009843abde9cf4fc1ddd6aedc9290 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 10 Mar 2025 17:42:58 +0700 Subject: [PATCH 30/53] fixed light sampling nee --- .../app_resources/glsl/common.glsl | 2 +- .../app_resources/hlsl/material_system.hlsl | 15 +++-- .../hlsl/next_event_estimator.hlsl | 57 ++++++++++++---- .../app_resources/hlsl/pathtracer.hlsl | 67 +++++++++---------- 4 files changed, 86 insertions(+), 55 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/glsl/common.glsl b/31_HLSLPathTracer/app_resources/glsl/common.glsl index 2463f82cf..15b3662d0 100644 --- a/31_HLSLPathTracer/app_resources/glsl/common.glsl +++ b/31_HLSLPathTracer/app_resources/glsl/common.glsl @@ -7,7 +7,7 @@ //#define VISUALIZE_HIGH_VARIANCE // debug -//#define NEE_ONLY +#define NEE_ONLY 1 layout(set = 2, binding = 0) uniform sampler2D envMap; layout(set = 2, binding = 1) uniform usamplerBuffer sampleSequence; diff --git a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl index 7fb153791..af8d5b131 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl @@ -23,7 +23,7 @@ struct Material DIELECTRIC }; - NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 32; + NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 1; uint32_t type : 2; uint32_t unused : 30; // possible space for flags @@ -66,7 +66,7 @@ struct System case Material::Type::DIFFUSE: { diffuseBxDF.init(cparams); - return cparams.albedo * (measure_type)diffuseBxDF.eval(params); + return (measure_type)diffuseBxDF.eval(params); } break; case Material::Type::CONDUCTOR: @@ -123,8 +123,13 @@ struct System quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, NBL_CONST_REF_ARG(params_t) params) { + + const bool transmissive = material.type == Material::Type::DIELECTRIC; + const float clampedNdotV = math::conditionalAbsOrMax(transmissive, params.uNdotV, 0.0); + const float clampedNdotL = math::conditionalAbsOrMax(transmissive, params.uNdotL, 0.0); + const float minimumProjVectorLen = 0.00000001; - if (params.NdotV > minimumProjVectorLen && params.NdotL > minimumProjVectorLen) + if (clampedNdotV > minimumProjVectorLen && clampedNdotL > minimumProjVectorLen) { switch(material.type) { @@ -147,10 +152,10 @@ struct System } break; default: - return quotient_pdf_type::create((measure_type)0.0, numeric_limits::infinity); + return quotient_pdf_type::create((measure_type)0.0, 0.0); } } - return quotient_pdf_type::create((measure_type)0.0, numeric_limits::infinity); + return quotient_pdf_type::create((measure_type)0.0, 0.0); } DiffuseBxDF diffuseBxDF; diff --git a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl index 949db8456..65646b3c1 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl @@ -63,9 +63,18 @@ struct Estimator break; case PST_RECTANGLE: { - vector3_type offset = vector3_type(asfloat(event.data[2]), asfloat(event.data[3]), asfloat(event.data[4])); - vector3_type edge0 = vector3_type(asfloat(event.data[5]), asfloat(event.data[6]), asfloat(event.data[7])); - vector3_type edge1 = vector3_type(asfloat(event.data[8]), asfloat(event.data[9]), asfloat(event.data[10])); + const vector3_type offset = vector3_type( + bit_cast(event.data[2]), + bit_cast(event.data[3]), + bit_cast(event.data[4])); + const vector3_type edge0 = vector3_type( + bit_cast(event.data[5]), + bit_cast(event.data[6]), + bit_cast(event.data[7])); + const vector3_type edge1 = vector3_type( + bit_cast(event.data[8]), + bit_cast(event.data[9]), + bit_cast(event.data[10])); Shape rect = Shape::create(offset, edge0, edge1, event.data[11]); pdf *= rect.template deferredPdf(ray); } @@ -115,8 +124,12 @@ struct Estimator { case PST_SPHERE: { - vector3_type position = vector3_type(asfloat(event.data[2]), asfloat(event.data[3]), asfloat(event.data[4])); - Shape sphere = Shape::create(position, asfloat(event.data[5]), event.data[6]); + const vector3_type position = vector3_type( + bit_cast(event.data[2]), + bit_cast(event.data[3]), + bit_cast(event.data[4])); + Shape sphere = Shape::create(position, bit_cast(event.data[5]), event.data[6]); + const vector3_type sampleL = sphere.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); const vector3_type V = interaction.isotropic.V.getDirection(); const scalar_type VdotL = nbl::hlsl::dot(V, sampleL); @@ -127,10 +140,20 @@ struct Estimator break; case PST_TRIANGLE: { - vector3_type vertex0 = vector3_type(asfloat(event.data[2]), asfloat(event.data[3]), asfloat(event.data[4])); - vector3_type vertex1 = vector3_type(asfloat(event.data[5]), asfloat(event.data[6]), asfloat(event.data[7])); - vector3_type vertex2 = vector3_type(asfloat(event.data[8]), asfloat(event.data[9]), asfloat(event.data[10])); + const vector3_type vertex0 = vector3_type( + bit_cast(event.data[2]), + bit_cast(event.data[3]), + bit_cast(event.data[4])); + const vector3_type vertex1 = vector3_type( + bit_cast(event.data[5]), + bit_cast(event.data[6]), + bit_cast(event.data[7])); + const vector3_type vertex2 = vector3_type( + bit_cast(event.data[8]), + bit_cast(event.data[9]), + bit_cast(event.data[10])); Shape tri = Shape::create(vertex0, vertex1, vertex2, event.data[11]); + const vector3_type sampleL = tri.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); const vector3_type V = interaction.isotropic.V.getDirection(); const scalar_type VdotL = nbl::hlsl::dot(V, sampleL); @@ -141,10 +164,20 @@ struct Estimator break; case PST_RECTANGLE: { - vector3_type offset = vector3_type(asfloat(event.data[2]), asfloat(event.data[3]), asfloat(event.data[4])); - vector3_type edge0 = vector3_type(asfloat(event.data[5]), asfloat(event.data[6]), asfloat(event.data[7])); - vector3_type edge1 = vector3_type(asfloat(event.data[8]), asfloat(event.data[9]), asfloat(event.data[10])); + const vector3_type offset = vector3_type( + bit_cast(event.data[2]), + bit_cast(event.data[3]), + bit_cast(event.data[4])); + const vector3_type edge0 = vector3_type( + bit_cast(event.data[5]), + bit_cast(event.data[6]), + bit_cast(event.data[7])); + const vector3_type edge1 = vector3_type( + bit_cast(event.data[8]), + bit_cast(event.data[9]), + bit_cast(event.data[10])); Shape rect = Shape::create(offset, edge0, edge1, event.data[11]); + const vector3_type sampleL = rect.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); const vector3_type V = interaction.isotropic.V.getDirection(); const scalar_type VdotL = nbl::hlsl::dot(V, sampleL); @@ -154,7 +187,7 @@ struct Estimator } break; default: - pdf = numeric_limits::infinity; + pdf = bit_cast(numeric_limits::infinity); break; } diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index df4792a9c..6f1518a46 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -99,7 +99,7 @@ struct Unidirectional scalar_type getLuma(NBL_CONST_REF_ARG(vector3_type) col) { - return nbl::hlsl::dot(nbl::hlsl::transpose(colorspace::scRGBtoXYZ)[1], col); + return hlsl::dot(hlsl::transpose(colorspace::scRGBtoXYZ)[1], col); } // TODO: probably will only work with procedural shapes, do the other ones @@ -123,8 +123,8 @@ struct Unidirectional bsdfLightIDs = scene.getBsdfLightIDs(objectID); vector3_type N = scene.getNormal(objectID, intersection); N = nbl::hlsl::normalize(N); - typename isotropic_type::ray_dir_info_type V; - V.direction = nbl::hlsl::normalize(-ray.direction); + ray_dir_info_type V; + V.direction = -ray.direction; isotropic_type iso_interaction = isotropic_type::create(V, N); interaction = anisotropic_type::create(iso_interaction); } @@ -143,8 +143,6 @@ struct Unidirectional ray.payload.accumulation += nee.deferredEvalAndPdf(_pdf, scene.lights[lightID], ray, scene.toNextEvent(lightID)) * throughput / (1.0 + _pdf * _pdf * ray.payload.otherTechniqueHeuristic); } - return false; // emissive only - const uint32_t bsdfID = glsl::bitfieldExtract(bsdfLightIDs, 0, 16); if (bsdfID == bxdfnode_type::INVALID_ID) return false; @@ -163,9 +161,9 @@ struct Unidirectional // 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 = nbl::hlsl::transpose(colorspace::sRGBtoXYZ)[1] * throughput; // TODO: this only works if spectral_type is dim 3 + const vector3_type throughputCIE_Y = hlsl::transpose(colorspace::sRGBtoXYZ)[1] * throughput; // TODO: this only works if spectral_type is dim 3 const measure_type eta = bxdf.params.ior0 / bxdf.params.ior1; // assume it's real, not imaginary? - const scalar_type monochromeEta = nbl::hlsl::dot(throughputCIE_Y, eta) / (throughputCIE_Y.r + throughputCIE_Y.g + throughputCIE_Y.b); // TODO: imaginary eta? + 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); @@ -185,7 +183,6 @@ struct Unidirectional // but if we allowed non-watertight transmitters (single water surface), it would make sense just to apply this line by itself anisocache_type _cache; validPath = validPath && anisocache_type::template compute(_cache, interaction, nee_sample, monochromeEta); - bxdf.params.A = nbl::hlsl::max(bxdf.params.A, vector(0,0)); bxdf.params.eta = monochromeEta; if (neeContrib_pdf.pdf < numeric_limits::max) @@ -231,7 +228,7 @@ struct Unidirectional } quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(material, bxdf.params, params); - bsdf_quotient_pdf.quotient *= throughput; + bsdf_quotient_pdf.quotient *= bxdf.albedo * throughput; neeContrib_pdf.quotient *= bsdf_quotient_pdf.quotient; const scalar_type otherGenOverChoice = bsdf_quotient_pdf.pdf * rcpChoiceProb; // const scalar_type otherGenOverLightAndChoice = otherGenOverChoice / bsdf_quotient_pdf.pdf; @@ -268,30 +265,30 @@ struct Unidirectional { params = params_type::template create(bsdf_sample, iso_interaction, bxdf::BCM_MAX); } - else if (!isBSDF && bxdf.materialType != ext::MaterialSystem::Material::DIFFUSE) - { - if (bxdf.params.is_aniso) - params = params_type::template create(bsdf_sample, interaction, _cache, bxdf::BCM_MAX); - else - { - isocache_type isocache = _cache.iso_cache; - params = params_type::template create(bsdf_sample, iso_interaction, isocache, bxdf::BCM_MAX); - } - } - else if (isBSDF && bxdf.materialType == ext::MaterialSystem::Material::DIFFUSE) - { - params = params_type::template create(bsdf_sample, iso_interaction, bxdf::BCM_ABS); - } - else if (isBSDF && bxdf.materialType != ext::MaterialSystem::Material::DIFFUSE) - { - if (bxdf.params.is_aniso) - params = params_type::template create(bsdf_sample, interaction, _cache, bxdf::BCM_ABS); - else - { - isocache_type isocache = _cache.iso_cache; - params = params_type::template create(bsdf_sample, iso_interaction, isocache, bxdf::BCM_ABS); - } - } + // else if (!isBSDF && bxdf.materialType != ext::MaterialSystem::Material::DIFFUSE) + // { + // if (bxdf.params.is_aniso) + // params = params_type::template create(bsdf_sample, interaction, _cache, bxdf::BCM_MAX); + // else + // { + // isocache_type isocache = _cache.iso_cache; + // params = params_type::template create(bsdf_sample, iso_interaction, isocache, bxdf::BCM_MAX); + // } + // } + // else if (isBSDF && bxdf.materialType == ext::MaterialSystem::Material::DIFFUSE) + // { + // params = params_type::template create(bsdf_sample, iso_interaction, bxdf::BCM_ABS); + // } + // else if (isBSDF && bxdf.materialType != ext::MaterialSystem::Material::DIFFUSE) + // { + // if (bxdf.params.is_aniso) + // params = params_type::template create(bsdf_sample, interaction, _cache, bxdf::BCM_ABS); + // else + // { + // isocache_type isocache = _cache.iso_cache; + // params = params_type::template create(bsdf_sample, iso_interaction, isocache, bxdf::BCM_ABS); + // } + // } // the value of the bsdf divided by the probability of the sample being generated quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(material, bxdf.params, params); @@ -353,11 +350,7 @@ struct Unidirectional hit = ray.objectID.id != -1; if (hit) - { - // float pp = float(ray.objectID.id) / 10.0; - // ray.payload.accumulation = measure_type(pp, 1.0-pp, 0.3); rayAlive = closestHitProgram(1, i, ray, scene); - } } if (!hit) From 077d150bded805ce9ed10bc7711544576779ad39 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 11 Mar 2025 17:02:59 +0700 Subject: [PATCH 31/53] 1st working ver, sort of --- .../app_resources/glsl/common.glsl | 2 +- .../app_resources/hlsl/common.hlsl | 4 +- .../app_resources/hlsl/material_system.hlsl | 7 +- .../app_resources/hlsl/pathtracer.hlsl | 92 ++++++++++--------- 31_HLSLPathTracer/imgui.ini | 8 ++ 5 files changed, 63 insertions(+), 50 deletions(-) create mode 100644 31_HLSLPathTracer/imgui.ini diff --git a/31_HLSLPathTracer/app_resources/glsl/common.glsl b/31_HLSLPathTracer/app_resources/glsl/common.glsl index 15b3662d0..2463f82cf 100644 --- a/31_HLSLPathTracer/app_resources/glsl/common.glsl +++ b/31_HLSLPathTracer/app_resources/glsl/common.glsl @@ -7,7 +7,7 @@ //#define VISUALIZE_HIGH_VARIANCE // debug -#define NEE_ONLY 1 +//#define NEE_ONLY layout(set = 2, binding = 0) uniform sampler2D envMap; layout(set = 2, binding = 1) uniform usamplerBuffer sampleSequence; diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index f67716060..ac1e0f09a 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -268,7 +268,7 @@ struct Shape } template - float32_t3 generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, float32_t3 xi) + float32_t3 generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, NBL_CONST_REF_ARG(float32_t3) xi) { float32_t3 Z = position - origin; const float distanceSQ = hlsl::dot(Z,Z); @@ -279,7 +279,7 @@ struct Shape Z *= rcpDistance; const float cosThetaMax = hlsl::sqrt(cosThetaMax2); - const float cosTheta = nbl::hlsl::mix(1.0, cosThetaMax, xi.x); + const float cosTheta = hlsl::mix(1.0, cosThetaMax, xi.x); float32_t3 L = Z * cosTheta; diff --git a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl index af8d5b131..0d739d9ec 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl @@ -123,13 +123,8 @@ struct System quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, NBL_CONST_REF_ARG(params_t) params) { - - const bool transmissive = material.type == Material::Type::DIELECTRIC; - const float clampedNdotV = math::conditionalAbsOrMax(transmissive, params.uNdotV, 0.0); - const float clampedNdotL = math::conditionalAbsOrMax(transmissive, params.uNdotL, 0.0); - const float minimumProjVectorLen = 0.00000001; - if (clampedNdotV > minimumProjVectorLen && clampedNdotL > minimumProjVectorLen) + if (params.NdotV > minimumProjVectorLen && params.NdotL > minimumProjVectorLen) { switch(material.type) { diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index 6f1518a46..6ed89de7a 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -94,7 +94,7 @@ struct Unidirectional uint32_t address = glsl::bitfieldInsert(protoDimension, _sample, MAX_DEPTH_LOG2, MAX_SAMPLES_LOG2); uint32_t3 seqVal = sampleSequence[address + i].xyz; seqVal ^= randGen(); - return vector3_type(seqVal) * asfloat(0x2f800004u); + return vector3_type(seqVal) * bit_cast(0x2f800004u); } scalar_type getLuma(NBL_CONST_REF_ARG(vector3_type) col) @@ -177,6 +177,7 @@ struct Unidirectional scene.lights[lightID], intersection, interaction, isBSDF, eps0, depth, scene.toNextEvent(lightID) ); + //printf("%f %f %f\n", nee_sample.L.direction.x, nee_sample.L.direction.y, nee_sample.L.direction.z); // We don't allow non watertight transmitters in this renderer bool validPath = nee_sample.NdotL > numeric_limits::min; @@ -195,47 +196,51 @@ struct Unidirectional { ext::MaterialSystem::Material material; material.type = bxdf.materialType; - params_type params; + + bxdf::BxDFClampMode _clamp; + _clamp = (bxdf.materialType == ext::MaterialSystem::Material::Type::DIELECTRIC) ? bxdf::BxDFClampMode::BCM_ABS : bxdf::BxDFClampMode::BCM_MAX; + // example only uses isotropic bxdfs + params_type params = params_type::template create(nee_sample, interaction.isotropic, _cache.iso_cache, _clamp); // TODO: does not yet account for smooth dielectric - if (!isBSDF && bxdf.materialType == ext::MaterialSystem::Material::DIFFUSE) - { - params = params_type::template create(nee_sample, iso_interaction, bxdf::BCM_MAX); - } - else if (!isBSDF && bxdf.materialType != ext::MaterialSystem::Material::DIFFUSE) - { - if (bxdf.params.is_aniso) - params = params_type::template create(nee_sample, interaction, _cache, bxdf::BCM_MAX); - else - { - isocache_type isocache = _cache.iso_cache; - params = params_type::template create(nee_sample, iso_interaction, isocache, bxdf::BCM_MAX); - } - } - else if (isBSDF && bxdf.materialType == ext::MaterialSystem::Material::DIFFUSE) - { - params = params_type::template create(nee_sample, iso_interaction, bxdf::BCM_ABS); - } - else if (isBSDF && bxdf.materialType != ext::MaterialSystem::Material::DIFFUSE) - { - if (bxdf.params.is_aniso) - params = params_type::template create(nee_sample, interaction, _cache, bxdf::BCM_ABS); - else - { - isocache_type isocache = _cache.iso_cache; - params = params_type::template create(nee_sample, iso_interaction, isocache, bxdf::BCM_ABS); - } - } + // if (!isBSDF && bxdf.materialType == ext::MaterialSystem::Material::DIFFUSE) + // { + // params = params_type::template create(nee_sample, interaction.isotropic, bxdf::BCM_MAX); + // } + // else if (!isBSDF && bxdf.materialType != ext::MaterialSystem::Material::DIFFUSE) + // { + // if (bxdf.params.is_aniso) + // params = params_type::template create(nee_sample, interaction, _cache, bxdf::BCM_MAX); + // else + // { + // isocache_type isocache = _cache.iso_cache; + // params = params_type::template create(nee_sample, interaction.isotropic, _cache.iso_cache, bxdf::BCM_MAX); + // } + // } + // else if (isBSDF && bxdf.materialType == ext::MaterialSystem::Material::DIFFUSE) + // { + // params = params_type::template create(nee_sample, interaction.isotropic, bxdf::BCM_ABS); + // } + // else if (isBSDF && bxdf.materialType != ext::MaterialSystem::Material::DIFFUSE) + // { + // if (bxdf.params.is_aniso) + // params = params_type::template create(nee_sample, interaction, _cache, bxdf::BCM_ABS); + // else + // { + // isocache_type isocache = _cache.iso_cache; + // params = params_type::template create(nee_sample, interaction.isotropic, _cache.iso_cache, bxdf::BCM_ABS); + // } + // } quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(material, bxdf.params, params); bsdf_quotient_pdf.quotient *= bxdf.albedo * throughput; neeContrib_pdf.quotient *= 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 + 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; + // neeContrib_pdf.quotient *= otherGenOverChoice; ray_type nee_ray; nee_ray.origin = intersection + nee_sample.L.direction * t * Tolerance::getStart(depth); @@ -247,7 +252,7 @@ struct Unidirectional } } - return false; // NEE only + //return false; // NEE only // sample BSDF scalar_type bxdfPdf; @@ -259,12 +264,17 @@ struct Unidirectional anisocache_type _cache; sample_type bsdf_sample = materialSystem.generate(material, bxdf.params, interaction, eps1, _cache); + bxdf::BxDFClampMode _clamp; + _clamp = (bxdf.materialType == ext::MaterialSystem::Material::Type::DIELECTRIC) ? bxdf::BxDFClampMode::BCM_ABS : bxdf::BxDFClampMode::BCM_MAX; + // example only uses isotropic bxdfs + params_type params = params_type::template create(bsdf_sample, interaction.isotropic, _cache.iso_cache, _clamp); + // TODO: does not yet account for smooth dielectric - params_type params; - if (!isBSDF && bxdf.materialType == ext::MaterialSystem::Material::DIFFUSE) - { - params = params_type::template create(bsdf_sample, iso_interaction, bxdf::BCM_MAX); - } + // params_type params; + // if (!isBSDF && bxdf.materialType == ext::MaterialSystem::Material::DIFFUSE) + // { + // params = params_type::template create(bsdf_sample, iso_interaction, bxdf::BCM_MAX); + // } // else if (!isBSDF && bxdf.materialType != ext::MaterialSystem::Material::DIFFUSE) // { // if (bxdf.params.is_aniso) @@ -292,7 +302,8 @@ struct Unidirectional // the value of the bsdf divided by the probability of the sample being generated quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(material, bxdf.params, params); - throughput *= bsdf_quotient_pdf.quotient; + throughput *= bxdf.albedo * bsdf_quotient_pdf.quotient; + bxdfPdf = bsdf_quotient_pdf.pdf; bxdfSample = bsdf_sample.L.direction; } @@ -351,7 +362,6 @@ struct Unidirectional hit = ray.objectID.id != -1; if (hit) rayAlive = closestHitProgram(1, i, ray, scene); - } if (!hit) missProgram(ray); diff --git a/31_HLSLPathTracer/imgui.ini b/31_HLSLPathTracer/imgui.ini new file mode 100644 index 000000000..e60624929 --- /dev/null +++ b/31_HLSLPathTracer/imgui.ini @@ -0,0 +1,8 @@ +[Window][Debug##Default] +Pos=60,60 +Size=400,400 + +[Window][Controls] +Pos=10,10 +Size=320,340 + From 6abb635b5d47e689e8f8ad3eb1ef35887ed53df6 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 13 Mar 2025 15:56:08 +0700 Subject: [PATCH 32/53] fixed nan and accumulation going black problem --- .../app_resources/hlsl/common.hlsl | 10 ++++++- .../hlsl/next_event_estimator.hlsl | 6 ++--- .../app_resources/hlsl/pathtracer.hlsl | 22 +++++++--------- .../app_resources/hlsl/render.comp.hlsl | 26 +++---------------- .../app_resources/hlsl/render_common.hlsl | 23 ++++++++++++++++ 31_HLSLPathTracer/main.cpp | 19 +++++++++----- 6 files changed, 61 insertions(+), 45 deletions(-) create mode 100644 31_HLSLPathTracer/app_resources/hlsl/render_common.hlsl diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index ac1e0f09a..9e2249732 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -18,7 +18,7 @@ namespace hlsl namespace ext { -template +template // TODO make type T Spectrum struct Payload { using this_t = Payload; @@ -85,6 +85,14 @@ struct Light NBL_CONSTEXPR_STATIC_INLINE uint32_t INVALID_ID = 0xffffu; + static Light create(NBL_CONST_REF_ARG(spectral_type) radiance, uint32_t objId, uint32_t mode, ProceduralShapeType shapeType) + { + Light retval; + retval.radiance = radiance; + retval.objectID = ObjectID::create(objId, mode, shapeType); + return retval; + } + static Light create(NBL_CONST_REF_ARG(spectral_type) radiance, NBL_CONST_REF_ARG(ObjectID) objectID) { Light retval; diff --git a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl index 65646b3c1..c1528216d 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl @@ -89,7 +89,7 @@ struct Estimator static spectral_type deferredEvalAndPdf(NBL_REF_ARG(scalar_type) pdf, NBL_CONST_REF_ARG(light_type) light, NBL_CONST_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(Event) event) { - const uint32_t mode = event.mode; + const IntersectMode mode = (IntersectMode)event.mode; switch (mode) { case IM_RAY_QUERY: @@ -192,7 +192,7 @@ struct Estimator } newRayMaxT *= Tolerance::getEnd(depth); - pdf *= 1.0 / lightCount; + pdf *= 1.0 / scalar_type(lightCount); spectral_type quo = light.radiance / pdf; quotient_pdf = quotient_pdf_type::create(quo, pdf); @@ -201,7 +201,7 @@ struct Estimator static sample_type generate_and_quotient_and_pdf(NBL_REF_ARG(quotient_pdf_type) quotient_pdf, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(light_type) light, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(interaction_type) interaction, bool isBSDF, NBL_CONST_REF_ARG(vector3_type) xi, uint32_t depth, NBL_CONST_REF_ARG(Event) event) { - const uint32_t mode = event.mode; + const IntersectMode mode = (IntersectMode)event.mode; sample_type L; switch (mode) { diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index 6ed89de7a..5c01db852 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -79,13 +79,12 @@ struct Unidirectional // NextEventEstimator nee) // {} - static this_t create(NBL_CONST_REF_ARG(PathTracerCreationParams) params, Buffer sampleSequence) + static this_t create(NBL_CONST_REF_ARG(PathTracerCreationParams) params) { this_t retval; retval.randGen = randgen_type::create(params.rngState); retval.rayGen = raygen_type::create(params.pixOffsetParam, params.camPos, params.NDC, params.invMVP); retval.materialSystem = material_system_type::create(params.diffuseParams, params.conductorParams, params.dielectricParams); - retval.sampleSequence = sampleSequence; return retval; } @@ -170,14 +169,14 @@ struct Unidirectional scalar_type rcpChoiceProb; if (!math::partitionRandVariable(neeProbability, eps0.z, rcpChoiceProb) && depth < 2u) { + uint32_t randLightID = uint32_t(float32_t(randGen().x) / numeric_limits::max) * scene.lightCount; quotient_pdf_type neeContrib_pdf; scalar_type t; sample_type nee_sample = nee.generate_and_quotient_and_pdf( neeContrib_pdf, t, - scene.lights[lightID], intersection, interaction, - isBSDF, eps0, depth, scene.toNextEvent(lightID) + scene.lights[randLightID], intersection, interaction, + isBSDF, eps0, depth, scene.toNextEvent(randLightID) ); - //printf("%f %f %f\n", nee_sample.L.direction.x, nee_sample.L.direction.y, nee_sample.L.direction.z); // We don't allow non watertight transmitters in this renderer bool validPath = nee_sample.NdotL > numeric_limits::min; @@ -233,8 +232,7 @@ struct Unidirectional // } quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(material, bxdf.params, params); - bsdf_quotient_pdf.quotient *= bxdf.albedo * throughput; - neeContrib_pdf.quotient *= bsdf_quotient_pdf.quotient; + 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 @@ -252,7 +250,7 @@ struct Unidirectional } } - //return false; // NEE only + // return false; // NEE only // sample BSDF scalar_type bxdfPdf; @@ -312,8 +310,8 @@ struct Unidirectional if (bxdfPdf > bxdfPdfThreshold && getLuma(throughput) > lumaThroughputThreshold) { ray.payload.throughput = throughput; - ray.payload.otherTechniqueHeuristic = neeProbability / bxdfPdf; // numerically stable, don't touch - ray.payload.otherTechniqueHeuristic *= ray.payload.otherTechniqueHeuristic; + 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); @@ -354,7 +352,7 @@ struct Unidirectional // bounces bool hit = true; bool rayAlive = true; - for (int d = 1; d <= depth && hit && rayAlive; d += 2) + for (int d = 1; (d <= depth) && hit && rayAlive; d += 2) { ray.intersectionT = numeric_limits::max; ray.objectID = intersector_type::traceRay(ray, scene); @@ -385,8 +383,6 @@ struct Unidirectional raygen_type rayGen; material_system_type materialSystem; nee_type nee; - - Buffer sampleSequence; }; } diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index e25961b56..d19007dd4 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -6,6 +6,7 @@ #include "nbl/builtin/hlsl/bxdf/reflection.hlsl" #include "nbl/builtin/hlsl/bxdf/transmission.hlsl" +#include "render_common.hlsl" #include "pathtracer.hlsl" // add these defines (one at a time) using -D argument to dxc @@ -26,25 +27,6 @@ NBL_CONSTEXPR uint32_t WorkgroupSize = 32; NBL_CONSTEXPR uint32_t MAX_DEPTH_LOG2 = 4; NBL_CONSTEXPR uint32_t MAX_SAMPLES_LOG2 = 10; -struct SPushConstants -{ - float32_t4x4 invMVP; - int sampleCount; - int depth; -}; - -[[vk::push_constant]] SPushConstants pc; - -[[vk::combinedImageSampler]][[vk::binding(0, 2)]] Texture2D envMap; // unused -[[vk::combinedImageSampler]][[vk::binding(0, 2)]] SamplerState envSampler; - -[[vk::binding(1, 2)]] Buffer sampleSequence; - -[[vk::combinedImageSampler]][[vk::binding(2, 2)]] Texture2D scramblebuf; // unused -[[vk::combinedImageSampler]][[vk::binding(2, 2)]] SamplerState scrambleSampler; - -[[vk::image_format("rgba16f")]][[vk::binding(0, 0)]] RWTexture2D outImage; - int32_t2 getCoordinates() { return int32_t2(glsl::gl_GlobalInvocationID().xy); @@ -115,7 +97,7 @@ static const ext::Shape rectangles[RECTANGLE_COUNT] = { #define LIGHT_COUNT 1 static const light_type lights[LIGHT_COUNT] = { - light_type::create(spectral_t(30.0,25.0,15.0), ext::ObjectID::create(8u, ext::IntersectMode::IM_PROCEDURAL, LIGHT_TYPE)) + light_type::create(spectral_t(30.0,25.0,15.0), 8u, ext::IntersectMode::IM_PROCEDURAL, LIGHT_TYPE) }; #define BXDF_COUNT 7 @@ -154,7 +136,7 @@ void main(uint32_t3 threadID : SV_DispatchThreadID) // set up path tracer ext::PathTracer::PathTracerCreationParams ptCreateParams; - ptCreateParams.rngState = pcg(); + ptCreateParams.rngState = scramblebuf[coords].rg; uint2 scrambleDim; scramblebuf.GetDimensions(scrambleDim.x, scrambleDim.y); @@ -174,7 +156,7 @@ void main(uint32_t3 threadID : SV_DispatchThreadID) ptCreateParams.conductorParams = bxdfs[3].params; ptCreateParams.dielectricParams = bxdfs[6].params; - pathtracer_type pathtracer = pathtracer_type::create(ptCreateParams, sampleSequence); + pathtracer_type pathtracer = pathtracer_type::create(ptCreateParams); // set up scene (can do as global var?) ext::Scene scene; diff --git a/31_HLSLPathTracer/app_resources/hlsl/render_common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render_common.hlsl new file mode 100644 index 000000000..5e5cf89da --- /dev/null +++ b/31_HLSLPathTracer/app_resources/hlsl/render_common.hlsl @@ -0,0 +1,23 @@ +#ifndef _NBL_HLSL_PATHTRACER_RENDER_COMMON_INCLUDED_ +#define _NBL_HLSL_PATHTRACER_RENDER_COMMON_INCLUDED_ + +struct SPushConstants +{ + float32_t4x4 invMVP; + int sampleCount; + int depth; +}; + +[[vk::push_constant]] SPushConstants pc; + +[[vk::combinedImageSampler]][[vk::binding(0, 2)]] Texture2D envMap; // unused +[[vk::combinedImageSampler]][[vk::binding(0, 2)]] SamplerState envSampler; + +[[vk::binding(1, 2)]] Buffer sampleSequence; + +[[vk::combinedImageSampler]][[vk::binding(2, 2)]] Texture2D scramblebuf; // unused +[[vk::combinedImageSampler]][[vk::binding(2, 2)]] SamplerState scrambleSampler; + +[[vk::image_format("rgba16f")]][[vk::binding(0, 0)]] RWTexture2D outImage; + +#endif diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index 8da32083e..30a0fad8d 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -74,6 +74,13 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, inline bool isComputeOnly() const override { return false; } + //inline video::IAPIConnection::SFeatures getAPIFeaturesToEnable() override + //{ + // auto retval = device_base_t::getAPIFeaturesToEnable(); + // retval.synchronizationValidation = true; + // return retval; + //} + inline core::vector getSurfaces() const override { if (!m_surface) @@ -359,11 +366,11 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, options.stage = IShader::E_SHADER_STAGE::ESS_COMPUTE; // should be compute options.targetSpirvVersion = m_device->getPhysicalDevice()->getLimits().spirvVersion; options.spirvOptimizer = nullptr; -#ifndef _NBL_DEBUG - ISPIRVOptimizer::E_OPTIMIZER_PASS optPasses = ISPIRVOptimizer::EOP_STRIP_DEBUG_INFO; - auto opt = make_smart_refctd_ptr(std::span(&optPasses, 1)); - options.spirvOptimizer = opt.get(); -#endif +//#ifndef _NBL_DEBUG +// ISPIRVOptimizer::E_OPTIMIZER_PASS optPasses = ISPIRVOptimizer::EOP_STRIP_DEBUG_INFO; +// auto opt = make_smart_refctd_ptr(std::span(&optPasses, 1)); +// options.spirvOptimizer = opt.get(); +//#endif options.debugInfoFlags |= IShaderCompiler::E_DEBUG_INFO_FLAGS::EDIF_SOURCE_BIT; options.preprocessorOptions.sourceIdentifier = source->getFilepathHint(); options.preprocessorOptions.logger = m_logger.get(); @@ -1343,7 +1350,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, int PTPipline = E_LIGHT_GEOMETRY::ELG_SPHERE; int renderMode = E_RENDER_MODE::ERM_HLSL; int spp = 32; - int depth = 3; + int depth = 1; bool m_firstFrame = true; IGPUCommandBuffer::SClearColorValue clearColor = { .float32 = {0.f,0.f,0.f,1.f} }; From 1eee3ca8ade05d2afdd1ae3eeb1033edee372a66 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 13 Mar 2025 16:06:04 +0700 Subject: [PATCH 33/53] fixed triangle light, rectangle needs checking --- 31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index d19007dd4..065d93b7b 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -97,7 +97,13 @@ static const ext::Shape rectangles[RECTANGLE_COUNT] = { #define LIGHT_COUNT 1 static const light_type lights[LIGHT_COUNT] = { - light_type::create(spectral_t(30.0,25.0,15.0), 8u, ext::IntersectMode::IM_PROCEDURAL, LIGHT_TYPE) + light_type::create(spectral_t(30.0,25.0,15.0), +#ifdef SPHERE_LIGHT + 8u, +#else + 0u, +#endif + ext::IntersectMode::IM_PROCEDURAL, LIGHT_TYPE) }; #define BXDF_COUNT 7 From 011fbfb376ef9d926b74960a05a5bfcfaf851fbf Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 13 Mar 2025 16:24:12 +0700 Subject: [PATCH 34/53] simplified material data --- .../app_resources/hlsl/material_system.hlsl | 61 +++++++++++-------- .../app_resources/hlsl/pathtracer.hlsl | 20 +++--- .../app_resources/hlsl/render.comp.hlsl | 14 ++--- 31_HLSLPathTracer/main.cpp | 2 +- 4 files changed, 49 insertions(+), 48 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl index 0d739d9ec..feffee9ef 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl @@ -14,20 +14,27 @@ namespace ext namespace MaterialSystem { -struct Material -{ - enum Type : uint32_t // enum class? - { - DIFFUSE, - CONDUCTOR, - DIELECTRIC - }; +// struct Material +// { +// enum Type : uint32_t // enum class? +// { +// DIFFUSE, +// CONDUCTOR, +// DIELECTRIC +// }; - NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 1; +// NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 1; - uint32_t type : 2; - uint32_t unused : 30; // possible space for flags - uint32_t data[DataSize]; +// uint32_t type : 2; +// uint32_t unused : 30; // possible space for flags +// uint32_t data[DataSize]; +// }; + +enum MaterialType : uint32_t // enum class? +{ + DIFFUSE, + CONDUCTOR, + DIELECTRIC }; template // NOTE: these bxdfs should match the ones in Scene BxDFNode @@ -59,23 +66,23 @@ struct System return retval; } - measure_type eval(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, NBL_CONST_REF_ARG(params_t) params) + measure_type eval(uint32_t material, NBL_CONST_REF_ARG(create_params_t) cparams, NBL_CONST_REF_ARG(params_t) params) { - switch(material.type) + switch(material) { - case Material::Type::DIFFUSE: + case MaterialType::DIFFUSE: { diffuseBxDF.init(cparams); return (measure_type)diffuseBxDF.eval(params); } break; - case Material::Type::CONDUCTOR: + case MaterialType::CONDUCTOR: { conductorBxDF.init(cparams); return conductorBxDF.eval(params); } break; - case Material::Type::DIELECTRIC: + case MaterialType::DIELECTRIC: { dielectricBxDF.init(cparams); return dielectricBxDF.eval(params); @@ -86,23 +93,23 @@ struct System } } - sample_type generate(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, NBL_CONST_REF_ARG(anisotropic_type) interaction, NBL_CONST_REF_ARG(vector3_type) u, NBL_REF_ARG(anisocache_type) _cache) + sample_type generate(uint32_t material, NBL_CONST_REF_ARG(create_params_t) cparams, NBL_CONST_REF_ARG(anisotropic_type) interaction, NBL_CONST_REF_ARG(vector3_type) u, NBL_REF_ARG(anisocache_type) _cache) { - switch(material.type) + switch(material) { - case Material::Type::DIFFUSE: + case MaterialType::DIFFUSE: { diffuseBxDF.init(cparams); return diffuseBxDF.generate(interaction, u.xy); } break; - case Material::Type::CONDUCTOR: + case MaterialType::CONDUCTOR: { conductorBxDF.init(cparams); return conductorBxDF.generate(interaction, u.xy, _cache); } break; - case Material::Type::DIELECTRIC: + case MaterialType::DIELECTRIC: { dielectricBxDF.init(cparams); return dielectricBxDF.generate(interaction, u, _cache); @@ -121,26 +128,26 @@ struct System return sample_type::create(L, 0, (vector3_type)0); } - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(Material) material, NBL_CONST_REF_ARG(create_params_t) cparams, NBL_CONST_REF_ARG(params_t) params) + quotient_pdf_type quotient_and_pdf(uint32_t material, NBL_CONST_REF_ARG(create_params_t) cparams, NBL_CONST_REF_ARG(params_t) params) { const float minimumProjVectorLen = 0.00000001; if (params.NdotV > minimumProjVectorLen && params.NdotL > minimumProjVectorLen) { - switch(material.type) + switch(material) { - case Material::Type::DIFFUSE: + case MaterialType::DIFFUSE: { diffuseBxDF.init(cparams); return diffuseBxDF.quotient_and_pdf(params); } break; - case Material::Type::CONDUCTOR: + case MaterialType::CONDUCTOR: { conductorBxDF.init(cparams); return conductorBxDF.quotient_and_pdf(params); } break; - case Material::Type::DIELECTRIC: + case MaterialType::DIELECTRIC: { dielectricBxDF.init(cparams); return dielectricBxDF.quotient_and_pdf(params); diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index 5c01db852..553094e21 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -150,8 +150,8 @@ struct Unidirectional // TODO: ifdef kill diffuse specular paths - const bool isBSDF = (bxdf.materialType == ext::MaterialSystem::Material::Type::DIFFUSE) ? bxdf_traits::type == BT_BSDF : - (bxdf.materialType == ext::MaterialSystem::Material::Type::CONDUCTOR) ? bxdf_traits::type == BT_BSDF : + const bool isBSDF = (bxdf.materialType == ext::MaterialSystem::MaterialType::DIFFUSE) ? bxdf_traits::type == BT_BSDF : + (bxdf.materialType == ext::MaterialSystem::MaterialType::CONDUCTOR) ? bxdf_traits::type == BT_BSDF : bxdf_traits::type == BT_BSDF; vector3_type eps0 = rand3d(depth, _sample, 0u); @@ -193,11 +193,8 @@ struct Unidirectional ray.payload.accumulation += vector3_type(0.f, 1000.f, 0.f); else if (validPath) { - ext::MaterialSystem::Material material; - material.type = bxdf.materialType; - bxdf::BxDFClampMode _clamp; - _clamp = (bxdf.materialType == ext::MaterialSystem::Material::Type::DIELECTRIC) ? bxdf::BxDFClampMode::BCM_ABS : bxdf::BxDFClampMode::BCM_MAX; + _clamp = (bxdf.materialType == ext::MaterialSystem::MaterialType::DIELECTRIC) ? bxdf::BxDFClampMode::BCM_ABS : bxdf::BxDFClampMode::BCM_MAX; // example only uses isotropic bxdfs params_type params = params_type::template create(nee_sample, interaction.isotropic, _cache.iso_cache, _clamp); @@ -231,7 +228,7 @@ struct Unidirectional // } // } - quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(material, bxdf.params, params); + quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(bxdf.materialType, bxdf.params, params); 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; @@ -256,14 +253,11 @@ struct Unidirectional scalar_type bxdfPdf; vector3_type bxdfSample; { - ext::MaterialSystem::Material material; - material.type = bxdf.materialType; - anisocache_type _cache; - sample_type bsdf_sample = materialSystem.generate(material, bxdf.params, interaction, eps1, _cache); + sample_type bsdf_sample = materialSystem.generate(bxdf.materialType, bxdf.params, interaction, eps1, _cache); bxdf::BxDFClampMode _clamp; - _clamp = (bxdf.materialType == ext::MaterialSystem::Material::Type::DIELECTRIC) ? bxdf::BxDFClampMode::BCM_ABS : bxdf::BxDFClampMode::BCM_MAX; + _clamp = (bxdf.materialType == ext::MaterialSystem::MaterialType::DIELECTRIC) ? bxdf::BxDFClampMode::BCM_ABS : bxdf::BxDFClampMode::BCM_MAX; // example only uses isotropic bxdfs params_type params = params_type::template create(bsdf_sample, interaction.isotropic, _cache.iso_cache, _clamp); @@ -299,7 +293,7 @@ struct Unidirectional // } // the value of the bsdf divided by the probability of the sample being generated - quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(material, bxdf.params, params); + quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(bxdf.materialType, bxdf.params, params); throughput *= bxdf.albedo * bsdf_quotient_pdf.quotient; bxdfPdf = bsdf_quotient_pdf.pdf; bxdfSample = bsdf_sample.L.direction; diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index 065d93b7b..5dea2d1bf 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -108,13 +108,13 @@ static const light_type lights[LIGHT_COUNT] = { #define BXDF_COUNT 7 static const bxdfnode_type bxdfs[BXDF_COUNT] = { - bxdfnode_type::create(ext::MaterialSystem::Material::Type::DIFFUSE, false, float2(0,0), spectral_t(0.8,0.8,0.8)), - bxdfnode_type::create(ext::MaterialSystem::Material::Type::DIFFUSE, false, float2(0,0), spectral_t(0.8,0.4,0.4)), - bxdfnode_type::create(ext::MaterialSystem::Material::Type::DIFFUSE, false, float2(0,0), spectral_t(0.4,0.8,0.4)), - bxdfnode_type::create(ext::MaterialSystem::Material::Type::CONDUCTOR, false, float2(0,0), spectral_t(1,1,1), spectral_t(0.98,0.98,0.77)), - bxdfnode_type::create(ext::MaterialSystem::Material::Type::CONDUCTOR, false, float2(0,0), spectral_t(1,1,1), spectral_t(0.98,0.77,0.98)), - bxdfnode_type::create(ext::MaterialSystem::Material::Type::CONDUCTOR, false, float2(0.15,0.15), spectral_t(1,1,1), spectral_t(0.98,0.77,0.98)), - bxdfnode_type::create(ext::MaterialSystem::Material::Type::DIELECTRIC, false, float2(0.0625,0.0625), spectral_t(1,1,1), spectral_t(0.71,0.69,0.67)) + bxdfnode_type::create(ext::MaterialSystem::MaterialType::DIFFUSE, false, float2(0,0), spectral_t(0.8,0.8,0.8)), + bxdfnode_type::create(ext::MaterialSystem::MaterialType::DIFFUSE, false, float2(0,0), spectral_t(0.8,0.4,0.4)), + bxdfnode_type::create(ext::MaterialSystem::MaterialType::DIFFUSE, false, float2(0,0), spectral_t(0.4,0.8,0.4)), + bxdfnode_type::create(ext::MaterialSystem::MaterialType::CONDUCTOR, false, float2(0,0), spectral_t(1,1,1), spectral_t(0.98,0.98,0.77)), + bxdfnode_type::create(ext::MaterialSystem::MaterialType::CONDUCTOR, false, float2(0,0), spectral_t(1,1,1), spectral_t(0.98,0.77,0.98)), + bxdfnode_type::create(ext::MaterialSystem::MaterialType::CONDUCTOR, false, float2(0.15,0.15), spectral_t(1,1,1), spectral_t(0.98,0.77,0.98)), + bxdfnode_type::create(ext::MaterialSystem::MaterialType::DIELECTRIC, false, float2(0.0625,0.0625), spectral_t(1,1,1), spectral_t(0.71,0.69,0.67)) }; [numthreads(WorkgroupSize, WorkgroupSize, 1)] diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index 30a0fad8d..46597d738 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -1350,7 +1350,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, int PTPipline = E_LIGHT_GEOMETRY::ELG_SPHERE; int renderMode = E_RENDER_MODE::ERM_HLSL; int spp = 32; - int depth = 1; + int depth = 3; bool m_firstFrame = true; IGPUCommandBuffer::SClearColorValue clearColor = { .float32 = {0.f,0.f,0.f,1.f} }; From 63b64e3182dc395d83ada3fe95f46b5febc41d29 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 14 Mar 2025 11:14:01 +0700 Subject: [PATCH 35/53] made scene a static global var --- .../app_resources/hlsl/render.comp.hlsl | 70 +++++++++---------- .../app_resources/hlsl/scene.hlsl | 67 +++++++++++++++--- 2 files changed, 91 insertions(+), 46 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index 5dea2d1bf..f8cf2ae22 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -6,9 +6,6 @@ #include "nbl/builtin/hlsl/bxdf/reflection.hlsl" #include "nbl/builtin/hlsl/bxdf/transmission.hlsl" -#include "render_common.hlsl" -#include "pathtracer.hlsl" - // add these defines (one at a time) using -D argument to dxc // #define SPHERE_LIGHT // #define TRIANGLE_LIGHT @@ -17,10 +14,33 @@ #ifdef SPHERE_LIGHT #define SPHERE_COUNT 9 #define LIGHT_TYPE ext::PST_SPHERE -#else + +#define TRIANGLE_COUNT 0 +#define RECTANGLE_COUNT 0 +#endif + +#ifdef TRIANGLE_LIGHT +#define TRIANGLE_COUNT 1 +#define LIGHT_TYPE ext::PST_TRIANGLE + +#define SPHERE_COUNT 8 +#define RECTANGLE_COUNT 0 +#endif + +#ifdef RECTANGLE_LIGHT +#define RECTANGLE_COUNT 1 +#define LIGHT_TYPE ext::PST_RECTANGLE + #define SPHERE_COUNT 8 +#define TRIANGLE_COUNT 0 #endif +#define LIGHT_COUNT 1 +#define BXDF_COUNT 7 + +#include "render_common.hlsl" +#include "pathtracer.hlsl" + using namespace nbl::hlsl; NBL_CONSTEXPR uint32_t WorkgroupSize = 32; @@ -80,22 +100,21 @@ static const ext::Shape spheres[SPHERE_COUNT] = { }; #ifdef TRIANGLE_LIGHT -#define LIGHT_TYPE ext::PST_TRIANGLE -#define TRIANGLE_COUNT 1 static const ext::Shape triangles[TRIANGLE_COUNT] = { ext::Shape::create(float3(-1.8,0.35,0.3) * 10.0, float3(-1.2,0.35,0.0) * 10.0, float3(-1.5,0.8,-0.3) * 10.0, bxdfnode_type::INVALID_ID, 0u) }; +#else +static const ext::Shape triangles[1]; #endif #ifdef RECTANGLE_LIGHT -#define LIGHT_TYPE ext::PST_RECTANGLE -#define RECTANGLE_COUNT 1 static const ext::Shape rectangles[RECTANGLE_COUNT] = { ext::Shape::create(float3(-3.8,0.35,1.3), normalize(float3(2,0,-1))*7.0, normalize(float3(2,-5,4))*0.1, bxdfnode_type::INVALID_ID, 0u) }; +#else +static const ext::Shape rectangles[1]; #endif -#define LIGHT_COUNT 1 static const light_type lights[LIGHT_COUNT] = { light_type::create(spectral_t(30.0,25.0,15.0), #ifdef SPHERE_LIGHT @@ -106,7 +125,6 @@ static const light_type lights[LIGHT_COUNT] = { ext::IntersectMode::IM_PROCEDURAL, LIGHT_TYPE) }; -#define BXDF_COUNT 7 static const bxdfnode_type bxdfs[BXDF_COUNT] = { bxdfnode_type::create(ext::MaterialSystem::MaterialType::DIFFUSE, false, float2(0,0), spectral_t(0.8,0.8,0.8)), bxdfnode_type::create(ext::MaterialSystem::MaterialType::DIFFUSE, false, float2(0,0), spectral_t(0.8,0.4,0.4)), @@ -117,6 +135,12 @@ static const bxdfnode_type bxdfs[BXDF_COUNT] = { bxdfnode_type::create(ext::MaterialSystem::MaterialType::DIELECTRIC, false, float2(0.0625,0.0625), spectral_t(1,1,1), spectral_t(0.71,0.69,0.67)) }; +static const ext::Scene scene = ext::Scene::create( + spheres, triangles, rectangles, + SPHERE_COUNT, TRIANGLE_COUNT, RECTANGLE_COUNT, + lights, LIGHT_COUNT, bxdfs, BXDF_COUNT +); + [numthreads(WorkgroupSize, WorkgroupSize, 1)] void main(uint32_t3 threadID : SV_DispatchThreadID) { @@ -164,32 +188,6 @@ void main(uint32_t3 threadID : SV_DispatchThreadID) pathtracer_type pathtracer = pathtracer_type::create(ptCreateParams); - // set up scene (can do as global var?) - ext::Scene scene; - scene.sphereCount = SPHERE_COUNT; - for (uint32_t i = 0; i < SPHERE_COUNT; i++) - scene.spheres[i] = spheres[i]; -#ifdef TRIANGLE_LIGHT - scene.triangleCount = TRIANGLE_COUNT; - for (uint32_t i = 0; i < TRIANGLE_COUNT; i++) - scene.triangles[i] = triangles[i]; -#else - scene.triangleCount = 0; -#endif -#ifdef RECTANGLE_LIGHT - scene.rectangleCount = RECTANGLE_COUNT; - for (uint32_t i = 0; i < RECTANGLE_COUNT; i++) - scene.rectangles[i] = rectangles[i]; -#else - scene.rectangleCount = 0; -#endif - scene.lightCount = LIGHT_COUNT; - for (uint32_t i = 0; i < LIGHT_COUNT; i++) - scene.lights[i] = lights[i]; - scene.bxdfCount = BXDF_COUNT; - for (uint32_t i = 0; i < BXDF_COUNT; i++) - scene.bxdfs[i] = bxdfs[i]; - float32_t3 color = pathtracer.getMeasure(pc.sampleCount, pc.depth, scene); float32_t4 pixCol = float32_t4(color, 1.0); outImage[coords] = pixCol; diff --git a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl index 5b4178ec4..887d20c48 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl @@ -15,31 +15,78 @@ struct Scene { using light_type = Light; using bxdfnode_type = BxdfNode; + using this_t = Scene; - NBL_CONSTEXPR_STATIC_INLINE uint32_t maxSphereCount = 25; - NBL_CONSTEXPR_STATIC_INLINE uint32_t maxTriangleCount = 12; - NBL_CONSTEXPR_STATIC_INLINE uint32_t maxRectangleCount = 12; + // NBL_CONSTEXPR_STATIC_INLINE uint32_t maxSphereCount = 25; + // NBL_CONSTEXPR_STATIC_INLINE uint32_t maxTriangleCount = 12; + // NBL_CONSTEXPR_STATIC_INLINE uint32_t maxRectangleCount = 12; - Shape spheres[maxSphereCount]; - Shape triangles[maxTriangleCount]; - Shape rectangles[maxRectangleCount]; +#if SPHERE_COUNT < 1 +#define SCENE_SPHERE_COUNT 1 +#else +#define SCENE_SPHERE_COUNT SPHERE_COUNT +#endif + +#if TRIANGLE_COUNT < 1 +#define SCENE_TRIANGLE_COUNT 1 +#else +#define SCENE_TRIANGLE_COUNT TRIANGLE_COUNT +#endif + +#if RECTANGLE_COUNT < 1 +#define SCENE_RECTANGLE_COUNT 1 +#else +#define SCENE_RECTANGLE_COUNT RECTANGLE_COUNT +#endif + + Shape spheres[SCENE_SPHERE_COUNT]; + Shape triangles[SCENE_TRIANGLE_COUNT]; + Shape rectangles[SCENE_RECTANGLE_COUNT]; uint32_t sphereCount; uint32_t triangleCount; uint32_t rectangleCount; - NBL_CONSTEXPR_STATIC_INLINE uint32_t maxLightCount = 4; + // NBL_CONSTEXPR_STATIC_INLINE uint32_t maxLightCount = 4; - light_type lights[maxLightCount]; + light_type lights[LIGHT_COUNT]; uint32_t lightCount; - NBL_CONSTEXPR_STATIC_INLINE uint32_t maxBxdfCount = 16; // TODO: limit change? + // NBL_CONSTEXPR_STATIC_INLINE uint32_t maxBxdfCount = 16; - bxdfnode_type bxdfs[maxBxdfCount]; + bxdfnode_type bxdfs[BXDF_COUNT]; uint32_t bxdfCount; // AS ases; + static this_t create( + NBL_CONST_REF_ARG(Shape) spheres[SCENE_SPHERE_COUNT], + NBL_CONST_REF_ARG(Shape) triangles[SCENE_TRIANGLE_COUNT], + NBL_CONST_REF_ARG(Shape) rectangles[SCENE_RECTANGLE_COUNT], + uint32_t sphereCount, uint32_t triangleCount, uint32_t rectangleCount, + NBL_CONST_REF_ARG(light_type) lights[LIGHT_COUNT], uint32_t lightCount, + NBL_CONST_REF_ARG(bxdfnode_type) bxdfs[BXDF_COUNT], uint32_t bxdfCount) + { + this_t retval; + retval.spheres = spheres; + retval.triangles = triangles; + retval.rectangles = rectangles; + retval.sphereCount = sphereCount; + retval.triangleCount = triangleCount; + retval.rectangleCount = rectangleCount; + + retval.lights = lights; + retval.lightCount = lightCount; + + retval.bxdfs = bxdfs; + retval.bxdfCount = bxdfCount; + return retval; + } + +#undef SCENE_SPHERE_COUNT +#undef SCENE_TRIANGLE_COUNT +#undef SCENE_RECTANGLE_COUNT + // obsolete? // Intersector::IntersectData toIntersectData(uint32_t mode, ProceduralShapeType type) // { From 7bd69e96d5512998df8efd85e5cdac33e1bde18d Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 14 Mar 2025 16:56:18 +0700 Subject: [PATCH 36/53] fixed most of rectangle light issues, still red pixels --- .../app_resources/hlsl/common.hlsl | 23 +++-- .../hlsl/next_event_estimator.hlsl | 86 +++++++++---------- 31_HLSLPathTracer/main.cpp | 4 +- 3 files changed, 56 insertions(+), 57 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index 9e2249732..28261a634 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -388,7 +388,7 @@ struct Shape sampling::ProjectedSphericalTriangle pst = sampling::ProjectedSphericalTriangle::create(st); const float pdf = pst.pdf(ray.normalAtOrigin, ray.wasBSDFAtOrigin, 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::max ? pdf : 0.0; + return pdf < numeric_limits::max ? pdf : numeric_limits::max; } break; default: @@ -427,7 +427,7 @@ struct Shape const float32_t3 L = sst.generate(rcpPdf, xi.xy); - pdf = rcpPdf > numeric_limits::min ? (1.0 / rcpPdf) : 0.0; + pdf = rcpPdf > numeric_limits::min ? (1.0 / rcpPdf) : numeric_limits::max; const float32_t3 N = getNormalTimesArea(); newRayMaxT = hlsl::dot(N, vertex0 - origin) / hlsl::dot(N, L); @@ -443,7 +443,7 @@ struct Shape const float32_t3 L = sst.generate(rcpPdf, interaction.isotropic.N, isBSDF, xi.xy); - pdf = rcpPdf > numeric_limits::min ? (1.0 / rcpPdf) : 0.0; + pdf = rcpPdf > numeric_limits::min ? (1.0 / rcpPdf) : numeric_limits::max; const float32_t3 N = getNormalTimesArea(); newRayMaxT = hlsl::dot(N, vertex0 - origin) / hlsl::dot(N, L); @@ -513,8 +513,6 @@ struct Shape basis[0] = edge0 / extents[0]; basis[1] = edge1 / extents[1]; basis[2] = normalize(cross(basis[0],basis[1])); - - basis = nbl::hlsl::transpose(basis); // TODO: double check transpose } template @@ -541,17 +539,18 @@ struct Shape if (solidAngle > numeric_limits::min) pdf = 1.f / solidAngle; else - pdf = numeric_limits::infinity; + pdf = bit_cast(numeric_limits::infinity); return pdf; } break; case PPM_APPROX_PROJECTED_SOLID_ANGLE: { - return numeric_limits::infinity; + // currently broken + return bit_cast(numeric_limits::infinity); } break; default: - return numeric_limits::infinity; + return bit_cast(numeric_limits::infinity); } } @@ -577,7 +576,6 @@ struct Shape // #ifdef TRIANGLE_REFERENCE ? case PPM_SOLID_ANGLE: { - float pdf; float32_t3x3 rectNormalBasis; float32_t2 rectExtents; getNormalBasis(rectNormalBasis, rectExtents); @@ -594,7 +592,7 @@ struct Shape pdf = 1.f / solidAngle; } else - pdf = numeric_limits::infinity; + pdf = bit_cast(numeric_limits::infinity); newRayMaxT = hlsl::dot(N, origin2origin) / hlsl::dot(N, L); return L; @@ -602,12 +600,13 @@ struct Shape break; case PPM_APPROX_PROJECTED_SOLID_ANGLE: { - pdf = numeric_limits::infinity; + // currently broken + pdf = bit_cast(numeric_limits::infinity); return (float32_t3)0.0; } break; default: - pdf = numeric_limits::infinity; + pdf = bit_cast(numeric_limits::infinity); return (float32_t3)0.0; } } diff --git a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl index c1528216d..9c41f6627 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl @@ -89,28 +89,28 @@ struct Estimator static spectral_type deferredEvalAndPdf(NBL_REF_ARG(scalar_type) pdf, NBL_CONST_REF_ARG(light_type) light, NBL_CONST_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(Event) event) { - const IntersectMode mode = (IntersectMode)event.mode; - switch (mode) - { - case IM_RAY_QUERY: - { - // TODO: do ray query stuff - } - break; - case IM_RAY_TRACING: - { - // TODO: do ray tracing stuff - } - break; - case IM_PROCEDURAL: - { + // const IntersectMode mode = (IntersectMode)event.mode; + // switch (mode) + // { + // case IM_RAY_QUERY: + // { + // // TODO: do ray query stuff + // } + // break; + // case IM_RAY_TRACING: + // { + // // TODO: do ray tracing stuff + // } + // break; + // case IM_PROCEDURAL: + // { return proceduralDeferredEvalAndPdf(pdf, light, ray, event); - } - break; - default: - return (spectral_type)0.0; - } - return (spectral_type)0.0; + // } + // break; + // default: + // return (spectral_type)0.0; + // } + // return (spectral_type)0.0; } static sample_type procedural_generate_and_quotient_and_pdf(NBL_REF_ARG(quotient_pdf_type) quotient_pdf, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(light_type) light, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(interaction_type) interaction, bool isBSDF, NBL_CONST_REF_ARG(vector3_type) xi, uint32_t depth, NBL_CONST_REF_ARG(Event) event) @@ -203,29 +203,29 @@ struct Estimator { const IntersectMode mode = (IntersectMode)event.mode; sample_type L; - switch (mode) - { - case IM_RAY_QUERY: - { - // TODO: do ray query stuff - } - break; - case IM_RAY_TRACING: - { - // TODO: do ray tracing stuff - } - break; - case IM_PROCEDURAL: - { + // switch (mode) + // { + // case IM_RAY_QUERY: + // { + // // TODO: do ray query stuff + // } + // break; + // case IM_RAY_TRACING: + // { + // // TODO: do ray tracing stuff + // } + // break; + // case IM_PROCEDURAL: + // { return procedural_generate_and_quotient_and_pdf(quotient_pdf, newRayMaxT, light, origin, interaction, isBSDF, xi, depth, event); - } - break; - default: - { - return L; - } - } - return L; + // } + // break; + // default: + // { + // return L; + // } + // } + // return L; } }; diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index 46597d738..b8e3ea044 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -1347,10 +1347,10 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, float viewWidth = 10.f; float camYAngle = 165.f / 180.f * 3.14159f; float camXAngle = 32.f / 180.f * 3.14159f; - int PTPipline = E_LIGHT_GEOMETRY::ELG_SPHERE; + int PTPipline = E_LIGHT_GEOMETRY::ELG_RECTANGLE; int renderMode = E_RENDER_MODE::ERM_HLSL; int spp = 32; - int depth = 3; + int depth = 1; bool m_firstFrame = true; IGPUCommandBuffer::SClearColorValue clearColor = { .float32 = {0.f,0.f,0.f,1.f} }; From ab0aa1231e1fd0eb24ade01a918f139a7c6f758a Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 17 Mar 2025 13:56:17 +0700 Subject: [PATCH 37/53] fix for nan samples --- 31_HLSLPathTracer/app_resources/hlsl/common.hlsl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index 28261a634..d5cbbea81 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -588,7 +588,8 @@ struct Shape if (solidAngle > numeric_limits::min) { float32_t3 sph_sample = sphUv[0] * edge0 + sphUv[1] * edge1 + offset; - L = nbl::hlsl::normalize(sph_sample - origin); + L = sph_sample - origin; + L = hlsl::mix(nbl::hlsl::normalize(L), (float32_t3)0.0, hlsl::abs(L) > (float32_t3)numeric_limits::min); // TODO? sometimes L is vec3(0), find cause pdf = 1.f / solidAngle; } else From 96c7497430a12d064c932e53644b85a7b1984e1d Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 17 Mar 2025 14:07:21 +0700 Subject: [PATCH 38/53] revert to intial scene settings --- 31_HLSLPathTracer/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index b8e3ea044..46597d738 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -1347,10 +1347,10 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, float viewWidth = 10.f; float camYAngle = 165.f / 180.f * 3.14159f; float camXAngle = 32.f / 180.f * 3.14159f; - int PTPipline = E_LIGHT_GEOMETRY::ELG_RECTANGLE; + int PTPipline = E_LIGHT_GEOMETRY::ELG_SPHERE; int renderMode = E_RENDER_MODE::ERM_HLSL; int spp = 32; - int depth = 1; + int depth = 3; bool m_firstFrame = true; IGPUCommandBuffer::SClearColorValue clearColor = { .float32 = {0.f,0.f,0.f,1.f} }; From b483aa6b7474526bb6c89e05c1965ff43880cfde Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 19 Mar 2025 10:42:37 +0700 Subject: [PATCH 39/53] better hlsl dispatch --- .../app_resources/hlsl/render.comp.hlsl | 8 ++++--- 31_HLSLPathTracer/main.cpp | 21 +++++++++++-------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index f8cf2ae22..b54f5721d 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -43,13 +43,15 @@ using namespace nbl::hlsl; -NBL_CONSTEXPR uint32_t WorkgroupSize = 32; +NBL_CONSTEXPR uint32_t WorkgroupSize = 256; NBL_CONSTEXPR uint32_t MAX_DEPTH_LOG2 = 4; NBL_CONSTEXPR uint32_t MAX_SAMPLES_LOG2 = 10; int32_t2 getCoordinates() { - return int32_t2(glsl::gl_GlobalInvocationID().xy); + uint32_t width, height; + outImage.GetDimensions(width, height); + return int32_t2(glsl::gl_GlobalInvocationID().x % width, glsl::gl_GlobalInvocationID().x / width); } float32_t2 getTexCoords() @@ -141,7 +143,7 @@ static const ext::Scene scene = ext::ScenegetPhysicalDevice()->getLimits().spirvVersion; options.spirvOptimizer = nullptr; -//#ifndef _NBL_DEBUG -// ISPIRVOptimizer::E_OPTIMIZER_PASS optPasses = ISPIRVOptimizer::EOP_STRIP_DEBUG_INFO; -// auto opt = make_smart_refctd_ptr(std::span(&optPasses, 1)); -// options.spirvOptimizer = opt.get(); -//#endif - options.debugInfoFlags |= IShaderCompiler::E_DEBUG_INFO_FLAGS::EDIF_SOURCE_BIT; +#ifndef _NBL_DEBUG + ISPIRVOptimizer::E_OPTIMIZER_PASS optPasses = ISPIRVOptimizer::EOP_STRIP_DEBUG_INFO; + auto opt = make_smart_refctd_ptr(std::span(&optPasses, 1)); + options.spirvOptimizer = opt.get(); +#endif + options.debugInfoFlags = IShaderCompiler::E_DEBUG_INFO_FLAGS::EDIF_NONE; options.preprocessorOptions.sourceIdentifier = source->getFilepathHint(); options.preprocessorOptions.logger = m_logger.get(); options.preprocessorOptions.includeFinder = compiler->getDefaultIncludeFinder(); @@ -418,8 +418,8 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, params.shader.shader = ptShader.get(); params.shader.entryPoint = "main"; params.shader.entries = nullptr; - params.shader.requireFullSubgroups = true; - params.shader.requiredSubgroupSize = static_cast(5); + params.shader.requireFullSubgroups = true; + params.shader.requiredSubgroupSize = static_cast(5); if (!m_device->createComputePipelines(nullptr, { ¶ms, 1 }, m_PTGLSLPipelines.data() + index)) return logFail("Failed to create GLSL compute pipeline!\n"); } @@ -1068,7 +1068,10 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, cmdbuf->bindDescriptorSets(EPBP_COMPUTE, pipeline->getLayout(), 0u, 1u, &m_descriptorSet0.get()); cmdbuf->bindDescriptorSets(EPBP_COMPUTE, pipeline->getLayout(), 2u, 1u, &m_descriptorSet2.get()); cmdbuf->pushConstants(pipeline->getLayout(), IShader::E_SHADER_STAGE::ESS_COMPUTE, 0, sizeof(PTPushConstant), &pc); - cmdbuf->dispatch(1 + (WindowDimensions.x - 1) / DefaultWorkGroupSize, 1 + (WindowDimensions.y - 1) / DefaultWorkGroupSize, 1u); + if (renderMode == E_RENDER_MODE::ERM_HLSL) + cmdbuf->dispatch(1 + (WindowDimensions.x * WindowDimensions.y - 1) / 256u, 1u, 1u); + else + cmdbuf->dispatch(1 + (WindowDimensions.x - 1) / DefaultWorkGroupSize, 1 + (WindowDimensions.y - 1) / DefaultWorkGroupSize, 1u); } // TRANSITION m_outImgView to READ (because of descriptorSets0 -> ComputeShader Writes into the image) From 773733d3bed7f17073ff02af29d16700767988a9 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 19 Mar 2025 15:23:06 +0700 Subject: [PATCH 40/53] refactor NEE to use templated light types and sampling --- .../app_resources/hlsl/common.hlsl | 272 +------- .../hlsl/next_event_estimator.hlsl | 579 ++++++++++++------ .../app_resources/hlsl/pathtracer.hlsl | 75 +-- .../app_resources/hlsl/render.comp.hlsl | 22 +- .../app_resources/hlsl/scene.hlsl | 139 ----- 31_HLSLPathTracer/main.cpp | 9 +- 6 files changed, 413 insertions(+), 683 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index d5cbbea81..dea682c8b 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -35,6 +35,7 @@ struct Payload enum ProceduralShapeType : uint16_t { + PST_NONE = 0, PST_SPHERE, PST_TRIANGLE, PST_RECTANGLE @@ -173,33 +174,6 @@ enum PTPolygonMethod : uint16_t PPM_APPROX_PROJECTED_SOLID_ANGLE }; -// namespace Intersector -// { -// // ray query method -// // ray query struct holds AS info -// // pass in address to vertex/index buffers? - -// // ray tracing pipeline method - -// // procedural data store: [obj count] [intersect type] [obj1] [obj2] [...] - -// struct IntersectData -// { -// enum Mode : uint32_t // enum class? -// { -// RAY_QUERY, -// RAY_TRACING, -// PROCEDURAL -// }; - -// NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 128; - -// uint32_t mode : 2; -// uint32_t unused : 30; // possible space for flags -// uint32_t data[DataSize]; -// }; -// } - enum IntersectMode : uint32_t { IM_RAY_QUERY, @@ -207,20 +181,6 @@ enum IntersectMode : uint32_t IM_PROCEDURAL }; -namespace NextEventEstimator -{ -// procedural data store: [light count] [event type] [obj] - -struct Event -{ - NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 16; - - uint32_t mode : 2; - uint32_t unused : 30; // possible space for flags - uint32_t data[DataSize]; -}; -} - template struct Shape; @@ -269,45 +229,6 @@ struct Shape return 2.0 * numbers::pi * (1.0 - cosThetaMax); } - template - float deferredPdf(NBL_CONST_REF_ARG(Ray) ray) - { - return 1.0 / getSolidAngle(ray.origin); - } - - template - float32_t3 generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, NBL_CONST_REF_ARG(float32_t3) xi) - { - float32_t3 Z = position - origin; - const float distanceSQ = hlsl::dot(Z,Z); - const float cosThetaMax2 = 1.0 - radius2 / distanceSQ; - if (cosThetaMax2 > 0.0) - { - const float rcpDistance = 1.0 / hlsl::sqrt(distanceSQ); - Z *= rcpDistance; - - const float cosThetaMax = hlsl::sqrt(cosThetaMax2); - const float cosTheta = hlsl::mix(1.0, cosThetaMax, xi.x); - - float32_t3 L = Z * cosTheta; - - const float cosTheta2 = cosTheta * cosTheta; - const float sinTheta = hlsl::sqrt(1.0 - cosTheta2); - float sinPhi, cosPhi; - math::sincos(2.0 * numbers::pi * xi.y - numbers::pi, sinPhi, cosPhi); - float32_t3 X, Y; - math::frisvad(Z, X, Y); - - L += (X * cosPhi + Y * sinPhi) * sinTheta; - - newRayMaxT = (cosTheta - hlsl::sqrt(cosTheta2 - cosThetaMax2)) / rcpDistance; - pdf = 1.0 / (2.0 * numbers::pi * (1.0 - cosThetaMax)); - return L; - } - pdf = 0.0; - return float32_t3(0.0,0.0,0.0); - } - NBL_CONSTEXPR_STATIC_INLINE uint32_t ObjSize = 5; float32_t3 position; @@ -361,100 +282,6 @@ struct Shape return hlsl::cross(edges[0], edges[1]) * 0.5f; } - template - float deferredPdf(NBL_CONST_REF_ARG(Ray) ray) - { - const float32_t3 L = ray.direction; - switch (polygonMethod) - { - case PPM_AREA: - { - const float dist = ray.intersectionT; - const float32_t3 L = ray.direction; - return dist * dist / hlsl::abs(hlsl::dot(getNormalTimesArea(), L)); - } - break; - case PPM_SOLID_ANGLE: - { - shapes::SphericalTriangle st = shapes::SphericalTriangle::create(vertex0, vertex1, vertex2, ray.origin); - const float rcpProb = st.solidAngleOfTriangle(); - // if `rcpProb` is NAN then the triangle's solid angle was close to 0.0 - return rcpProb > numeric_limits::min ? (1.0 / rcpProb) : numeric_limits::max; - } - break; - case PPM_APPROX_PROJECTED_SOLID_ANGLE: - { - shapes::SphericalTriangle st = shapes::SphericalTriangle::create(vertex0, vertex1, vertex2, ray.origin); - sampling::ProjectedSphericalTriangle pst = sampling::ProjectedSphericalTriangle::create(st); - const float pdf = pst.pdf(ray.normalAtOrigin, ray.wasBSDFAtOrigin, 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::max ? pdf : numeric_limits::max; - } - break; - default: - return 0.0; - } - } - - template - float32_t3 generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, float32_t3 xi) - { - switch(polygonMethod) - { - case PPM_AREA: - { - const float32_t3 edge0 = vertex1 - vertex0; - const float32_t3 edge1 = vertex2 - vertex0; - const float sqrtU = hlsl::sqrt(xi.x); - float32_t3 pnt = vertex0 + edge0 * (1.0 - sqrtU) + edge1 * sqrtU * xi.y; - float32_t3 L = pnt - origin; - - const float distanceSq = hlsl::dot(L,L); - const float rcpDistance = 1.0 / hlsl::sqrt(distanceSq); - L *= rcpDistance; - - pdf = distanceSq / hlsl::abs(hlsl::dot(hlsl::cross(edge0, edge1) * 0.5f, L)); - newRayMaxT = 1.0 / rcpDistance; - return L; - } - break; - case PPM_SOLID_ANGLE: - { - float rcpPdf; - - shapes::SphericalTriangle st = shapes::SphericalTriangle::create(vertex0, vertex1, vertex2, origin); - sampling::SphericalTriangle sst = sampling::SphericalTriangle::create(st); - - const float32_t3 L = sst.generate(rcpPdf, xi.xy); - - pdf = rcpPdf > numeric_limits::min ? (1.0 / rcpPdf) : numeric_limits::max; - - const float32_t3 N = getNormalTimesArea(); - newRayMaxT = hlsl::dot(N, vertex0 - origin) / hlsl::dot(N, L); - return L; - } - break; - case PPM_APPROX_PROJECTED_SOLID_ANGLE: - { - float rcpPdf; - - shapes::SphericalTriangle st = shapes::SphericalTriangle::create(vertex0, vertex1, vertex2, origin); - sampling::ProjectedSphericalTriangle sst = sampling::ProjectedSphericalTriangle::create(st); - - const float32_t3 L = sst.generate(rcpPdf, interaction.isotropic.N, isBSDF, xi.xy); - - pdf = rcpPdf > numeric_limits::min ? (1.0 / rcpPdf) : numeric_limits::max; - - const float32_t3 N = getNormalTimesArea(); - newRayMaxT = hlsl::dot(N, vertex0 - origin) / hlsl::dot(N, L); - return L; - } - break; - default: - return (float32_t3)0.0; - } - } - NBL_CONSTEXPR_STATIC_INLINE uint32_t ObjSize = 10; float32_t3 vertex0; @@ -515,103 +342,6 @@ struct Shape basis[2] = normalize(cross(basis[0],basis[1])); } - template - float deferredPdf(NBL_CONST_REF_ARG(Ray) ray) - { - switch (polygonMethod) - { - case PPM_AREA: - { - const float dist = ray.intersectionT; - const float32_t3 L = ray.direction; - return dist * dist / hlsl::abs(hlsl::dot(getNormalTimesArea(), L)); - } - break; - // #ifdef TRIANGLE_REFERENCE ? - case PPM_SOLID_ANGLE: - { - float pdf; - float32_t3x3 rectNormalBasis; - float32_t2 rectExtents; - getNormalBasis(rectNormalBasis, rectExtents); - shapes::SphericalRectangle sphR0 = shapes::SphericalRectangle::create(ray.origin, offset, rectNormalBasis); - float solidAngle = sphR0.solidAngleOfRectangle(rectExtents); - if (solidAngle > numeric_limits::min) - pdf = 1.f / solidAngle; - else - pdf = bit_cast(numeric_limits::infinity); - return pdf; - } - break; - case PPM_APPROX_PROJECTED_SOLID_ANGLE: - { - // currently broken - return bit_cast(numeric_limits::infinity); - } - break; - default: - return bit_cast(numeric_limits::infinity); - } - } - - template - float32_t3 generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, float32_t3 xi) - { - const float32_t3 N = getNormalTimesArea(); - const float32_t3 origin2origin = offset - origin; - - switch (polygonMethod) - { - case PPM_AREA: - { - float32_t3 L = origin2origin + edge0 * xi.x + edge1 * xi.y; - const float distSq = hlsl::dot(L, L); - const float rcpDist = 1.0 / hlsl::sqrt(distSq); - L *= rcpDist; - pdf = distSq / hlsl::abs(hlsl::dot(N, L)); - newRayMaxT = 1.0 / rcpDist; - return L; - } - break; - // #ifdef TRIANGLE_REFERENCE ? - case PPM_SOLID_ANGLE: - { - float32_t3x3 rectNormalBasis; - float32_t2 rectExtents; - getNormalBasis(rectNormalBasis, rectExtents); - shapes::SphericalRectangle sphR0 = shapes::SphericalRectangle::create(origin, offset, rectNormalBasis); - float32_t3 L = (float32_t3)0.0; - float solidAngle = sphR0.solidAngleOfRectangle(rectExtents); - - sampling::SphericalRectangle ssph = sampling::SphericalRectangle::create(sphR0); - float32_t2 sphUv = ssph.generate(rectExtents, xi.xy, solidAngle); - if (solidAngle > numeric_limits::min) - { - float32_t3 sph_sample = sphUv[0] * edge0 + sphUv[1] * edge1 + offset; - L = sph_sample - origin; - L = hlsl::mix(nbl::hlsl::normalize(L), (float32_t3)0.0, hlsl::abs(L) > (float32_t3)numeric_limits::min); // TODO? sometimes L is vec3(0), find cause - pdf = 1.f / solidAngle; - } - else - pdf = bit_cast(numeric_limits::infinity); - - newRayMaxT = hlsl::dot(N, origin2origin) / hlsl::dot(N, L); - return L; - } - break; - case PPM_APPROX_PROJECTED_SOLID_ANGLE: - { - // currently broken - pdf = bit_cast(numeric_limits::infinity); - return (float32_t3)0.0; - } - break; - default: - pdf = bit_cast(numeric_limits::infinity); - return (float32_t3)0.0; - } - } - NBL_CONSTEXPR_STATIC_INLINE uint32_t ObjSize = 10; float32_t3 offset; diff --git a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl index 9c41f6627..7c157aadf 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl @@ -12,220 +12,425 @@ namespace ext namespace NextEventEstimator { -template -struct Estimator +template +struct ShapeSampling; + +template +struct ShapeSampling +{ + static ShapeSampling create(NBL_CONST_REF_ARG(Shape) sphere) + { + ShapeSampling retval; + retval.sphere = sphere; + return retval; + } + + template + float deferredPdf(NBL_CONST_REF_ARG(Ray) ray) + { + return 1.0 / sphere.getSolidAngle(ray.origin); + } + + template + float32_t3 generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, NBL_CONST_REF_ARG(float32_t3) xi) + { + float32_t3 Z = sphere.position - origin; + const float distanceSQ = hlsl::dot(Z,Z); + const float cosThetaMax2 = 1.0 - sphere.radius2 / distanceSQ; + if (cosThetaMax2 > 0.0) + { + const float rcpDistance = 1.0 / hlsl::sqrt(distanceSQ); + Z *= rcpDistance; + + const float cosThetaMax = hlsl::sqrt(cosThetaMax2); + const float cosTheta = hlsl::mix(1.0, cosThetaMax, xi.x); + + float32_t3 L = Z * cosTheta; + + const float cosTheta2 = cosTheta * cosTheta; + const float sinTheta = hlsl::sqrt(1.0 - cosTheta2); + float sinPhi, cosPhi; + math::sincos(2.0 * numbers::pi * xi.y - numbers::pi, sinPhi, cosPhi); + float32_t3 X, Y; + math::frisvad(Z, X, Y); + + L += (X * cosPhi + Y * sinPhi) * sinTheta; + + newRayMaxT = (cosTheta - hlsl::sqrt(cosTheta2 - cosThetaMax2)) / rcpDistance; + pdf = 1.0 / (2.0 * numbers::pi * (1.0 - cosThetaMax)); + return L; + } + pdf = 0.0; + return float32_t3(0.0,0.0,0.0); + } + + Shape sphere; +}; + +template<> +struct ShapeSampling +{ + static ShapeSampling create(NBL_CONST_REF_ARG(Shape) tri) + { + ShapeSampling retval; + retval.tri = tri; + return retval; + } + + template + float deferredPdf(NBL_CONST_REF_ARG(Ray) ray) + { + const float dist = ray.intersectionT; + const float32_t3 L = ray.direction; + return dist * dist / hlsl::abs(hlsl::dot(tri.getNormalTimesArea(), L)); + } + + template + float32_t3 generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, NBL_CONST_REF_ARG(float32_t3) xi) + { + const float32_t3 edge0 = tri.vertex1 - tri.vertex0; + const float32_t3 edge1 = tri.vertex2 - tri.vertex0; + const float sqrtU = hlsl::sqrt(xi.x); + float32_t3 pnt = tri.vertex0 + edge0 * (1.0 - sqrtU) + edge1 * sqrtU * xi.y; + float32_t3 L = pnt - origin; + + const float distanceSq = hlsl::dot(L,L); + const float rcpDistance = 1.0 / hlsl::sqrt(distanceSq); + L *= rcpDistance; + + pdf = distanceSq / hlsl::abs(hlsl::dot(hlsl::cross(edge0, edge1) * 0.5f, L)); + newRayMaxT = 1.0 / rcpDistance; + return L; + } + + Shape tri; +}; + +template<> +struct ShapeSampling +{ + static ShapeSampling create(NBL_CONST_REF_ARG(Shape) tri) + { + ShapeSampling retval; + retval.tri = tri; + return retval; + } + + template + float deferredPdf(NBL_CONST_REF_ARG(Ray) ray) + { + shapes::SphericalTriangle st = shapes::SphericalTriangle::create(tri.vertex0, tri.vertex1, tri.vertex2, ray.origin); + const float rcpProb = st.solidAngleOfTriangle(); + // if `rcpProb` is NAN then the triangle's solid angle was close to 0.0 + return rcpProb > numeric_limits::min ? (1.0 / rcpProb) : numeric_limits::max; + } + + template + float32_t3 generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, NBL_CONST_REF_ARG(float32_t3) xi) + { + float rcpPdf; + shapes::SphericalTriangle st = shapes::SphericalTriangle::create(tri.vertex0, tri.vertex1, tri.vertex2, origin); + sampling::SphericalTriangle sst = sampling::SphericalTriangle::create(st); + + const float32_t3 L = sst.generate(rcpPdf, xi.xy); + + pdf = rcpPdf > numeric_limits::min ? (1.0 / rcpPdf) : numeric_limits::max; + + const float32_t3 N = tri.getNormalTimesArea(); + newRayMaxT = hlsl::dot(N, tri.vertex0 - origin) / hlsl::dot(N, L); + return L; + } + + Shape tri; +}; + +template<> +struct ShapeSampling +{ + static ShapeSampling create(NBL_CONST_REF_ARG(Shape) tri) + { + ShapeSampling retval; + retval.tri = tri; + return retval; + } + + template + float deferredPdf(NBL_CONST_REF_ARG(Ray) ray) + { + const float32_t3 L = ray.direction; + shapes::SphericalTriangle st = shapes::SphericalTriangle::create(tri.vertex0, tri.vertex1, tri.vertex2, ray.origin); + sampling::ProjectedSphericalTriangle pst = sampling::ProjectedSphericalTriangle::create(st); + const float pdf = pst.pdf(ray.normalAtOrigin, ray.wasBSDFAtOrigin, 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::max ? pdf : numeric_limits::max; + } + + template + float32_t3 generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, NBL_CONST_REF_ARG(float32_t3) xi) + { + float rcpPdf; + shapes::SphericalTriangle st = shapes::SphericalTriangle::create(tri.vertex0, tri.vertex1, tri.vertex2, origin); + sampling::ProjectedSphericalTriangle sst = sampling::ProjectedSphericalTriangle::create(st); + + const float32_t3 L = sst.generate(rcpPdf, interaction.isotropic.N, isBSDF, xi.xy); + + pdf = rcpPdf > numeric_limits::min ? (1.0 / rcpPdf) : numeric_limits::max; + + const float32_t3 N = tri.getNormalTimesArea(); + newRayMaxT = hlsl::dot(N, tri.vertex0 - origin) / hlsl::dot(N, L); + return L; + } + + Shape tri; +}; + +template<> +struct ShapeSampling +{ + static ShapeSampling create(NBL_CONST_REF_ARG(Shape) rect) + { + ShapeSampling retval; + retval.rect = rect; + return retval; + } + + template + float deferredPdf(NBL_CONST_REF_ARG(Ray) ray) + { + const float dist = ray.intersectionT; + const float32_t3 L = ray.direction; + return dist * dist / hlsl::abs(hlsl::dot(rect.getNormalTimesArea(), L)); + } + + template + float32_t3 generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, NBL_CONST_REF_ARG(float32_t3) xi) + { + const float32_t3 N = rect.getNormalTimesArea(); + const float32_t3 origin2origin = rect.offset - origin; + + float32_t3 L = origin2origin + rect.edge0 * xi.x + rect.edge1 * xi.y; + const float distSq = hlsl::dot(L, L); + const float rcpDist = 1.0 / hlsl::sqrt(distSq); + L *= rcpDist; + pdf = distSq / hlsl::abs(hlsl::dot(N, L)); + newRayMaxT = 1.0 / rcpDist; + return L; + } + + Shape rect; +}; + +template<> +struct ShapeSampling +{ + static ShapeSampling create(NBL_CONST_REF_ARG(Shape) rect) + { + ShapeSampling retval; + retval.rect = rect; + return retval; + } + + template + float deferredPdf(NBL_CONST_REF_ARG(Ray) ray) + { + float pdf; + float32_t3x3 rectNormalBasis; + float32_t2 rectExtents; + rect.getNormalBasis(rectNormalBasis, rectExtents); + shapes::SphericalRectangle sphR0 = shapes::SphericalRectangle::create(ray.origin, rect.offset, rectNormalBasis); + float solidAngle = sphR0.solidAngleOfRectangle(rectExtents); + if (solidAngle > numeric_limits::min) + pdf = 1.f / solidAngle; + else + pdf = bit_cast(numeric_limits::infinity); + return pdf; + } + + template + float32_t3 generate_and_pdf(NBL_REF_ARG(float32_t) pdf, NBL_REF_ARG(float32_t) newRayMaxT, NBL_CONST_REF_ARG(float32_t3) origin, NBL_CONST_REF_ARG(Aniso) interaction, bool isBSDF, NBL_CONST_REF_ARG(float32_t3) xi) + { + const float32_t3 N = rect.getNormalTimesArea(); + const float32_t3 origin2origin = rect.offset - origin; + + float32_t3x3 rectNormalBasis; + float32_t2 rectExtents; + rect.getNormalBasis(rectNormalBasis, rectExtents); + shapes::SphericalRectangle sphR0 = shapes::SphericalRectangle::create(origin, rect.offset, rectNormalBasis); + float32_t3 L = (float32_t3)0.0; + float solidAngle = sphR0.solidAngleOfRectangle(rectExtents); + + sampling::SphericalRectangle ssph = sampling::SphericalRectangle::create(sphR0); + float32_t2 sphUv = ssph.generate(rectExtents, xi.xy, solidAngle); + if (solidAngle > numeric_limits::min) + { + float32_t3 sph_sample = sphUv[0] * rect.edge0 + sphUv[1] * rect.edge1 + rect.offset; + L = sph_sample - origin; + L = hlsl::mix(nbl::hlsl::normalize(L), (float32_t3)0.0, hlsl::abs(L) > (float32_t3)numeric_limits::min); // TODO? sometimes L is vec3(0), find cause + pdf = 1.f / solidAngle; + } + else + pdf = bit_cast(numeric_limits::infinity); + + newRayMaxT = hlsl::dot(N, origin2origin) / hlsl::dot(N, L); + return L; + } + + Shape rect; +}; + +// PPM_APPROX_PROJECTED_SOLID_ANGLE not available for PST_TRIANGLE + + +template +struct Estimator; + +template +struct Estimator { using scalar_type = typename Ray::scalar_type; using vector3_type = vector; using ray_type = Ray; - using light_type = Light; - using spectral_type = typename Light::spectral_type; + using scene_type = Scene; + using light_type = typename Scene::light_type; + using spectral_type = typename light_type::spectral_type; using interaction_type = Aniso; using quotient_pdf_type = bxdf::quotient_and_pdf; using sample_type = LightSample; using ray_dir_info_type = typename sample_type::ray_dir_info_type; - static spectral_type proceduralDeferredEvalAndPdf(NBL_REF_ARG(scalar_type) pdf, NBL_CONST_REF_ARG(light_type) light, NBL_CONST_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(Event) event) + static spectral_type deferredEvalAndPdf(NBL_REF_ARG(scalar_type) pdf, NBL_CONST_REF_ARG(scene_type) scene, uint32_t lightID, NBL_CONST_REF_ARG(ray_type) ray) { - const uint32_t lightCount = event.data[0]; - const ProceduralShapeType type = (ProceduralShapeType)event.data[1]; - - pdf = 1.0 / lightCount; - switch (type) - { - case PST_SPHERE: - { - const vector3_type position = vector3_type( - bit_cast(event.data[2]), - bit_cast(event.data[3]), - bit_cast(event.data[4])); - Shape sphere = Shape::create(position, bit_cast(event.data[5]), event.data[6]); - pdf *= sphere.template deferredPdf(ray); - } - break; - case PST_TRIANGLE: - { - const vector3_type vertex0 = vector3_type( - bit_cast(event.data[2]), - bit_cast(event.data[3]), - bit_cast(event.data[4])); - const vector3_type vertex1 = vector3_type( - bit_cast(event.data[5]), - bit_cast(event.data[6]), - bit_cast(event.data[7])); - const vector3_type vertex2 = vector3_type( - bit_cast(event.data[8]), - bit_cast(event.data[9]), - bit_cast(event.data[10])); - Shape tri = Shape::create(vertex0, vertex1, vertex2, event.data[11]); - pdf *= tri.template deferredPdf(ray); - } - break; - case PST_RECTANGLE: - { - const vector3_type offset = vector3_type( - bit_cast(event.data[2]), - bit_cast(event.data[3]), - bit_cast(event.data[4])); - const vector3_type edge0 = vector3_type( - bit_cast(event.data[5]), - bit_cast(event.data[6]), - bit_cast(event.data[7])); - const vector3_type edge1 = vector3_type( - bit_cast(event.data[8]), - bit_cast(event.data[9]), - bit_cast(event.data[10])); - Shape rect = Shape::create(offset, edge0, edge1, event.data[11]); - pdf *= rect.template deferredPdf(ray); - } - break; - default: - pdf = bit_cast(numeric_limits::infinity); - break; - } + pdf = 1.0 / scene.lightCount; + const light_type light = scene.lights[lightID]; + const Shape sphere = scene.spheres[light.objectID.id]; + const ShapeSampling sampling = ShapeSampling::create(sphere); + pdf *= sampling.template deferredPdf(ray); return light.radiance; } - static spectral_type deferredEvalAndPdf(NBL_REF_ARG(scalar_type) pdf, NBL_CONST_REF_ARG(light_type) light, NBL_CONST_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(Event) event) - { - // const IntersectMode mode = (IntersectMode)event.mode; - // switch (mode) - // { - // case IM_RAY_QUERY: - // { - // // TODO: do ray query stuff - // } - // break; - // case IM_RAY_TRACING: - // { - // // TODO: do ray tracing stuff - // } - // break; - // case IM_PROCEDURAL: - // { - return proceduralDeferredEvalAndPdf(pdf, light, ray, event); - // } - // break; - // default: - // return (spectral_type)0.0; - // } - // return (spectral_type)0.0; - } - - static sample_type procedural_generate_and_quotient_and_pdf(NBL_REF_ARG(quotient_pdf_type) quotient_pdf, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(light_type) light, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(interaction_type) interaction, bool isBSDF, NBL_CONST_REF_ARG(vector3_type) xi, uint32_t depth, NBL_CONST_REF_ARG(Event) event) - { - const uint32_t lightCount = event.data[0]; - const ProceduralShapeType type = (ProceduralShapeType)event.data[1]; + static sample_type generate_and_quotient_and_pdf(NBL_REF_ARG(quotient_pdf_type) quotient_pdf, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(scene_type) scene, uint32_t lightID, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(interaction_type) interaction, bool isBSDF, NBL_CONST_REF_ARG(vector3_type) xi, uint32_t depth) + { + sample_type L; + scalar_type pdf; + + const light_type light = scene.lights[lightID]; + const Shape sphere = scene.spheres[light.objectID.id]; + const ShapeSampling sampling = ShapeSampling::create(sphere); + + const vector3_type sampleL = sampling.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); + const vector3_type V = interaction.isotropic.V.getDirection(); + const scalar_type VdotL = nbl::hlsl::dot(V, sampleL); + ray_dir_info_type rayL; + rayL.direction = sampleL; + L = sample_type::create(rayL,VdotL,interaction.T,interaction.B,interaction.isotropic.N); + + newRayMaxT *= Tolerance::getEnd(depth); + pdf *= 1.0 / scalar_type(scene.lightCount); + spectral_type quo = light.radiance / pdf; + quotient_pdf = quotient_pdf_type::create(quo, pdf); + + return L; + } +}; + +template +struct Estimator +{ + using scalar_type = typename Ray::scalar_type; + using vector3_type = vector; + using ray_type = Ray; + using scene_type = Scene; + using light_type = typename Scene::light_type; + using spectral_type = typename light_type::spectral_type; + using interaction_type = Aniso; + using quotient_pdf_type = bxdf::quotient_and_pdf; + using sample_type = LightSample; + using ray_dir_info_type = typename sample_type::ray_dir_info_type; + + static spectral_type deferredEvalAndPdf(NBL_REF_ARG(scalar_type) pdf, NBL_CONST_REF_ARG(scene_type) scene, uint32_t lightID, NBL_CONST_REF_ARG(ray_type) ray) + { + pdf = 1.0 / scene.lightCount; + const light_type light = scene.lights[lightID]; + const Shape tri = scene.triangles[light.objectID.id]; + const ShapeSampling sampling = ShapeSampling::create(tri); + pdf *= sampling.template deferredPdf(ray); + + return light.radiance; + } + static sample_type generate_and_quotient_and_pdf(NBL_REF_ARG(quotient_pdf_type) quotient_pdf, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(scene_type) scene, uint32_t lightID, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(interaction_type) interaction, bool isBSDF, NBL_CONST_REF_ARG(vector3_type) xi, uint32_t depth) + { sample_type L; scalar_type pdf; - switch (type) - { - case PST_SPHERE: - { - const vector3_type position = vector3_type( - bit_cast(event.data[2]), - bit_cast(event.data[3]), - bit_cast(event.data[4])); - Shape sphere = Shape::create(position, bit_cast(event.data[5]), event.data[6]); - - const vector3_type sampleL = sphere.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); - const vector3_type V = interaction.isotropic.V.getDirection(); - const scalar_type VdotL = nbl::hlsl::dot(V, sampleL); - ray_dir_info_type rayL; - rayL.direction = sampleL; - L = sample_type::create(rayL,VdotL,interaction.T,interaction.B,interaction.isotropic.N); - } - break; - case PST_TRIANGLE: - { - const vector3_type vertex0 = vector3_type( - bit_cast(event.data[2]), - bit_cast(event.data[3]), - bit_cast(event.data[4])); - const vector3_type vertex1 = vector3_type( - bit_cast(event.data[5]), - bit_cast(event.data[6]), - bit_cast(event.data[7])); - const vector3_type vertex2 = vector3_type( - bit_cast(event.data[8]), - bit_cast(event.data[9]), - bit_cast(event.data[10])); - Shape tri = Shape::create(vertex0, vertex1, vertex2, event.data[11]); - - const vector3_type sampleL = tri.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); - const vector3_type V = interaction.isotropic.V.getDirection(); - const scalar_type VdotL = nbl::hlsl::dot(V, sampleL); - ray_dir_info_type rayL; - rayL.direction = sampleL; - L = sample_type::create(rayL,VdotL,interaction.T,interaction.B,interaction.isotropic.N); - } - break; - case PST_RECTANGLE: - { - const vector3_type offset = vector3_type( - bit_cast(event.data[2]), - bit_cast(event.data[3]), - bit_cast(event.data[4])); - const vector3_type edge0 = vector3_type( - bit_cast(event.data[5]), - bit_cast(event.data[6]), - bit_cast(event.data[7])); - const vector3_type edge1 = vector3_type( - bit_cast(event.data[8]), - bit_cast(event.data[9]), - bit_cast(event.data[10])); - Shape rect = Shape::create(offset, edge0, edge1, event.data[11]); - - const vector3_type sampleL = rect.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); - const vector3_type V = interaction.isotropic.V.getDirection(); - const scalar_type VdotL = nbl::hlsl::dot(V, sampleL); - ray_dir_info_type rayL; - rayL.direction = sampleL; - L = sample_type::create(rayL,VdotL,interaction.T,interaction.B,interaction.isotropic.N); - } - break; - default: - pdf = bit_cast(numeric_limits::infinity); - break; - } + + const light_type light = scene.lights[lightID]; + const Shape tri = scene.triangles[light.objectID.id]; + const ShapeSampling sampling = ShapeSampling::create(tri); + + const vector3_type sampleL = sampling.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); + const vector3_type V = interaction.isotropic.V.getDirection(); + const scalar_type VdotL = nbl::hlsl::dot(V, sampleL); + ray_dir_info_type rayL; + rayL.direction = sampleL; + L = sample_type::create(rayL,VdotL,interaction.T,interaction.B,interaction.isotropic.N); newRayMaxT *= Tolerance::getEnd(depth); - pdf *= 1.0 / scalar_type(lightCount); + pdf *= 1.0 / scalar_type(scene.lightCount); spectral_type quo = light.radiance / pdf; quotient_pdf = quotient_pdf_type::create(quo, pdf); return L; } +}; + +template +struct Estimator +{ + using scalar_type = typename Ray::scalar_type; + using vector3_type = vector; + using ray_type = Ray; + using scene_type = Scene; + using light_type = typename Scene::light_type; + using spectral_type = typename light_type::spectral_type; + using interaction_type = Aniso; + using quotient_pdf_type = bxdf::quotient_and_pdf; + using sample_type = LightSample; + using ray_dir_info_type = typename sample_type::ray_dir_info_type; + + static spectral_type deferredEvalAndPdf(NBL_REF_ARG(scalar_type) pdf, NBL_CONST_REF_ARG(scene_type) scene, uint32_t lightID, NBL_CONST_REF_ARG(ray_type) ray) + { + pdf = 1.0 / scene.lightCount; + const light_type light = scene.lights[lightID]; + const Shape rect = scene.rectangles[light.objectID.id]; + const ShapeSampling sampling = ShapeSampling::create(rect); + pdf *= sampling.template deferredPdf(ray); + + return light.radiance; + } - static sample_type generate_and_quotient_and_pdf(NBL_REF_ARG(quotient_pdf_type) quotient_pdf, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(light_type) light, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(interaction_type) interaction, bool isBSDF, NBL_CONST_REF_ARG(vector3_type) xi, uint32_t depth, NBL_CONST_REF_ARG(Event) event) + static sample_type generate_and_quotient_and_pdf(NBL_REF_ARG(quotient_pdf_type) quotient_pdf, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(scene_type) scene, uint32_t lightID, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(interaction_type) interaction, bool isBSDF, NBL_CONST_REF_ARG(vector3_type) xi, uint32_t depth) { - const IntersectMode mode = (IntersectMode)event.mode; sample_type L; - // switch (mode) - // { - // case IM_RAY_QUERY: - // { - // // TODO: do ray query stuff - // } - // break; - // case IM_RAY_TRACING: - // { - // // TODO: do ray tracing stuff - // } - // break; - // case IM_PROCEDURAL: - // { - return procedural_generate_and_quotient_and_pdf(quotient_pdf, newRayMaxT, light, origin, interaction, isBSDF, xi, depth, event); - // } - // break; - // default: - // { - // return L; - // } - // } - // return L; + scalar_type pdf; + + const light_type light = scene.lights[lightID]; + const Shape rect = scene.rectangles[light.objectID.id]; + const ShapeSampling sampling = ShapeSampling::create(rect); + + const vector3_type sampleL = sampling.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); + const vector3_type V = interaction.isotropic.V.getDirection(); + const scalar_type VdotL = nbl::hlsl::dot(V, sampleL); + ray_dir_info_type rayL; + rayL.direction = sampleL; + L = sample_type::create(rayL,VdotL,interaction.T,interaction.B,interaction.isotropic.N); + + newRayMaxT *= Tolerance::getEnd(depth); + pdf *= 1.0 / scalar_type(scene.lightCount); + spectral_type quo = light.radiance / pdf; + quotient_pdf = quotient_pdf_type::create(quo, pdf); + + return L; } }; diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index 553094e21..3082e599e 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -71,14 +71,6 @@ struct Unidirectional using conductor_op_type = typename MaterialSystem::conductor_op_type; using dielectric_op_type = typename MaterialSystem::dielectric_op_type; - // static this_t create(RandGen randGen, - // RayGen rayGen, - // Intersector intersector, - // MaterialSystem materialSystem, - // /* PathGuider pathGuider, */ - // NextEventEstimator nee) - // {} - static this_t create(NBL_CONST_REF_ARG(PathTracerCreationParams) params) { this_t retval; @@ -139,7 +131,7 @@ struct Unidirectional if (lightID != light_type::INVALID_ID) { float _pdf; - ray.payload.accumulation += nee.deferredEvalAndPdf(_pdf, scene.lights[lightID], ray, scene.toNextEvent(lightID)) * throughput / (1.0 + _pdf * _pdf * ray.payload.otherTechniqueHeuristic); + 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); @@ -174,8 +166,8 @@ struct Unidirectional scalar_type t; sample_type nee_sample = nee.generate_and_quotient_and_pdf( neeContrib_pdf, t, - scene.lights[randLightID], intersection, interaction, - isBSDF, eps0, depth, scene.toNextEvent(randLightID) + scene, randLightID, intersection, interaction, + isBSDF, eps0, depth ); // We don't allow non watertight transmitters in this renderer @@ -198,36 +190,6 @@ struct Unidirectional // example only uses isotropic bxdfs params_type params = params_type::template create(nee_sample, interaction.isotropic, _cache.iso_cache, _clamp); - // TODO: does not yet account for smooth dielectric - // if (!isBSDF && bxdf.materialType == ext::MaterialSystem::Material::DIFFUSE) - // { - // params = params_type::template create(nee_sample, interaction.isotropic, bxdf::BCM_MAX); - // } - // else if (!isBSDF && bxdf.materialType != ext::MaterialSystem::Material::DIFFUSE) - // { - // if (bxdf.params.is_aniso) - // params = params_type::template create(nee_sample, interaction, _cache, bxdf::BCM_MAX); - // else - // { - // isocache_type isocache = _cache.iso_cache; - // params = params_type::template create(nee_sample, interaction.isotropic, _cache.iso_cache, bxdf::BCM_MAX); - // } - // } - // else if (isBSDF && bxdf.materialType == ext::MaterialSystem::Material::DIFFUSE) - // { - // params = params_type::template create(nee_sample, interaction.isotropic, bxdf::BCM_ABS); - // } - // else if (isBSDF && bxdf.materialType != ext::MaterialSystem::Material::DIFFUSE) - // { - // if (bxdf.params.is_aniso) - // params = params_type::template create(nee_sample, interaction, _cache, bxdf::BCM_ABS); - // else - // { - // isocache_type isocache = _cache.iso_cache; - // params = params_type::template create(nee_sample, interaction.isotropic, _cache.iso_cache, bxdf::BCM_ABS); - // } - // } - quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(bxdf.materialType, bxdf.params, params); neeContrib_pdf.quotient *= bxdf.albedo * throughput * bsdf_quotient_pdf.quotient; const scalar_type otherGenOverChoice = bsdf_quotient_pdf.pdf * rcpChoiceProb; @@ -261,37 +223,6 @@ struct Unidirectional // example only uses isotropic bxdfs params_type params = params_type::template create(bsdf_sample, interaction.isotropic, _cache.iso_cache, _clamp); - // TODO: does not yet account for smooth dielectric - // params_type params; - // if (!isBSDF && bxdf.materialType == ext::MaterialSystem::Material::DIFFUSE) - // { - // params = params_type::template create(bsdf_sample, iso_interaction, bxdf::BCM_MAX); - // } - // else if (!isBSDF && bxdf.materialType != ext::MaterialSystem::Material::DIFFUSE) - // { - // if (bxdf.params.is_aniso) - // params = params_type::template create(bsdf_sample, interaction, _cache, bxdf::BCM_MAX); - // else - // { - // isocache_type isocache = _cache.iso_cache; - // params = params_type::template create(bsdf_sample, iso_interaction, isocache, bxdf::BCM_MAX); - // } - // } - // else if (isBSDF && bxdf.materialType == ext::MaterialSystem::Material::DIFFUSE) - // { - // params = params_type::template create(bsdf_sample, iso_interaction, bxdf::BCM_ABS); - // } - // else if (isBSDF && bxdf.materialType != ext::MaterialSystem::Material::DIFFUSE) - // { - // if (bxdf.params.is_aniso) - // params = params_type::template create(bsdf_sample, interaction, _cache, bxdf::BCM_ABS); - // else - // { - // isocache_type isocache = _cache.iso_cache; - // params = params_type::template create(bsdf_sample, iso_interaction, isocache, bxdf::BCM_ABS); - // } - // } - // 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, params); throughput *= bxdf.albedo * bsdf_quotient_pdf.quotient; diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index b54f5721d..5e8102f6f 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -13,24 +13,18 @@ #ifdef SPHERE_LIGHT #define SPHERE_COUNT 9 -#define LIGHT_TYPE ext::PST_SPHERE - #define TRIANGLE_COUNT 0 #define RECTANGLE_COUNT 0 #endif #ifdef TRIANGLE_LIGHT #define TRIANGLE_COUNT 1 -#define LIGHT_TYPE ext::PST_TRIANGLE - #define SPHERE_COUNT 8 #define RECTANGLE_COUNT 0 #endif #ifdef RECTANGLE_LIGHT #define RECTANGLE_COUNT 1 -#define LIGHT_TYPE ext::PST_RECTANGLE - #define SPHERE_COUNT 8 #define TRIANGLE_COUNT 0 #endif @@ -47,6 +41,18 @@ NBL_CONSTEXPR uint32_t WorkgroupSize = 256; NBL_CONSTEXPR uint32_t MAX_DEPTH_LOG2 = 4; NBL_CONSTEXPR uint32_t MAX_SAMPLES_LOG2 = 10; +#ifdef SPHERE_LIGHT +NBL_CONSTEXPR ext::ProceduralShapeType LIGHT_TYPE = ext::PST_SPHERE; +#endif +#ifdef TRIANGLE_LIGHT +NBL_CONSTEXPR ext::ProceduralShapeType LIGHT_TYPE = ext::PST_TRIANGLE; +#endif +#ifdef RECTANGLE_LIGHT +NBL_CONSTEXPR ext::ProceduralShapeType LIGHT_TYPE = ext::PST_RECTANGLE; +#endif + +NBL_CONSTEXPR ext::PTPolygonMethod POLYGON_METHOD = ext::PPM_SOLID_ANGLE; + int32_t2 getCoordinates() { uint32_t width, height; @@ -80,11 +86,12 @@ using dielectric_bxdf_type = bxdf::transmission::SGGXDielectricBxDF; using light_type = ext::Light; using bxdfnode_type = ext::BxDFNode; +using scene_type = ext::Scene; using randgen_type = ext::RandGen::Uniform3D; using raygen_type = ext::RayGen::Basic; using intersector_type = ext::Intersector::Comprehensive; using material_system_type = ext::MaterialSystem::System; -using nee_type = ext::NextEventEstimator::Estimator; +using nee_type = ext::NextEventEstimator::Estimator; using pathtracer_type = ext::PathTracer::Unidirectional; static const ext::Shape spheres[SPHERE_COUNT] = { @@ -164,7 +171,6 @@ void main(uint32_t3 threadID : SV_DispatchThreadID) } int flatIdx = glsl::gl_GlobalInvocationID().y * glsl::gl_NumWorkGroups().x * WorkgroupSize + glsl::gl_GlobalInvocationID().x; - PCG32x2 pcg = PCG32x2::construct(flatIdx); // replaces scramblebuf? // set up path tracer ext::PathTracer::PathTracerCreationParams ptCreateParams; diff --git a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl index 887d20c48..40fb01057 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/scene.hlsl @@ -87,145 +87,6 @@ struct Scene #undef SCENE_TRIANGLE_COUNT #undef SCENE_RECTANGLE_COUNT - // obsolete? - // Intersector::IntersectData toIntersectData(uint32_t mode, ProceduralShapeType type) - // { - // Intersector::IntersectData retval; - // retval.mode = mode; - - // uint32_t objCount = (type == PST_SPHERE) ? sphereCount : - // (type == PST_TRIANGLE) ? triangleCount : - // (type == PST_RECTANGLE) ? rectangleCount : - // -1; - // retval.data[0] = objCount; - // retval.data[1] = type; - - // switch (type) - // { - // case PST_SPHERE: - // { - // for (int i = 0; i < objCount; i++) - // { - // Shape sphere = spheres[i]; - // uint32_t3 uintPos = bit_cast(sphere.position); - // retval.data[2 + i * Shape::ObjSize] = uintPos.x; - // retval.data[2 + i * Shape::ObjSize + 1] = uintPos.y; - // retval.data[2 + i * Shape::ObjSize + 2] = uintPos.z; - // retval.data[2 + i * Shape::ObjSize + 3] = bit_cast(sphere.radius2); - // retval.data[2 + i * Shape::ObjSize + 4] = sphere.bsdfLightIDs; - // } - // } - // break; - // case PST_TRIANGLE: - // { - // for (int i = 0; i < objCount; i++) - // { - // Shape tri = triangles[i]; - // retval.data[2 + i * Shape::ObjSize] = asuint(tri.vertex0.x); - // retval.data[2 + i * Shape::ObjSize + 1] = asuint(tri.vertex0.y); - // retval.data[2 + i * Shape::ObjSize + 2] = asuint(tri.vertex0.z); - // retval.data[2 + i * Shape::ObjSize + 3] = asuint(tri.vertex1.x); - // retval.data[2 + i * Shape::ObjSize + 4] = asuint(tri.vertex1.y); - // retval.data[2 + i * Shape::ObjSize + 5] = asuint(tri.vertex1.z); - // retval.data[2 + i * Shape::ObjSize + 6] = asuint(tri.vertex2.x); - // retval.data[2 + i * Shape::ObjSize + 7] = asuint(tri.vertex2.y); - // retval.data[2 + i * Shape::ObjSize + 8] = asuint(tri.vertex2.z); - // retval.data[2 + i * Shape::ObjSize + 9] = tri.bsdfLightIDs; - // } - // } - // break; - // case PST_RECTANGLE: - // { - // for (int i = 0; i < objCount; i++) - // { - // Shape rect = rectangles[i]; - // retval.data[2 + i * Shape::ObjSize] = asuint(rect.offset.x); - // retval.data[2 + i * Shape::ObjSize + 1] = asuint(rect.offset.y); - // retval.data[2 + i * Shape::ObjSize + 2] = asuint(rect.offset.z); - // retval.data[2 + i * Shape::ObjSize + 3] = asuint(rect.edge0.x); - // retval.data[2 + i * Shape::ObjSize + 4] = asuint(rect.edge0.y); - // retval.data[2 + i * Shape::ObjSize + 5] = asuint(rect.edge0.z); - // retval.data[2 + i * Shape::ObjSize + 6] = asuint(rect.edge1.x); - // retval.data[2 + i * Shape::ObjSize + 7] = asuint(rect.edge1.y); - // retval.data[2 + i * Shape::ObjSize + 8] = asuint(rect.edge1.z); - // retval.data[2 + i * Shape::ObjSize + 9] = rect.bsdfLightIDs; - // } - // } - // break; - // default: - // // for ASes - // break; - // } - // return retval; - // } - - NextEventEstimator::Event toNextEvent(uint32_t lightID) - { - NextEventEstimator::Event retval; - - ObjectID objectID = lights[lightID].objectID; - retval.mode = objectID.mode; - - retval.data[0] = lightCount; - retval.data[1] = objectID.shapeType; - - uint32_t id = objectID.id; - switch (objectID.shapeType) - { - case PST_SPHERE: - { - Shape sphere = spheres[id]; - uint32_t3 position = bit_cast(sphere.position); - retval.data[2] = position.x; - retval.data[3] = position.y; - retval.data[4] = position.z; - retval.data[5] = bit_cast(sphere.radius2); - retval.data[6] = sphere.bsdfLightIDs; - } - break; - case PST_TRIANGLE: - { - Shape tri = triangles[id]; - uint32_t3 vertex = bit_cast(tri.vertex0); - retval.data[2] = vertex.x; - retval.data[3] = vertex.y; - retval.data[4] = vertex.z; - vertex = bit_cast(tri.vertex1); - retval.data[5] = vertex.x; - retval.data[6] = vertex.y; - retval.data[7] = vertex.z; - vertex = bit_cast(tri.vertex2); - retval.data[8] = vertex.x; - retval.data[9] = vertex.y; - retval.data[10] = vertex.z; - retval.data[11] = tri.bsdfLightIDs; - } - break; - case PST_RECTANGLE: - { - Shape rect = rectangles[id]; - uint32_t3 tmp = bit_cast(rect.offset); - retval.data[2] = tmp.x; - retval.data[3] = tmp.y; - retval.data[4] = tmp.z; - tmp = bit_cast(rect.edge0); - retval.data[5] = tmp.x; - retval.data[6] = tmp.y; - retval.data[7] = tmp.z; - tmp = bit_cast(rect.edge1); - retval.data[8] = tmp.x; - retval.data[9] = tmp.y; - retval.data[10] = tmp.z; - retval.data[11] = rect.bsdfLightIDs; - } - break; - default: - // for ASes - break; - } - return retval; - } - // TODO: get these to work with AS types as well uint32_t getBsdfLightIDs(NBL_CONST_REF_ARG(ObjectID) objectID) { diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index 10889f37f..ae9f162a4 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -48,7 +48,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, constexpr static inline uint32_t2 WindowDimensions = { 1280, 720 }; constexpr static inline uint32_t MaxFramesInFlight = 5; constexpr static inline clock_t::duration DisplayImageDuration = std::chrono::milliseconds(900); - constexpr static inline uint32_t DefaultWorkGroupSize = 16u; + constexpr static inline uint32_t DefaultWorkGroupSize = 256u; constexpr static inline uint32_t MaxDescriptorCount = 256u; constexpr static inline uint32_t MaxDepthLog2 = 4u; // 5 constexpr static inline uint32_t MaxSamplesLog2 = 10u; // 18 @@ -1068,10 +1068,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, cmdbuf->bindDescriptorSets(EPBP_COMPUTE, pipeline->getLayout(), 0u, 1u, &m_descriptorSet0.get()); cmdbuf->bindDescriptorSets(EPBP_COMPUTE, pipeline->getLayout(), 2u, 1u, &m_descriptorSet2.get()); cmdbuf->pushConstants(pipeline->getLayout(), IShader::E_SHADER_STAGE::ESS_COMPUTE, 0, sizeof(PTPushConstant), &pc); - if (renderMode == E_RENDER_MODE::ERM_HLSL) - cmdbuf->dispatch(1 + (WindowDimensions.x * WindowDimensions.y - 1) / 256u, 1u, 1u); - else - cmdbuf->dispatch(1 + (WindowDimensions.x - 1) / DefaultWorkGroupSize, 1 + (WindowDimensions.y - 1) / DefaultWorkGroupSize, 1u); + cmdbuf->dispatch(1 + (WindowDimensions.x * WindowDimensions.y - 1) / DefaultWorkGroupSize, 1u, 1u); } // TRANSITION m_outImgView to READ (because of descriptorSets0 -> ComputeShader Writes into the image) @@ -1351,7 +1348,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, float camYAngle = 165.f / 180.f * 3.14159f; float camXAngle = 32.f / 180.f * 3.14159f; int PTPipline = E_LIGHT_GEOMETRY::ELG_SPHERE; - int renderMode = E_RENDER_MODE::ERM_HLSL; + int renderMode = E_RENDER_MODE::ERM_GLSL; int spp = 32; int depth = 3; From b889b60e4db77dbc435ea6c5baefbcba0089e01c Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 19 Mar 2025 15:24:14 +0700 Subject: [PATCH 41/53] use 1D workgroup dispatch --- 31_HLSLPathTracer/app_resources/glsl/common.glsl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/glsl/common.glsl b/31_HLSLPathTracer/app_resources/glsl/common.glsl index 2463f82cf..b09c90824 100644 --- a/31_HLSLPathTracer/app_resources/glsl/common.glsl +++ b/31_HLSLPathTracer/app_resources/glsl/common.glsl @@ -16,12 +16,13 @@ layout(set = 2, binding = 2) uniform usampler2D scramblebuf; layout(set=0, binding=0, rgba16f) uniform image2D outImage; #ifndef _NBL_GLSL_WORKGROUP_SIZE_ -#define _NBL_GLSL_WORKGROUP_SIZE_ 32 -layout(local_size_x=_NBL_GLSL_WORKGROUP_SIZE_, local_size_y=_NBL_GLSL_WORKGROUP_SIZE_, local_size_z=1) in; +#define _NBL_GLSL_WORKGROUP_SIZE_ 256 +layout(local_size_x=_NBL_GLSL_WORKGROUP_SIZE_, local_size_y=1, local_size_z=1) in; #endif ivec2 getCoordinates() { - return ivec2(gl_GlobalInvocationID.xy); + ivec2 imageSize = imageSize(outImage); + return ivec2(gl_GlobalInvocationID.x % imageSize.x, gl_GlobalInvocationID.x / imageSize.x); } vec2 getTexCoords() { From 79ee9da780900a7977d630ef156128f7287d2222 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 19 Mar 2025 15:24:57 +0700 Subject: [PATCH 42/53] removed obsolete commented sections --- .../app_resources/hlsl/intersector.hlsl | 118 ------------------ 1 file changed, 118 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl index 03a45f866..e59fdc2c3 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/intersector.hlsl @@ -78,124 +78,6 @@ struct Comprehensive return objectID; } - - // note for future consideration: still need to encode to IntersectData? - // obsolete? - // static ObjectID traceProcedural(NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(IntersectData) intersect) - // { - // const bool anyHit = ray.intersectionT != numeric_limits::max; - // const uint32_t objCount = intersect.data[0]; - // const ProceduralShapeType type = (ProceduralShapeType)intersect.data[1]; - - // ObjectID objectID = ray.objectID; - // objectID.mode = IM_PROCEDURAL; - // objectID.shapeType = type; - // for (int i = 0; i < objCount; i++) - // { - // float t; - // switch (type) - // { - // case PST_SPHERE: - // { - // vector3_type position = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize]), asfloat(intersect.data[2 + i * Shape::ObjSize + 1]), asfloat(intersect.data[2 + i * Shape::ObjSize + 2])); - // Shape sphere = Shape::create(position, asfloat(intersect.data[2 + i * Shape::ObjSize + 3]), intersect.data[2 + i * Shape::ObjSize + 4]); - // t = sphere.intersect(ray.origin, ray.direction); - // } - // break; - // case PST_TRIANGLE: - // { - // vector3_type vertex0 = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize]), asfloat(intersect.data[2 + i * Shape::ObjSize + 1]), asfloat(intersect.data[2 + i * Shape::ObjSize + 2])); - // vector3_type vertex1 = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize + 3]), asfloat(intersect.data[2 + i * Shape::ObjSize + 4]), asfloat(intersect.data[2 + i * Shape::ObjSize + 5])); - // vector3_type vertex2 = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize + 6]), asfloat(intersect.data[2 + i * Shape::ObjSize + 7]), asfloat(intersect.data[2 + i * Shape::ObjSize + 8])); - // Shape tri = Shape::create(vertex0, vertex1, vertex2, intersect.data[2 + i * Shape::ObjSize + 9]); - // t = tri.intersect(ray.origin, ray.direction); - // } - // break; - // case PST_RECTANGLE: - // { - // vector3_type offset = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize]), asfloat(intersect.data[2 + i * Shape::ObjSize + 1]), asfloat(intersect.data[2 + i * Shape::ObjSize + 2])); - // vector3_type edge0 = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize + 3]), asfloat(intersect.data[2 + i * Shape::ObjSize + 4]), asfloat(intersect.data[2 + i * Shape::ObjSize + 5])); - // vector3_type edge1 = vector3_type(asfloat(intersect.data[2 + i * Shape::ObjSize + 6]), asfloat(intersect.data[2 + i * Shape::ObjSize + 7]), asfloat(intersect.data[2 + i * Shape::ObjSize + 8])); - // Shape rect = Shape::create(offset, edge0, edge1, intersect.data[2 + i * Shape::ObjSize + 9]); - // t = rect.intersect(ray.origin, ray.direction); - // } - // break; - // default: - // t = numeric_limits::infinity; - // break; - // } - - // bool closerIntersection = t > 0.0 && t < ray.intersectionT; - - // ray.intersectionT = closerIntersection ? t : ray.intersectionT; - // objectID.id = closerIntersection ? i : objectID.id; - - // // allowing early out results in a performance regression, WTF!? - // //if (anyHit && closerIntersection) - // //break; - // } - // return objectID; - // } - - // obsolete? - // static ObjectID traceRay(NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(IntersectData) intersect) - // { - // const uint32_t mode = intersect.mode; - // switch (mode) - // { - // case IM_RAY_QUERY: - // { - // // TODO: do ray query stuff - // } - // break; - // case IM_RAY_TRACING: - // { - // // TODO: do ray tracing stuff - // } - // break; - // case IM_PROCEDURAL: - // { - // return traceProcedural(ray, intersect); - // } - // break; - // default: - // { - // return ObjectID::create(-1, 0, PST_SPHERE); - // } - // } - // return ObjectID::create(-1, 0, PST_SPHERE); - // } - - // static ObjectID traceRay(NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(scene_type) scene) - // { - // IntersectData data; - - // ObjectID objectID; - // objectID.id = -1; // start with no intersect - - // // prodedural shapes - // if (scene.sphereCount > 0) - // { - // data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_SPHERE); - // objectID = traceRay(ray, data); - // } - - // if (scene.triangleCount > 0) - // { - // data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_TRIANGLE); - // objectID = traceRay(ray, data); - // } - - // if (scene.rectangleCount > 0) - // { - // data = scene.toIntersectData(ext::Intersector::IntersectData::Mode::PROCEDURAL, PST_RECTANGLE); - // objectID = traceRay(ray, data); - // } - - // // TODO: trace AS - - // return objectID; - // } }; } From ca8f2ec8fa84a2bd1bfeb4348263f82d14026bca Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 19 Mar 2025 16:01:15 +0700 Subject: [PATCH 43/53] some minor corrections --- .../app_resources/hlsl/common.hlsl | 6 +--- .../hlsl/next_event_estimator.hlsl | 28 +++++++++++-------- .../app_resources/hlsl/pathtracer.hlsl | 9 +++--- 31_HLSLPathTracer/main.cpp | 2 +- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index dea682c8b..2e2561345 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -68,7 +68,7 @@ struct Ray vector3_type origin; vector3_type direction; - // TODO: polygon method == 2 stuff + // polygon method == PPM_APPROX_PROJECTED_SOLID_ANGLE vector3_type normalAtOrigin; bool wasBSDFAtOrigin; @@ -246,7 +246,6 @@ struct Shape retval.vertex1 = vertex1; retval.vertex2 = vertex2; retval.bsdfLightIDs = bsdfLightIDs; - retval.polygonMethod = PPM_SOLID_ANGLE; return retval; } @@ -288,7 +287,6 @@ struct Shape float32_t3 vertex1; float32_t3 vertex2; uint32_t bsdfLightIDs; - PTPolygonMethod polygonMethod; }; template<> @@ -301,7 +299,6 @@ struct Shape retval.edge0 = edge0; retval.edge1 = edge1; retval.bsdfLightIDs = bsdfLightIDs; - retval.polygonMethod = PPM_SOLID_ANGLE; return retval; } @@ -348,7 +345,6 @@ struct Shape float32_t3 edge0; float32_t3 edge1; uint32_t bsdfLightIDs; - PTPolygonMethod polygonMethod; }; } diff --git a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl index 7c157aadf..51c018ac5 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl @@ -298,6 +298,10 @@ struct Estimator using sample_type = LightSample; using ray_dir_info_type = typename sample_type::ray_dir_info_type; + // affected by https://github.com/microsoft/DirectXShaderCompiler/issues/7007 + // NBL_CONSTEXPR_STATIC_INLINE PTPolygonMethod PolygonMethod = PPM; + enum : uint16_t { PolygonMethod = PPM }; + static spectral_type deferredEvalAndPdf(NBL_REF_ARG(scalar_type) pdf, NBL_CONST_REF_ARG(scene_type) scene, uint32_t lightID, NBL_CONST_REF_ARG(ray_type) ray) { pdf = 1.0 / scene.lightCount; @@ -311,19 +315,17 @@ struct Estimator static sample_type generate_and_quotient_and_pdf(NBL_REF_ARG(quotient_pdf_type) quotient_pdf, NBL_REF_ARG(scalar_type) newRayMaxT, NBL_CONST_REF_ARG(scene_type) scene, uint32_t lightID, NBL_CONST_REF_ARG(vector3_type) origin, NBL_CONST_REF_ARG(interaction_type) interaction, bool isBSDF, NBL_CONST_REF_ARG(vector3_type) xi, uint32_t depth) { - sample_type L; - scalar_type pdf; - const light_type light = scene.lights[lightID]; const Shape sphere = scene.spheres[light.objectID.id]; const ShapeSampling sampling = ShapeSampling::create(sphere); + scalar_type pdf; const vector3_type sampleL = sampling.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); const vector3_type V = interaction.isotropic.V.getDirection(); const scalar_type VdotL = nbl::hlsl::dot(V, sampleL); ray_dir_info_type rayL; rayL.direction = sampleL; - L = sample_type::create(rayL,VdotL,interaction.T,interaction.B,interaction.isotropic.N); + sample_type L = sample_type::create(rayL,VdotL,interaction.T,interaction.B,interaction.isotropic.N); newRayMaxT *= Tolerance::getEnd(depth); pdf *= 1.0 / scalar_type(scene.lightCount); @@ -348,6 +350,9 @@ struct Estimator tri = scene.triangles[light.objectID.id]; const ShapeSampling sampling = ShapeSampling::create(tri); + scalar_type pdf; const vector3_type sampleL = sampling.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); const vector3_type V = interaction.isotropic.V.getDirection(); const scalar_type VdotL = nbl::hlsl::dot(V, sampleL); ray_dir_info_type rayL; rayL.direction = sampleL; - L = sample_type::create(rayL,VdotL,interaction.T,interaction.B,interaction.isotropic.N); + sample_type L = sample_type::create(rayL,VdotL,interaction.T,interaction.B,interaction.isotropic.N); newRayMaxT *= Tolerance::getEnd(depth); pdf *= 1.0 / scalar_type(scene.lightCount); @@ -398,6 +401,9 @@ struct Estimator rect = scene.rectangles[light.objectID.id]; const ShapeSampling sampling = ShapeSampling::create(rect); + scalar_type pdf; const vector3_type sampleL = sampling.template generate_and_pdf(pdf, newRayMaxT, origin, interaction, isBSDF, xi); const vector3_type V = interaction.isotropic.V.getDirection(); const scalar_type VdotL = nbl::hlsl::dot(V, sampleL); ray_dir_info_type rayL; rayL.direction = sampleL; - L = sample_type::create(rayL,VdotL,interaction.T,interaction.B,interaction.isotropic.N); + sample_type L = sample_type::create(rayL,VdotL,interaction.T,interaction.B,interaction.isotropic.N); newRayMaxT *= Tolerance::getEnd(depth); pdf *= 1.0 / scalar_type(scene.lightCount); diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index 3082e599e..f5d5206dc 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -241,10 +241,11 @@ struct Unidirectional // trace new ray ray.origin = intersection + bxdfSample * (1.0/*kSceneSize*/) * Tolerance::getStart(depth); ray.direction = bxdfSample; - // #if POLYGON_METHOD==2 - // ray._immutable.normalAtOrigin = interaction.isotropic.N; - // ray._immutable.wasBSDFAtOrigin = isBSDF; - // #endif + if ((PTPolygonMethod)nee_type::PolygonMethod == PPM_APPROX_PROJECTED_SOLID_ANGLE) + { + ray.normalAtOrigin = interaction.isotropic.N; + ray.wasBSDFAtOrigin = isBSDF; + } return true; } diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index ae9f162a4..e3e0b7d7a 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -1348,7 +1348,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, float camYAngle = 165.f / 180.f * 3.14159f; float camXAngle = 32.f / 180.f * 3.14159f; int PTPipline = E_LIGHT_GEOMETRY::ELG_SPHERE; - int renderMode = E_RENDER_MODE::ERM_GLSL; + int renderMode = E_RENDER_MODE::ERM_HLSL; int spp = 32; int depth = 3; From e95f09d5d20181c4107064cec08bddc689a7f399 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 21 Mar 2025 16:48:41 +0700 Subject: [PATCH 44/53] changed workgroup size to 512 --- 31_HLSLPathTracer/app_resources/glsl/common.glsl | 2 +- .../app_resources/hlsl/render.comp.hlsl | 2 +- 31_HLSLPathTracer/imgui.ini | 8 -------- 31_HLSLPathTracer/main.cpp | 16 ++++++++-------- 4 files changed, 10 insertions(+), 18 deletions(-) delete mode 100644 31_HLSLPathTracer/imgui.ini diff --git a/31_HLSLPathTracer/app_resources/glsl/common.glsl b/31_HLSLPathTracer/app_resources/glsl/common.glsl index b09c90824..9015f755d 100644 --- a/31_HLSLPathTracer/app_resources/glsl/common.glsl +++ b/31_HLSLPathTracer/app_resources/glsl/common.glsl @@ -16,7 +16,7 @@ layout(set = 2, binding = 2) uniform usampler2D scramblebuf; layout(set=0, binding=0, rgba16f) uniform image2D outImage; #ifndef _NBL_GLSL_WORKGROUP_SIZE_ -#define _NBL_GLSL_WORKGROUP_SIZE_ 256 +#define _NBL_GLSL_WORKGROUP_SIZE_ 1024 layout(local_size_x=_NBL_GLSL_WORKGROUP_SIZE_, local_size_y=1, local_size_z=1) in; #endif diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index 5e8102f6f..d0c969b8b 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -37,7 +37,7 @@ using namespace nbl::hlsl; -NBL_CONSTEXPR uint32_t WorkgroupSize = 256; +NBL_CONSTEXPR uint32_t WorkgroupSize = 1024; NBL_CONSTEXPR uint32_t MAX_DEPTH_LOG2 = 4; NBL_CONSTEXPR uint32_t MAX_SAMPLES_LOG2 = 10; diff --git a/31_HLSLPathTracer/imgui.ini b/31_HLSLPathTracer/imgui.ini deleted file mode 100644 index e60624929..000000000 --- a/31_HLSLPathTracer/imgui.ini +++ /dev/null @@ -1,8 +0,0 @@ -[Window][Debug##Default] -Pos=60,60 -Size=400,400 - -[Window][Controls] -Pos=10,10 -Size=320,340 - diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index e3e0b7d7a..add980078 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -48,7 +48,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, constexpr static inline uint32_t2 WindowDimensions = { 1280, 720 }; constexpr static inline uint32_t MaxFramesInFlight = 5; constexpr static inline clock_t::duration DisplayImageDuration = std::chrono::milliseconds(900); - constexpr static inline uint32_t DefaultWorkGroupSize = 256u; + constexpr static inline uint32_t DefaultWorkGroupSize = 1024u; constexpr static inline uint32_t MaxDescriptorCount = 256u; constexpr static inline uint32_t MaxDepthLog2 = 4u; // 5 constexpr static inline uint32_t MaxSamplesLog2 = 10u; // 18 @@ -366,12 +366,12 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, options.stage = IShader::E_SHADER_STAGE::ESS_COMPUTE; // should be compute options.targetSpirvVersion = m_device->getPhysicalDevice()->getLimits().spirvVersion; options.spirvOptimizer = nullptr; -#ifndef _NBL_DEBUG - ISPIRVOptimizer::E_OPTIMIZER_PASS optPasses = ISPIRVOptimizer::EOP_STRIP_DEBUG_INFO; - auto opt = make_smart_refctd_ptr(std::span(&optPasses, 1)); - options.spirvOptimizer = opt.get(); -#endif - options.debugInfoFlags = IShaderCompiler::E_DEBUG_INFO_FLAGS::EDIF_NONE; +//#ifndef _NBL_DEBUG +// ISPIRVOptimizer::E_OPTIMIZER_PASS optPasses = ISPIRVOptimizer::EOP_STRIP_DEBUG_INFO; +// auto opt = make_smart_refctd_ptr(std::span(&optPasses, 1)); +// options.spirvOptimizer = opt.get(); +//#endif + options.debugInfoFlags |= IShaderCompiler::E_DEBUG_INFO_FLAGS::EDIF_LINE_BIT; options.preprocessorOptions.sourceIdentifier = source->getFilepathHint(); options.preprocessorOptions.logger = m_logger.get(); options.preprocessorOptions.includeFinder = compiler->getDefaultIncludeFinder(); @@ -1348,7 +1348,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, float camYAngle = 165.f / 180.f * 3.14159f; float camXAngle = 32.f / 180.f * 3.14159f; int PTPipline = E_LIGHT_GEOMETRY::ELG_SPHERE; - int renderMode = E_RENDER_MODE::ERM_HLSL; + int renderMode = E_RENDER_MODE::ERM_GLSL; int spp = 32; int depth = 3; From 56994a9d36ae0e21e54a07aa76e1e5bbe2e2d959 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 21 Mar 2025 16:51:39 +0700 Subject: [PATCH 45/53] workgroup size 512 for sure this time --- 31_HLSLPathTracer/app_resources/glsl/common.glsl | 2 +- .../app_resources/hlsl/render.comp.hlsl | 2 +- 31_HLSLPathTracer/main.cpp | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/glsl/common.glsl b/31_HLSLPathTracer/app_resources/glsl/common.glsl index 9015f755d..1a1594e6a 100644 --- a/31_HLSLPathTracer/app_resources/glsl/common.glsl +++ b/31_HLSLPathTracer/app_resources/glsl/common.glsl @@ -16,7 +16,7 @@ layout(set = 2, binding = 2) uniform usampler2D scramblebuf; layout(set=0, binding=0, rgba16f) uniform image2D outImage; #ifndef _NBL_GLSL_WORKGROUP_SIZE_ -#define _NBL_GLSL_WORKGROUP_SIZE_ 1024 +#define _NBL_GLSL_WORKGROUP_SIZE_ 512 layout(local_size_x=_NBL_GLSL_WORKGROUP_SIZE_, local_size_y=1, local_size_z=1) in; #endif diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index d0c969b8b..b0d221a20 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -37,7 +37,7 @@ using namespace nbl::hlsl; -NBL_CONSTEXPR uint32_t WorkgroupSize = 1024; +NBL_CONSTEXPR uint32_t WorkgroupSize = 512; NBL_CONSTEXPR uint32_t MAX_DEPTH_LOG2 = 4; NBL_CONSTEXPR uint32_t MAX_SAMPLES_LOG2 = 10; diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index add980078..8394889db 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -48,7 +48,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, constexpr static inline uint32_t2 WindowDimensions = { 1280, 720 }; constexpr static inline uint32_t MaxFramesInFlight = 5; constexpr static inline clock_t::duration DisplayImageDuration = std::chrono::milliseconds(900); - constexpr static inline uint32_t DefaultWorkGroupSize = 1024u; + constexpr static inline uint32_t DefaultWorkGroupSize = 512u; constexpr static inline uint32_t MaxDescriptorCount = 256u; constexpr static inline uint32_t MaxDepthLog2 = 4u; // 5 constexpr static inline uint32_t MaxSamplesLog2 = 10u; // 18 @@ -366,11 +366,11 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, options.stage = IShader::E_SHADER_STAGE::ESS_COMPUTE; // should be compute options.targetSpirvVersion = m_device->getPhysicalDevice()->getLimits().spirvVersion; options.spirvOptimizer = nullptr; -//#ifndef _NBL_DEBUG -// ISPIRVOptimizer::E_OPTIMIZER_PASS optPasses = ISPIRVOptimizer::EOP_STRIP_DEBUG_INFO; -// auto opt = make_smart_refctd_ptr(std::span(&optPasses, 1)); -// options.spirvOptimizer = opt.get(); -//#endif +#ifndef _NBL_DEBUG + ISPIRVOptimizer::E_OPTIMIZER_PASS optPasses = ISPIRVOptimizer::EOP_STRIP_DEBUG_INFO; + auto opt = make_smart_refctd_ptr(std::span(&optPasses, 1)); + options.spirvOptimizer = opt.get(); +#endif options.debugInfoFlags |= IShaderCompiler::E_DEBUG_INFO_FLAGS::EDIF_LINE_BIT; options.preprocessorOptions.sourceIdentifier = source->getFilepathHint(); options.preprocessorOptions.logger = m_logger.get(); @@ -1348,7 +1348,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, float camYAngle = 165.f / 180.f * 3.14159f; float camXAngle = 32.f / 180.f * 3.14159f; int PTPipline = E_LIGHT_GEOMETRY::ELG_SPHERE; - int renderMode = E_RENDER_MODE::ERM_GLSL; + int renderMode = E_RENDER_MODE::ERM_HLSL; int spp = 32; int depth = 3; From 3cdfb4baf2df319643620a8189c277dec20cb163 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 24 Mar 2025 14:43:11 +0700 Subject: [PATCH 46/53] use morton and virtual indexing --- .../app_resources/glsl/common.glsl | 199 +++++++++--------- .../app_resources/hlsl/render.comp.hlsl | 93 ++++---- 31_HLSLPathTracer/main.cpp | 3 +- 3 files changed, 156 insertions(+), 139 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/glsl/common.glsl b/31_HLSLPathTracer/app_resources/glsl/common.glsl index 1a1594e6a..c04ad2b11 100644 --- a/31_HLSLPathTracer/app_resources/glsl/common.glsl +++ b/31_HLSLPathTracer/app_resources/glsl/common.glsl @@ -9,7 +9,7 @@ // debug //#define NEE_ONLY -layout(set = 2, binding = 0) uniform sampler2D envMap; +layout(set = 2, binding = 0) uniform sampler2D envMap; layout(set = 2, binding = 1) uniform usamplerBuffer sampleSequence; layout(set = 2, binding = 2) uniform usampler2D scramblebuf; @@ -35,6 +35,7 @@ vec2 getTexCoords() { #include #include #include +#include #include @@ -51,7 +52,7 @@ struct Sphere vec3 position; float radius2; uint bsdfLightIDs; -}; +}; Sphere Sphere_Sphere(in vec3 position, in float radius, in uint bsdfID, in uint lightID) { @@ -188,7 +189,7 @@ void Rectangle_getNormalBasis(in Rectangle rect, out mat3 basis, out vec2 extent basis[0] = rect.edge0/extents[0]; basis[1] = rect.edge1/extents[1]; basis[2] = normalize(cross(basis[0],basis[1])); -} +} // return intersection distance if found, nbl_glsl_FLT_NAN otherwise float Rectangle_intersect(in Rectangle rect, in vec3 origin, in vec3 direction) @@ -222,7 +223,7 @@ vec3 Rectangle_getNormalTimesArea(in Rectangle rect) #define OP_BITS_OFFSET 0 #define OP_BITS_SIZE 2 struct BSDFNode -{ +{ uvec4 data[2]; }; @@ -386,13 +387,13 @@ vec2 SampleSphericalMap(vec3 v) { vec2 uv = vec2(atan(v.z, v.x), asin(v.y)); uv *= nbl_glsl_RECIPROCAL_PI*0.5; - uv += 0.5; + uv += 0.5; return uv; } void missProgram(in ImmutableRay_t _immutable, inout Payload_t _payload) { - vec3 finalContribution = _payload.throughput; + vec3 finalContribution = _payload.throughput; // #define USE_ENVMAP #ifdef USE_ENVMAP vec2 uv = SampleSphericalMap(_immutable.direction); @@ -415,7 +416,7 @@ nbl_glsl_LightSample nbl_glsl_bsdf_cos_generate(in nbl_glsl_AnisotropicViewSurfa { const float a = BSDFNode_getRoughness(bsdf); const mat2x3 ior = BSDFNode_getEta(bsdf); - + // fresnel stuff for dielectrics float orientedEta, rcpOrientedEta; const bool viewerInsideMedium = nbl_glsl_getOrientedEtas(orientedEta,rcpOrientedEta,interaction.isotropic.NdotV,monochromeEta); @@ -519,7 +520,7 @@ int traceRay(inout float intersectionT, in vec3 origin, in vec3 direction) intersectionT = closerIntersection ? t : intersectionT; objectID = closerIntersection ? i:objectID; - + // allowing early out results in a performance regression, WTF!? //if (anyHit && closerIntersection) //break; @@ -543,7 +544,7 @@ nbl_glsl_LightSample nbl_glsl_light_generate_and_remainder_and_pdf(out vec3 rema { // normally we'd pick from set of lights, using `xi.z` const Light light = lights[0]; - + vec3 L = nbl_glsl_light_generate_and_pdf(pdf,newRayMaxT,origin,interaction,isBSDF,xi,Light_getObjectID(light)); newRayMaxT *= getEndTolerance(depth); @@ -663,7 +664,7 @@ bool closestHitProgram(in uint depth, in uint _sample, inout Ray_t ray, inout nb // bsdfSampleL = bsdf_sample.L; } - + // additional threshold const float lumaThroughputThreshold = lumaContributionThreshold; if (bsdfPdf>bsdfPdfThreshold && getLuma(throughput)>lumaThroughputThreshold) @@ -671,7 +672,7 @@ bool closestHitProgram(in uint depth, in uint _sample, inout Ray_t ray, inout nb ray._payload.throughput = throughput; ray._payload.otherTechniqueHeuristic = neeProbability/bsdfPdf; // numerically stable, don't touch ray._payload.otherTechniqueHeuristic *= ray._payload.otherTechniqueHeuristic; - + // trace new ray ray._immutable.origin = intersection+bsdfSampleL*(1.0/*kSceneSize*/)*getStartTolerance(depth); ray._immutable.direction = bsdfSampleL; @@ -688,109 +689,115 @@ bool closestHitProgram(in uint depth, in uint _sample, inout Ray_t ray, inout nb void main() { const ivec2 imageExtents = imageSize(outImage); - const ivec2 coords = getCoordinates(); - vec2 texCoord = vec2(coords) / vec2(imageExtents); - texCoord.y = 1.0 - texCoord.y; - - if (false == (all(lessThanEqual(ivec2(0),coords)) && all(greaterThan(imageExtents,coords)))) { - return; - } - if (((PTPushConstant.depth-1)>>MAX_DEPTH_LOG2)>0 || ((PTPushConstant.sampleCount-1)>>MAX_SAMPLES_LOG2)>0) + uint virtualThreadIndex; + for (uint virtualThreadBase = gl_WorkGroupID.x * _NBL_GLSL_WORKGROUP_SIZE_; virtualThreadBase < 1920*1080; virtualThreadBase += gl_NumWorkGroups.x * _NBL_GLSL_WORKGROUP_SIZE_) // not sure why 1280*720 doesn't cover entire window { - vec4 pixelCol = vec4(1.0,0.0,0.0,1.0); - imageStore(outImage, coords, pixelCol); - return; - } - - nbl_glsl_xoroshiro64star_state_t scramble_start_state = texelFetch(scramblebuf,coords,0).rg; - const vec2 pixOffsetParam = vec2(1.0)/vec2(textureSize(scramblebuf,0)); + virtualThreadIndex = virtualThreadBase + gl_LocalInvocationIndex.x; + const ivec2 coords = ivec2(nbl_glsl_morton_decode2d32b(virtualThreadIndex)); // getCoordinates(); + vec2 texCoord = vec2(coords) / vec2(imageExtents); + texCoord.y = 1.0 - texCoord.y; + if (false == (all(lessThanEqual(ivec2(0),coords)) && all(greaterThan(imageExtents,coords)))) { + continue; + } - const mat4 invMVP = PTPushConstant.invMVP; - - vec4 NDC = vec4(texCoord*vec2(2.0,-2.0)+vec2(-1.0,1.0),0.0,1.0); - vec3 camPos; - { - vec4 tmp = invMVP*NDC; - camPos = tmp.xyz/tmp.w; - NDC.z = 1.0; - } + if (((PTPushConstant.depth-1)>>MAX_DEPTH_LOG2)>0 || ((PTPushConstant.sampleCount-1)>>MAX_SAMPLES_LOG2)>0) + { + vec4 pixelCol = vec4(1.0,0.0,0.0,1.0); + imageStore(outImage, coords, pixelCol); + continue; + } - vec3 color = vec3(0.0); - float meanLumaSquared = 0.0; - // TODO: if we collapse the nested for loop, then all GPUs will get `PTPushConstant.depth` factor speedup, not just NV with separate PC - for (int i=0; i5.0) + color = vec3(1.0,0.0,0.0); #endif - } - #ifdef VISUALIZE_HIGH_VARIANCE - float variance = getLuma(color); - variance *= variance; - variance = meanLumaSquared-variance; - if (variance>5.0) - color = vec3(1.0,0.0,0.0); - #endif - - vec4 pixelCol = vec4(color, 1.0); - imageStore(outImage, coords, pixelCol); + vec4 pixelCol = vec4(color, 1.0); + imageStore(outImage, coords, pixelCol); + } } /** TODO: Improving Rendering diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index b0d221a20..ed7e4a85e 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -2,6 +2,7 @@ #include "nbl/builtin/hlsl/glsl_compat/core.hlsl" #include "nbl/builtin/hlsl/random/pcg.hlsl" #include "nbl/builtin/hlsl/random/xoroshiro.hlsl" +#include "nbl/builtin/hlsl/math/morton.hlsl" #include "nbl/builtin/hlsl/bxdf/reflection.hlsl" #include "nbl/builtin/hlsl/bxdf/transmission.hlsl" @@ -35,7 +36,8 @@ #include "render_common.hlsl" #include "pathtracer.hlsl" -using namespace nbl::hlsl; +using namespace nbl; +using namespace hlsl; NBL_CONSTEXPR uint32_t WorkgroupSize = 512; NBL_CONSTEXPR uint32_t MAX_DEPTH_LOG2 = 4; @@ -155,48 +157,55 @@ void main(uint32_t3 threadID : SV_DispatchThreadID) { uint32_t width, height; outImage.GetDimensions(width, height); - const int32_t2 coords = getCoordinates(); - float32_t2 texCoord = float32_t2(coords) / float32_t2(width, height); - texCoord.y = 1.0 - texCoord.y; - if (false == (all((int32_t2)0 < coords)) && all(int32_t2(width, height) < coords)) { - return; - } - - if (((pc.depth - 1) >> MAX_DEPTH_LOG2) > 0 || ((pc.sampleCount - 1) >> MAX_SAMPLES_LOG2) > 0) - { - float32_t4 pixelCol = float32_t4(1.0,0.0,0.0,1.0); - outImage[coords] = pixelCol; - return; - } - - int flatIdx = glsl::gl_GlobalInvocationID().y * glsl::gl_NumWorkGroups().x * WorkgroupSize + glsl::gl_GlobalInvocationID().x; - - // set up path tracer - ext::PathTracer::PathTracerCreationParams ptCreateParams; - ptCreateParams.rngState = scramblebuf[coords].rg; - - uint2 scrambleDim; - scramblebuf.GetDimensions(scrambleDim.x, scrambleDim.y); - ptCreateParams.pixOffsetParam = (float2)1.0 / float2(scrambleDim); - - float4 NDC = float4(texCoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); + uint32_t virtualThreadIndex; + [loop] + for (uint32_t virtualThreadBase = glsl::gl_WorkGroupID().x * WorkgroupSize; virtualThreadBase < 1920*1080; virtualThreadBase += glsl::gl_NumWorkGroups().x * WorkgroupSize) // not sure why 1280*720 doesn't cover entire window { - float4 tmp = mul(pc.invMVP, NDC); - ptCreateParams.camPos = tmp.xyz / tmp.w; - NDC.z = 1.0; + virtualThreadIndex = virtualThreadBase + glsl::gl_LocalInvocationIndex().x; + const int32_t2 coords = (int32_t2)math::Morton::decode2d(virtualThreadIndex); // getCoordinates(); + float32_t2 texCoord = float32_t2(coords) / float32_t2(width, height); + texCoord.y = 1.0 - texCoord.y; + + if (false == (hlsl::all((int32_t2)0 < coords)) && hlsl::all(int32_t2(width, height) < coords)) { + continue; + } + + if (((pc.depth - 1) >> MAX_DEPTH_LOG2) > 0 || ((pc.sampleCount - 1) >> MAX_SAMPLES_LOG2) > 0) + { + float32_t4 pixelCol = float32_t4(1.0,0.0,0.0,1.0); + outImage[coords] = pixelCol; + continue; + } + + int flatIdx = glsl::gl_GlobalInvocationID().y * glsl::gl_NumWorkGroups().x * WorkgroupSize + glsl::gl_GlobalInvocationID().x; + + // set up path tracer + ext::PathTracer::PathTracerCreationParams ptCreateParams; + ptCreateParams.rngState = scramblebuf[coords].rg; + + uint2 scrambleDim; + scramblebuf.GetDimensions(scrambleDim.x, scrambleDim.y); + ptCreateParams.pixOffsetParam = (float2)1.0 / float2(scrambleDim); + + float4 NDC = float4(texCoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); + { + float4 tmp = hlsl::mul(pc.invMVP, NDC); + ptCreateParams.camPos = tmp.xyz / tmp.w; + NDC.z = 1.0; + } + + ptCreateParams.NDC = NDC; + ptCreateParams.invMVP = pc.invMVP; + + ptCreateParams.diffuseParams = bxdfs[0].params; + ptCreateParams.conductorParams = bxdfs[3].params; + ptCreateParams.dielectricParams = bxdfs[6].params; + + pathtracer_type pathtracer = pathtracer_type::create(ptCreateParams); + + float32_t3 color = pathtracer.getMeasure(pc.sampleCount, pc.depth, scene); + float32_t4 pixCol = float32_t4(color, 1.0); + outImage[coords] = pixCol; } - - ptCreateParams.NDC = NDC; - ptCreateParams.invMVP = pc.invMVP; - - ptCreateParams.diffuseParams = bxdfs[0].params; - ptCreateParams.conductorParams = bxdfs[3].params; - ptCreateParams.dielectricParams = bxdfs[6].params; - - pathtracer_type pathtracer = pathtracer_type::create(ptCreateParams); - - float32_t3 color = pathtracer.getMeasure(pc.sampleCount, pc.depth, scene); - float32_t4 pixCol = float32_t4(color, 1.0); - outImage[coords] = pixCol; } diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index 8394889db..db1e198c5 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -1068,7 +1068,8 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, cmdbuf->bindDescriptorSets(EPBP_COMPUTE, pipeline->getLayout(), 0u, 1u, &m_descriptorSet0.get()); cmdbuf->bindDescriptorSets(EPBP_COMPUTE, pipeline->getLayout(), 2u, 1u, &m_descriptorSet2.get()); cmdbuf->pushConstants(pipeline->getLayout(), IShader::E_SHADER_STAGE::ESS_COMPUTE, 0, sizeof(PTPushConstant), &pc); - cmdbuf->dispatch(1 + (WindowDimensions.x * WindowDimensions.y - 1) / DefaultWorkGroupSize, 1u, 1u); + uint32_t dispatchSize = m_physicalDevice->getLimits().computeOptimalPersistentWorkgroupDispatchSize(WindowDimensions.x * WindowDimensions.y, DefaultWorkGroupSize); + cmdbuf->dispatch(dispatchSize, 1u, 1u); } // TRANSITION m_outImgView to READ (because of descriptorSets0 -> ComputeShader Writes into the image) From 5f93cec878eafcd03a0af1b3d1e4a136deb9bade Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 24 Mar 2025 15:32:09 +0700 Subject: [PATCH 47/53] reverted virtual index, fix hlsl colors --- .../app_resources/glsl/common.glsl | 173 +++++++++--------- .../app_resources/hlsl/render.comp.hlsl | 96 +++++----- 31_HLSLPathTracer/main.cpp | 3 +- 3 files changed, 128 insertions(+), 144 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/glsl/common.glsl b/31_HLSLPathTracer/app_resources/glsl/common.glsl index c04ad2b11..6c2b5f42f 100644 --- a/31_HLSLPathTracer/app_resources/glsl/common.glsl +++ b/31_HLSLPathTracer/app_resources/glsl/common.glsl @@ -35,7 +35,6 @@ vec2 getTexCoords() { #include #include #include -#include #include @@ -689,115 +688,109 @@ bool closestHitProgram(in uint depth, in uint _sample, inout Ray_t ray, inout nb void main() { const ivec2 imageExtents = imageSize(outImage); + const ivec2 coords = getCoordinates(); + vec2 texCoord = vec2(coords) / vec2(imageExtents); + texCoord.y = 1.0 - texCoord.y; - uint virtualThreadIndex; - for (uint virtualThreadBase = gl_WorkGroupID.x * _NBL_GLSL_WORKGROUP_SIZE_; virtualThreadBase < 1920*1080; virtualThreadBase += gl_NumWorkGroups.x * _NBL_GLSL_WORKGROUP_SIZE_) // not sure why 1280*720 doesn't cover entire window + if (false == (all(lessThanEqual(ivec2(0),coords)) && all(greaterThan(imageExtents,coords)))) { + return; + } + + if (((PTPushConstant.depth-1)>>MAX_DEPTH_LOG2)>0 || ((PTPushConstant.sampleCount-1)>>MAX_SAMPLES_LOG2)>0) { - virtualThreadIndex = virtualThreadBase + gl_LocalInvocationIndex.x; - const ivec2 coords = ivec2(nbl_glsl_morton_decode2d32b(virtualThreadIndex)); // getCoordinates(); - vec2 texCoord = vec2(coords) / vec2(imageExtents); - texCoord.y = 1.0 - texCoord.y; + vec4 pixelCol = vec4(1.0,0.0,0.0,1.0); + imageStore(outImage, coords, pixelCol); + return; + } - if (false == (all(lessThanEqual(ivec2(0),coords)) && all(greaterThan(imageExtents,coords)))) { - continue; - } + nbl_glsl_xoroshiro64star_state_t scramble_start_state = texelFetch(scramblebuf,coords,0).rg; + const vec2 pixOffsetParam = vec2(1.0)/vec2(textureSize(scramblebuf,0)); - if (((PTPushConstant.depth-1)>>MAX_DEPTH_LOG2)>0 || ((PTPushConstant.sampleCount-1)>>MAX_SAMPLES_LOG2)>0) - { - vec4 pixelCol = vec4(1.0,0.0,0.0,1.0); - imageStore(outImage, coords, pixelCol); - continue; - } - nbl_glsl_xoroshiro64star_state_t scramble_start_state = texelFetch(scramblebuf,coords,0).rg; - const vec2 pixOffsetParam = vec2(1.0)/vec2(textureSize(scramblebuf,0)); + const mat4 invMVP = PTPushConstant.invMVP; + vec4 NDC = vec4(texCoord*vec2(2.0,-2.0)+vec2(-1.0,1.0),0.0,1.0); + vec3 camPos; + { + vec4 tmp = invMVP*NDC; + camPos = tmp.xyz/tmp.w; + NDC.z = 1.0; + } - const mat4 invMVP = PTPushConstant.invMVP; + vec3 color = vec3(0.0); + float meanLumaSquared = 0.0; + // TODO: if we collapse the nested for loop, then all GPUs will get `PTPushConstant.depth` factor speedup, not just NV with separate PC + for (int i=0; i5.0) - color = vec3(1.0,0.0,0.0); + float luma = getLuma(accumulation); + meanLumaSquared += (luma*luma-meanLumaSquared)*rcpSampleSize; #endif - - vec4 pixelCol = vec4(color, 1.0); - imageStore(outImage, coords, pixelCol); } + + #ifdef VISUALIZE_HIGH_VARIANCE + float variance = getLuma(color); + variance *= variance; + variance = meanLumaSquared-variance; + if (variance>5.0) + color = vec3(1.0,0.0,0.0); + #endif + + vec4 pixelCol = vec4(color, 1.0); + imageStore(outImage, coords, pixelCol); } /** TODO: Improving Rendering diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index ed7e4a85e..b187a1b33 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -2,7 +2,6 @@ #include "nbl/builtin/hlsl/glsl_compat/core.hlsl" #include "nbl/builtin/hlsl/random/pcg.hlsl" #include "nbl/builtin/hlsl/random/xoroshiro.hlsl" -#include "nbl/builtin/hlsl/math/morton.hlsl" #include "nbl/builtin/hlsl/bxdf/reflection.hlsl" #include "nbl/builtin/hlsl/bxdf/transmission.hlsl" @@ -140,9 +139,9 @@ static const bxdfnode_type bxdfs[BXDF_COUNT] = { bxdfnode_type::create(ext::MaterialSystem::MaterialType::DIFFUSE, false, float2(0,0), spectral_t(0.8,0.8,0.8)), bxdfnode_type::create(ext::MaterialSystem::MaterialType::DIFFUSE, false, float2(0,0), spectral_t(0.8,0.4,0.4)), bxdfnode_type::create(ext::MaterialSystem::MaterialType::DIFFUSE, false, float2(0,0), spectral_t(0.4,0.8,0.4)), - bxdfnode_type::create(ext::MaterialSystem::MaterialType::CONDUCTOR, false, float2(0,0), spectral_t(1,1,1), spectral_t(0.98,0.98,0.77)), - bxdfnode_type::create(ext::MaterialSystem::MaterialType::CONDUCTOR, false, float2(0,0), spectral_t(1,1,1), spectral_t(0.98,0.77,0.98)), - bxdfnode_type::create(ext::MaterialSystem::MaterialType::CONDUCTOR, false, float2(0.15,0.15), spectral_t(1,1,1), spectral_t(0.98,0.77,0.98)), + bxdfnode_type::create(ext::MaterialSystem::MaterialType::CONDUCTOR, false, float2(0,0), spectral_t(1.02,1.02,1.3), spectral_t(1.0,1.0,2.0)), + bxdfnode_type::create(ext::MaterialSystem::MaterialType::CONDUCTOR, false, float2(0,0), spectral_t(1.02,1.3,1.02), spectral_t(1.0,2.0,1.0)), + bxdfnode_type::create(ext::MaterialSystem::MaterialType::CONDUCTOR, false, float2(0.15,0.15), spectral_t(1.02,1.3,1.02), spectral_t(1.0,2.0,1.0)), bxdfnode_type::create(ext::MaterialSystem::MaterialType::DIELECTRIC, false, float2(0.0625,0.0625), spectral_t(1,1,1), spectral_t(0.71,0.69,0.67)) }; @@ -157,55 +156,48 @@ void main(uint32_t3 threadID : SV_DispatchThreadID) { uint32_t width, height; outImage.GetDimensions(width, height); + const int32_t2 coords = getCoordinates(); + float32_t2 texCoord = float32_t2(coords) / float32_t2(width, height); + texCoord.y = 1.0 - texCoord.y; - uint32_t virtualThreadIndex; - [loop] - for (uint32_t virtualThreadBase = glsl::gl_WorkGroupID().x * WorkgroupSize; virtualThreadBase < 1920*1080; virtualThreadBase += glsl::gl_NumWorkGroups().x * WorkgroupSize) // not sure why 1280*720 doesn't cover entire window + if (false == (all((int32_t2)0 < coords)) && all(int32_t2(width, height) < coords)) { + return; + } + + if (((pc.depth - 1) >> MAX_DEPTH_LOG2) > 0 || ((pc.sampleCount - 1) >> MAX_SAMPLES_LOG2) > 0) + { + float32_t4 pixelCol = float32_t4(1.0,0.0,0.0,1.0); + outImage[coords] = pixelCol; + return; + } + + int flatIdx = glsl::gl_GlobalInvocationID().y * glsl::gl_NumWorkGroups().x * WorkgroupSize + glsl::gl_GlobalInvocationID().x; + + // set up path tracer + ext::PathTracer::PathTracerCreationParams ptCreateParams; + ptCreateParams.rngState = scramblebuf[coords].rg; + + uint2 scrambleDim; + scramblebuf.GetDimensions(scrambleDim.x, scrambleDim.y); + ptCreateParams.pixOffsetParam = (float2)1.0 / float2(scrambleDim); + + float4 NDC = float4(texCoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); { - virtualThreadIndex = virtualThreadBase + glsl::gl_LocalInvocationIndex().x; - const int32_t2 coords = (int32_t2)math::Morton::decode2d(virtualThreadIndex); // getCoordinates(); - float32_t2 texCoord = float32_t2(coords) / float32_t2(width, height); - texCoord.y = 1.0 - texCoord.y; - - if (false == (hlsl::all((int32_t2)0 < coords)) && hlsl::all(int32_t2(width, height) < coords)) { - continue; - } - - if (((pc.depth - 1) >> MAX_DEPTH_LOG2) > 0 || ((pc.sampleCount - 1) >> MAX_SAMPLES_LOG2) > 0) - { - float32_t4 pixelCol = float32_t4(1.0,0.0,0.0,1.0); - outImage[coords] = pixelCol; - continue; - } - - int flatIdx = glsl::gl_GlobalInvocationID().y * glsl::gl_NumWorkGroups().x * WorkgroupSize + glsl::gl_GlobalInvocationID().x; - - // set up path tracer - ext::PathTracer::PathTracerCreationParams ptCreateParams; - ptCreateParams.rngState = scramblebuf[coords].rg; - - uint2 scrambleDim; - scramblebuf.GetDimensions(scrambleDim.x, scrambleDim.y); - ptCreateParams.pixOffsetParam = (float2)1.0 / float2(scrambleDim); - - float4 NDC = float4(texCoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); - { - float4 tmp = hlsl::mul(pc.invMVP, NDC); - ptCreateParams.camPos = tmp.xyz / tmp.w; - NDC.z = 1.0; - } - - ptCreateParams.NDC = NDC; - ptCreateParams.invMVP = pc.invMVP; - - ptCreateParams.diffuseParams = bxdfs[0].params; - ptCreateParams.conductorParams = bxdfs[3].params; - ptCreateParams.dielectricParams = bxdfs[6].params; - - pathtracer_type pathtracer = pathtracer_type::create(ptCreateParams); - - float32_t3 color = pathtracer.getMeasure(pc.sampleCount, pc.depth, scene); - float32_t4 pixCol = float32_t4(color, 1.0); - outImage[coords] = pixCol; + float4 tmp = mul(pc.invMVP, NDC); + ptCreateParams.camPos = tmp.xyz / tmp.w; + NDC.z = 1.0; } + + ptCreateParams.NDC = NDC; + ptCreateParams.invMVP = pc.invMVP; + + ptCreateParams.diffuseParams = bxdfs[0].params; + ptCreateParams.conductorParams = bxdfs[3].params; + ptCreateParams.dielectricParams = bxdfs[6].params; + + pathtracer_type pathtracer = pathtracer_type::create(ptCreateParams); + + float32_t3 color = pathtracer.getMeasure(pc.sampleCount, pc.depth, scene); + float32_t4 pixCol = float32_t4(color, 1.0); + outImage[coords] = pixCol; } diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index db1e198c5..8394889db 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -1068,8 +1068,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, cmdbuf->bindDescriptorSets(EPBP_COMPUTE, pipeline->getLayout(), 0u, 1u, &m_descriptorSet0.get()); cmdbuf->bindDescriptorSets(EPBP_COMPUTE, pipeline->getLayout(), 2u, 1u, &m_descriptorSet2.get()); cmdbuf->pushConstants(pipeline->getLayout(), IShader::E_SHADER_STAGE::ESS_COMPUTE, 0, sizeof(PTPushConstant), &pc); - uint32_t dispatchSize = m_physicalDevice->getLimits().computeOptimalPersistentWorkgroupDispatchSize(WindowDimensions.x * WindowDimensions.y, DefaultWorkGroupSize); - cmdbuf->dispatch(dispatchSize, 1u, 1u); + cmdbuf->dispatch(1 + (WindowDimensions.x * WindowDimensions.y - 1) / DefaultWorkGroupSize, 1u, 1u); } // TRANSITION m_outImgView to READ (because of descriptorSets0 -> ComputeShader Writes into the image) From 78de4f546a100b78ce6998f4cd49099b604176fa Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 25 Mar 2025 15:45:07 +0700 Subject: [PATCH 48/53] fixed some bugs for cpp compat --- 31_HLSLPathTracer/app_resources/hlsl/common.hlsl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl index 2e2561345..31bcca26a 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/common.hlsl @@ -10,6 +10,8 @@ #include #include #include +#include +#include namespace nbl { @@ -121,7 +123,7 @@ struct BxDFNode retval.albedo = albedo; retval.materialType = materialType; retval.params.is_aniso = isAniso; - retval.params.A = hlsl::max(A, 1e-4); + retval.params.A = hlsl::max(A, (float32_t2)1e-4); retval.params.ior0 = (spectral_type)1.0; retval.params.ior1 = (spectral_type)1.0; return retval; @@ -134,7 +136,7 @@ struct BxDFNode retval.albedo = (spectral_type)1.0; retval.materialType = materialType; retval.params.is_aniso = isAniso; - retval.params.A = hlsl::max(A, 1e-4); + retval.params.A = hlsl::max(A, (float32_t2)1e-4); retval.params.ior0 = ior0; retval.params.ior1 = ior1; return retval; @@ -218,7 +220,7 @@ struct Shape float32_t3 getNormal(NBL_CONST_REF_ARG(float32_t3) hitPosition) { - const float radiusRcp = spirv::inverseSqrt(radius2); + const float radiusRcp = hlsl::rsqrt(radius2); return (hitPosition - position) * radiusRcp; } From f8237715997821ec0bf5f7fa2fed92dbabe56e52 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 28 Mar 2025 10:46:10 +0700 Subject: [PATCH 49/53] use dropdown, more options --- 31_HLSLPathTracer/main.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index 8394889db..706a0f713 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -41,7 +41,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, { ERM_GLSL, ERM_HLSL, - ERM_CHECKERED, + // ERM_CHECKERED, ERM_COUNT }; @@ -68,6 +68,11 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, "ELG_RECTANGLE" }; + const char* shaderTypes[E_RENDER_MODE::ERM_COUNT] = { + "ERM_GLSL", + "ERM_HLSL" + }; + public: inline HLSLComputePathtracer(const path& _localInputCWD, const path& _localOutputCWD, const path& _sharedInputCWD, const path& _sharedOutputCWD) : IApplicationFramework(_localInputCWD, _localOutputCWD, _sharedInputCWD, _sharedOutputCWD) {} @@ -935,7 +940,8 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, ImGui::SliderFloat("Fov", &fov, 20.f, 150.f); ImGui::SliderFloat("zNear", &zNear, 0.1f, 100.f); ImGui::SliderFloat("zFar", &zFar, 110.f, 10000.f); - ImGui::ListBox("Shader", &PTPipline, shaderNames, E_LIGHT_GEOMETRY::ELG_COUNT); + ImGui::Combo("Shader", &PTPipeline, shaderNames, E_LIGHT_GEOMETRY::ELG_COUNT); + ImGui::Combo("Render Mode", &renderMode, shaderTypes, E_RENDER_MODE::ERM_COUNT); ImGui::SliderInt("SPP", &spp, 1, MaxBufferSamples); ImGui::SliderInt("Depth", &depth, 1, MaxBufferDimensions / 3); @@ -1063,7 +1069,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, // cube envmap handle { - auto pipeline = renderMode == E_RENDER_MODE::ERM_HLSL ? m_PTHLSLPipelines[PTPipline].get() : m_PTGLSLPipelines[PTPipline].get(); + auto pipeline = renderMode == E_RENDER_MODE::ERM_HLSL ? m_PTHLSLPipelines[PTPipeline].get() : m_PTGLSLPipelines[PTPipeline].get(); cmdbuf->bindComputePipeline(pipeline); cmdbuf->bindDescriptorSets(EPBP_COMPUTE, pipeline->getLayout(), 0u, 1u, &m_descriptorSet0.get()); cmdbuf->bindDescriptorSets(EPBP_COMPUTE, pipeline->getLayout(), 2u, 1u, &m_descriptorSet2.get()); @@ -1347,7 +1353,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, float viewWidth = 10.f; float camYAngle = 165.f / 180.f * 3.14159f; float camXAngle = 32.f / 180.f * 3.14159f; - int PTPipline = E_LIGHT_GEOMETRY::ELG_SPHERE; + int PTPipeline = E_LIGHT_GEOMETRY::ELG_SPHERE; int renderMode = E_RENDER_MODE::ERM_HLSL; int spp = 32; int depth = 3; From 1535561525c1df59d227969692ae7405b507962b Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 28 Mar 2025 12:45:14 +0700 Subject: [PATCH 50/53] added persistent workgroup toggle --- .../app_resources/glsl/common.glsl | 25 ++++++ .../app_resources/hlsl/render.comp.hlsl | 24 ++++++ 31_HLSLPathTracer/main.cpp | 82 +++++++++++++++++-- 3 files changed, 123 insertions(+), 8 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/glsl/common.glsl b/31_HLSLPathTracer/app_resources/glsl/common.glsl index 6c2b5f42f..6b6e96710 100644 --- a/31_HLSLPathTracer/app_resources/glsl/common.glsl +++ b/31_HLSLPathTracer/app_resources/glsl/common.glsl @@ -35,6 +35,9 @@ vec2 getTexCoords() { #include #include #include +#ifdef PERSISTENT_WORKGROUPS +#include +#endif #include @@ -688,19 +691,37 @@ bool closestHitProgram(in uint depth, in uint _sample, inout Ray_t ray, inout nb void main() { const ivec2 imageExtents = imageSize(outImage); + +#ifdef PERSISTENT_WORKGROUPS + uint virtualThreadIndex; + for (uint virtualThreadBase = gl_WorkGroupID.x * _NBL_GLSL_WORKGROUP_SIZE_; virtualThreadBase < 1920*1080; virtualThreadBase += gl_NumWorkGroups.x * _NBL_GLSL_WORKGROUP_SIZE_) // not sure why 1280*720 doesn't cover draw surface + { + virtualThreadIndex = virtualThreadBase + gl_LocalInvocationIndex.x; + const ivec2 coords = ivec2(nbl_glsl_morton_decode2d32b(virtualThreadIndex)); +#else const ivec2 coords = getCoordinates(); +#endif + vec2 texCoord = vec2(coords) / vec2(imageExtents); texCoord.y = 1.0 - texCoord.y; if (false == (all(lessThanEqual(ivec2(0),coords)) && all(greaterThan(imageExtents,coords)))) { +#ifdef PERSISTENT_WORKGROUPS + continue; +#else return; +#endif } if (((PTPushConstant.depth-1)>>MAX_DEPTH_LOG2)>0 || ((PTPushConstant.sampleCount-1)>>MAX_SAMPLES_LOG2)>0) { vec4 pixelCol = vec4(1.0,0.0,0.0,1.0); imageStore(outImage, coords, pixelCol); +#ifdef PERSISTENT_WORKGROUPS + continue; +#else return; +#endif } nbl_glsl_xoroshiro64star_state_t scramble_start_state = texelFetch(scramblebuf,coords,0).rg; @@ -791,6 +812,10 @@ void main() vec4 pixelCol = vec4(color, 1.0); imageStore(outImage, coords, pixelCol); + +#ifdef PERSISTENT_WORKGROUPS + } +#endif } /** TODO: Improving Rendering diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index b187a1b33..81736f508 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -2,6 +2,9 @@ #include "nbl/builtin/hlsl/glsl_compat/core.hlsl" #include "nbl/builtin/hlsl/random/pcg.hlsl" #include "nbl/builtin/hlsl/random/xoroshiro.hlsl" +#ifdef PERSISTENT_WORKGROUPS +#include "nbl/builtin/hlsl/math/morton.hlsl" +#endif #include "nbl/builtin/hlsl/bxdf/reflection.hlsl" #include "nbl/builtin/hlsl/bxdf/transmission.hlsl" @@ -156,19 +159,36 @@ void main(uint32_t3 threadID : SV_DispatchThreadID) { uint32_t width, height; outImage.GetDimensions(width, height); +#ifdef PERSISTENT_WORKGROUPS + uint32_t virtualThreadIndex; + [loop] + for (uint32_t virtualThreadBase = glsl::gl_WorkGroupID().x * WorkgroupSize; virtualThreadBase < 1920*1080; virtualThreadBase += glsl::gl_NumWorkGroups().x * WorkgroupSize) // not sure why 1280*720 doesn't cover draw surface + { + virtualThreadIndex = virtualThreadBase + glsl::gl_LocalInvocationIndex().x; + const int32_t2 coords = (int32_t2)math::Morton::decode2d(virtualThreadIndex); +#else const int32_t2 coords = getCoordinates(); +#endif float32_t2 texCoord = float32_t2(coords) / float32_t2(width, height); texCoord.y = 1.0 - texCoord.y; if (false == (all((int32_t2)0 < coords)) && all(int32_t2(width, height) < coords)) { +#ifdef PERSISTENT_WORKGROUPS + continue; +#else return; +#endif } if (((pc.depth - 1) >> MAX_DEPTH_LOG2) > 0 || ((pc.sampleCount - 1) >> MAX_SAMPLES_LOG2) > 0) { float32_t4 pixelCol = float32_t4(1.0,0.0,0.0,1.0); outImage[coords] = pixelCol; +#ifdef PERSISTENT_WORKGROUPS + continue; +#else return; +#endif } int flatIdx = glsl::gl_GlobalInvocationID().y * glsl::gl_NumWorkGroups().x * WorkgroupSize + glsl::gl_GlobalInvocationID().x; @@ -200,4 +220,8 @@ void main(uint32_t3 threadID : SV_DispatchThreadID) float32_t3 color = pathtracer.getMeasure(pc.sampleCount, pc.depth, scene); float32_t4 pixCol = float32_t4(color, 1.0); outImage[coords] = pixCol; + +#ifdef PERSISTENT_WORKGROUPS + } +#endif } diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index 706a0f713..0dc5fc053 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -323,7 +323,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, m_presentDescriptorSet = presentDSPool->createDescriptorSet(gpuPresentDescriptorSetLayout); // Create Shaders - auto loadAndCompileGLSLShader = [&](const std::string& pathToShader) -> smart_refctd_ptr + auto loadAndCompileGLSLShader = [&](const std::string& pathToShader, bool persistentWorkGroups = false) -> smart_refctd_ptr { IAssetLoader::SAssetLoadParams lp = {}; lp.workingDirectory = localInputCWD; @@ -339,6 +339,27 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, // The down-cast should not fail! assert(source); + auto compiler = make_smart_refctd_ptr(smart_refctd_ptr(m_system)); + CGLSLCompiler::SOptions options = {}; + options.stage = IShader::E_SHADER_STAGE::ESS_COMPUTE; // should be compute + options.targetSpirvVersion = m_device->getPhysicalDevice()->getLimits().spirvVersion; + options.spirvOptimizer = nullptr; +#ifndef _NBL_DEBUG + ISPIRVOptimizer::E_OPTIMIZER_PASS optPasses = ISPIRVOptimizer::EOP_STRIP_DEBUG_INFO; + auto opt = make_smart_refctd_ptr(std::span(&optPasses, 1)); + options.spirvOptimizer = opt.get(); +#endif + options.debugInfoFlags |= IShaderCompiler::E_DEBUG_INFO_FLAGS::EDIF_LINE_BIT; + options.preprocessorOptions.sourceIdentifier = source->getFilepathHint(); + options.preprocessorOptions.logger = m_logger.get(); + options.preprocessorOptions.includeFinder = compiler->getDefaultIncludeFinder(); + + const IShaderCompiler::SMacroDefinition persistentDefine = { "PERSISTENT_WORKGROUPS", "1" }; + if (persistentWorkGroups) + options.preprocessorOptions.extraDefines = { &persistentDefine, &persistentDefine + 1 }; + + source = compiler->compileToSPIRV((const char*)source->getContent()->getPointer(), options); + // this time we skip the use of the asset converter since the ICPUShader->IGPUShader path is quick and simple auto shader = m_device->createShader(source.get()); if (!shader) @@ -350,7 +371,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, return shader; }; - auto loadAndCompileHLSLShader = [&](const std::string& pathToShader, const std::string& defineMacro) -> smart_refctd_ptr + auto loadAndCompileHLSLShader = [&](const std::string& pathToShader, const std::string& defineMacro = "", bool persistentWorkGroups = false) -> smart_refctd_ptr { IAssetLoader::SAssetLoadParams lp = {}; lp.workingDirectory = localInputCWD; @@ -368,7 +389,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, auto compiler = make_smart_refctd_ptr(smart_refctd_ptr(m_system)); CHLSLCompiler::SOptions options = {}; - options.stage = IShader::E_SHADER_STAGE::ESS_COMPUTE; // should be compute + options.stage = IShader::E_SHADER_STAGE::ESS_COMPUTE; options.targetSpirvVersion = m_device->getPhysicalDevice()->getLimits().spirvVersion; options.spirvOptimizer = nullptr; #ifndef _NBL_DEBUG @@ -381,8 +402,11 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, options.preprocessorOptions.logger = m_logger.get(); options.preprocessorOptions.includeFinder = compiler->getDefaultIncludeFinder(); - const IShaderCompiler::SMacroDefinition variantDefine = { defineMacro, "" }; - options.preprocessorOptions.extraDefines = { &variantDefine, &variantDefine + 1 }; + const IShaderCompiler::SMacroDefinition defines[2] = { {defineMacro, ""}, { "PERSISTENT_WORKGROUPS", "1" } }; + if (!defineMacro.empty() && persistentWorkGroups) + options.preprocessorOptions.extraDefines = { defines, defines + 2 }; + else if (!defineMacro.empty() && !persistentWorkGroups) + options.preprocessorOptions.extraDefines = { defines, defines + 1 }; source = compiler->compileToSPIRV((const char*)source->getContent()->getPointer(), options); @@ -441,6 +465,34 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, if (!m_device->createComputePipelines(nullptr, { ¶ms, 1 }, m_PTHLSLPipelines.data() + index)) return logFail("Failed to create HLSL compute pipeline!\n"); } + + // persistent wg pipelines + { + auto ptShader = loadAndCompileGLSLShader(PTGLSLShaderPaths[index], true); + + IGPUComputePipeline::SCreationParams params = {}; + params.layout = ptPipelineLayout.get(); + params.shader.shader = ptShader.get(); + params.shader.entryPoint = "main"; + params.shader.entries = nullptr; + params.shader.requireFullSubgroups = true; + params.shader.requiredSubgroupSize = static_cast(5); + if (!m_device->createComputePipelines(nullptr, { ¶ms, 1 }, m_PTGLSLPersistentWGPipelines.data() + index)) + return logFail("Failed to create GLSL PersistentWG compute pipeline!\n"); + } + { + auto ptShader = loadAndCompileHLSLShader(PTHLSLShaderPath, PTHLSLShaderVariants[index], true); + + IGPUComputePipeline::SCreationParams params = {}; + params.layout = ptPipelineLayout.get(); + params.shader.shader = ptShader.get(); + params.shader.entryPoint = "main"; + params.shader.entries = nullptr; + params.shader.requireFullSubgroups = true; + params.shader.requiredSubgroupSize = static_cast(5); + if (!m_device->createComputePipelines(nullptr, { ¶ms, 1 }, m_PTHLSLPersistentWGPipelines.data() + index)) + return logFail("Failed to create HLSL PersistentWG compute pipeline!\n"); + } } } @@ -452,7 +504,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, return logFail("Failed to create Full Screen Triangle protopipeline or load its vertex shader!"); // Load Fragment Shader - auto fragmentShader = loadAndCompileGLSLShader(PresentShaderPath); + auto fragmentShader = loadAndCompileHLSLShader(PresentShaderPath); if (!fragmentShader) return logFail("Failed to Load and Compile Fragment Shader: lumaMeterShader!"); @@ -944,6 +996,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, ImGui::Combo("Render Mode", &renderMode, shaderTypes, E_RENDER_MODE::ERM_COUNT); ImGui::SliderInt("SPP", &spp, 1, MaxBufferSamples); ImGui::SliderInt("Depth", &depth, 1, MaxBufferDimensions / 3); + ImGui::Checkbox("Persistent WorkGroups", &usePersistentWorkGroups); ImGui::Text("X: %f Y: %f", io.MousePos.x, io.MousePos.y); @@ -1069,12 +1122,22 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, // cube envmap handle { - auto pipeline = renderMode == E_RENDER_MODE::ERM_HLSL ? m_PTHLSLPipelines[PTPipeline].get() : m_PTGLSLPipelines[PTPipeline].get(); + IGPUComputePipeline* pipeline; + if (usePersistentWorkGroups) + pipeline = renderMode == E_RENDER_MODE::ERM_HLSL ? m_PTHLSLPersistentWGPipelines[PTPipeline].get() : m_PTGLSLPersistentWGPipelines[PTPipeline].get(); + else + pipeline = renderMode == E_RENDER_MODE::ERM_HLSL ? m_PTHLSLPipelines[PTPipeline].get() : m_PTGLSLPipelines[PTPipeline].get(); cmdbuf->bindComputePipeline(pipeline); cmdbuf->bindDescriptorSets(EPBP_COMPUTE, pipeline->getLayout(), 0u, 1u, &m_descriptorSet0.get()); cmdbuf->bindDescriptorSets(EPBP_COMPUTE, pipeline->getLayout(), 2u, 1u, &m_descriptorSet2.get()); cmdbuf->pushConstants(pipeline->getLayout(), IShader::E_SHADER_STAGE::ESS_COMPUTE, 0, sizeof(PTPushConstant), &pc); - cmdbuf->dispatch(1 + (WindowDimensions.x * WindowDimensions.y - 1) / DefaultWorkGroupSize, 1u, 1u); + if (usePersistentWorkGroups) + { + uint32_t dispatchSize = m_physicalDevice->getLimits().computeOptimalPersistentWorkgroupDispatchSize(WindowDimensions.x * WindowDimensions.y, DefaultWorkGroupSize); + cmdbuf->dispatch(dispatchSize, 1u, 1u); + } + else + cmdbuf->dispatch(1 + (WindowDimensions.x * WindowDimensions.y - 1) / DefaultWorkGroupSize, 1u, 1u); } // TRANSITION m_outImgView to READ (because of descriptorSets0 -> ComputeShader Writes into the image) @@ -1306,6 +1369,8 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, smart_refctd_ptr m_cmdPool; std::array, E_LIGHT_GEOMETRY::ELG_COUNT> m_PTGLSLPipelines; std::array, E_LIGHT_GEOMETRY::ELG_COUNT> m_PTHLSLPipelines; + std::array, E_LIGHT_GEOMETRY::ELG_COUNT> m_PTGLSLPersistentWGPipelines; + std::array, E_LIGHT_GEOMETRY::ELG_COUNT> m_PTHLSLPersistentWGPipelines; smart_refctd_ptr m_presentPipeline; uint64_t m_realFrameIx = 0; std::array, MaxFramesInFlight> m_cmdBufs; @@ -1357,6 +1422,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, int renderMode = E_RENDER_MODE::ERM_HLSL; int spp = 32; int depth = 3; + bool usePersistentWorkGroups = false; bool m_firstFrame = true; IGPUCommandBuffer::SClearColorValue clearColor = { .float32 = {0.f,0.f,0.f,1.f} }; From 52c1aa54cf859b63a8ff6df648f743003e5e13fe Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 27 Jun 2025 10:18:40 +0700 Subject: [PATCH 51/53] ready changes for mesh_loaders merge, requires examples.hpp from mesh_loaders --- .../include/nbl/this_example/common.hpp | 2 +- 31_HLSLPathTracer/main.cpp | 34 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/31_HLSLPathTracer/include/nbl/this_example/common.hpp b/31_HLSLPathTracer/include/nbl/this_example/common.hpp index ff3dd8095..b08656eee 100644 --- a/31_HLSLPathTracer/include/nbl/this_example/common.hpp +++ b/31_HLSLPathTracer/include/nbl/this_example/common.hpp @@ -6,7 +6,7 @@ // common api #include "CCamera.hpp" #include "SimpleWindowedApplication.hpp" -#include "nbl/application_templates/MonoAssetManagerAndBuiltinResourceApplication.hpp" +#include "nbl/examples/examples.hpp" #include "CEventCallback.hpp" // example's own headers diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index 0dc5fc053..6b4cad224 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -23,10 +23,10 @@ struct PTPushConstant { // TODO: Add a QueryPool for timestamping once its ready // TODO: Do buffer creation using assConv -class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, public application_templates::MonoAssetManagerAndBuiltinResourceApplication +class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, public examples::BuiltinResourcesApplication { using device_base_t = examples::SimpleWindowedApplication; - using asset_base_t = application_templates::MonoAssetManagerAndBuiltinResourceApplication; + using asset_base_t = examples::BuiltinResourcesApplication; using clock_t = std::chrono::steady_clock; enum E_LIGHT_GEOMETRY : uint8_t @@ -323,7 +323,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, m_presentDescriptorSet = presentDSPool->createDescriptorSet(gpuPresentDescriptorSetLayout); // Create Shaders - auto loadAndCompileGLSLShader = [&](const std::string& pathToShader, bool persistentWorkGroups = false) -> smart_refctd_ptr + auto loadAndCompileGLSLShader = [&](const std::string& pathToShader, bool persistentWorkGroups = false) -> smart_refctd_ptr { IAssetLoader::SAssetLoadParams lp = {}; lp.workingDirectory = localInputCWD; @@ -335,7 +335,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, std::exit(-1); } - auto source = IAsset::castDown(assets[0]); + auto source = smart_refctd_ptr_static_cast(assets[0]); // The down-cast should not fail! assert(source); @@ -361,7 +361,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, source = compiler->compileToSPIRV((const char*)source->getContent()->getPointer(), options); // this time we skip the use of the asset converter since the ICPUShader->IGPUShader path is quick and simple - auto shader = m_device->createShader(source.get()); + auto shader = m_device->compileShader({ source.get(), nullptr, nullptr, nullptr }); if (!shader) { m_logger->log("GLSL shader creationed failed: %s!", ILogger::ELL_ERROR, pathToShader); @@ -371,7 +371,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, return shader; }; - auto loadAndCompileHLSLShader = [&](const std::string& pathToShader, const std::string& defineMacro = "", bool persistentWorkGroups = false) -> smart_refctd_ptr + auto loadAndCompileHLSLShader = [&](const std::string& pathToShader, const std::string& defineMacro = "", bool persistentWorkGroups = false) -> smart_refctd_ptr { IAssetLoader::SAssetLoadParams lp = {}; lp.workingDirectory = localInputCWD; @@ -383,7 +383,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, std::exit(-1); } - auto source = IAsset::castDown(assets[0]); + auto source = smart_refctd_ptr_static_cast(assets[0]); // The down-cast should not fail! assert(source); @@ -410,7 +410,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, source = compiler->compileToSPIRV((const char*)source->getContent()->getPointer(), options); - auto shader = m_device->createShader(source.get()); + auto shader = m_device->compileShader({ source.get(), nullptr, nullptr, nullptr }); if (!shader) { m_logger->log("HLSL shader creationed failed: %s!", ILogger::ELL_ERROR, pathToShader); @@ -447,8 +447,8 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, params.shader.shader = ptShader.get(); params.shader.entryPoint = "main"; params.shader.entries = nullptr; - params.shader.requireFullSubgroups = true; - params.shader.requiredSubgroupSize = static_cast(5); + params.cached.requireFullSubgroups = true; + params.shader.requiredSubgroupSize = static_cast(5); if (!m_device->createComputePipelines(nullptr, { ¶ms, 1 }, m_PTGLSLPipelines.data() + index)) return logFail("Failed to create GLSL compute pipeline!\n"); } @@ -460,8 +460,8 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, params.shader.shader = ptShader.get(); params.shader.entryPoint = "main"; params.shader.entries = nullptr; - params.shader.requireFullSubgroups = true; - params.shader.requiredSubgroupSize = static_cast(5); + params.cached.requireFullSubgroups = true; + params.shader.requiredSubgroupSize = static_cast(5); if (!m_device->createComputePipelines(nullptr, { ¶ms, 1 }, m_PTHLSLPipelines.data() + index)) return logFail("Failed to create HLSL compute pipeline!\n"); } @@ -475,8 +475,8 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, params.shader.shader = ptShader.get(); params.shader.entryPoint = "main"; params.shader.entries = nullptr; - params.shader.requireFullSubgroups = true; - params.shader.requiredSubgroupSize = static_cast(5); + params.cached.requireFullSubgroups = true; + params.shader.requiredSubgroupSize = static_cast(5); if (!m_device->createComputePipelines(nullptr, { ¶ms, 1 }, m_PTGLSLPersistentWGPipelines.data() + index)) return logFail("Failed to create GLSL PersistentWG compute pipeline!\n"); } @@ -488,8 +488,8 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, params.shader.shader = ptShader.get(); params.shader.entryPoint = "main"; params.shader.entries = nullptr; - params.shader.requireFullSubgroups = true; - params.shader.requiredSubgroupSize = static_cast(5); + params.cached.requireFullSubgroups = true; + params.shader.requiredSubgroupSize = static_cast(5); if (!m_device->createComputePipelines(nullptr, { ¶ms, 1 }, m_PTHLSLPersistentWGPipelines.data() + index)) return logFail("Failed to create HLSL PersistentWG compute pipeline!\n"); } @@ -508,7 +508,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, if (!fragmentShader) return logFail("Failed to Load and Compile Fragment Shader: lumaMeterShader!"); - const IGPUShader::SSpecInfo fragSpec = { + const IGPUPipelineBase::SShaderSpecInfo fragSpec = { .entryPoint = "main", .shader = fragmentShader.get() }; From c43c93b75a11870cacef0ad16bc2a8bdf40ae0e3 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 27 Jun 2025 11:26:29 +0700 Subject: [PATCH 52/53] cpp fixes so it compiles at least --- .../include/nbl/this_example/common.hpp | 6 +++--- 31_HLSLPathTracer/main.cpp | 17 +++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/31_HLSLPathTracer/include/nbl/this_example/common.hpp b/31_HLSLPathTracer/include/nbl/this_example/common.hpp index b08656eee..db051bb3e 100644 --- a/31_HLSLPathTracer/include/nbl/this_example/common.hpp +++ b/31_HLSLPathTracer/include/nbl/this_example/common.hpp @@ -4,10 +4,10 @@ #include // common api -#include "CCamera.hpp" -#include "SimpleWindowedApplication.hpp" +#include "nbl/examples/common/SimpleWindowedApplication.hpp" #include "nbl/examples/examples.hpp" -#include "CEventCallback.hpp" +#include "nbl/examples/cameras/CCamera.hpp" +#include "nbl/examples/common/CEventCallback.hpp" // example's own headers #include "nbl/ui/ICursorControl.h" diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index 6b4cad224..576a4c7b0 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -14,6 +14,7 @@ using namespace system; using namespace asset; using namespace ui; using namespace video; +using namespace nbl::examples; struct PTPushConstant { matrix4SIMD invMVP; @@ -23,10 +24,10 @@ struct PTPushConstant { // TODO: Add a QueryPool for timestamping once its ready // TODO: Do buffer creation using assConv -class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, public examples::BuiltinResourcesApplication +class HLSLComputePathtracer final : public SimpleWindowedApplication, public BuiltinResourcesApplication { - using device_base_t = examples::SimpleWindowedApplication; - using asset_base_t = examples::BuiltinResourcesApplication; + using device_base_t = SimpleWindowedApplication; + using asset_base_t = BuiltinResourcesApplication; using clock_t = std::chrono::steady_clock; enum E_LIGHT_GEOMETRY : uint8_t @@ -91,7 +92,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, if (!m_surface) { { - auto windowCallback = core::make_smart_refctd_ptr(smart_refctd_ptr(m_inputSystem), smart_refctd_ptr(m_logger)); + auto windowCallback = core::make_smart_refctd_ptr(smart_refctd_ptr(m_inputSystem), smart_refctd_ptr(m_logger)); IWindow::SCreationParams params = {}; params.callback = core::make_smart_refctd_ptr(); params.width = WindowDimensions.x; @@ -118,7 +119,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, { // Init systems { - m_inputSystem = make_smart_refctd_ptr(logger_opt_smart_ptr(smart_refctd_ptr(m_logger))); + m_inputSystem = make_smart_refctd_ptr(logger_opt_smart_ptr(smart_refctd_ptr(m_logger))); // Remember to call the base class initialization! if (!device_base_t::onAppInitialized(smart_refctd_ptr(system))) @@ -509,8 +510,8 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, return logFail("Failed to Load and Compile Fragment Shader: lumaMeterShader!"); const IGPUPipelineBase::SShaderSpecInfo fragSpec = { - .entryPoint = "main", - .shader = fragmentShader.get() + .shader = fragmentShader.get(), + .entryPoint = "main" }; auto presentLayout = m_device->createPipelineLayout( @@ -1381,7 +1382,7 @@ class HLSLComputePathtracer final : public examples::SimpleWindowedApplication, // system resources core::smart_refctd_ptr m_inputSystem; - InputSystem::ChannelReader mouse; + InputSystem::ChannelReader mouse; InputSystem::ChannelReader keyboard; // pathtracer resources From 8b31859520069831b246d13270b43b97aea83141 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 27 Jun 2025 14:53:17 +0700 Subject: [PATCH 53/53] a bazillion fixes since last time bxdf usages changed --- .../app_resources/hlsl/material_system.hlsl | 84 +++++++++++++------ .../hlsl/next_event_estimator.hlsl | 6 +- .../app_resources/hlsl/pathtracer.hlsl | 41 ++++----- .../app_resources/hlsl/render.comp.hlsl | 11 ++- 31_HLSLPathTracer/main.cpp | 7 -- 5 files changed, 87 insertions(+), 62 deletions(-) diff --git a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl index feffee9ef..4e2fdc5a0 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/material_system.hlsl @@ -14,22 +14,6 @@ namespace ext namespace MaterialSystem { -// struct Material -// { -// enum Type : uint32_t // enum class? -// { -// DIFFUSE, -// CONDUCTOR, -// DIELECTRIC -// }; - -// NBL_CONSTEXPR_STATIC_INLINE uint32_t DataSize = 1; - -// uint32_t type : 2; -// uint32_t unused : 30; // possible space for flags -// uint32_t data[DataSize]; -// }; - enum MaterialType : uint32_t // enum class? { DIFFUSE, @@ -37,6 +21,52 @@ enum MaterialType : uint32_t // enum class? DIELECTRIC }; +template +struct MaterialParams +{ + using this_t = MaterialParams; + using sample_type = typename DiffuseBxDF::sample_type; + using anisotropic_interaction_type = typename DiffuseBxDF::anisotropic_interaction_type; + using isotropic_interaction_type = typename anisotropic_interaction_type::isotropic_interaction_type; + using anisocache_type = typename ConductorBxDF::anisocache_type; + using isocache_type = typename anisocache_type::isocache_type; + + using diffuse_params_type = typename DiffuseBxDF::params_isotropic_t; + using conductor_params_type = typename ConductorBxDF::params_isotropic_t; + using dielectric_params_type = typename DielectricBxDF::params_isotropic_t; + + // we're only doing isotropic for this example + static this_t create(sample_type _sample, isotropic_interaction_type _interaction, isocache_type _cache, bxdf::BxDFClampMode _clamp) + { + this_t retval; + retval._Sample = _sample; + retval.interaction = _interaction; + retval.cache = _cache; + retval.clampMode = _clamp; + return retval; + } + + diffuse_params_type getDiffuseParams() + { + return diffuse_params_type::create(_Sample, interaction, clampMode); + } + + conductor_params_type getConductorParams() + { + return conductor_params_type::create(_Sample, interaction, cache, clampMode); + } + + dielectric_params_type getDielectricParams() + { + return dielectric_params_type::create(_Sample, interaction, cache, clampMode); + } + + sample_type _Sample; + isotropic_interaction_type interaction; + isocache_type cache; + bxdf::BxDFClampMode clampMode; +}; + template // NOTE: these bxdfs should match the ones in Scene BxDFNode struct System { @@ -48,9 +78,11 @@ struct System using sample_type = typename DiffuseBxDF::sample_type; using ray_dir_info_type = typename sample_type::ray_dir_info_type; using quotient_pdf_type = typename DiffuseBxDF::quotient_pdf_type; - using anisotropic_type = typename DiffuseBxDF::anisotropic_type; + using anisotropic_interaction_type = typename DiffuseBxDF::anisotropic_interaction_type; + using isotropic_interaction_type = typename anisotropic_interaction_type::isotropic_interaction_type; using anisocache_type = typename ConductorBxDF::anisocache_type; - using params_t = bxdf::SBxDFParams; + using isocache_type = typename anisocache_type::isocache_type; + using params_t = MaterialParams; using create_params_t = bxdf::SBxDFCreationParams; using diffuse_op_type = DiffuseBxDF; @@ -73,19 +105,19 @@ struct System case MaterialType::DIFFUSE: { diffuseBxDF.init(cparams); - return (measure_type)diffuseBxDF.eval(params); + return (measure_type)diffuseBxDF.eval(params.getDiffuseParams()); } break; case MaterialType::CONDUCTOR: { conductorBxDF.init(cparams); - return conductorBxDF.eval(params); + return conductorBxDF.eval(params.getConductorParams()); } break; case MaterialType::DIELECTRIC: { dielectricBxDF.init(cparams); - return dielectricBxDF.eval(params); + return dielectricBxDF.eval(params.getDielectricParams()); } break; default: @@ -93,7 +125,7 @@ struct System } } - sample_type generate(uint32_t material, NBL_CONST_REF_ARG(create_params_t) cparams, NBL_CONST_REF_ARG(anisotropic_type) interaction, NBL_CONST_REF_ARG(vector3_type) u, NBL_REF_ARG(anisocache_type) _cache) + sample_type generate(uint32_t material, NBL_CONST_REF_ARG(create_params_t) cparams, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector3_type) u, NBL_REF_ARG(anisocache_type) _cache) { switch(material) { @@ -131,26 +163,26 @@ struct System quotient_pdf_type quotient_and_pdf(uint32_t material, NBL_CONST_REF_ARG(create_params_t) cparams, NBL_CONST_REF_ARG(params_t) params) { const float minimumProjVectorLen = 0.00000001; - if (params.NdotV > minimumProjVectorLen && params.NdotL > minimumProjVectorLen) + if (params.interaction.getNdotV() > minimumProjVectorLen && params._Sample.getNdotL() > minimumProjVectorLen) { switch(material) { case MaterialType::DIFFUSE: { diffuseBxDF.init(cparams); - return diffuseBxDF.quotient_and_pdf(params); + return diffuseBxDF.quotient_and_pdf(params.getDiffuseParams()); } break; case MaterialType::CONDUCTOR: { conductorBxDF.init(cparams); - return conductorBxDF.quotient_and_pdf(params); + return conductorBxDF.quotient_and_pdf(params.getConductorParams()); } break; case MaterialType::DIELECTRIC: { dielectricBxDF.init(cparams); - return dielectricBxDF.quotient_and_pdf(params); + return dielectricBxDF.quotient_and_pdf(params.getDielectricParams()); } break; default: diff --git a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl index 51c018ac5..ac74b1abf 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/next_event_estimator.hlsl @@ -294,7 +294,7 @@ struct Estimator using light_type = typename Scene::light_type; using spectral_type = typename light_type::spectral_type; using interaction_type = Aniso; - using quotient_pdf_type = bxdf::quotient_and_pdf; + using quotient_pdf_type = sampling::quotient_and_pdf; using sample_type = LightSample; using ray_dir_info_type = typename sample_type::ray_dir_info_type; @@ -346,7 +346,7 @@ struct Estimator; + using quotient_pdf_type = sampling::quotient_and_pdf; using sample_type = LightSample; using ray_dir_info_type = typename sample_type::ray_dir_info_type; @@ -397,7 +397,7 @@ struct Estimator; + using quotient_pdf_type = sampling::quotient_and_pdf; using sample_type = LightSample; using ray_dir_info_type = typename sample_type::ray_dir_info_type; diff --git a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl index f5d5206dc..add1eb8a9 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/pathtracer.hlsl @@ -58,8 +58,8 @@ struct Unidirectional using ray_type = typename RayGen::ray_type; using light_type = Light; using bxdfnode_type = BxDFNode; - using anisotropic_type = typename MaterialSystem::anisotropic_type; - using isotropic_type = typename anisotropic_type::isotropic_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; @@ -100,8 +100,8 @@ struct Unidirectional const vector3_type intersection = ray.origin + ray.direction * ray.intersectionT; uint32_t bsdfLightIDs; - anisotropic_type interaction; - isotropic_type iso_interaction; + anisotropic_interaction_type interaction; + isotropic_interaction_type iso_interaction; uint32_t mode = objectID.mode; switch (mode) { @@ -116,8 +116,8 @@ struct Unidirectional N = nbl::hlsl::normalize(N); ray_dir_info_type V; V.direction = -ray.direction; - isotropic_type iso_interaction = isotropic_type::create(V, N); - interaction = anisotropic_type::create(iso_interaction); + isotropic_interaction_type iso_interaction = isotropic_interaction_type::create(V, N); + interaction = anisotropic_interaction_type::create(iso_interaction); } break; default: @@ -142,9 +142,9 @@ struct Unidirectional // TODO: ifdef kill diffuse specular paths - const bool isBSDF = (bxdf.materialType == ext::MaterialSystem::MaterialType::DIFFUSE) ? bxdf_traits::type == BT_BSDF : - (bxdf.materialType == ext::MaterialSystem::MaterialType::CONDUCTOR) ? bxdf_traits::type == BT_BSDF : - bxdf_traits::type == BT_BSDF; + const bool isBSDF = (bxdf.materialType == ext::MaterialSystem::MaterialType::DIFFUSE) ? bxdf::traits::type == bxdf::BT_BSDF : + (bxdf.materialType == ext::MaterialSystem::MaterialType::CONDUCTOR) ? bxdf::traits::type == bxdf::BT_BSDF : + bxdf::traits::type == bxdf::BT_BSDF; vector3_type eps0 = rand3d(depth, _sample, 0u); vector3_type eps1 = rand3d(depth, _sample, 1u); @@ -171,24 +171,25 @@ struct Unidirectional ); // We don't allow non watertight transmitters in this renderer - bool validPath = nee_sample.NdotL > numeric_limits::min; + bool validPath = nee_sample.getNdotL() > numeric_limits::min; // but if we allowed non-watertight transmitters (single water surface), it would make sense just to apply this line by itself - anisocache_type _cache; - validPath = validPath && anisocache_type::template compute(_cache, interaction, nee_sample, monochromeEta); + bxdf::fresnel::OrientedEtas orientedEta = bxdf::fresnel::OrientedEtas::create(interaction.getNdotV(), monochromeEta); + anisocache_type _cache = anisocache_type::template create(interaction, nee_sample, orientedEta); + validPath = validPath && _cache.getNdotH() >= 0.0; bxdf.params.eta = monochromeEta; if (neeContrib_pdf.pdf < numeric_limits::max) { - if (nbl::hlsl::any(isnan(nee_sample.L.direction))) + if (nbl::hlsl::any(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.L.direction)) + 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) { bxdf::BxDFClampMode _clamp; _clamp = (bxdf.materialType == ext::MaterialSystem::MaterialType::DIELECTRIC) ? bxdf::BxDFClampMode::BCM_ABS : bxdf::BxDFClampMode::BCM_MAX; // example only uses isotropic bxdfs - params_type params = params_type::template create(nee_sample, interaction.isotropic, _cache.iso_cache, _clamp); + params_type params = params_type::create(nee_sample, interaction.isotropic, _cache.iso_cache, _clamp); quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(bxdf.materialType, bxdf.params, params); neeContrib_pdf.quotient *= bxdf.albedo * throughput * bsdf_quotient_pdf.quotient; @@ -200,8 +201,8 @@ struct Unidirectional // neeContrib_pdf.quotient *= otherGenOverChoice; ray_type nee_ray; - nee_ray.origin = intersection + nee_sample.L.direction * t * Tolerance::getStart(depth); - nee_ray.direction = nee_sample.L.direction; + 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; @@ -221,13 +222,13 @@ struct Unidirectional bxdf::BxDFClampMode _clamp; _clamp = (bxdf.materialType == ext::MaterialSystem::MaterialType::DIELECTRIC) ? bxdf::BxDFClampMode::BCM_ABS : bxdf::BxDFClampMode::BCM_MAX; // example only uses isotropic bxdfs - params_type params = params_type::template create(bsdf_sample, interaction.isotropic, _cache.iso_cache, _clamp); + params_type params = params_type::create(bsdf_sample, interaction.isotropic, _cache.iso_cache, _clamp); // 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, params); throughput *= bxdf.albedo * bsdf_quotient_pdf.quotient; bxdfPdf = bsdf_quotient_pdf.pdf; - bxdfSample = bsdf_sample.L.direction; + bxdfSample = bsdf_sample.getL().getDirection(); } // additional threshold @@ -243,7 +244,7 @@ struct Unidirectional ray.direction = bxdfSample; if ((PTPolygonMethod)nee_type::PolygonMethod == PPM_APPROX_PROJECTED_SOLID_ANGLE) { - ray.normalAtOrigin = interaction.isotropic.N; + ray.normalAtOrigin = interaction.getN(); ray.wasBSDFAtOrigin = isBSDF; } return true; diff --git a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl index 81736f508..a40eb3dd0 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render.comp.hlsl @@ -74,18 +74,17 @@ float32_t2 getTexCoords() using ray_dir_info_t = bxdf::ray_dir_info::SBasic; using iso_interaction = bxdf::surface_interactions::SIsotropic; -using aniso_interaction = bxdf::surface_interactions::SAnisotropic; +using aniso_interaction = bxdf::surface_interactions::SAnisotropic; using sample_t = bxdf::SLightSample; using iso_cache = bxdf::SIsotropicMicrofacetCache; -using aniso_cache = bxdf::SAnisotropicMicrofacetCache; -using quotient_pdf_t = bxdf::quotient_and_pdf; +using aniso_cache = bxdf::SAnisotropicMicrofacetCache; +using quotient_pdf_t = sampling::quotient_and_pdf; using spectral_t = vector; -using params_t = bxdf::SBxDFParams; using create_params_t = bxdf::SBxDFCreationParams; using diffuse_bxdf_type = bxdf::reflection::SOrenNayarBxDF; -using conductor_bxdf_type = bxdf::reflection::SGGXBxDF; -using dielectric_bxdf_type = bxdf::transmission::SGGXDielectricBxDF; +using conductor_bxdf_type = bxdf::reflection::SGGXBxDF; +using dielectric_bxdf_type = bxdf::transmission::SGGXDielectricBxDF; using ray_type = ext::Ray; using light_type = ext::Light; diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index 576a4c7b0..2e139af8d 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -80,13 +80,6 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui inline bool isComputeOnly() const override { return false; } - //inline video::IAPIConnection::SFeatures getAPIFeaturesToEnable() override - //{ - // auto retval = device_base_t::getAPIFeaturesToEnable(); - // retval.synchronizationValidation = true; - // return retval; - //} - inline core::vector getSurfaces() const override { if (!m_surface)