diff --git a/3rdparty/EGL b/3rdparty/EGL new file mode 160000 index 0000000000..9eeb49d626 --- /dev/null +++ b/3rdparty/EGL @@ -0,0 +1 @@ +Subproject commit 9eeb49d626cb1d78c76149602b87aef6720e9c3d diff --git a/3rdparty/SPIRV-Headers b/3rdparty/SPIRV-Headers new file mode 160000 index 0000000000..b8047fbe45 --- /dev/null +++ b/3rdparty/SPIRV-Headers @@ -0,0 +1 @@ +Subproject commit b8047fbe45f426f5918fadc67e8408f5b108c3c9 diff --git a/3rdparty/SPIRV-Tools b/3rdparty/SPIRV-Tools new file mode 160000 index 0000000000..75e53b9f68 --- /dev/null +++ b/3rdparty/SPIRV-Tools @@ -0,0 +1 @@ +Subproject commit 75e53b9f685830ac42242cf0c46cc9af523bd0df diff --git a/3rdparty/radeonrays b/3rdparty/radeonrays new file mode 160000 index 0000000000..e42145aa3b --- /dev/null +++ b/3rdparty/radeonrays @@ -0,0 +1 @@ +Subproject commit e42145aa3b1bedd3a3bb0157a873e561aabb8632 diff --git a/compile.bat b/compile.bat new file mode 100644 index 0000000000..f338de83c0 --- /dev/null +++ b/compile.bat @@ -0,0 +1,114 @@ + +@echo off + +setlocal enabledelayedexpansion + + +:: List of shaders to compile (paths relative to include/nbl/builtin/hlsl/) +set file_path[0]=common +set file_path[1]=algorithm +set file_path[2]=ieee754 + +set file_path[3]=limits/numeric + +set file_path[4]=math/complex +set file_path[5]=math/constants +set file_path[6]=math/functions +set file_path[7]=math/binary_operator_functions +set file_path[8]=math/quaternions + +set file_path[9]=scene/animation +set file_path[10]=scene/keyframe +set file_path[11]=scene/node + +set file_path[12]=format/constants +set file_path[13]=format/decode +set file_path[14]=format/encode + +set file_path[15]=colorspace/decodeCIEXYZ +set file_path[16]=colorspace/encodeCIEXYZ +set file_path[17]=colorspace/EOTF +set file_path[18]=colorspace/OETF + +set file_path[19]=shapes/aabb +set file_path[20]=shapes/rectangle +set file_path[21]=shapes/triangle +set file_path[22]=shapes/frustum + +set file_path[23]=random/xoroshiro + +set file_path[24]=utils/compressed_normal_matrix_t +set file_path[25]=utils/acceleration_structures +set file_path[26]=utils/common +set file_path[27]=utils/culling +set file_path[28]=utils/morton +set file_path[29]=utils/normal_decode +set file_path[30]=utils/normal_encode +set file_path[31]=utils/surface_transform +set file_path[32]=utils/transform +rem set file_path[33]=utils/indirect_commands + +set file_path[33]=loader/mtl/common + +set file_path[34]=lod_library/structs +rem set file_path[36]=lod_library/descriptor_set + +set file_path[35]=property_pool/transfer + +set file_path[36]=blit/formats_encode +rem set file_path[37]=blit/blit/descriptors + + + + +rem set file_path[23]=bxdf/fresnel + + + + + + +set "XOUTPUT_PATH=include/nbl/builtin/hlsl/xoutput/" +set "HLSL_PATH=include/nbl/builtin/hlsl/" + + +:: Count elements in "file_path" array +set /a len=0 +:Loop +if defined file_path[%len%] ( + set /a len+=1 + GOTO :Loop +) +set /a len-=1 + + +cd include/nbl/builtin/hlsl + + +:: Create non-existing file paths +for /L %%a in (0, 1, %len%) do ( + if not exist "xoutput\!file_path[%%a]!" ( + mkdir "xoutput/!file_path[%%a]!" + rmdir "xoutput\!file_path[%%a]!" + ) +) + +cd ../../../../ + +echo: +echo: +echo Compiling HLSL shaders... +echo: +echo: +:: Compile all +for /L %%a in (0, 1, %len%) do ( + 3rdparty\dxc\dxc\bin\x64\dxc.exe -HV 2021 -T lib_6_7 -I include/ -Zi -Qembed_debug -Fo %XOUTPUT_PATH%!file_path[%%a]!.bin %HLSL_PATH%!file_path[%%a]!.hlsl +) +echo: +echo: +echo Done Compiling! +echo Compiled shaders are in - "%XOUTPUT_PATH%" +echo: +echo: + +pause \ No newline at end of file diff --git a/examples_tests b/examples_tests index a1ba284240..68aa6fd867 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit a1ba2842407fef41b1fe11a8bc9c1d638c1b4ab3 +Subproject commit 68aa6fd8674cf6f907706f6b37bf3ed384ccfaf2 diff --git a/include/nbl/builtin/hlsl/bxdf/brdf/diffuse/fresnel_correction.hlsl b/include/nbl/builtin/hlsl/bxdf/brdf/diffuse/fresnel_correction.hlsl new file mode 100644 index 0000000000..9bfc2d049d --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/brdf/diffuse/fresnel_correction.hlsl @@ -0,0 +1,44 @@ +// 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 +#ifndef _NBL_BUILTIN_HLSL_BXDF_BRDF_DIFFUSE_FRESNEL_CORRECTION_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_BRDF_DIFFUSE_FRESNEL_CORRECTION_INCLUDED_ + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace brdf +{ +namespace diffuse +{ + +float3 diffuseFresnelCorrectionFactor(in vec3 n, in vec3 n2) +{ + const float C1 = 554.33; + const float C2 = 380.7; + const float C3 = 298.25; + const float C4 = 261.38; + const float C5 = 138.43; + const float C6 = 0.8078843897748912; + const float C7 = -1.67; + const float C8 = 0.1921156102251088; + + + //assert(n*n==n2); + bool3 TIR = (n < 1.0); + vec3 invdenum = lerp(float3(1.0,1.0,1.0), float3(1.0,1.0,1.0)/(n2*n2*(vec3(C1,C1,C1) - C2*n)), TIR); + vec3 num = n*lerp(float3(C8,C8,C8),n*C3 - C4*n2 + C5,TIR); + num += lerp(float3(C6,C6,C6),float3(C7,C7,C7),TIR); + return num*invdenum; +} + +} +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/brdf/diffuse/lambert.hlsl b/include/nbl/builtin/hlsl/bxdf/brdf/diffuse/lambert.hlsl new file mode 100644 index 0000000000..bd0d4da012 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/brdf/diffuse/lambert.hlsl @@ -0,0 +1,130 @@ +// 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 +#ifndef _NBL_BUILTIN_HLSL_BXDF_BRDF_DIFFUSE_LAMBERT_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_BRDF_DIFFUSE_LAMBERT_INCLUDED_ + +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace brdf +{ +namespace diffuse +{ + +float lambertian() +{ + return math::RECIPROCAL_PI; +} + +float lambertian_cos_eval_rec_pi_factored_out_wo_clamps(in float maxNdotL) +{ + return maxNdotL; +} +float lambertian_cos_eval_rec_pi_factored_out(in float NdotL) +{ + return lambertian_cos_eval_rec_pi_factored_out_wo_clamps(max(NdotL,0.0f)); +} + +float lambertian_cos_eval_wo_clamps(in float maxNdotL) +{ + return lambertian_cos_eval_rec_pi_factored_out_wo_clamps(maxNdotL)*lambertian(); +} +template +float lambertian_cos_eval(in LightSample _sample) +{ + return lambertian_cos_eval_rec_pi_factored_out(_sample.NdotL)*lambertian(); +} + +template +LightSample lambertian_cos_generate_wo_clamps(in float3 tangentSpaceV, in float3x3 m, in float2 u) +{ + float3 L = sampling::projected_hemisphere_generate(u); + + return LightSample::createTangentSpace(tangentSpaceV,IncomingRayDirInfo::create(L),m); +} +template +LightSample lambertian_cos_generate(in surface_interactions::Anisotropic interaction, in float2 u) +{ + return lambertian_cos_generate_wo_clamps(interaction.getTangentSpaceV(),interaction.getTangentFrame(),u); +} + + + +float lambertian_pdf_wo_clamps(in float maxNdotL) +{ + return sampling::projected_hemisphere_pdf(maxNdotL); +} + +template +float lambertian_pdf(in LightSample s, in surface_interactions::Isotropic i) +{ + return lambertian_pdf_wo_clamps(max(s.NdotL,0.0f)); +} + + +quotient_and_pdf_scalar lambertian_cos_quotient_and_pdf_wo_clamps(in float maxNdotL) +{ + float pdf; + float q = sampling::projected_hemisphere_quotient_and_pdf(pdf, maxNdotL); + + return quotient_and_pdf_scalar::create(q, pdf); +} +template +quotient_and_pdf_scalar lambertian_cos_quotient_and_pdf(in LightSample s) +{ + float pdf; + float q = sampling::projected_hemisphere_quotient_and_pdf(pdf, max(s.NdotL,0.0f)); + + return quotient_and_pdf_scalar::create(q, pdf); +} + +template +struct Lambertian : BxDFBase, surface_interactions::Isotropic > +{ + using base_t = BxDFBase, surface_interactions::Isotropic >; + + static Lambertian create() + { + Lambertian lambertian; + return lambertian; + } + + typename base_t::spectrum_t cos_eval(in typename base_t::sample_t s, in typename base_t::interaction_t interaction) + { + return math::RECIPROCAL_PI * max(s.NdotL, 0.0f); + } + + static + typename base_t::sample_t generate(in surface_interactions::Anisotropic interaction, inout float3 u) + { + float3 L = sampling::projected_hemisphere_generate(u.xy); + + const float3 tangentSpaceV = interaction.getTangentSpaceV(); + const float3x3 tangentFrame = interaction.getTangentFrame(); + + return LightSample::createTangentSpace(tangentSpaceV, IncomingRayDirInfo::create(L), tangentFrame); + } + + typename base_t::q_pdf_t cos_quotient_and_pdf(in typename base_t::sample_t s, in typename base_t::interaction_t interaction) + { + float pdf; + float q = sampling::projected_hemisphere_quotient_and_pdf(pdf, max(s.NdotL, 0.0f)); + + return quotient_and_pdf_scalar::create(q, pdf); + } +}; + +} +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/brdf/diffuse/oren_nayar.hlsl b/include/nbl/builtin/hlsl/bxdf/brdf/diffuse/oren_nayar.hlsl new file mode 100644 index 0000000000..721ec0a73f --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/brdf/diffuse/oren_nayar.hlsl @@ -0,0 +1,155 @@ +// 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 +#ifndef _NBL_BUILTIN_HLSL_BXDF_BRDF_DIFFUSE_OREN_NAYAR_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_BRDF_DIFFUSE_OREN_NAYAR_INCLUDED_ + +#include + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace brdf +{ +namespace diffuse +{ + +float oren_nayar_cos_rec_pi_factored_out_wo_clamps(in float _a2, in float VdotL, in float maxNdotL, in float maxNdotV) +{ + // theta - polar angles + // phi - azimuth angles + float a2 = _a2*0.5; //todo read about this& + float2 AB = float2(1.0, 0.0) + float2(-0.5, 0.45) * float2(a2, a2)/float2(a2+0.33, a2+0.09); + float C = 1.0 / max(maxNdotL, maxNdotV); + + // should be equal to cos(phi)*sin(theta_i)*sin(theta_o) + // where `phi` is the angle in the tangent plane to N, between L and V + // and `theta_i` is the sine of the angle between L and N, similarily for `theta_o` but with V + float cos_phi_sin_theta = max(VdotL-maxNdotL*maxNdotV,0.0f); + + return (AB.x + AB.y * cos_phi_sin_theta * C); +} + + +float oren_nayar_cos_eval_wo_clamps(in float a2, in float VdotL, in float maxNdotL, in float maxNdotV) +{ + return maxNdotL*math::RECIPROCAL_PI*oren_nayar_cos_rec_pi_factored_out_wo_clamps(a2,VdotL,maxNdotL,maxNdotV); +} + +template +float oren_nayar_cos_eval(in LightSample _sample, in surface_interactions::Isotropic inter, in float a2) +{ + return oren_nayar_cos_eval_wo_clamps(a2, _sample.VdotL, max(_sample.NdotL,0.0f), max(inter.NdotV,0.0f)); +} + + +template +LightSample oren_nayar_cos_generate_wo_clamps(in float3 tangentSpaceV, in float3x3 m, in float2 u) +{ + // until we find something better + return lambertian_cos_generate_wo_clamps(tangentSpaceV, m, u); +} +template +LightSample oren_nayar_cos_generate(in surface_interactions::Anisotropic interaction, in float2 u, in float a2) +{ + return oren_nayar_cos_generate_wo_clamps(getTangentSpaceV(interaction),getTangentFrame(interaction),u); +} + + +float oren_nayar_pdf_wo_clamps(in float maxNdotL) +{ + return lambertian_pdf_wo_clamps(maxNdotL); +} + +template +float oren_nayar_pdf(in LightSample s, in surface_interactions::Isotropic i) +{ + return lambertian_pdf(s, i); +} + + +quotient_and_pdf_scalar oren_nayar_cos_quotient_and_pdf_wo_clamps(in float a2, in float VdotL, in float maxNdotL, in float maxNdotV) +{ + float pdf = oren_nayar_pdf_wo_clamps(maxNdotL); + return quotient_and_pdf_scalar::create( + oren_nayar_cos_rec_pi_factored_out_wo_clamps(a2,VdotL,maxNdotL,maxNdotV), + pdf + ); +} + +template +quotient_and_pdf_scalar oren_nayar_cos_quotient_and_pdf(in LightSample s, in surface_interactions::Isotropic interaction, in float a2) +{ + return oren_nayar_cos_quotient_and_pdf_wo_clamps(a2,dot(interaction.V.getDirection(),s.L), max(s.NdotL,0.0f), max(interaction.NdotV,0.0f)); +} + + +template +struct OrenNayar : BxDFBase, surface_interactions::Isotropic > +{ + float a2; + + using base_t = BxDFBase, surface_interactions::Isotropic >; + + static OrenNayar create(in float _a2) + { + OrenNayar orennayar; + orennayar.a2 = _a2; + return orennayar; + } + + static + float quotient(in float _a2, in float VdotL, in float maxNdotL, in float maxNdotV) + { + // theta - polar angles + // phi - azimuth angles + float a2 = _a2 * 0.5; //todo read about this + float2 AB = float2(1.0, 0.0) + float2(-0.5, 0.45) * float2(a2, a2) / float2(a2 + 0.33, a2 + 0.09); + float C = 1.0 / max(maxNdotL, maxNdotV); + + // should be equal to cos(phi)*sin(theta_i)*sin(theta_o) + // where `phi` is the angle in the tangent plane to N, between L and V + // and `theta_i` is the sine of the angle between L and N, similarily for `theta_o` but with V + float cos_phi_sin_theta = max(VdotL - maxNdotL * maxNdotV, 0.0f); + + return (AB.x + AB.y * cos_phi_sin_theta * C); + } + float quotient(in typename base_t::sample_t s, in typename base_t::interaction_t interaction, out float maxNdotL) + { + /*out*/maxNdotL = max(s.NdotL, 0.0f); + const float maxNdotV = max(interaction.NdotV, 0.0f); + + return quotient(a2, s.VdotL, maxNdotL, maxNdotV); + } + + typename base_t::spectrum_t cos_eval(in typename base_t::sample_t s, in typename base_t::interaction_t interaction) + { + float maxNdotL; + float q = quotient(s, interaction, maxNdotL); + return math::RECIPROCAL_PI * maxNdotL * q; + } + + typename base_t::sample_t generate(in surface_interactions::Anisotropic interaction, inout float3 u) + { + return Lambertian::generate(interaction, u); + } + + typename base_t::q_pdf_t cos_quotient_and_pdf(in typename base_t::sample_t s, in typename base_t::interaction_t interaction) + { + float maxNdotL; + float q = quotient(s, interaction, maxNdotL); + + return quotient_and_pdf_scalar::create(q, maxNdotL * math::RECIPROCAL_PI); + } +}; + +} +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/brdf/specular/beckmann.hlsl b/include/nbl/builtin/hlsl/bxdf/brdf/specular/beckmann.hlsl new file mode 100644 index 0000000000..c02071f0f3 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/brdf/specular/beckmann.hlsl @@ -0,0 +1,290 @@ +// 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 +#ifndef _NBL_BUILTIN_HLSL_BXDF_BRDF_SPECULAR_BECKMANN_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_BRDF_SPECULAR_BECKMANN_INCLUDED_ + +#include +#include +#include +#include +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace brdf +{ +namespace specular +{ + +float3 beckmann_cos_generate_wo_clamps(in float3 localV, in float2 u, in float ax, in float ay) +{ + //stretch + float3 V = normalize(float3(ax*localV.x, ay*localV.y, localV.z)); + + float2 slope; + if (V.z > 0.9999)//V.z=NdotV=cosTheta in tangent space + { + float r = sqrt(-log(1.0-u.x)); + float sinPhi = sin(2.0*math::PI*u.y); + float cosPhi = cos(2.0*math::PI*u.y); + slope = float2(r,r)*float2(cosPhi,sinPhi); + } + else + { + float cosTheta = V.z; + float sinTheta = sqrt(1.0 - cosTheta*cosTheta); + float tanTheta = sinTheta/cosTheta; + float cotTheta = 1.0/tanTheta; + + float a = -1.0; + float c = math::erf(cosTheta); + float sample_x = max(u.x, 1.0e-6f); + float theta = acos(cosTheta); + float fit = 1.0 + theta * (-0.876 + theta * (0.4265 - 0.0594*theta)); + float b = c - (1.0 + c) * pow(1.0-sample_x, fit); + + float normalization = 1.0 / (1.0 + c + math::SQRT_RECIPROCAL_PI * tanTheta * exp(-cosTheta*cosTheta)); + + const int ITER_THRESHOLD = 10; + const float MAX_ACCEPTABLE_ERR = 1.0e-5; + int it = 0; + float value=1000.0; + while (++itMAX_ACCEPTABLE_ERR) + { + if (!(b>=a && b<=c)) + b = 0.5 * (a+c); + + float invErf = math::erfInv(b); + value = normalization * (1.0 + b + math::SQRT_RECIPROCAL_PI * tanTheta * exp(-invErf*invErf)) - sample_x; + float derivative = normalization * (1.0 - invErf*cosTheta); + + if (value > 0.0) + c = b; + else + a = b; + + b -= value/derivative; + } + // TODO: investigate if we can replace these two erf^-1 calls with a box muller transform + slope.x = math::erfInv(b); + slope.y = math::erfInv(2.0f * max(u.y,1.0e-6f) - 1.0f); + } + + float sinTheta = sqrt(1.0f - V.z*V.z); + float cosPhi = sinTheta==0.0f ? 1.0f : clamp(V.x/sinTheta, -1.0f, 1.0f); + float sinPhi = sinTheta==0.0f ? 0.0f : clamp(V.y/sinTheta, -1.0f, 1.0f); + //rotate + float tmp = cosPhi*slope.x - sinPhi*slope.y; + slope.y = sinPhi*slope.x + cosPhi*slope.y; + slope.x = tmp; + + //unstretch + slope = float2(ax,ay)*slope; + + return normalize(float3(-slope, 1.0)); +} + +// TODO: unifty the two following functions into `microfacet_BRDF_cos_generate_wo_clamps(float3 H,...)` and `microfacet_BRDF_cos_generate` or at least a auto declaration macro in lieu of a template +template +LightSample beckmann_cos_generate_wo_clamps(in float3 localV, in float3x3 m, in float2 u, in float ax, in float ay, out AnisotropicMicrofacetCache _cache) +{ + const float3 H = beckmann_cos_generate_wo_clamps(localV,u,ax,ay); + + _cache = AnisotropicMicrofacetCache::create(localV,H); + float3 localL = math::reflect(localV, H, _cache.VdotH); + + return LightSample::createTangentSpace(localV, RayDirInfo::create(localL), m); +} + +template +LightSample beckmann_cos_generate(in surface_interactions::Anisotropic interaction, in float2 u, in float ax, in float ay, out AnisotropicMicrofacetCache _cache) +{ + const float3 localV = interaction.getTangentSpaceV(); + const float3x3 m = interaction.getTangentFrame(); + return beckmann_cos_generate_wo_clamps(localV,m,u,ax,ay,_cache); +} + + + +// isotropic PDF +float beckmann_pdf_wo_clamps(in float ndf, in float maxNdotV, in float NdotV2, in float a2, out float onePlusLambda_V) +{ + const float lambda = geom_smith::beckmann::Lambda(NdotV2, a2); + return geom_smith::VNDF_pdf_wo_clamps(ndf,lambda,maxNdotV,onePlusLambda_V); +} + +float beckmann_pdf_wo_clamps(in float NdotH2, in float maxNdotV, in float NdotV2, in float a2) +{ + float ndf = ndf::beckmann(a2, NdotH2); + + float dummy; + return beckmann_pdf_wo_clamps(ndf, maxNdotV,NdotV2, a2, dummy); +} + +// anisotropic PDF +float beckmann_pdf_wo_clamps(in float ndf, in float maxNdotV, in float TdotV2, in float BdotV2, in float NdotV2, in float ax2, in float ay2, out float onePlusLambda_V) +{ + float c2 = geom_smith::beckmann::C2(TdotV2, BdotV2, NdotV2, ax2, ay2); + float lambda = geom_smith::beckmann::Lambda(c2); + + return geom_smith::VNDF_pdf_wo_clamps(ndf, lambda, maxNdotV, onePlusLambda_V); +} + +float beckmann_pdf_wo_clamps(in float NdotH2, in float TdotH2, in float BdotH2, in float maxNdotV, in float TdotV2, in float BdotV2, in float NdotV2, in float ax, in float ax2, in float ay, in float ay2) +{ + float ndf = ndf::beckmann(ax, ay, ax2, ay2, TdotH2, BdotH2, NdotH2); + + float dummy; + return beckmann_pdf_wo_clamps(ndf, maxNdotV, TdotV2, BdotV2, NdotV2, ax2, ay2, dummy); +} + +quotient_and_pdf_rgb beckmann_cos_quotient_and_pdf_wo_clamps(in float ndf, in float NdotL2, in float maxNdotV, in float NdotV2, in float3 reflectance, in float a2) +{ + float onePlusLambda_V; + float pdf = beckmann_pdf_wo_clamps(ndf,maxNdotV,NdotV2,a2,onePlusLambda_V); + + float G2_over_G1 = geom_smith::beckmann::G2_over_G1(onePlusLambda_V, NdotL2, a2); + return quotient_and_pdf_rgb::create(reflectance*G2_over_G1, pdf); +} +template +quotient_and_pdf_rgb beckmann_cos_quotient_and_pdf(in LightSample _sample, in surface_interactions::Isotropic interaction, in IsotropicMicrofacetCache _cache, in float2x3 ior, in float a2) +{ + const float ndf = ndf::beckmann(a2, _cache.NdotH2); + float onePlusLambda_V; + float pdf = beckmann_pdf_wo_clamps(ndf, interaction.NdotV, interaction.NdotV2, a2, onePlusLambda_V); + float3 rem = float3(0.0,0.0,0.0); + if (_sample.NdotL>FLT_MIN && interaction.NdotV>FLT_MIN) + { + const float3 reflectance = fresnel::conductor(ior[0], ior[1], _cache.VdotH); + + float G2_over_G1 = beckmann_smith_G2_over_G1(onePlusLambda_V, _sample.NdotL2, a2); + rem = reflectance * G2_over_G1; + } + + return quotient_and_pdf_rgb::create(rem, pdf); +} + + + +quotient_and_pdf_rgb beckmann_aniso_cos_quotient_and_pdf_wo_clamps(in float ndf, in float NdotL2, in float TdotL2, in float BdotL2, in float maxNdotV, in float TdotV2, in float BdotV2, in float NdotV2, in float3 reflectance, in float ax2, in float ay2) +{ + float onePlusLambda_V; + float pdf = beckmann_pdf_wo_clamps(ndf,maxNdotV,TdotV2,BdotV2,NdotV2,ax2,ay2,onePlusLambda_V); + + float G2_over_G1 = geom_smith::beckmann::G2_over_G1(onePlusLambda_V, TdotL2, BdotL2, NdotL2, ax2, ay2); + return quotient_and_pdf_rgb::create(reflectance * G2_over_G1, pdf); +} +template +quotient_and_pdf_rgb beckmann_aniso_cos_quotient_and_pdf(in LightSample _sample, in surface_interactions::Anisotropic interaction, in AnisotropicMicrofacetCache _cache, in float2x3 ior, in float ax, in float ay) +{ + const float ax2 = ax * ax; + const float ay2 = ay * ay; + + const float TdotH2 = _cache.TdotH * _cache.TdotH; + const float BdotH2 = _cache.BdotH * _cache.BdotH; + const float TdotV2 = interaction.TdotV * interaction.TdotV; + const float BdotV2 = interaction.BdotV * interaction.BdotV; + + const float NdotV2 = interaction.NdotV2; + + const float ndf = ndf::beckmann(ax, ay, ax2, ay2, TdotH2, BdotH2, _cache.NdotH2); + float onePlusLambda_V; + float pdf = beckmann_pdf_wo_clamps(ndf, interaction.NdotV, TdotV2, BdotV2, NdotV2, ax2, ay2, onePlusLambda_V); + quotient_and_pdf_rgb qpdf = quotient_and_pdf_rgb::create(float3(0.0, 0.0, 0.0), pdf); + if (_sample.NdotL>FLT_MIN && interaction.NdotV>FLT_MIN) + { + const float TdotL2 = _sample.TdotL*_sample.TdotL; + const float BdotL2 = _sample.BdotL*_sample.BdotL; + + const float3 reflectance = fresnel::conductor(ior[0], ior[1], _cache.VdotH); + + qpdf = beckmann_aniso_cos_quotient_and_pdf_wo_clamps(ndf, _sample.NdotL2, TdotL2, BdotL2, interaction.NdotV, TdotV2, BdotV2, NdotV2, reflectance, ax2, ay2); + } + + return qpdf; +} + + +float beckmann_height_correlated_cos_eval_DG_wo_clamps(in float NdotH2, in float NdotL2, in float NdotV2, in float a2) +{ + float NG = ndf::beckmann(a2, NdotH2); + if (a2>FLT_MIN) + NG *= geom_smith::beckmann::correlated(NdotV2, NdotL2, a2); + + return NG; +} +float3 beckmann_height_correlated_cos_eval_wo_clamps(in float NdotH2, in float NdotL2, in float maxNdotV, in float NdotV2, in float VdotH, in float2x3 ior, in float a2) +{ + const float NG = beckmann_height_correlated_cos_eval_DG_wo_clamps(NdotH2, NdotL2, NdotV2, a2); + + const float3 fr = fresnel::conductor(ior[0], ior[1], VdotH); + + return fr*ndf::microfacet_to_light_measure_transform(NG,maxNdotV); +} +template +float3 beckmann_height_correlated_cos_eval(in LightSample _sample, in surface_interactions::Isotropic interaction, in IsotropicMicrofacetCache _cache, in float2x3 ior, in float a2) +{ + if (interaction.NdotV>FLT_MIN) + return beckmann_height_correlated_cos_eval_wo_clamps(_cache.NdotH2,_sample.NdotL2,interaction.NdotV,interaction.NdotV2,_cache.VdotH,ior,a2); + else + return float3(0.0,0.0,0.0); +} + +float beckmann_aniso_height_correlated_cos_eval_DG_wo_clamps(in float NdotH2, in float TdotH2, in float BdotH2, in float NdotL2, in float TdotL2, in float BdotL2, in float NdotV2, in float TdotV2, in float BdotV2, in float ax, in float ax2, in float ay, in float ay2) +{ + float NG = ndf::beckmann(ax, ay, ax2, ay2, TdotH2, BdotH2, NdotH2); + if (ax>FLT_MIN || ay>FLT_MIN) + NG *= geom_smith::beckmann::correlated(TdotV2, BdotV2, NdotV2, TdotL2, BdotL2, NdotL2, ax2, ay2); + + return NG; +} +float3 beckmann_aniso_height_correlated_cos_eval_wo_clamps(in float NdotH2, in float TdotH2, in float BdotH2, in float NdotL2, in float TdotL2, in float BdotL2, in float maxNdotV, in float NdotV2, in float TdotV2, in float BdotV2, in float VdotH, in float2x3 ior, in float ax, in float ax2, in float ay, in float ay2) +{ + const float NG = beckmann_aniso_height_correlated_cos_eval_DG_wo_clamps(NdotH2,TdotH2,BdotH2, NdotL2,TdotL2,BdotL2, NdotV2,TdotV2,BdotV2, ax, ax2, ay, ay2); + + const float3 fr = fresnel::conductor(ior[0], ior[1], VdotH); + + return fr*ndf::microfacet_to_light_measure_transform(NG,maxNdotV); +} +template +float3 beckmann_aniso_height_correlated_cos_eval(in LightSample _sample, in surface_interactions::Isotropic interaction, in AnisotropicMicrofacetCache _cache, in float2x3 ior, in float ax, in float ay) +{ + if (interaction.NdotV>FLT_MIN) + { + const float TdotH2 = _cache.TdotH*_cache.TdotH; + const float BdotH2 = _cache.BdotH*_cache.BdotH; + + const float TdotL2 = _sample.TdotL*_sample.TdotL; + const float BdotL2 = _sample.BdotL*_sample.BdotL; + + const float TdotV2 = interaction.TdotV*interaction.TdotV; + const float BdotV2 = interaction.BdotV*interaction.BdotV; + return beckmann_aniso_height_correlated_cos_eval_wo_clamps(_cache.NdotH2,TdotH2,BdotH2, _sample.NdotL2,TdotL2,BdotL2, interaction.NdotV,interaction.NdotV2,TdotV2,BdotV2, _cache.VdotH, ior,ax,ax*ax,ay,ay*ay); + } + else + { + return float3(0.0, 0.0, 0.0); + } +} + + +template +using IsotropicBeckmann = IsotropicCookTorrance; + +template +using AnisotropicBeckmann = AnisotropicCookTorrance; + +} +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/brdf/specular/blinn_phong.hlsl b/include/nbl/builtin/hlsl/bxdf/brdf/specular/blinn_phong.hlsl new file mode 100644 index 0000000000..14f9d64364 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/brdf/specular/blinn_phong.hlsl @@ -0,0 +1,167 @@ +// 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 + +#ifndef _NBL_BUILTIN_HLSL_BXDF_BRDF_SPECULAR_BLINN_PHONG_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_BRDF_SPECULAR_BLINN_PHONG_INCLUDED_ + +#include +#include +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace brdf +{ +namespace specular +{ + +//conversion between alpha and Phong exponent, Walter et.al. +float phong_exp_to_alpha2(in float n) +{ + return 2.0/(n+2.0); +} +//+INF for a2==0.0 +float alpha2_to_phong_exp(in float a2) +{ + return 2.0/a2 - 2.0; +} + +//https://zhuanlan.zhihu.com/p/58205525 +//only NDF sampling +//however we dont really care about phong sampling +float3 blinn_phong_cos_generate(in float2 u, in float n) +{ + float phi = 2.0*math::PI*u.y; + float cosTheta = pow(u.x, 1.0/(n+1.0)); + float sinTheta = sqrt(1.0 - cosTheta*cosTheta); + float cosPhi = cos(phi); + float sinPhi = sin(phi); + return float3(cosPhi*sinTheta, sinPhi*sinTheta, cosTheta); +} +template +LightSample blinn_phong_cos_generate(in surface_interactions::Anisotropic interaction, in float2 u, in float n, out AnisotropicMicrofacetCache _cache) +{ + const float3 H = blinn_phong_cos_generate(u,n); + const float3 localV = interaction.getTangentSpaceV(); + + _cache = AnisotropicMicrofacetCache::create(localV, H); + float3 localL; + localL = math::reflect(localV, H, _cache.VdotH); + + const float3x3 m = interaction.getTangentFrame(); + + return LightSample::createTangentSpace(localV, RayDirInfo::create(localL), m); +} + +/* +float3 blinn_phong_dielectric_cos_quotient_and_pdf(out float& pdf, in BxDFSample s, in surface_interactions::Isotropic interaction, in float n, in float3 ior) +{ + pdf = (n+1.0)*0.5*RECIPROCAL_PI * 0.25*pow(s.NdotH,n)/s.VdotH; + + float3 fr = fresnel_dielectric(ior, s.VdotH); + return fr * s.NdotL * (n*(n + 6.0) + 8.0) * s.VdotH / ((pow(0.5,0.5*n) + n) * (n + 1.0)); +} + +float3 blinn_phong_conductor_cos_quotient_and_pdf(out float& pdf, in BxDFSample s, in surface_interactions::Isotropic interaction, in float n, in float2x3 ior) +{ + pdf = (n+1.0)*0.5*RECIPROCAL_PI * 0.25*pow(s.NdotH,n)/s.VdotH; + + float3 fr = fresnel_conductor(ior[0], ior[1], s.VdotH); + return fr * s.NdotL * (n*(n + 6.0) + 8.0) * s.VdotH / ((pow(0.5,0.5*n) + n) * (n + 1.0)); +} +*/ + +float blinn_phong_cos_eval_DG_wo_clamps(in float NdotH, in float NdotV_squared, in float NdotL2, in float n, in float a2) +{ + float NG = blinn_phong(NdotH, n); + if (a2>FLT_MIN) + NG *= geom_smith::beckmann::correlated(NdotV_squared, NdotL2, a2); + return NG; +} +float blinn_phong_cos_eval_DG_wo_clamps(in float NdotH, in float NdotV_squared, in float NdotL2, in float n) +{ + float a2 = phong_exp_to_alpha2(n); + return blinn_phong_cos_eval_DG_wo_clamps(NdotH, NdotV_squared, NdotL2, n, a2); +} + +float3 blinn_phong_cos_eval_wo_clamps(in float NdotH, in float maxNdotV, in float NdotV_squared, in float NdotL2, in float VdotH, in float n, in float2x3 ior, in float a2) +{ + float scalar_part = blinn_phong_cos_eval_DG_wo_clamps(NdotH, NdotV_squared, NdotL2, n, a2); + return fresnel::conductor(ior[0], ior[1], VdotH)*ndf::microfacet_to_light_measure_transform(scalar_part,maxNdotV); +} +float3 blinn_phong_cos_eval_wo_clamps(in float NdotH, in float maxNdotV, in float NdotV_squared, in float NdotL2, in float VdotH, in float n, in float2x3 ior) +{ + float a2 = phong_exp_to_alpha2(n); + return blinn_phong_cos_eval_wo_clamps(NdotH, maxNdotV, NdotV_squared, NdotL2, VdotH, n, ior, a2); +} +template +float3 blinn_phong_cos_eval(in LightSample _sample, in surface_interactions::Isotropic interaction, in IsotropicMicrofacetCache _cache, in float n, in float2x3 ior) +{ + if (interaction.NdotV>FLT_MIN) + return blinn_phong_cos_eval_wo_clamps(_cache.NdotH, interaction.NdotV, interaction.NdotV_squared, _sample.NdotL2, _cache.VdotH, n, ior); + else + return float3(0.0,0.0,0.0); +} + + +float blinn_phong_cos_eval_DG_wo_clamps(in float NdotH, in float NdotH2, in float TdotH2, in float BdotH2, float TdotL2, float BdotL2, in float TdotV2, in float BdotV2, in float NdotV_squared, in float NdotL2, in float nx, in float ny, in float ax2, in float ay2) +{ + float DG = blinn_phong(NdotH, 1.0/(1.0-NdotH2), TdotH2, BdotH2, nx, ny); + if (ax2>FLT_MIN || ay2>FLT_MIN) + DG *= geom_smith::beckmann::correlated(TdotV2, BdotV2, NdotV_squared, TdotL2, BdotL2, NdotL2, ax2, ay2); + return DG; +} +float blinn_phong_cos_eval_DG_wo_clamps(in float NdotH, in float NdotH2, in float TdotH2, in float BdotH2, in float TdotL2, in float BdotL2, in float TdotV2, in float BdotV2, in float NdotV_squared, in float NdotL2, in float nx, in float ny) +{ + float ax2 = phong_exp_to_alpha2(nx); + float ay2 = phong_exp_to_alpha2(ny); + + return blinn_phong_cos_eval_DG_wo_clamps(NdotH, NdotH2, TdotH2, BdotH2, TdotL2, BdotL2, TdotV2, BdotV2, NdotV_squared, NdotL2, nx, ny, ax2, ay2); +} + +float3 blinn_phong_cos_eval_wo_clamps(in float NdotH, in float NdotH2, in float TdotH2, in float BdotH2, in float TdotL2, in float BdotL2, in float maxNdotV, in float TdotV2, in float BdotV2, in float NdotV_squared, in float NdotL2, in float VdotH, in float nx, in float ny, in float2x3 ior, in float ax2, in float ay2) +{ + float scalar_part = blinn_phong_cos_eval_DG_wo_clamps(NdotH, NdotH2, TdotH2, BdotH2, TdotL2, BdotL2, TdotV2, BdotV2, NdotV_squared, NdotL2, nx, ny, ax2, ay2); + + return fresnel::conductor(ior[0], ior[1], VdotH)*ndf::microfacet_to_light_measure_transform(scalar_part,maxNdotV); +} +float3 blinn_phong_cos_eval_wo_clamps(in float NdotH, in float NdotH2, in float TdotH2, in float BdotH2, in float TdotL2, in float BdotL2, in float maxNdotV, in float TdotV2, in float BdotV2, in float NdotV_squared, in float NdotL2, in float VdotH, in float nx, in float ny, in float2x3 ior) +{ + float ax2 = phong_exp_to_alpha2(nx); + float ay2 = phong_exp_to_alpha2(ny); + + return blinn_phong_cos_eval_wo_clamps(NdotH, NdotH2, TdotH2, BdotH2, TdotL2, BdotL2, maxNdotV, TdotV2, BdotV2, NdotV_squared, NdotL2, VdotH, nx, ny, ior, ax2, ay2); +} +template +float3 blinn_phong_cos_eval(in LightSample _sample, in surface_interactions::Anisotropic interaction, in AnisotropicMicrofacetCache _cache, in float nx, in float ny, in float2x3 ior) +{ + if (interaction.NdotV>FLT_MIN) + { + const float TdotH2 = _cache.TdotH*_cache.TdotH; + const float BdotH2 = _cache.BdotH*_cache.BdotH; + + const float TdotL2 = _sample.TdotL*_sample.TdotL; + const float BdotL2 = _sample.BdotL*_sample.BdotL; + + const float TdotV2 = interaction.TdotV*interaction.TdotV; + const float BdotV2 = interaction.BdotV*interaction.BdotV; + return blinn_phong_cos_eval_wo_clamps(_cache.NdotH, _cache.NdotH2, TdotH2, BdotH2, TdotL2, BdotL2, interaction.NdotV, TdotV2, BdotV2, interaction.NdotV_squared, _sample.NdotL2, _cache.VdotH, nx, ny, ior); + } + else + return float3(0.0,0.0,0.0); +} + +} +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/brdf/specular/common.hlsl b/include/nbl/builtin/hlsl/bxdf/brdf/specular/common.hlsl new file mode 100644 index 0000000000..99230c6fc4 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/brdf/specular/common.hlsl @@ -0,0 +1,166 @@ +// 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 +#ifndef _NBL_BUILTIN_HLSL_BXDF_BRDF_SPECULAR_COMMON_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_BRDF_SPECULAR_COMMON_INCLUDED_ + +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace brdf +{ +namespace specular +{ + +template +struct CookTorrance : MicrofacetBxDFBase +{ + fresnel_t fresnel; + ndf::ndf_traits ndf; +}; + +template +struct IsotropicCookTorrance : CookTorrance, surface_interactions::Isotropic, IsotropicMicrofacetCache> +{ + using base_t = CookTorrance, surface_interactions::Isotropic, IsotropicMicrofacetCache>; + + typename base_t::spectrum_t cos_eval( + in typename base_t::sample_t s, + in typename base_t::interaction_t interaction, + in typename base_t::cache_t cache) + { + if (interaction.NdotV > FLT_MIN) + { + float NG = base_t::ndf.ndf.D(cache.NdotH2); + if (base_t::ndf.ndf.a2 > FLT_MIN) + NG *= base_t::ndf.G2(interaction.NdotV2, s.NdotL2); + + const float3 fr = base_t::fresnel(cache.VdotH); + + return fr * base_t::ndf.dHdL(NG, max(interaction.NdotV, 0.0f)); + } + else + return float3(0.0, 0.0, 0.0); + } + + typename base_t::sample_t generate( + in surface_interactions::Anisotropic interaction, + inout float3 u, + out AnisotropicMicrofacetCache cache) + { + const float3 localH = base_t::ndf.ndf.generateH(interaction, u, cache); + + const float3 localV = interaction.getTangentSpaceV(); + const float3 localL = math::reflect(localV, localH, cache.VdotH); + + return typename base_t::sample_t::createTangentSpace(localV, IncomingRayDirInfo::create(localL), interaction.getTangentFrame()); + } + + typename base_t::q_pdf_t cos_quotient_and_pdf( + in typename base_t::sample_t s, + in typename base_t::interaction_t interaction, + in typename base_t::cache_t cache) + { + float3 q = 0.0f; + if (s.NdotL > FLT_MIN && interaction.NdotV > FLT_MIN) + { + const float3 reflectance = base_t::fresnel(cache.VdotH); + + float G2_over_G1 = base_t::ndf.G2_over_G1(interaction.NdotV2, s.NdotL2); + q = reflectance * G2_over_G1; + } + float pdf = base_t::ndf.VNDF(cache.NdotH2, interaction.NdotV2, max(interaction.NdotV, 0.0f)); + + return typename base_t::q_pdf_t::create(q, pdf); + } +}; + +template +struct AnisotropicCookTorrance : CookTorrance, surface_interactions::Anisotropic, AnisotropicMicrofacetCache> +{ + using base_t = CookTorrance, surface_interactions::Anisotropic, AnisotropicMicrofacetCache>; + + typename base_t::spectrum_t cos_eval( + in typename base_t::sample_t s, + in typename base_t::interaction_t interaction, + in typename base_t::cache_t cache) + { + if (interaction.NdotV > FLT_MIN) + { + const float TdotL2 = s.TdotL * s.TdotL; + const float BdotL2 = s.BdotL * s.BdotL; + + const float TdotV2 = interaction.TdotV * interaction.TdotV; + const float BdotV2 = interaction.BdotV * interaction.BdotV; + + float NG = base_t::ndf.ndf.D(cache.TdotH2, cache.BdotH2, cache.NdotH2); + if (base_t::ndf.ndf.ax > FLT_MIN || base_t::ndf.ndf.ay > FLT_MIN) + NG *= base_t::ndf.G2(TdotV2, BdotV2, interaction.NdotV2, TdotL2, BdotL2, s.NdotL2); + + const float3 fr = base_t::fresnel(cache.VdotH); + + return fr * base_t::ndf.dHdL(NG, max(interaction.NdotV, 0.0f)); + } + else + return float3(0.0, 0.0, 0.0); + } + + typename base_t::sample_t generate( + in surface_interactions::Anisotropic interaction, + inout float3 u, + out AnisotropicMicrofacetCache cache) + { + const float3 localH = base_t::ndf.ndf.generateH(interaction, u, cache); + + const float3 localV = interaction.getTangentSpaceV(); + const float3 localL = math::reflect(localV, localH, cache.VdotH); + + return typename base_t::sample_t::createTangentSpace(localV, IncomingRayDirInfo::create(localL), interaction.getTangentFrame()); + } + + typename base_t::q_pdf_t cos_quotient_and_pdf( + in typename base_t::sample_t s, + in typename base_t::interaction_t interaction, + in typename base_t::cache_t cache) + { + const float TdotV2 = interaction.TdotV * interaction.TdotV; + const float BdotV2 = interaction.BdotV * interaction.BdotV; + + float3 q = 0.0f; + if (s.NdotL > FLT_MIN && interaction.NdotV > FLT_MIN) + { + const float TdotL2 = s.TdotL * s.TdotL; + const float BdotL2 = s.BdotL * s.BdotL; + + const float3 reflectance = base_t::fresnel(cache.VdotH); + + float G2_over_G1 = base_t::ndf.G2_over_G1(TdotV2, BdotV2, interaction.NdotV2, TdotL2, BdotL2, s.NdotL2); + q = reflectance * G2_over_G1; + } + + const float TdotH2 = cache.TdotH * cache.TdotH; + const float BdotH2 = cache.BdotH * cache.BdotH; + + float pdf = base_t::ndf.VNDF( + TdotH2, BdotH2, cache.NdotH2, + TdotV2, BdotV2, interaction.NdotV2, + max(interaction.NdotV, 0.0f) + ); + + return typename base_t::q_pdf_t::create(q, pdf); + } +}; + +} +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/brdf/specular/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/brdf/specular/ggx.hlsl new file mode 100644 index 0000000000..9bf644482c --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/brdf/specular/ggx.hlsl @@ -0,0 +1,263 @@ +// 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 + +#ifndef _NBL_BUILTIN_HLSL_BXDF_BRDF_SPECULAR_GGX_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_BRDF_SPECULAR_GGX_INCLUDED_ + +#include +#include +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace brdf +{ +namespace specular +{ + +//depr +/* + float3 ggx_height_correlated_aniso_cos_eval(in BSDFAnisotropicParams params, in surface_interactions::Anisotropic inter, in float2x3 ior, in float a2, in float2 atb, in float aniso) +{ + float g = geom_smith::ggx::height_correlated_aniso_wo_numerator(atb.x, atb.y, params.TdotL, interaction.TdotV, params.BdotL, interaction.BdotV, params.NdotL, interaction.NdotV); + float ndf = ggx_burley_aniso(aniso, a2, params.TdotH, params.BdotH, params.NdotH); + float3 fr = fresnel_conductor(ior[0], ior[1], params.VdotH); + + return params.NdotL * g*ndf*fr; +} +*/ +//defined using NDF function with better API (compared to burley used above) and new impl of correlated smith + float ggx_height_correlated_aniso_cos_eval_DG_wo_clamps(in float NdotH2, in float TdotH2, in float BdotH2, in float maxNdotL, in float NdotL2, in float TdotL2, in float BdotL2, in float maxNdotV, in float NdotV2, in float TdotV2, in float BdotV2, in float ax, in float ax2, in float ay, in float ay2) +{ + float NG = ndf::ggx::aniso(TdotH2,BdotH2,NdotH2, ax, ay, ax2, ay2); + if (ax>FLT_MIN || ay>FLT_MIN) + { + NG *= geom_smith::ggx::correlated_wo_numerator( + maxNdotV, TdotV2, BdotV2, NdotV2, + maxNdotL, TdotL2, BdotL2, NdotL2, + ax2, ay2 + ); + } + + return NG; +} + + float3 ggx_height_correlated_aniso_cos_eval_wo_clamps(in float NdotH2, in float TdotH2, in float BdotH2, in float maxNdotL, in float NdotL2, in float TdotL2, in float BdotL2, in float maxNdotV, in float NdotV2, in float TdotV2, in float BdotV2, in float VdotH, in float2x3 ior, in float ax, in float ax2, in float ay, in float ay2) +{ + float NG_already_in_reflective_dL_measure = ggx_height_correlated_aniso_cos_eval_DG_wo_clamps(NdotH2,TdotH2,BdotH2,maxNdotL,NdotL2,TdotL2,BdotL2,maxNdotV,NdotV2,TdotV2,BdotV2,ax,ax2,ay,ay2); + + float3 fr = fresnel::conductor(ior[0], ior[1], VdotH); + return fr*ndf::ggx::microfacet_to_light_measure_transform(NG_already_in_reflective_dL_measure,maxNdotL); +} + + template + float3 ggx_height_correlated_aniso_cos_eval(in LightSample _sample, in surface_interactions::Anisotropic interaction, in AnisotropicMicrofacetCache _cache, in float2x3 ior, in float ax, in float ay) +{ + if (_sample.NdotL>FLT_MIN && interaction.NdotV>FLT_MIN) + { + const float TdotH2 = _cache.TdotH*_cache.TdotH; + const float BdotH2 = _cache.BdotH*_cache.BdotH; + + const float TdotL2 = _sample.TdotL*_sample.TdotL; + const float BdotL2 = _sample.BdotL*_sample.BdotL; + + const float TdotV2 = interaction.TdotV*interaction.TdotV; + const float BdotV2 = interaction.BdotV*interaction.BdotV; + return ggx_height_correlated_aniso_cos_eval_wo_clamps(_cache.NdotH2, TdotH2, BdotH2, _sample.NdotL,_sample.NdotL2,TdotL2,BdotL2, interaction.NdotV,interaction.NdotV_squared,TdotV2,BdotV2, _cache.VdotH, ior, ax,ax*ax,ay,ay*ay); + } + else + return float3(0.0,0.0,0.0); +} + + + float ggx_height_correlated_cos_eval_DG_wo_clamps(in float NdotH2, in float maxNdotL, in float NdotL2, in float maxNdotV, in float NdotV2, in float a2) +{ + float NG = ndf::ggx::trowbridge_reitz(a2, NdotH2); + if (a2>FLT_MIN) + NG *= geom_smith::ggx::correlated_wo_numerator(maxNdotV, NdotV2, maxNdotL, NdotL2, a2); + + return NG; +} + + float3 ggx_height_correlated_cos_eval_wo_clamps(in float NdotH2, in float maxNdotL, in float NdotL2, in float maxNdotV, in float NdotV2, in float VdotH, in float2x3 ior, in float a2) +{ + float NG_already_in_reflective_dL_measure = ggx_height_correlated_cos_eval_DG_wo_clamps(NdotH2, maxNdotL, NdotL2, maxNdotV, NdotV2, a2); + + float3 fr = fresnel::conductor(ior[0], ior[1], VdotH); + + return fr*ndf::ggx::microfacet_to_light_measure_transform(NG_already_in_reflective_dL_measure, maxNdotL); +} + + template + float3 ggx_height_correlated_cos_eval(in LightSample _sample, in surface_interactions::Isotropic interaction, in IsotropicMicrofacetCache _cache, in float2x3 ior, in float a2) +{ + if (_sample.NdotL>FLT_MIN && interaction.NdotV>FLT_MIN) + return ggx_height_correlated_cos_eval_wo_clamps(_cache.NdotH2,max(_sample.NdotL,0.0),_sample.NdotL2, max(interaction.NdotV,0.0), interaction.NdotV_squared, _cache.VdotH,ior,a2); + else + return float3(0.0,0.0,0.0); +} + + + +//Heitz's 2018 paper "Sampling the GGX Distribution of Visible Normals" + float3 ggx_cos_generate(in float3 localV, in float2 u, in float _ax, in float _ay) +{ + float3 V = normalize(float3(_ax*localV.x, _ay*localV.y, localV.z));//stretch view vector so that we're sampling as if roughness=1.0 + + float lensq = V.x*V.x + V.y*V.y; + float3 T1 = lensq > 0.0 ? float3(-V.y, V.x, 0.0)*rsqrt(lensq) : float3(1.0,0.0,0.0); + float3 T2 = cross(V,T1); + + float r = sqrt(u.x); + float phi = 2.0 * math::PI * u.y; + float t1 = r * cos(phi); + float t2 = r * sin(phi); + float s = 0.5 * (1.0 + V.z); + t2 = (1.0 - s)*sqrt(1.0 - t1*t1) + s*t2; + + //reprojection onto hemisphere + //TODO try it wothout the& max(), not sure if -t1*t1-t2*t2>-1.0 + float3 H = t1*T1 + t2*T2 + sqrt(max(0.0, 1.0-t1*t1-t2*t2))*V; + //unstretch + return normalize(float3(_ax*H.x, _ay*H.y, H.z)); +} + +// TODO: unifty the two following functions into `microfacet_BRDF_cos_generate_wo_clamps(float3 H,...)` and `microfacet_BRDF_cos_generate` or at least a auto declaration macro in lieu of a template + template + LightSample ggx_cos_generate_wo_clamps(in float3 localV, in float3x3 m, in float2 u, in float _ax, in float _ay, out AnisotropicMicrofacetCache _cache) +{ + const float3 H = ggx_cos_generate(localV,u,_ax,_ay); + + _cache = AnisotropicMicrofacetCache::create(localV, H); + float3 localL; + localL = math::reflect(localV, H, _cache.VdotH); + + return LightSample::createTangentSpace(localV, RayDirInfo::create(localL), m); +} + + template + LightSample ggx_cos_generate(in surface_interactions::Anisotropic interaction, in float2 u, in float _ax, in float _ay, out AnisotropicMicrofacetCache _cache) +{ + const float3 localV = interaction.getTangentSpaceV(); + const float3x3 m = interaction.getTangentFrame(); + return ggx_cos_generate_wo_clamps(localV,m,u,_ax,_ay,_cache); +} + + + + float ggx_pdf_wo_clamps(in float ndf, in float devsh_v, in float maxNdotV) +{ + return geom_smith::VNDF_pdf_wo_clamps(ndf, geom_smith::ggx::G1_wo_numerator(maxNdotV,devsh_v)); +} + float ggx_pdf_wo_clamps(in float NdotH2, in float maxNdotV, in float NdotV2, in float a2) +{ + const float ndf = ndf::ggx::trowbridge_reitz(a2, NdotH2); + const float devsh_v = geom_smith::ggx::devsh_part(NdotV2, a2, 1.0-a2); + + return ggx_pdf_wo_clamps(ndf, devsh_v, maxNdotV); +} + + float ggx_pdf_wo_clamps(in float NdotH2, in float TdotH2, in float BdotH2, in float maxNdotV, in float NdotV2, in float TdotV2, in float BdotV2, in float ax, in float ay, in float ax2, in float ay2) +{ + const float ndf = ndf::ggx::aniso(TdotH2,BdotH2,NdotH2, ax, ay, ax2, ay2); + const float devsh_v = geom_smith::ggx::devsh_part(TdotV2, BdotV2, NdotV2, ax2, ay2); + + return ggx_pdf_wo_clamps(ndf, devsh_v, maxNdotV); +} + + + quotient_and_pdf_rgb ggx_cos_quotient_and_pdf_wo_clamps(in float ndf, in float maxNdotL, in float NdotL2, in float maxNdotV, in float NdotV2, in float3 reflectance, in float a2) +{ + const float one_minus_a2 = 1.0 - a2; + const float devsh_v = geom_smith::ggx::devsh_part(NdotV2, a2, one_minus_a2); + const float pdf = ggx_pdf_wo_clamps(ndf, devsh_v, maxNdotV); + + const float G2_over_G1 = geom_smith::ggx::G2_over_G1_devsh(maxNdotL, NdotL2, maxNdotV, devsh_v, a2, one_minus_a2); + + return quotient_and_pdf_rgb::create(reflectance * G2_over_G1, pdf); +} + + template + quotient_and_pdf_rgb ggx_cos_quotient_and_pdf(in LightSample _sample, in surface_interactions::Isotropic interaction, in IsotropicMicrofacetCache _cache, in float2x3 ior, in float a2) +{ + const float one_minus_a2 = 1.0 - a2; + const float ndf = ndf::ggx::trowbridge_reitz(a2, _cache.NdotH2); + const float devsh_v = geom_smith::ggx::devsh_part(interaction.NdotV_squared, a2, one_minus_a2); + const float pdf = ggx_pdf_wo_clamps(ndf, devsh_v, interaction.NdotV); + + quotient_and_pdf_rgb qpdf = quotient_and_pdf_rgb::create(float3(0.0, 0.0, 0.0), pdf); + if (_sample.NdotL>FLT_MIN && interaction.NdotV>FLT_MIN) + { + const float3 reflectance = fresnel::conductor(ior[0], ior[1], _cache.VdotH); + const float G2_over_G1 = geom_smith::ggx::G2_over_G1_devsh(_sample.NdotL, _sample.NdotL2, interaction.NdotV, devsh_v, a2, one_minus_a2); + + qpdf.quotient = reflectance * G2_over_G1; + } + + return qpdf; +} + + + quotient_and_pdf_rgb ggx_aniso_cos_quotient_and_pdf_wo_clamps(in float ndf, in float maxNdotL, in float NdotL2, in float TdotL2, in float BdotL2, in float maxNdotV, in float TdotV2, in float BdotV2, in float NdotV2, in float3 reflectance, in float ax2,in float ay2) +{ + const float devsh_v = geom_smith::ggx::devsh_part(TdotV2, BdotV2, NdotV2, ax2, ay2); + const float pdf = ggx_pdf_wo_clamps(ndf, devsh_v, maxNdotV); + + const float G2_over_G1 = geom_smith::ggx::G2_over_G1_devsh( + maxNdotL, TdotL2,BdotL2,NdotL2, + maxNdotV, devsh_v, + ax2, ay2 + ); + + return quotient_and_pdf_rgb::create(reflectance * G2_over_G1, pdf); +} + + template + quotient_and_pdf_rgb ggx_aniso_cos_quotient_and_pdf(in LightSample _sample, in surface_interactions::Anisotropic interaction, in AnisotropicMicrofacetCache _cache, in float2x3 ior, in float ax, in float ay) +{ + const float ax2 = ax * ax; + const float ay2 = ay * ay; + + const float TdotV2 = interaction.TdotV * interaction.TdotV; + const float BdotV2 = interaction.BdotV * interaction.BdotV; + const float NdotV2 = interaction.NdotV_squared; + + const float TdotH2 = _cache.TdotH * _cache.TdotH; + const float BdotH2 = _cache.BdotH * _cache.BdotH; + + const float devsh_v = geom_smith::ggx::devsh_part(TdotV2, BdotV2, NdotV2, ax2, ay2); + const float ndf = ndf::ggx::aniso(TdotH2, BdotH2, _cache.NdotH2, ax, ay, ax2, ay2); + const float pdf = ggx_pdf_wo_clamps(ndf, devsh_v, interaction.NdotV); + quotient_and_pdf_rgb qpdf = quotient_and_pdf_rgb::create(float3(0.0, 0.0, 0.0), pdf); + if (_sample.NdotL>FLT_MIN && interaction.NdotV>FLT_MIN) + { + const float TdotL2 = _sample.TdotL*_sample.TdotL; + const float BdotL2 = _sample.BdotL*_sample.BdotL; + + const float3 reflectance = fresnel::conductor(ior[0], ior[1], _cache.VdotH); + const float G2_over_G1 = geom_smith::ggx::G2_over_G1_devsh( + _sample.NdotL, TdotL2, BdotL2, _sample.NdotL2, + interaction.NdotV, devsh_v, + ax2, ay2 + ); + + qpdf.quotient = reflectance * G2_over_G1; + } + + return qpdf; +} + +} +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/bsdf/diffuse/lambert.hlsl b/include/nbl/builtin/hlsl/bxdf/bsdf/diffuse/lambert.hlsl new file mode 100644 index 0000000000..2be40a881c --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/bsdf/diffuse/lambert.hlsl @@ -0,0 +1,122 @@ +// 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 + +#ifndef _NBL_BUILTIN_HLSL_BXDF_BSDF_DIFFUSE_LAMBERT_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_BSDF_DIFFUSE_LAMBERT_INCLUDED_ + +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace bsdf +{ +namespace diffuse +{ + +float lambertian() +{ + return math::RECIPROCAL_PI * 0.5; +} + +float lambertian_cos_eval_rec_2pi_factored_out_wo_clamps(in float absNdotL) +{ + return absNdotL; +} +float lambertian_cos_eval_rec_2pi_factored_out(in float NdotL) +{ + return lambertian_cos_eval_rec_2pi_factored_out_wo_clamps(abs(NdotL)); +} + +float lambertian_cos_eval_wo_clamps(in float absNdotL) +{ + return lambertian_cos_eval_rec_2pi_factored_out_wo_clamps(absNdotL) * lambertian(); +} +template +float lambertian_cos_eval(in LightSample _sample) +{ + return lambertian_cos_eval_rec_2pi_factored_out(_sample.NdotL) * lambertian(); +} + +template +LightSample lambertian_cos_generate_wo_clamps(in float3 tangentSpaceV, in float3x3 m, inout float3 u) +{ + float3 L = sampling::projected_sphere_generate(u); + + return LightSample::createTangentSpace(tangentSpaceV, IncomingRayDirInfo::create(L), m); +} +template +LightSample lambertian_cos_generate(in surface_interactions::Anisotropic interaction, inout float3 u) +{ + return lambertian_cos_generate_wo_clamps(interaction.getTangentSpaceV(), interaction.getTangentFrame(), u); +} + +template +quotient_and_pdf_scalar lambertian_cos_quotient_and_pdf_wo_clamps(in float absNdotL) +{ + float pdf; + float q = sampling::projected_sphere_quotient_and_pdf(pdf, absNdotL); + return quotient_and_pdf_scalar::create(q, pdf); +} +template +quotient_and_pdf_scalar lambertian_cos_quotient_and_pdf(in LightSample s) +{ + float pdf; + float q = lambertian_cos_quotient_and_pdf_wo_clamps(pdf, abs(s.NdotL)); + return quotient_and_pdf_scalar::create(q, pdf); +} + +float lambertian_pdf_wo_clamps(in float absNdotL) +{ + return sampling::projected_sphere_pdf(absNdotL); +} + + +template +struct Lambertian : BxDFBase, surface_interactions::Isotropic > +{ + using base_t = BxDFBase, surface_interactions::Isotropic >; + + static Lambertian create() + { + Lambertian lambertian; + return lambertian; + } + + typename base_t::spectrum_t cos_eval(in typename base_t::sample_t s, in typename base_t::interaction_t interaction) + { + return 0.5f * math::RECIPROCAL_PI * max(s.NdotL, 0.0f); + } + + static + typename base_t::sample_t generate(in surface_interactions::Anisotropic interaction, inout float3 u) + { + float3 L = sampling::projected_sphere_generate(u); + + const float3 tangentSpaceV = interaction.getTangentSpaceV(); + const float3x3 tangentFrame = interaction.getTangentFrame(); + + return LightSample::createTangentSpace(tangentSpaceV, IncomingRayDirInfo::create(L), tangentFrame); + } + + typename base_t::q_pdf_t cos_quotient_and_pdf(in typename base_t::sample_t s, in typename base_t::interaction_t interaction) + { + float pdf; + float q = sampling::projected_sphere_quotient_and_pdf(pdf, abs(s.NdotL)); + + return quotient_and_pdf_scalar::create(q, pdf); + } +}; + +} +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/bsdf/specular/beckmann.hlsl b/include/nbl/builtin/hlsl/bxdf/bsdf/specular/beckmann.hlsl new file mode 100644 index 0000000000..2b5641ac31 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/bsdf/specular/beckmann.hlsl @@ -0,0 +1,223 @@ +// 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 + +#ifndef _NBL_BUILTIN_HLSL_BXDF_BSDF_SPECULAR_BECKMANN_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_BSDF_SPECULAR_BECKMANN_INCLUDED_ + +#include +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace bsdf +{ +namespace specular +{ + +// TODO why `backside` is here? its not used. is it some convention? +template +LightSample beckmann_cos_generate_wo_clamps(in float3 localV, in bool backside, in float3 upperHemisphereLocalV, in float3x3 m, float3 u, in float ax, in float ay, in float rcpOrientedEta, in float orientedEta2, in float rcpOrientedEta2, out AnisotropicMicrofacetCache _cache) +{ + + // thanks to this manouvre the H will always be in the upper hemisphere (NdotH>0.0) + const float3 H = brdf::specular::beckmann_cos_generate_wo_clamps(upperHemisphereLocalV,u.xy,ax,ay); + + const float VdotH = dot(localV,H); + const float reflectance = fresnel::dielectric_common(orientedEta2,abs(VdotH)); + + float rcpChoiceProb; + const bool transmitted = math::partitionRandVariable(reflectance, u.z, rcpChoiceProb); + + float3 localL; + _cache = AnisotropicMicrofacetCache::create(localV, H, transmitted, rcpOrientedEta, rcpOrientedEta2); + localL = math::reflect_refract(transmitted, localV, H, VdotH, _cache.LdotH, rcpOrientedEta); + + return LightSample::createTangentSpace(localV, IncomingRayDirInfo::create(localL), m); +} + +// `u` should be inout right? +template +LightSample beckmann_cos_generate(in surface_interactions::Anisotropic interaction, inout float3 u, in float ax, in float ay, in float eta, out AnisotropicMicrofacetCache _cache) +{ + const float3 localV = interaction.getTangentSpaceV(); + + float orientedEta, rcpOrientedEta; + const bool backside = getOrientedEtas(orientedEta, rcpOrientedEta, interaction.NdotV, eta); + + const float3 upperHemisphereV = backside ? (-localV):localV; + + const float3x3 m = interaction.getTangentFrame(); + return beckmann_cos_generate_wo_clamps(localV,backside,upperHemisphereV,m, u,ax,ay, rcpOrientedEta,orientedEta*orientedEta,rcpOrientedEta*rcpOrientedEta,_cache); +} + + + +// isotropic PDF +float beckmann_pdf_wo_clamps(in bool transmitted, in float reflectance, in float ndf, in float absNdotV, in float NdotV2, in float VdotH, in float LdotH, in float VdotHLdotH, in float a2, in float orientedEta, out float onePlusLambda_V) +{ + const float lambda = geom_smith::beckmann::Lambda(NdotV2, a2); + return geom_smith::VNDF_pdf_wo_clamps(ndf,lambda,absNdotV,transmitted,VdotH,LdotH,VdotHLdotH,orientedEta,reflectance,onePlusLambda_V); +} + +// anisotropic PDF +float beckmann_pdf_wo_clamps(in bool transmitted, in float reflectance, in float ndf, in float absNdotV, in float TdotV2, in float BdotV2, in float NdotV2, in float VdotH, in float LdotH, in float VdotHLdotH, in float ax2, in float ay2, in float orientedEta, out float onePlusLambda_V) +{ + float c2 = geom_smith::beckmann::C2(TdotV2, BdotV2, NdotV2, ax2, ay2); + float lambda = geom_smith::beckmann::Lambda(c2); + return geom_smith::VNDF_pdf_wo_clamps(ndf,lambda,absNdotV,transmitted,VdotH,LdotH,VdotHLdotH,orientedEta,reflectance,onePlusLambda_V); +} + + + +quotient_and_pdf_scalar beckmann_cos_quotient_and_pdf_wo_clamps(in float ndf, in bool transmitted, in float NdotL2, in float absNdotV, in float NdotV2, in float VdotH, in float LdotH, in float VdotHLdotH, in float reflectance, in float orientedEta, in float a2) +{ + float onePlusLambda_V; + const float pdf = beckmann_pdf_wo_clamps(transmitted, reflectance, ndf, absNdotV, NdotV2, VdotH, LdotH, VdotHLdotH, a2, orientedEta, onePlusLambda_V); + + return quotient_and_pdf_scalar::create( geom_smith::beckmann::G2_over_G1(onePlusLambda_V, NdotL2, a2), pdf ); +} + +template +quotient_and_pdf_scalar beckmann_cos_quotient_and_pdf(in LightSample _sample, in surface_interactions::Isotropic interaction, in IsotropicMicrofacetCache _cache, in float eta, in float a2) +{ + const float ndf = ndf::beckmann(a2,_cache.NdotH2); + + float orientedEta, dummy; + const bool backside = math::getOrientedEtas(orientedEta, dummy, _cache.VdotH, eta); + const float orientedEta2 = orientedEta*orientedEta; + + const float VdotHLdotH = _cache.VdotH*_cache.LdotH; + const bool transmitted = VdotHLdotH<0.0; + + const float reflectance = fresnel::dielectric_common(orientedEta2,abs(_cache.VdotH)); + + const float absNdotV = abs(interaction.NdotV); + + return beckmann_cos_quotient_and_pdf_wo_clamps(ndf, transmitted, _sample.NdotL2, absNdotV, interaction.NdotV_squared, _cache.VdotH, _cache.LdotH, VdotHLdotH, reflectance, orientedEta, a2); +} + +quotient_and_pdf_scalar beckmann_aniso_dielectric_cos_quotient_and_pdf_wo_clamps(in float ndf, in bool transmitted, in float NdotL2, in float TdotL2, in float BdotL2, in float absNdotV, in float TdotV2, in float BdotV2, in float NdotV2, in float VdotH, in float LdotH, in float VdotHLdotH, in float reflectance, in float orientedEta, in float ax2, in float ay2) +{ + float onePlusLambda_V; + const float pdf = beckmann_pdf_wo_clamps(transmitted,reflectance, ndf,absNdotV,TdotV2,BdotV2,NdotV2, VdotH,LdotH,VdotHLdotH, ax2,ay2,orientedEta,onePlusLambda_V); + + return quotient_and_pdf_scalar::create( geom_smith::beckmann::G2_over_G1(onePlusLambda_V, TdotL2, BdotL2, NdotL2, ax2, ay2), pdf ); +} +template +quotient_and_pdf_scalar beckmann_aniso_dielectric_cos_quotient_and_pdf(in LightSample _sample, in surface_interactions::Anisotropic interaction, in AnisotropicMicrofacetCache _cache, in float eta, in float ax, in float ay) +{ + const float ax2 = ax*ax; + const float ay2 = ay*ay; + const float TdotH2 = _cache.TdotH*_cache.TdotH; + const float BdotH2 = _cache.BdotH*_cache.BdotH; + const float ndf = ndf::beckmann(ax,ay,ax2,ay2, TdotH2,BdotH2,_cache.NdotH2); + + const float TdotL2 = _sample.TdotL*_sample.TdotL; + const float BdotL2 = _sample.BdotL*_sample.BdotL; + + const float TdotV2 = interaction.TdotV*interaction.TdotV; + const float BdotV2 = interaction.BdotV*interaction.BdotV; + + const float VdotH = _cache.VdotH; + + float orientedEta, dummy; + const bool backside = math::getOrientedEtas(orientedEta, dummy, VdotH, eta); + const float orientedEta2 = orientedEta*orientedEta; + + const float VdotHLdotH = VdotH*_cache.LdotH; + const bool transmitted = VdotHLdotH<0.0; + + const float reflectance = fresnel::dielectric_common(orientedEta2,abs(VdotH)); + return beckmann_aniso_dielectric_cos_quotient_and_pdf_wo_clamps(ndf, transmitted, _sample.NdotL2,TdotL2,BdotL2, abs(interaction.NdotV),TdotV2,BdotV2, interaction.NdotV_squared, VdotH,_cache.LdotH,VdotHLdotH, reflectance,orientedEta, ax2,ay2); +} + + + +float beckmann_smith_height_correlated_dielectric_cos_eval_wo_clamps( + in float NdotH2, in float NdotL2, in float absNdotV, in float NdotV2, + in bool transmitted, in float VdotH, in float LdotH, in float VdotHLdotH, + in float orientedEta, in float orientedEta2, in float a2) +{ + const float scalar_part = brdf::specular::beckmann_height_correlated_cos_eval_DG_wo_clamps(NdotH2, NdotL2, NdotV2, a2); + + const float reflectance = fresnel::dielectric_common(orientedEta2,abs(VdotH)); + + return reflectance*ndf::microfacet_to_light_measure_transform(scalar_part,absNdotV,transmitted,VdotH,LdotH,VdotHLdotH,orientedEta); +} + +// before calling you must ensure that `AnisotropicMicrofacetCache` is valid (if a given V vector can "see" the L vector) +template +float beckmann_smith_height_correlated_dielectric_cos_eval_wo_cache_validation(in LightSample _sample, in surface_interactions::Isotropic interaction, in IsotropicMicrofacetCache _cache, in float eta, in float a2) +{ + float orientedEta, dummy; + const bool backside = math::getOrientedEtas(orientedEta, dummy, _cache.VdotH, eta); + const float orientedEta2 = orientedEta*orientedEta; + + const float VdotHLdotH = _cache.VdotH*_cache.LdotH; + const bool transmitted = VdotHLdotH<0.0; + + return beckmann_smith_height_correlated_dielectric_cos_eval_wo_clamps( + _cache.NdotH2,_sample.NdotL2,abs(interaction.NdotV),interaction.NdotV_squared, + transmitted,_cache.VdotH,_cache.LdotH,VdotHLdotH, + orientedEta,orientedEta2,a2); +} + +float beckmann_aniso_smith_height_correlated_dielectric_cos_eval_wo_clamps( + in float NdotH2, in float TdotH2, in float BdotH2, + in float NdotL2, in float TdotL2, in float BdotL2, + in float absNdotV, in float NdotV2, in float TdotV2, in float BdotV2, + in bool transmitted, in float VdotH, in float LdotH, in float VdotHLdotH, + in float orientedEta, in float orientedEta2, + in float ax, in float ax2, in float ay, in float ay2) +{ + const float scalar_part = brdf::specular::beckmann_aniso_height_correlated_cos_eval_DG_wo_clamps(NdotH2,TdotH2,BdotH2, NdotL2,TdotL2,BdotL2, NdotV2,TdotV2,BdotV2, ax, ax2, ay, ay2); + + const float reflectance = fresnel::dielectric_common(orientedEta2,abs(VdotH)); + + return reflectance*ndf::microfacet_to_light_measure_transform(scalar_part,absNdotV,transmitted,VdotH,LdotH,VdotHLdotH,orientedEta); +} + +// before calling you must ensure that `AnisotropicMicrofacetCache` is valid (if a given V vector can "see" the L vector) +template +float beckmann_aniso_smith_height_correlated_cos_eval_wo_cache_validation(in LightSample _sample, in surface_interactions::Anisotropic interaction, in AnisotropicMicrofacetCache _cache, in float eta, in float ax, in float ax2, in float ay, in float ay2) +{ + const float TdotH2 = _cache.TdotH*_cache.TdotH; + const float BdotH2 = _cache.BdotH*_cache.BdotH; + + const float TdotL2 = _sample.TdotL*_sample.TdotL; + const float BdotL2 = _sample.BdotL*_sample.BdotL; + + const float TdotV2 = interaction.TdotV*interaction.TdotV; + const float BdotV2 = interaction.BdotV*interaction.BdotV; + + const float VdotH = _cache.VdotH; + + float orientedEta, dummy; + const bool backside = math::getOrientedEtas(orientedEta, dummy, VdotH, eta); + const float orientedEta2 = orientedEta*orientedEta; + + const float VdotHLdotH = VdotH*_cache.LdotH; + const bool transmitted = VdotHLdotH<0.0; + + return beckmann_aniso_smith_height_correlated_dielectric_cos_eval_wo_clamps( + _cache.NdotH2,TdotH2,BdotH2, + _sample.NdotL2,TdotL2,BdotL2, + abs(interaction.NdotV),interaction.NdotV_squared,TdotV2,BdotV2, + transmitted,VdotH,_cache.LdotH,VdotHLdotH, + orientedEta,orientedEta2,ax,ax*ax,ay,ay*ay); +} + +} +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/bsdf/specular/common.hlsl b/include/nbl/builtin/hlsl/bxdf/bsdf/specular/common.hlsl new file mode 100644 index 0000000000..d9439a7257 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/bsdf/specular/common.hlsl @@ -0,0 +1,14 @@ +// 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 + +#ifndef _NBL_BUILTIN_HLSL_BXDF_BSDF_SPECULAR_COMMON_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_BSDF_SPECULAR_COMMON_INCLUDED_ + +#include + +#include +#include + + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/bxdf/bsdf/specular/dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/bsdf/specular/dielectric.hlsl new file mode 100644 index 0000000000..cb4fdb9130 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/bsdf/specular/dielectric.hlsl @@ -0,0 +1,144 @@ +// 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 + +#ifndef _NBL_BUILTIN_HLSL_BXDF_BSDF_SPECULAR_DIELECTRIC_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_BSDF_SPECULAR_DIELECTRIC_INCLUDED_ + +#include +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace bsdf +{ +namespace specular +{ + +// usually `luminosityContributionHint` would be the Rec.709 luma coefficients (the Y row of the RGB to CIE XYZ matrix) +// its basically a set of weights that determine +// assert(1.0==luminosityContributionHint.r+luminosityContributionHint.g+luminosityContributionHint.b); +// `remainderMetadata` is a variable in which the generator function returns byproducts of sample generation that would otherwise have to be redundantly calculated in `remainder_and_pdf` +template +LightSample thin_smooth_dielectric_cos_generate_wo_clamps(in float3 V, in float3 T, in float3 B, in float3 N, in float NdotV, in float absNdotV, inout float3 u, in float3 eta2, in float3 luminosityContributionHint, out float3 remainderMetadata) +{ + // we will only ever intersect from the outside + const float3 reflectance = fresnel::thindielectric_infinite_scatter(fresnel::dielectric_common(eta2,absNdotV)); + + // we are only allowed one choice for the entire ray, so make the probability a weighted sum + const float reflectionProb = dot(reflectance, luminosityContributionHint); + + float rcpChoiceProb; + const bool transmitted = math::partitionRandVariable(reflectionProb, u.z, rcpChoiceProb); + remainderMetadata = (transmitted ? (float3(1.0,1.0,1.0)-reflectance):reflectance)*rcpChoiceProb; + + const float3 L = (transmitted ? float3(0.0,0.0,0.0):(N*2.0*NdotV))-V; + return LightSample::create(IncomingRayDirInfo::create(L), dot(V,L), T, B, N); +} + + +template +LightSample thin_smooth_dielectric_cos_generate_wo_clamps(in float3 V, in float3 T, in float3 B, in float3 N, in float NdotV, in float absNdotV, inout float3 u, in float3 eta2, in float3 luminosityContributionHint) +{ + float3 dummy; + return thin_smooth_dielectric_cos_generate_wo_clamps(V,T,B,N,NdotV,absNdotV,u,eta2,luminosityContributionHint,dummy); +} + +template +LightSample thin_smooth_dielectric_cos_generate(in surface_interactions::Anisotropic interaction, inout float3 u, in float3 eta2, in float3 luminosityContributionHint) +{ + return thin_smooth_dielectric_cos_generate_wo_clamps(interaction.V.dir,interaction.T,interaction.B,interaction.N,interaction.NdotV,abs(interaction.NdotV),u,eta2,luminosityContributionHint); +} + + + +quotient_and_pdf_rgb thin_smooth_dielectric_cos_quotient_and_pdf_wo_clamps(in float3 remainderMetadata) +{ + float pdf = 1.0 / 0.0; // should be reciprocal probability of the fresnel choice divided by 0.0, but would still be an INF. + return quotient_and_pdf_rgb::create(remainderMetadata, pdf); +} + +quotient_and_pdf_rgb thin_smooth_dielectric_cos_quotient_and_pdf_wo_clamps(in bool transmitted, in float absNdotV, in float3 eta2, in float3 luminosityContributionHint) +{ + const float3 reflectance = fresnel::thindielectric_infinite_scatter(fresnel::dielectric_common(eta2,absNdotV)); + const float3 sampleValue = transmitted ? (float3(1.0,1.0,1.0)-reflectance):reflectance; + + const float sampleProb = dot(sampleValue,luminosityContributionHint); + + return thin_smooth_dielectric_cos_quotient_and_pdf_wo_clamps(sampleValue / sampleProb); +} + +// for information why we don't check the relation between `V` and `L` or `N` and `H`, see comments for `transmission_cos_quotient_and_pdf` in `irr/builtin/glsl/bxdf/common_samples.hlsl` +template +quotient_and_pdf_rgb thin_smooth_dielectric_cos_quotient_and_pdf(in LightSample _sample, in surface_interactions::Isotropic interaction, in float3 eta2, in float3 luminosityContributionHint) +{ + const bool transmitted = math::isTransmissionPath(interaction.NdotV,_sample.NdotL); + return thin_smooth_dielectric_cos_quotient_and_pdf_wo_clamps(transmitted,abs(interaction.NdotV),eta2,luminosityContributionHint); +} + + + +template +LightSample smooth_dielectric_cos_generate_wo_clamps(in float3 V, in float3 T, in float3 B, in float3 N, in bool backside, in float NdotV, in float absNdotV, in float NdotV2, inout float3 u, in float rcpOrientedEta, in float orientedEta2, in float rcpOrientedEta2, out bool transmitted) +{ + const float reflectance = fresnel::dielectric_common(orientedEta2,absNdotV); + + float rcpChoiceProb; + transmitted = math::partitionRandVariable(reflectance, u.z, rcpChoiceProb); + + const float3 L = math::reflect_refract(transmitted, V, N, backside, NdotV, NdotV2, rcpOrientedEta, rcpOrientedEta2); + return LightSample::create(IncomingRayDirInfo::create(L), dot(V,L), T, B, N); +} + +template +LightSample smooth_dielectric_cos_generate(in surface_interactions::Anisotropic interaction, inout float3 u, in float eta) +{ + float orientedEta, rcpOrientedEta; + const bool backside = getOrientedEtas(orientedEta, rcpOrientedEta, interaction.NdotV, eta); + + bool dummy; + return smooth_dielectric_cos_generate_wo_clamps( + interaction.V.dir, + interaction.T,interaction.B,interaction.N, + backside, + interaction.NdotV, + abs(interaction.NdotV), + interaction.NdotV*interaction.NdotV, + u, + rcpOrientedEta, orientedEta*orientedEta, rcpOrientedEta*rcpOrientedEta, + dummy + ); +} + + +quotient_and_pdf_scalar smooth_dielectric_cos_quotient_and_pdf(in bool transmitted, in float rcpOrientedEta2) +{ + const float pdf = 1.0 / 0.0; // should be reciprocal probability of the fresnel choice divided by 0.0, but would still be an INF. + return quotient_and_pdf_scalar::create(transmitted ? rcpOrientedEta2 : 1.0, pdf); +} + +// for information why we don't check the relation between `V` and `L` or `N` and `H`, see comments for `transmission_cos_quotient_and_pdf` in `irr/builtin/glsl/bxdf/common_samples.hlsl` +template +quotient_and_pdf_scalar smooth_dielectric_cos_quotient_and_pdf(in LightSample _sample, in surface_interactions::Isotropic interaction, in float eta) +{ + const bool transmitted = math::isTransmissionPath(interaction.NdotV,_sample.NdotL); + + float dummy, rcpOrientedEta; + const bool backside = math::getOrientedEtas(dummy, rcpOrientedEta, interaction.NdotV, eta); + + return smooth_dielectric_cos_quotient_and_pdf(transmitted,rcpOrientedEta); +} + +} +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/bsdf/specular/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/bsdf/specular/ggx.hlsl new file mode 100644 index 0000000000..d06397539a --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/bsdf/specular/ggx.hlsl @@ -0,0 +1,236 @@ +// 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 + +#ifndef _NBL_BUILTIN_HLSL_BXDF_BSDF_SPECULAR_GGX_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_BSDF_SPECULAR_GGX_INCLUDED_ + +#include +#include +#include +#include + + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace bsdf +{ +namespace specular +{ + + +float ggx_height_correlated_aniso_cos_eval_wo_clamps( + in float NdotH2, in float TdotH2, in float BdotH2, + in float absNdotL, in float NdotL2, in float TdotL2, in float BdotL2, + in float absNdotV, in float NdotV2, in float TdotV2, in float BdotV2, + in bool transmitted, in float VdotH, in float LdotH, in float VdotHLdotH, + in float orientedEta, in float orientedEta2, + in float ax, in float ax2, in float ay, in float ay2) +{ + float NG_already_in_reflective_dL_measure = brdf::specular::ggx_height_correlated_aniso_cos_eval_DG_wo_clamps(NdotH2, TdotH2, BdotH2, absNdotL, NdotL2, TdotL2, BdotL2, absNdotV, NdotV2, TdotV2, BdotV2, ax, ax2, ay, ay2); + + const float reflectance = fresnel::dielectric_common(orientedEta2, abs(VdotH)); + + return reflectance * ndf::ggx::microfacet_to_light_measure_transform(NG_already_in_reflective_dL_measure, absNdotL, transmitted, VdotH, LdotH, VdotHLdotH, orientedEta); +} + +// before calling you must ensure that `AnisotropicMicrofacetCache` is valid (if a given V vector can "see" the L vector) +template +float ggx_height_correlated_aniso_cos_eval(in LightSample _sample, in surface_interactions::Anisotropic interaction, in AnisotropicMicrofacetCache _cache, in float eta, in float ax, in float ay) +{ + const float TdotH2 = _cache.TdotH * _cache.TdotH; + const float BdotH2 = _cache.BdotH * _cache.BdotH; + + const float TdotL2 = _sample.TdotL * _sample.TdotL; + const float BdotL2 = _sample.BdotL * _sample.BdotL; + + const float TdotV2 = interaction.TdotV * interaction.TdotV; + const float BdotV2 = interaction.BdotV * interaction.BdotV; + + const float VdotH = _cache.VdotH; + + float orientedEta, dummy; + const bool backside = math::getOrientedEtas(orientedEta, dummy, VdotH, eta); + const float orientedEta2 = orientedEta * orientedEta; + + const float VdotHLdotH = VdotH * _cache.LdotH; + const bool transmitted = VdotHLdotH < 0.0; + + return ggx_height_correlated_aniso_cos_eval_wo_clamps( + _cache.NdotH2, TdotH2, BdotH2, + abs(_sample.NdotL), _sample.NdotL2, TdotL2, BdotL2, + abs(interaction.NdotV), interaction.NdotV_squared, TdotV2, BdotV2, + transmitted, VdotH, _cache.LdotH, VdotHLdotH, orientedEta, orientedEta2, + ax, ax * ax, ay, ay * ay + ); +} + + +float ggx_height_correlated_cos_eval_wo_clamps( + in float NdotH2, in float absNdotL, in float NdotL2, + in float absNdotV, in float NdotV2, + in bool transmitted, in float VdotH, in float LdotH, in float VdotHLdotH, + in float orientedEta, in float orientedEta2, in float a2) +{ + const float NG_already_in_reflective_dL_measure = brdf::specular::ggx_height_correlated_cos_eval_DG_wo_clamps(NdotH2, absNdotL, NdotL2, absNdotV, NdotV2, a2); + + const float reflectance = fresnel::dielectric_common(orientedEta2, abs(VdotH)); + + return reflectance * ndf::ggx::microfacet_to_light_measure_transform(NG_already_in_reflective_dL_measure, absNdotL, transmitted, VdotH, LdotH, VdotHLdotH, orientedEta); +} + +// before calling you must ensure that `AnisotropicMicrofacetCache` is valid (if a given V vector can "see" the L vector) +template +float ggx_height_correlated_cos_eval(in LightSample _sample, in surface_interactions::Isotropic interaction, in IsotropicMicrofacetCache _cache, in float eta, in float a2) +{ + float orientedEta, dummy; + const bool backside = math::getOrientedEtas(orientedEta, dummy, _cache.VdotH, eta); + const float orientedEta2 = orientedEta * orientedEta; + + const float VdotHLdotH = _cache.VdotH * _cache.LdotH; + const bool transmitted = VdotHLdotH < 0.0; + + return ggx_height_correlated_cos_eval_wo_clamps( + _cache.NdotH2, abs(_sample.NdotL), _sample.NdotL2, + abs(interaction.NdotV), interaction.NdotV_squared, + transmitted, _cache.VdotH, _cache.LdotH, VdotHLdotH, orientedEta, orientedEta2, a2 + ); +} + +// TODO: unifty the two following functions into `microfacet_BSDF_cos_generate_wo_clamps(float3 H,...)` and `microfacet_BSDF_cos_generate` or at least a auto declaration macro in lieu of a template +template +LightSample ggx_cos_generate_wo_clamps(in float3 localV, in bool backside, in float3 upperHemisphereLocalV, in float3x3 m, inout float3 u, in float _ax, in float _ay, in float rcpOrientedEta, in float orientedEta2, in float rcpOrientedEta2, out AnisotropicMicrofacetCache _cache) +{ + // thanks to this manouvre the H will always be in the upper hemisphere (NdotH>0.0) + const float3 H = brdf::specular::ggx_cos_generate(upperHemisphereLocalV, u.xy, _ax, _ay); + + const float VdotH = dot(localV, H); + const float reflectance = fresnel::dielectric_common(orientedEta2, abs(VdotH)); + + float rcpChoiceProb; + bool transmitted = math::partitionRandVariable(reflectance, u.z, rcpChoiceProb); + + float3 localL; + _cache = AnisotropicMicrofacetCache::create(localV, H, transmitted, rcpOrientedEta, rcpOrientedEta2); + localL = math::reflect_refract(transmitted, localV, H, VdotH, _cache.LdotH, rcpOrientedEta); + + return LightSample::createTangentSpace(localV, IncomingRayDirInfo::create(localL), m); +} + +template +LightSample ggx_cos_generate(in surface_interactions::Anisotropic interaction, inout float3 u, in float ax, in float ay, in float eta, out AnisotropicMicrofacetCache _cache) +{ + const float3 localV = interaction.getTangentSpaceV(); + + float orientedEta, rcpOrientedEta; + const bool backside = math::getOrientedEtas(orientedEta, rcpOrientedEta, interaction.NdotV, eta); + + const float3 upperHemisphereV = backside ? (-localV) : localV; + + const float3x3 m = interaction.getTangentFrame(); + return ggx_cos_generate_wo_clamps(localV, backside, upperHemisphereV, m, u, ax, ay, rcpOrientedEta, orientedEta * orientedEta, rcpOrientedEta * rcpOrientedEta, _cache); +} + + + +float ggx_pdf_wo_clamps(in bool transmitted, in float reflectance, in float ndf, in float devsh_v, in float absNdotV, in float VdotH, in float LdotH, in float VdotHLdotH, in float orientedEta) +{ + return geom_smith::VNDF_pdf_wo_clamps(ndf, geom_smith::ggx::G1_wo_numerator(absNdotV, devsh_v), absNdotV, transmitted, VdotH, LdotH, VdotHLdotH, orientedEta, reflectance); +} +float ggx_pdf_wo_clamps(in bool transmitted, in float reflectance, in float NdotH2, in float absNdotV, in float NdotV2, in float VdotH, in float LdotH, in float VdotHLdotH, in float a2, in float orientedEta) +{ + const float ndf = ndf::ggx::trowbridge_reitz(a2, NdotH2); + const float devsh_v = geom_smith::ggx::devsh_part(NdotV2, a2, 1.0 - a2); + + return ggx_pdf_wo_clamps(transmitted, reflectance, ndf, devsh_v, absNdotV, VdotH, LdotH, VdotHLdotH, orientedEta); +} + +float ggx_pdf_wo_clamps(in bool transmitted, in float reflectance, in float NdotH2, in float TdotH2, in float BdotH2, in float absNdotV, in float NdotV2, in float TdotV2, in float BdotV2, in float VdotH, in float LdotH, in float VdotHLdotH, in float ax, in float ay, in float ax2, in float ay2, in float orientedEta) +{ + const float ndf = ndf::ggx::aniso(TdotH2, BdotH2, NdotH2, ax, ay, ax2, ay2); + const float devsh_v = geom_smith::ggx::devsh_part(TdotV2, BdotV2, NdotV2, ax2, ay2); + + return ggx_pdf_wo_clamps(transmitted, reflectance, ndf, devsh_v, absNdotV, VdotH, LdotH, VdotHLdotH, orientedEta); +} + +float ggx_cos_quotient_and_pdf_wo_clamps(out float pdf, in float ndf, in bool transmitted, in float absNdotL, in float NdotL2, in float absNdotV, in float NdotV2, in float VdotH, in float LdotH, in float VdotHLdotH, in float reflectance, in float orientedEta, in float a2) +{ + const float one_minus_a2 = 1.0 - a2; + const float devsh_v = geom_smith::ggx::devsh_part(NdotV2, a2, one_minus_a2); + pdf = ggx_pdf_wo_clamps(transmitted, reflectance, ndf, devsh_v, absNdotV, VdotH, LdotH, VdotHLdotH, orientedEta); + + return geom_smith::ggx::G2_over_G1_devsh(absNdotL, NdotL2, absNdotV, devsh_v, a2, one_minus_a2); +} + +template +float ggx_cos_quotient_and_pdf(out float pdf, in LightSample _sample, in surface_interactions::Isotropic interaction, in IsotropicMicrofacetCache _cache, in float eta, in float a2) +{ + const float ndf = ndf::ggx::trowbridge_reitz(a2, _cache.NdotH2); + + float orientedEta, dummy; + const bool backside = math::getOrientedEtas(orientedEta, dummy, _cache.VdotH, eta); + const float orientedEta2 = orientedEta * orientedEta; + + const float VdotHLdotH = _cache.VdotH * _cache.LdotH; + const bool transmitted = VdotHLdotH < 0.0; + + const float reflectance = fresnel::dielectric_common(orientedEta2, abs(_cache.VdotH)); + + const float absNdotV = abs(interaction.NdotV); + return ggx_cos_quotient_and_pdf_wo_clamps(pdf, ndf, transmitted, abs(_sample.NdotL), _sample.NdotL2, absNdotV, interaction.NdotV_squared, _cache.VdotH, _cache.LdotH, VdotHLdotH, reflectance, orientedEta, a2); +} + +template +float ggx_aniso_cos_quotient_and_pdf_wo_clamps(out float pdf, in float ndf, in bool transmitted, in float absNdotL, in float NdotL2, in float TdotL2, in float BdotL2, in float absNdotV, in float TdotV2, in float BdotV2, in float NdotV2, in float VdotH, in float LdotH, in float VdotHLdotH, in float reflectance, in float orientedEta, in float ax2, in float ay2) +{ + const float devsh_v = geom_smith::ggx::devsh_part(TdotV2, BdotV2, NdotV2, ax2, ay2); + pdf = ggx_pdf_wo_clamps(transmitted, reflectance, ndf, devsh_v, absNdotV, VdotH, LdotH, VdotHLdotH, orientedEta); + + return geom_smith::ggx::G2_over_G1_devsh( + absNdotL, TdotL2, BdotL2, NdotL2, + absNdotV, devsh_v, + ax2, ay2 + ); +} + +template +float ggx_aniso_cos_quotient_and_pdf(out float pdf, in LightSample _sample, in surface_interactions::Anisotropic interaction, in AnisotropicMicrofacetCache _cache, in float eta, in float ax, in float ay) +{ + const float ax2 = ax * ax; + const float ay2 = ay * ay; + const float TdotH2 = _cache.TdotH * _cache.TdotH; + const float BdotH2 = _cache.BdotH * _cache.BdotH; + const float ndf = ndf::ggx::aniso(TdotH2, BdotH2, _cache.NdotH2, ax, ay, ax2, ay2); + + const float TdotL2 = _sample.TdotL * _sample.TdotL; + const float BdotL2 = _sample.BdotL * _sample.BdotL; + + const float TdotV2 = interaction.TdotV * interaction.TdotV; + const float BdotV2 = interaction.BdotV * interaction.BdotV; + + const float VdotH = _cache.VdotH; + + float orientedEta, dummy; + const bool backside = math::getOrientedEtas(orientedEta, dummy, VdotH, eta); + const float orientedEta2 = orientedEta * orientedEta; + + const float VdotHLdotH = VdotH * _cache.LdotH; + const bool transmitted = VdotHLdotH < 0.0; + + const float reflectance = fresnel::dielectric_common(orientedEta2, abs(VdotH)); + + const float absNdotV = abs(interaction.NdotV); + return ggx_aniso_cos_quotient_and_pdf_wo_clamps(pdf, ndf, transmitted, abs(_sample.NdotL), _sample.NdotL2, TdotL2, BdotL2, absNdotV, TdotV2, BdotV2, interaction.NdotV_squared, VdotH, _cache.LdotH, VdotHLdotH, reflectance, orientedEta, ax2, ay2); +} + +} +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index 643a1111f5..4592b3bf9f 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -4,9 +4,9 @@ #ifndef _NBL_BUILTIN_HLSL_BXDF_COMMON_INCLUDED_ #define _NBL_BUILTIN_HLSL_BXDF_COMMON_INCLUDED_ -#include -#include -#include +#include +//#include // ??? +#include namespace nbl { @@ -22,6 +22,26 @@ namespace ray_dir_info // no ray-differentials, nothing struct Basic { + static Basic create(float3 dir) + { + Basic retval; + retval.direction = dir; + return retval; + } + + // `tform` must be orthonormal + void transform(float3x3 tform) + { + direction = mul(tform, direction); // TODO (make sure) is this correct multiplication order in HLSL? Original GLSL code is `tform * direction` + } + + // `tform` must be orthonormal + static Basic transform(Basic r, float3x3 tform) + { + r.transform(tform); + return r; + } + float3 getDirection() {return direction;} Basic transmit() @@ -34,7 +54,7 @@ struct Basic Basic reflect(const float3 N, const float directionDotN) { Basic retval; - retval.direction = nbl::hlsl::reflect(direction,N,directionDotN); + retval.direction = math::reflect(direction,N,directionDotN); return retval; } @@ -81,7 +101,7 @@ struct Anisotropic : Isotropic ) { Anisotropic retval; - retval::Isotropic = isotropic; + (Isotropic) retval = isotropic; retval.T = normalizedT; retval.B = normalizedB; @@ -97,11 +117,11 @@ struct Anisotropic : Isotropic } static Anisotropic create(const Isotropic isotropic) { - float2x3 TB = nbl::hlsl::frisvad(isotropic.N); + float2x3 TB = math::frisvad(isotropic.N); return create(isotropic,TB[0],TB[1]); } - float3 getTangentSpaceV() {return float3(Tdot,BdotV,Isotropic::NdotV);} + float3 getTangentSpaceV() {return float3(TdotV,BdotV,Isotropic::NdotV);} // WARNING: its the transpose of the old GLSL function return value! float3x3 getTangentFrame() {return float3x3(T,B,Isotropic::N);} @@ -195,7 +215,7 @@ struct IsotropicMicrofacetCache // always valid because its specialized for the reflective case static IsotropicMicrofacetCache createForReflection(const float NdotV, const float NdotL, const float VdotL, out float LplusV_rcpLen) { - LplusV_rcpLen = inversesqrt(2.0+2.0*VdotL); + LplusV_rcpLen = rsqrt(2.0+2.0*VdotL); IsotropicMicrofacetCache retval; @@ -227,12 +247,12 @@ struct IsotropicMicrofacetCache ) { // TODO: can we optimize? - H = computeMicrofacetNormal(transmitted,V,L,orientedEta); + H = math::computeMicrofacetNormal(transmitted,V,L,orientedEta); retval.NdotH = dot(N,H); // not coming from the medium (reflected) OR // exiting at the macro scale AND ( (not L outside the cone of possible directions given IoR with constraint VdotH*LdotH<0.0) OR (microfacet not facing toward the macrosurface, i.e. non heightfield profile of microsurface) ) - const bool valid = !transmitted || (VdotL<=-min(orientedEta,rcpOrientedEta) && _cache.NdotH>nbl::hlsl::numeric_limits::min()); + const bool valid = !transmitted || (VdotL<=-min(orientedEta,rcpOrientedEta) && retval.NdotH>nbl::hlsl::numeric_limits::min()); if (valid) { // TODO: can we optimize? @@ -253,15 +273,15 @@ struct IsotropicMicrofacetCache { const float NdotV = interaction.NdotV; const float NdotL = _sample.NdotL; - const bool transmitted = nbl_glsl_isTransmissionPath(NdotV,NdotL); + const bool transmitted = math::isTransmissionPath(NdotV,NdotL); float orientedEta, rcpOrientedEta; - const bool backside = nbl_glsl_getOrientedEtas(orientedEta,rcpOrientedEta,NdotV,eta); + const bool backside = math::getOrientedEtas(orientedEta,rcpOrientedEta,NdotV,eta); - const vec3 V = interaction.V.getDirection(); - const vec3 L = _sample.L; + const float3 V = interaction.V.getDirection(); + const float3 L = _sample.L; const float VdotL = dot(V,L); - return nbl_glsl_calcIsotropicMicrofacetCache(_cache,transmitted,V,L,interaction.N,NdotL,VdotL,orientedEta,rcpOrientedEta,H); + return nbl_glsl_calcIsotropicMicrofacetCache(retval,transmitted,V,L,interaction.N,NdotL,VdotL,orientedEta,rcpOrientedEta,H); } template static bool compute( @@ -272,7 +292,11 @@ struct IsotropicMicrofacetCache ) { float3 dummy; - return nbl_glsl_calcIsotropicMicrofacetCache(_cache,transmitted,V,L,interaction.N,NdotL,VdotL,orientedEta,rcpOrientedEta,dummy); + //float3 V = interaction.V.getDirection(); + //return nbl_glsl_calcIsotropicMicrofacetCache(retval,transmitted,V,L,interaction.N,NdotL,_sample.VdotL,orientedEta,rcpOrientedEta,dummy); + // TODO try to use other function that doesnt need `dummy` + // (computing H that is then lost) + return compute(retval, interaction, _sample, eta, dummy); } bool isValidVNDFMicrofacet(const bool is_bsdf, const bool transmission, const float VdotL, const float eta, const float rcp_eta) @@ -314,7 +338,7 @@ struct AnisotropicMicrofacetCache : IsotropicMicrofacetCache if (transmitted) { const float VdotH = retval.VdotH; - LdotH = transmitted ? refract_compute_NdotT(VdotH<0.0,VdotH*VdotH,rcpOrientedEta2); + retval.LdotH = transmitted ? math::refract_compute_NdotT(VdotH<0.0,VdotH*VdotH,rcpOrientedEta2) : VdotH; } return retval; @@ -325,7 +349,7 @@ struct AnisotropicMicrofacetCache : IsotropicMicrofacetCache AnisotropicMicrofacetCache retval; float LplusV_rcpLen; - retval = createForReflection(tangentSpaceV.z,tangentSpaceL.z,VdotL,LplusV_rcpLen); + (IsotropicMicrofacetCache) retval = IsotropicMicrofacetCache::createForReflection(tangentSpaceV.z,tangentSpaceL.z,VdotL,LplusV_rcpLen); retval.TdotH = (tangentSpaceV.x+tangentSpaceL.x)*LplusV_rcpLen; retval.BdotH = (tangentSpaceV.y+tangentSpaceL.y)*LplusV_rcpLen; @@ -347,7 +371,6 @@ struct AnisotropicMicrofacetCache : IsotropicMicrofacetCache const float orientedEta, const float rcpOrientedEta, out float3 H ) { - float3 H; const bool valid = IsotropicMicrofacetCache::compute(retval,transmitted,V,L,N,NdotL,VdotL,orientedEta,rcpOrientedEta,H); if (valid) { @@ -380,12 +403,12 @@ struct AnisotropicMicrofacetCache : IsotropicMicrofacetCache // finally fixed the semantic F-up, value/pdf = quotient not remainder -template +template struct quotient_and_pdf { - quotient_and_pdf create(const SpectralBins _quotient, const float _pdf) + static quotient_and_pdf create(const SpectralBins _quotient, const Pdf _pdf) { - quotient_and_pdf retval; + quotient_and_pdf retval; retval.quotient = _quotient; retval.pdf = _pdf; return retval; @@ -397,9 +420,60 @@ struct quotient_and_pdf } SpectralBins quotient; - float pdf; + Pdf pdf; }; +using quotient_and_pdf_scalar = quotient_and_pdf; +using quotient_and_pdf_rgb = quotient_and_pdf; + +// Utility class +// Making it easier to conform your BxDFs to the concept +template +struct BxDFBase +{ + // BxDFs must define such typenames: + using spectrum_t = Spectrum; + using pdf_t = Pdf; + using q_pdf_t = quotient_and_pdf; + + using sample_t = Sample; + using interaction_t = Interaction; + + /** + * BxDFs (i.e. types derived from this base) must define following member functions: + * + * spectrum_t eval(in sample_t, in interaction_t); + * + * sample_t generate(in surface_interactions::Anisotropic, inout float3 u); + * + * q_pdf_t quotient_and_pdf(in sample_t, in interaction_t); + */ +}; + +// Utility class +// Making it easier to conform your BxDFs to the concept +template +struct MicrofacetBxDFBase : BxDFBase +{ + // Microfacet BxDFs must define such typenames: + //using spectrum_t = Spectrum; + //using pdf_t = Pdf; + //using q_pdf_t = quotient_and_pdf; + + //using sample_t = Sample; + //using interaction_t = Interaction; + using cache_t = MicrofacetCache; + + /** + * Microfacet BxDFs (i.e. types derived from this base) must define following member functions: + * + * spectrum_t eval(in sample_t, in interaction_t, in cache_t); + * + * sample_t generate(in surface_interactions::Anisotropic, inout float3 u, out AnisotropicMicrofacetCache); + * + * q_pdf_t quotient_and_pdf(in sample_t, in interaction_t, in cache_t); + */ +}; } } diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl new file mode 100644 index 0000000000..e47fdc8773 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -0,0 +1,244 @@ + +// Copyright (C) 2018-2022 - 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 + +// TODO: rename Eta to something more fitting to let it be known that reciprocal Eta convention is used (ior_dest/ior_src) + +#ifndef _NBL_BUILTIN_HLSL_BXDF_FRESNEL_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_FRESNEL_INCLUDED_ + +#include + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace fresnel +{ + +// only works for implied IoR==1.333.... +float3 schlick(in float3 F0, in float VdotH) +{ + float x = 1.0 - VdotH; + return F0 + (1.0 - F0) * x*x*x*x*x; +} + +// TODO: provide a `nbl_glsl_fresnel_conductor_impl` that take `Eta` and `EtaLen2` directly +// conductors, only works for `CosTheta>=0` +float3 conductor(in float3 Eta, in float3 Etak, in float CosTheta) +{ + const float CosTheta2 = CosTheta*CosTheta; + const float SinTheta2 = 1.0 - CosTheta2; + + const float3 EtaLen2 = Eta*Eta+Etak*Etak; + const float3 etaCosTwice = Eta*CosTheta*2.0; + + const float3 rs_common = EtaLen2 + (CosTheta2).xxx; + const float3 rs2 = (rs_common - etaCosTwice)/(rs_common + etaCosTwice); + + const float3 rp_common = EtaLen2*CosTheta2 + (1.0).xxx; + const float3 rp2 = (rp_common - etaCosTwice)/(rp_common + etaCosTwice); + + return (rs2 + rp2)*0.5; +} + +float3 conductor_impl(in float3 Eta, in float3 EtaLen2, in float CosTheta) +{ + const float CosTheta2 = CosTheta*CosTheta; + const float SinTheta2 = 1.0 - CosTheta2; + + const float3 etaCosTwice = Eta*CosTheta*2.0; + + const float3 rs_common = EtaLen2 + (CosTheta2).xxx; + const float3 rs2 = (rs_common - etaCosTwice)/(rs_common + etaCosTwice); + + const float3 rp_common = EtaLen2*CosTheta2 + (1.0).xxx; + const float3 rp2 = (rp_common - etaCosTwice)/(rp_common + etaCosTwice); + + return (rs2 + rp2)*0.5; +} + + +// dielectrics +float dielectric_common(in float orientedEta2, in float AbsCosTheta) +{ + const float SinTheta2 = 1.0-AbsCosTheta*AbsCosTheta; + + // the max() clamping can handle TIR when orientedEta2<1.0 + const float t0 = sqrt(max(orientedEta2-SinTheta2,0.0)); + const float rs = (AbsCosTheta - t0) / (AbsCosTheta + t0); + + const float t2 = orientedEta2 * AbsCosTheta; + const float rp = (t0 - t2) / (t0 + t2); + + return (rs * rs + rp * rp) * 0.5; +} + +float3 dielectric_common(in float3 orientedEta2, in float AbsCosTheta) +{ + const float SinTheta2 = 1.0-AbsCosTheta*AbsCosTheta; + + // the max() clamping can handle TIR when orientedEta2<1.0 + const float3 t0 = sqrt(max(float3(orientedEta2)-SinTheta2, (0.0).xxx)); + const float3 rs = ((AbsCosTheta).xxx - t0) / ((AbsCosTheta).xxx + t0); + + const float3 t2 = orientedEta2*AbsCosTheta; + const float3 rp = (t0 - t2) / (t0 + t2); + + return (rs*rs + rp*rp)*0.5; +} + +float3 dielectric_frontface_only(in float3 Eta, in float CosTheta) +{ + return dielectric_common(Eta*Eta,CosTheta); +} + +float3 dielectric(in float3 Eta, in float CosTheta) +{ + float3 orientedEta,rcpOrientedEta; + math::getOrientedEtas(orientedEta,rcpOrientedEta,CosTheta,Eta); + return dielectric_common(orientedEta*orientedEta,abs(CosTheta)); +} + + +// gets the sum of all R, T R T, T R^3 T, T R^5 T, ... paths +float3 thindielectric_infinite_scatter(in float3 singleInterfaceReflectance) +{ + const float3 doubleInterfaceReflectance = singleInterfaceReflectance*singleInterfaceReflectance; + + return lerp( + (singleInterfaceReflectance-doubleInterfaceReflectance)/(float3(1.0,1.0,1.0)-doubleInterfaceReflectance)*2.0, + float3(1.0,1.0,1.0), + (doubleInterfaceReflectance > float3(0.9999,0.9999,0.9999)) + ); +} + +float thindielectric_infinite_scatter(in float singleInterfaceReflectance) +{ + const float doubleInterfaceReflectance = singleInterfaceReflectance*singleInterfaceReflectance; + + return doubleInterfaceReflectance>0.9999 ? 1.0:((singleInterfaceReflectance-doubleInterfaceReflectance)/(1.0-doubleInterfaceReflectance)*2.0); +} + + +// Utility class +template +struct FresnelBase +{ + // Fresnels must define such typenames: + using spectrum_t = Spectrum; + + /** + * Fresnels must define following member functions: + * + * spectrum_t operator()(...); // TODO is there some paremeter list that can be universal for all fresnels ever needed? + */ +}; + + +template +struct FresnelSchlick : FresnelBase +{ + Spectrum F0; + + static FresnelSchlick create(in Spectrum _F0) + { + FresnelSchlick fs; + fs.F0 = _F0; + return fs; + } + + Spectrum operator()(in float cosTheta) + { + float x = 1.0 - cosTheta; + return F0 + (1.0 - F0) * x*x*x*x*x; + } +}; +using FresnelSchlickScalar = FresnelSchlick; +using FresnelSchlickRGB = FresnelSchlick; + + +template +struct FresnelConductor : FresnelBase +{ + Spectrum eta; + Spectrum etak; + + static FresnelConductor create(in Spectrum _eta, in Spectrum _etak) + { + FresnelConductor f; + + f.eta = _eta; + f.etak = _etak; + + return f; + } + + Spectrum operator()(in float cosTheta) + { + const float CosTheta2 = cosTheta*cosTheta; + const float SinTheta2 = 1.0 - CosTheta2; + + const Spectrum EtaLen2 = eta*eta + etak*etak; + const Spectrum etaCosTwice = eta*cosTheta*2.0; + + const Spectrum rs_common = EtaLen2 + (CosTheta2).xxx; + const Spectrum rs2 = (rs_common - etaCosTwice)/(rs_common + etaCosTwice); + + const Spectrum rp_common = EtaLen2*CosTheta2 + Spectrum(1); + const Spectrum rp2 = (rp_common - etaCosTwice)/(rp_common + etaCosTwice); + + return (rs2 + rp2)*0.5; + } +}; +using FresnelConductorScalar = FresnelConductor; +using FresnelConductorRGB = FresnelConductor; + + +template +struct FresnelDielectric : FresnelBase +{ + Spectrum eta; + + static FresnelDielectric create(in Spectrum _eta) + { + FresnelDielectric f; + + f.eta = _eta; + + return f; + } + + Spectrum operator()(in float cosTheta) + { + Spectrum orientedEta, rcpOrientedEta; + math::getOrientedEtas(orientedEta, rcpOrientedEta, cosTheta, eta); + + const float AbsCosTheta = abs(cosTheta); + const Spectrum orientedEta2 = orientedEta * orientedEta; + + const float SinTheta2 = 1.0-AbsCosTheta*AbsCosTheta; + + // the max() clamping can handle TIR when orientedEta2<1.0 + const Spectrum t0 = sqrt(max(orientedEta2-SinTheta2, Spectrum(0))); + const Spectrum rs = (AbsCosTheta - t0) / (AbsCosTheta + t0); + + const Spectrum t2 = orientedEta2*AbsCosTheta; + const Spectrum rp = (t0 - t2) / (t0 + t2); + + return (rs*rs + rp*rp)*0.5; + } +}; +using FresnelDielectricScalar = FresnelDielectric; +using FresnelDielectricRGB = FresnelDielectric; + +} +} +} +} + + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/geom/smith/beckmann.hlsl b/include/nbl/builtin/hlsl/bxdf/geom/smith/beckmann.hlsl new file mode 100644 index 0000000000..91872d51b2 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/geom/smith/beckmann.hlsl @@ -0,0 +1,95 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_BXDF_GEOM_SMITH_BECKMANN_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_GEOM_SMITH_BECKMANN_INCLUDED_ + + + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace geom_smith +{ +namespace beckmann +{ + + +float C2(in float NdotX2, in float a2) +{ + return NdotX2 / (a2 * (1.0 - NdotX2)); +} + +float C2(in float TdotX2, in float BdotX2, in float NdotX2, in float ax2, in float ay2) +{ + return NdotX2/(TdotX2*ax2+BdotX2*ay2); +} + +//G1 = 1/(1+_Lambda) +float Lambda(in float c2) +{ + float c = sqrt(c2); + float nom = 1.0 - 1.259*c + 0.396*c2; + float denom = 2.181*c2 + 3.535*c; + return lerp(0.0, nom/denom, c<1.6); +} + +float Lambda(in float NdotX2, in float a2) +{ + return Lambda(C2(NdotX2, a2)); +} + +float Lambda(in float TdotX2, in float BdotX2, in float NdotX2, in float ax2, in float ay2) +{ + return Lambda(C2(TdotX2, BdotX2, NdotX2, ax2, ay2)); +} + + +float correlated(in float NdotV2, in float NdotL2, in float a2) +{ + float c2 = C2(NdotV2, a2); + float L_v = Lambda(c2); + c2 = C2(NdotL2, a2); + float L_l = Lambda(c2); + return 1.0 / (1.0 + L_v + L_l); +} + +float correlated(in float TdotV2, in float BdotV2, in float NdotV2, in float TdotL2, in float BdotL2, in float NdotL2, in float ax2, in float ay2) +{ + float c2 = C2(TdotV2, BdotV2, NdotV2, ax2, ay2); + float L_v = Lambda(c2); + c2 = C2(TdotL2, BdotL2, NdotL2, ax2, ay2); + float L_l = Lambda(c2); + return 1.0 / (1.0 + L_v + L_l); +} + + +float G2_over_G1(in float lambdaV_plus_one, in float NdotL2, in float a2) +{ + float lambdaL = Lambda(NdotL2, a2); + + return lambdaV_plus_one / (lambdaV_plus_one+lambdaL); +} + +float G2_over_G1(in float lambdaV_plus_one, in float TdotL2, in float BdotL2, in float NdotL2, in float ax2, in float ay2) +{ + float c2 = C2(TdotL2, BdotL2, NdotL2, ax2, ay2); + float lambdaL = Lambda(c2); + + return lambdaV_plus_one / (lambdaV_plus_one + lambdaL); +} + + +} +} +} +} +} + + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/bxdf/geom/smith/common.hlsl b/include/nbl/builtin/hlsl/bxdf/geom/smith/common.hlsl new file mode 100644 index 0000000000..d829a6e1a0 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/geom/smith/common.hlsl @@ -0,0 +1,81 @@ + + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_BXDF_GEOM_SMITH_COMMON_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_GEOM_SMITH_COMMON_INCLUDED_ + + +#include + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace geom_smith +{ + + +float G1(in float lambda) +{ + return 1.0 / (1.0 + lambda); +} + +float G2(in float lambda_V, in float lambda_L) +{ + return 1.0 / (1.0 + lambda_V + lambda_L); +} + + +float VNDF_pdf_wo_clamps(in float ndf, in float lambda_V, in float maxNdotV, out float onePlusLambda_V) +{ + onePlusLambda_V = 1.0+lambda_V; + + return ndf::microfacet_to_light_measure_transform(ndf/onePlusLambda_V,maxNdotV); +} + +float VNDF_pdf_wo_clamps(in float ndf, in float lambda_V, in float absNdotV, in bool transmitted, in float VdotH, in float LdotH, in float VdotHLdotH, in float orientedEta, in float reflectance, out float onePlusLambda_V) +{ + onePlusLambda_V = 1.0+lambda_V; + + return ndf::microfacet_to_light_measure_transform((transmitted ? (1.0-reflectance):reflectance)*ndf/onePlusLambda_V,absNdotV,transmitted,VdotH,LdotH,VdotHLdotH,orientedEta); +} + +// for when you know the NDF and the uncorrelated smith masking function +float VNDF_pdf_wo_clamps(in float ndf, in float G1_over_2NdotV) +{ + return ndf*0.5*G1_over_2NdotV; +} + +float FVNDF_pdf_wo_clamps(in float fresnel_ndf, in float G1_over_2NdotV, in float absNdotV, in bool transmitted, in float VdotH, in float LdotH, in float VdotHLdotH, in float orientedEta) +{ + float FNG = fresnel_ndf * G1_over_2NdotV; + float factor = 0.5; + if (transmitted) + { + const float VdotH_etaLdotH = (VdotH + orientedEta * LdotH); + // VdotHLdotH is negative under transmission, so this factor is negative + factor *= -2.0 * VdotHLdotH / (VdotH_etaLdotH * VdotH_etaLdotH); + } + return FNG * factor; +} + +float VNDF_pdf_wo_clamps(in float ndf, in float G1_over_2NdotV, in float absNdotV, in bool transmitted, in float VdotH, in float LdotH, in float VdotHLdotH, in float orientedEta, in float reflectance) +{ + float FN = (transmitted ? (1.0 - reflectance) : reflectance) * ndf; + + return FVNDF_pdf_wo_clamps(FN, G1_over_2NdotV, absNdotV, transmitted, VdotH, LdotH, VdotHLdotH, orientedEta); +} + + +} +} +} +} + + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/bxdf/geom/smith/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/geom/smith/ggx.hlsl new file mode 100644 index 0000000000..dd03112c06 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/geom/smith/ggx.hlsl @@ -0,0 +1,117 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_BXDF_GEOM_SMITH_GGX_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_GEOM_SMITH_GGX_INCLUDED_ + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace geom_smith +{ +namespace ggx +{ + + +float devsh_part(in float NdotX2, in float a2, in float one_minus_a2) +{ + return sqrt(a2+one_minus_a2*NdotX2); +} +float devsh_part(in float TdotX2, in float BdotX2, in float NdotX2, in float ax2, in float ay2) +{ + return sqrt(TdotX2*ax2+BdotX2*ay2+NdotX2); +} + +float G1_wo_numerator(in float NdotX, in float NdotX2, in float a2, in float one_minus_a2) +{ + return 1.0 / (NdotX + devsh_part(NdotX2,a2,one_minus_a2)); +} +float G1_wo_numerator(in float NdotX, in float TdotX2, in float BdotX2, in float NdotX2, in float ax2, in float ay2) +{ + return 1.0 / (NdotX + devsh_part(TdotX2, BdotX2, NdotX2, ax2, ay2)); +} +float G1_wo_numerator(in float NdotX, in float devsh_part) +{ + return 1.0 / (NdotX + devsh_part); +} + +float correlated_wo_numerator(in float NdotV, in float NdotV2, in float NdotL, in float NdotL2, in float a2, in float one_minus_a2) +{ + float Vterm = NdotL*devsh_part(NdotV2,a2,one_minus_a2); + float Lterm = NdotV*devsh_part(NdotL2,a2,one_minus_a2); + return 0.5 / (Vterm + Lterm); +} +/* depr +float correlated(in float NdotV, in float NdotV2, in float NdotL, in float NdotL2, in float a2, in float one_minus_a2) +{ + return 4.0*NdotV*NdotL*correlated_wo_numerator(NdotV, NdotV2, NdotL, NdotL2, a2, one_minus_a2); +} +*/ +float correlated_wo_numerator(in float NdotV, in float NdotV2, in float NdotL, in float NdotL2, in float a2) +{ + return correlated_wo_numerator(NdotV,NdotV2,NdotL,NdotL2,a2,1.0-a2); +} +/* depr +float correlated(in float NdotV, in float NdotV2, in float NdotL, in float NdotL2, in float a2) +{ + return 4.0*NdotV*NdotL*correlated_wo_numerator(NdotV, NdotV2, NdotL, NdotL2, a2); +} +*/ +float correlated_wo_numerator(in float NdotV, in float TdotV2, in float BdotV2, in float NdotV2, in float NdotL, in float TdotL2, in float BdotL2, in float NdotL2, in float ax2, in float ay2) +{ + float Vterm = NdotL*devsh_part(TdotV2,BdotV2,NdotV2,ax2,ay2); + float Lterm = NdotV*devsh_part(TdotL2,BdotL2,NdotL2,ax2,ay2); + return 0.5 / (Vterm + Lterm); +} +/* depr +float correlated(in float NdotV, in float TdotV2, in float BdotV2, in float NdotV2, in float NdotL, in float TdotL2, in float BdotL2, in float NdotL2, in float ax2, in float ay2) +{ + return 4.0*NdotV*NdotL*correlated_wo_numerator(NdotV, TdotV2, BdotV2, NdotV2, NdotL, TdotL2, BdotL2, NdotL2, ax2, ay2); +} +*/ + +float G2_over_G1(in float NdotL, in float NdotL2, in float NdotV, in float NdotV2, in float a2, in float one_minus_a2) +{ + float devsh_v = devsh_part(NdotV2,a2,one_minus_a2); + float G2_over_G1 = NdotL*(devsh_v + NdotV); // alternative `Vterm+NdotL*NdotV /// NdotL*NdotV could come as a parameter + G2_over_G1 /= NdotV*devsh_part(NdotL2,a2,one_minus_a2) + NdotL*devsh_v; + + return G2_over_G1; +} +float G2_over_G1_devsh(in float NdotL, in float NdotL2, in float NdotV, in float devsh_v, in float a2, in float one_minus_a2) +{ + float G2_over_G1 = NdotL*(devsh_v + NdotV); // alternative `Vterm+NdotL*NdotV /// NdotL*NdotV could come as a parameter + G2_over_G1 /= NdotV*devsh_part(NdotL2,a2,one_minus_a2) + NdotL*devsh_v; + + return G2_over_G1; +} +float G2_over_G1(in float NdotL, in float TdotL2, in float BdotL2, in float NdotL2, in float NdotV, in float TdotV2, in float BdotV2, in float NdotV2, in float ax2, in float ay2) +{ + float devsh_v = devsh_part(TdotV2,BdotV2,NdotV2,ax2,ay2); + float G2_over_G1 = NdotL*(devsh_v + NdotV); + G2_over_G1 /= NdotV*devsh_part(TdotL2,BdotL2,NdotL2,ax2,ay2) + NdotL*devsh_v; + + return G2_over_G1; +} +float G2_over_G1_devsh(in float NdotL, in float TdotL2, in float BdotL2, in float NdotL2, in float NdotV, in float devsh_v, in float ax2, in float ay2) +{ + float G2_over_G1 = NdotL*(devsh_v + NdotV); + G2_over_G1 /= NdotV*devsh_part(TdotL2,BdotL2,NdotL2,ax2,ay2) + NdotL*devsh_v; + + return G2_over_G1; +} + + +} +} +} +} +} + + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/bxdf/ndf.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf.hlsl new file mode 100644 index 0000000000..429b23e74b --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/ndf.hlsl @@ -0,0 +1,236 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_BXDF_NDF_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_NDF_INCLUDED_ + +#include + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace ndf +{ + +// general path +float microfacet_to_light_measure_transform(in float NDFcos, in float absNdotV, in bool transmitted, in float VdotH, in float LdotH, in float VdotHLdotH, in float orientedEta) +{ + float denominator = absNdotV; + if (transmitted) + { + const float VdotH_etaLdotH = (VdotH+orientedEta*LdotH); + // VdotHLdotH is negative under transmission, so thats denominator is negative + denominator *= -VdotH_etaLdotH*VdotH_etaLdotH; + } + return NDFcos*(transmitted ? VdotHLdotH:0.25)/denominator; +} +float microfacet_to_light_measure_transform(in float NDFcos, in float maxNdotV) +{ + return 0.25*NDFcos/maxNdotV; +} + + +namespace ggx +{ + +// specialized factorizations for GGX +float microfacet_to_light_measure_transform(in float NDFcos_already_in_reflective_dL_measure, in float absNdotL, in bool transmitted, in float VdotH, in float LdotH, in float VdotHLdotH, in float orientedEta) +{ + float factor = absNdotL; + if (transmitted) + { + const float VdotH_etaLdotH = (VdotH+orientedEta*LdotH); + // VdotHLdotH is negative under transmission, so thats denominator is negative + factor *= -4.0*VdotHLdotH/(VdotH_etaLdotH*VdotH_etaLdotH); + } + return NDFcos_already_in_reflective_dL_measure*factor; +} +float microfacet_to_light_measure_transform(in float NDFcos_already_in_reflective_dL_measure, in float maxNdotL) +{ + return NDFcos_already_in_reflective_dL_measure*maxNdotL; +} + +} + + +// Utility class +template +struct NDFBase +{ + // NDFs must define such typenames: + using scalar_t = Scalar; + + /** + * NDFs must define such member functions: + * + * // Note that generation is always anisotropic, + * // hence both interaction and microfacet cache must be anisotropic ones. + * template + * float3 generateH(in surface_interactions::Anisotropic, inout float3 u, out AnisotropicMicrofacetCache); + * + * // isotropic NDF evaluators: + * scalar_t D(in float NdotH2); + * scalar_t Lambda(in float NdotX2); + * + * // anisotropic NDF evaluators: + * scalar_t D(in float TdotH2, in float BdotH2, in float NdotH2); + * scalar_t Lambda(in float TdotX2, in float BdotX2, in float NdotX2); + */ +}; + + +// forward declaration so we can explicitly specialize, e.g. for GGX where optimized forms of the functions provided by the trait exist +template +struct ndf_traits; + +namespace impl +{ + template + struct ndf_traits + { + using scalar_t = typename ndf_t::scalar_t; + + scalar_t G1(in float NdotX2) { return scalar_t(1) / (scalar_t(1) + ndf.Lambda(NdotX2)); } + scalar_t G1(in float TdotX2, in float BdotX2, in float NdotX2) { return scalar_t(1) / (scalar_t(1) + ndf.Lambda(TdotX2, BdotX2, NdotX2)); } + + scalar_t G2(in float NdotV2, in float NdotL2) { return scalar_t(1) / (scalar_t(1) + ndf.Lambda(NdotV2) + ndf.Lambda(NdotL2)); } + scalar_t G2(in float TdotV2, in float BdotV2, in float NdotV2, in float TdotL2, in float BdotL2, in float NdotL2) + { + return scalar_t(1) / (scalar_t(1) + ndf.Lambda(TdotV2, BdotV2, NdotV2) + ndf.Lambda(TdotL2, BdotL2, NdotL2)); + } + + static scalar_t G2_over_G1_common(in scalar_t lambdaV, in scalar_t lambdaL) + { + const scalar_t lambdaV_plus_one = lambdaV + scalar_t(1); + return lambdaV_plus_one / (lambdaL + lambdaV_plus_one); + } + scalar_t G2_over_G1(in float NdotV2, in float NdotL2) + { + return G2_over_G1_common(ndf.Lambda(NdotV2), ndf.Lambda(NdotL2)); + } + scalar_t G2_over_G1(in float TdotV2, in float BdotV2, in float NdotV2, in float TdotL2, in float BdotL2, in float NdotL2) + { + return G2_over_G1_common( + ndf.Lambda(TdotV2, BdotV2, NdotV2), + ndf.Lambda(TdotL2, BdotL2, NdotL2) + ); + } + + // + // dHdL functions + // + // For BRDFs only: + scalar_t dHdL(in float NDFcos, in float maxNdotV) + { + return microfacet_to_light_measure_transform(NDFcos, maxNdotV); + } + // For all BxDFs: + scalar_t dHdL(in float NDFcos, in float absNdotV, in bool transmitted, in float VdotH, in float LdotH, in float VdotHLdotH, in float orientedEta) + { + return microfacet_to_light_measure_transform(NDFcos, absNdotV, transmitted, VdotH, LdotH, VdotHLdotH, orientedEta); + } + + // + // VNDF functions + // + // Statics: + static scalar_t VNDF_static(in scalar_t d, in scalar_t lambda_V, in float maxNdotV, out scalar_t onePlusLambda_V) + { + onePlusLambda_V = scalar_t(1) + lambda_V; + + return microfacet_to_light_measure_transform(d / onePlusLambda_V, maxNdotV); + } + static scalar_t VNDF_static(in scalar_t d, in scalar_t lambda_V, in float absNdotV, in bool transmitted, in float VdotH, in float LdotH, in float VdotHLdotH, in float orientedEta, in float reflectance, out float onePlusLambda_V) + { + onePlusLambda_V = scalar_t(1) + lambda_V; + + return microfacet_to_light_measure_transform((transmitted ? (1.0 - reflectance) : reflectance) * d / onePlusLambda_V, absNdotV, transmitted, VdotH, LdotH, VdotHLdotH, orientedEta); + } + static scalar_t VNDF_static(in scalar_t d, in scalar_t G1_over_2NdotV) + { + return d * 0.5 * G1_over_2NdotV; + } + + static scalar_t VNDF_fromLambda_impl(in scalar_t d, in scalar_t lambda, in float maxNdotV) + { + scalar_t dummy; + return VNDF_static(d, lambda, maxNdotV, dummy); + } + static scalar_t VNDF_fromG1_over_2NdotV_impl(in scalar_t d, in scalar_t G1_over_2NdotV) + { + return VNDF_static(d, G1_over_2NdotV); + } + + + + // VNDF isotropic variants + scalar_t VNDF(in float NdotH2, in float NdotV2, in float maxNdotV) + { + const float d = ndf.D(NdotH2); + const float lambda = ndf.Lambda(NdotV2); + return VNDF_fromLambda_impl(d, lambda, maxNdotV); + } + scalar_t VNDF(in float NdotH2, in float G1_over_2NdotV) + { + const float d = ndf.D(NdotH2); + return VNDF_fromG1_over_2NdotV_impl(d, G1_over_2NdotV); + } + scalar_t VNDF( + in float NdotH2, in float NdotV2, + in float absNdotV, in bool transmitted, in float VdotH, in float LdotH, in float VdotHLdotH, in float orientedEta, in float reflectance, out float onePlusLambda_V) + { + const float d = ndf.D(NdotH2); + const float lambda = ndf.Lambda(NdotV2); + + return VNDF_static(d, lambda, absNdotV, transmitted, VdotH, LdotH, VdotHLdotH, orientedEta, reflectance, onePlusLambda_V); + } + + // VNDF anisotropic variants + scalar_t VNDF( + in float TdotH2, in float BdotH2, in float NdotH2, + in float TdotV2, in float BdotV2, in float NdotV2, + in float maxNdotV) + { + const float d = ndf.D(TdotH2, BdotH2, NdotH2); + const float lambda = ndf.Lambda(TdotV2, BdotV2, NdotV2); + return VNDF_fromLambda_impl(d, lambda, maxNdotV); + } + scalar_t VNDF( + in float TdotH2, in float BdotH2, in float NdotH2, + in float G1_over_2NdotV) + { + const float d = ndf.D(TdotH2, BdotH2, NdotH2); + return VNDF_fromG1_over_2NdotV_impl(d, G1_over_2NdotV); + } + scalar_t VNDF( + in float TdotH2, in float BdotH2, in float NdotH2, + in float TdotV2, in float BdotV2, in float NdotV2, + in float absNdotV, in bool transmitted, in float VdotH, in float LdotH, in float VdotHLdotH, in float orientedEta, in float reflectance, out float onePlusLambda_V) + { + const float d = ndf.D(TdotH2, BdotH2, NdotH2); + const float lambda = ndf.Lambda(TdotV2, BdotV2, NdotV2); + + return VNDF_static(d, lambda, absNdotV, transmitted, VdotH, LdotH, VdotHLdotH, orientedEta, reflectance, onePlusLambda_V); + } + + ndf_t ndf; + }; +} + +// default specialization +template +struct ndf_traits : impl::ndf_traits {}; + +} +} +} +} + +#include + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl new file mode 100644 index 0000000000..7bd32a0bcf --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl @@ -0,0 +1,232 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_BXDF_NDF_BECKMANN_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_NDF_BECKMANN_INCLUDED_ + +#include +#include +#include +#include + + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace ndf +{ + + +float beckmann(in float a2, in float NdotH2) +{ + float nom = exp( (NdotH2-1.0)/(a2*NdotH2) ); // exp(x) == exp2(x/log(2)) ? + float denom = a2*NdotH2*NdotH2; + + return math::RECIPROCAL_PI * nom/denom; +} + +float beckmann(in float ax, in float ay, in float ax2, in float ay2, in float TdotH2, in float BdotH2, in float NdotH2) +{ + float nom = exp(-(TdotH2/ax2+BdotH2/ay2)/NdotH2); + float denom = ax * ay * NdotH2 * NdotH2; + + return math::RECIPROCAL_PI * nom / denom; +} + + +struct IsotropicBeckmann : NDFBase<> +{ + float a; + float a2; + + static IsotropicBeckmann create(float _a) + { + IsotropicBeckmann b; + b.a = _a; + b.a2 = _a*_a; + return b; + } + static IsotropicBeckmann create(float _a, float _a2) + { + IsotropicBeckmann b; + b.a = _a; + b.a2 = _a2; + return b; + } + + + static float C2(in float NdotX2, in float _a2) + { + return NdotX2 / (_a2 * (1.0 - NdotX2)); + } + static float C2(in float TdotX2, in float BdotX2, in float NdotX2, in float _ax2, in float _ay2) + { + return NdotX2 / (TdotX2 * _ax2 + BdotX2 * _ay2); + } + + template + static float3 generateH_impl(in surface_interactions::Anisotropic interaction, inout float3 u, out AnisotropicMicrofacetCache cache, in float _ax, in float _ay) + { + const float3 localV = interaction.getTangentSpaceV(); + + //stretch + float3 V = normalize(float3(_ax * localV.x, _ay * localV.y, localV.z)); + + float2 slope; + if (V.z > 0.9999)//V.z=NdotV=cosTheta in tangent space + { + float r = sqrt(-log(1.0 - u.x)); + float sinPhi = sin(2.0 * math::PI * u.y); + float cosPhi = cos(2.0 * math::PI * u.y); + slope = float2(r, r) * float2(cosPhi, sinPhi); + } + else + { + float cosTheta = V.z; + float sinTheta = sqrt(1.0 - cosTheta * cosTheta); + float tanTheta = sinTheta / cosTheta; + float cotTheta = 1.0 / tanTheta; + + float a = -1.0; + float c = math::erf(cosTheta); + float sample_x = max(u.x, 1.0e-6f); + float theta = acos(cosTheta); + float fit = 1.0 + theta * (-0.876 + theta * (0.4265 - 0.0594 * theta)); + float b = c - (1.0 + c) * pow(1.0 - sample_x, fit); + + float normalization = 1.0 / (1.0 + c + math::SQRT_RECIPROCAL_PI * tanTheta * exp(-cosTheta * cosTheta)); + + const int ITER_THRESHOLD = 10; + const float MAX_ACCEPTABLE_ERR = 1.0e-5; + int it = 0; + float value = 1000.0; + while (++itMAX_ACCEPTABLE_ERR) + { + if (!(b >= a && b <= c)) + b = 0.5 * (a + c); + + float invErf = math::erfInv(b); + value = normalization * (1.0 + b + math::SQRT_RECIPROCAL_PI * tanTheta * exp(-invErf * invErf)) - sample_x; + float derivative = normalization * (1.0 - invErf * cosTheta); + + if (value > 0.0) + c = b; + else + a = b; + + b -= value / derivative; + } + // TODO: investigate if we can replace these two erf^-1 calls with a box muller transform + slope.x = math::erfInv(b); + slope.y = math::erfInv(2.0f * max(u.y, 1.0e-6f) - 1.0f); + } + + float sinTheta = sqrt(1.0f - V.z * V.z); + float cosPhi = sinTheta == 0.0f ? 1.0f : clamp(V.x / sinTheta, -1.0f, 1.0f); + float sinPhi = sinTheta == 0.0f ? 0.0f : clamp(V.y / sinTheta, -1.0f, 1.0f); + //rotate + float tmp = cosPhi * slope.x - sinPhi * slope.y; + slope.y = sinPhi * slope.x + cosPhi * slope.y; + slope.x = tmp; + + //unstretch + slope = float2(_ax, _ay) * slope; + + const float3 localH = normalize(float3(-slope, 1.0)); + + cache = AnisotropicMicrofacetCache::create(localV, localH); + + return localH; + } + + template + float3 generateH(in surface_interactions::Anisotropic interaction, inout float3 u, out AnisotropicMicrofacetCache cache) + { + return generateH_impl(interaction, u, cache, a, a); + } + + scalar_t D(in float NdotH2) + { + float nom = exp((NdotH2 - 1.0) / (a2 * NdotH2)); // exp(x) == exp2(x/log(2)) ? + float denom = a2 * NdotH2 * NdotH2; + + return math::RECIPROCAL_PI * nom / denom; + } + + scalar_t Lambda_impl(in float c2) + { + float c = sqrt(c2); + float nom = 1.0 - 1.259 * c + 0.396 * c2; + float denom = 2.181 * c2 + 3.535 * c; + return lerp(0.0, nom / denom, c < 1.6); + } + scalar_t Lambda(in float NdotX2) + { + return Lambda_impl(C2(NdotX2, a2)); + } + + // TODO what about aniso variants of D and Lambda? + // return nan? + // since we dont have SFINAE in HLSL, they must be defined for ndf_traits to compile with the type +}; + +struct Beckmann : IsotropicBeckmann +{ + static Beckmann create(float _ax, float _ay, float _ax2, float _ay2) + { + Beckmann b; + b.a = _ax; + b.ay = _ay; + b.a2 = _ax2; + b.ay2 = _ay2; + return b; + } + static Beckmann create(float _ax, float _ay) + { + return create(_ax, _ay, _ax*_ax, _ay*_ay); + } + + + static float C2(in float TdotX2, in float BdotX2, in float NdotX2, in float _ax2, in float _ay2) + { + return NdotX2 / (TdotX2 * _ax2 + BdotX2 * _ay2); + } + + + template + float3 generateH(in surface_interactions::Anisotropic interaction, inout float3 u, out AnisotropicMicrofacetCache cache) + { + return generateH_impl(interaction, u, cache, a, ay); + } + + + scalar_t D(in float TdotH2, in float BdotH2, in float NdotH2) + { + float nom = exp(-(TdotH2 / a2 + BdotH2 / ay2) / NdotH2); + float denom = a * ay * NdotH2 * NdotH2; + + return math::RECIPROCAL_PI * nom / denom; + } + + float Lambda(in float TdotX2, in float BdotX2, in float NdotX2) + { + return Lambda_impl(C2(TdotX2, BdotX2, NdotX2, a2, ay2)); + } + + //float ax; // inherited from base as `a` + //float ax2; // inherited from base as `a2` + float ay; + float ay2; +}; + +} +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/blinn_phong.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/blinn_phong.hlsl new file mode 100644 index 0000000000..0a6151117b --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/ndf/blinn_phong.hlsl @@ -0,0 +1,126 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_BXDF_NDF_BLINN_PHONG_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_NDF_BLINN_PHONG_INCLUDED_ + +#include +#include +// for beckmann sampling +#include + + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace ndf +{ + +float blinn_phong(in float NdotH, in float n) +{ + return isinf(n) ? FLT_INF : math::RECIPROCAL_PI*0.5*(n+2.0) * pow(NdotH,n); +} +//ashikhmin-shirley ndf +float blinn_phong(in float NdotH, in float one_minus_NdotH2_rcp, in float TdotH2, in float BdotH2, in float nx, in float ny) +{ + float n = (TdotH2*ny + BdotH2*nx) * one_minus_NdotH2_rcp; + + return (isinf(nx)||isinf(ny)) ? FLT_INF : sqrt((nx + 2.0)*(ny + 2.0))*math::RECIPROCAL_PI*0.5 * pow(NdotH,n); +} + +struct IsotropicBlinnPhong : NDFBase<> +{ + float n; + + //conversion between alpha and Phong exponent, Walter et.al. + static float phong_exp_to_alpha2(in float _n) + { + return 2.0 / (_n + 2.0); + } + //+INF for a2==0.0 + static float alpha2_to_phong_exp(in float a2) + { + return 2.0 / a2 - 2.0; + } + + static IsotropicBlinnPhong create(in float _n) + { + IsotropicBlinnPhong bp; + bp.n = _n; + return bp; + } + + template + static float3 generateH_impl(in surface_interactions::Anisotropic interaction, inout float3 u, out AnisotropicMicrofacetCache cache, in float _ax, in float _ay) + { + IsotropicBeckmann::generateH_impl(interaction, u, cache, _ax, _ay); + } + + template + float3 generateH(in surface_interactions::Anisotropic interaction, inout float3 u, out AnisotropicMicrofacetCache cache) + { + float a = sqrt( phong_exp_to_alpha2(n) ); + return generateH_impl(interaction, u, cache, a, a); + } + + float D(in float NdotH2) + { + // here doing pow(NdotH2,n*0.5) instead of pow(NdotH,n) to keep compliant to common API (parameter of D() should be NdotH2) + return isinf(n) ? FLT_INF : math::RECIPROCAL_PI * 0.5 * (n + 2.0) * pow(NdotH2, n*0.5); + } + + float Lambda(in float NdotX2) + { + // TODO + // eh ill probably just use beckmann's lambda + return 0.f / 0.f;//nan + } +}; + +struct BlinnPhong : IsotropicBlinnPhong +{ + //float nx; // inherited from base as `n` + float ny; + + static BlinnPhong create(in float _nx, in float _ny) + { + BlinnPhong bp; + bp.n = _nx; + bp.ny = _ny; + return bp; + } + + template + float3 generateH(in surface_interactions::Anisotropic interaction, inout float3 u, out AnisotropicMicrofacetCache cache) + { + float ax = sqrt(phong_exp_to_alpha2(n)); + float ay = sqrt(phong_exp_to_alpha2(ny)); + return generateH_impl(interaction, u, cache, ax, ay); + } + + float D(in float TdotH2, in float BdotH2, in float NdotH2) + { + float aniso_n = (TdotH2 * ny + BdotH2 * n) / (1.0 - NdotH2); + + return (isinf(n) || isinf(ny)) ? FLT_INF : sqrt((n + 2.0) * (ny + 2.0)) * math::RECIPROCAL_PI * 0.5 * pow(NdotH2, aniso_n*0.5); + } + + float Lambda(in float TdotH2, in float BdotH2, in float NdotX2) + { + // TODO + // eh ill probably just use beckmann's lambda + return 0.f / 0.f; //nan + } +}; + +} +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl new file mode 100644 index 0000000000..1dfe15bbc7 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl @@ -0,0 +1,166 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_BXDF_NDF_GGX_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_NDF_GGX_INCLUDED_ + +#include +#include +#include +#include + + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace ndf +{ +namespace ggx +{ + + +float trowbridge_reitz(in float a2, in float NdotH2) +{ + float denom = NdotH2 * (a2 - 1.0) + 1.0; + return a2* math::RECIPROCAL_PI / (denom*denom); +} + +float burley_aniso(float anisotropy, float a2, float TdotH, float BdotH, float NdotH) +{ + float antiAniso = 1.0-anisotropy; + float atab = a2*antiAniso; + float anisoTdotH = antiAniso*TdotH; + float anisoNdotH = antiAniso*NdotH; + float w2 = antiAniso/(BdotH*BdotH+anisoTdotH*anisoTdotH+anisoNdotH*anisoNdotH*a2); + return w2*w2*atab * math::RECIPROCAL_PI; +} + +float aniso(in float TdotH2, in float BdotH2, in float NdotH2, in float ax, in float ay, in float ax2, in float ay2) +{ + float a2 = ax*ay; + float denom = TdotH2/ax2 + BdotH2/ay2 + NdotH2; + return math::RECIPROCAL_PI / (a2 * denom * denom); +} + + +struct IsotropicGGX : NDFBase<> +{ + float a; + float a2; + + static IsotropicGGX create(float _a) + { + IsotropicGGX b; + b.a = _a; + b.a2 = _a * _a; + return b; + } + static IsotropicGGX create(float _a, float _a2) + { + IsotropicGGX b; + b.a = _a; + b.a2 = _a2; + return b; + } + + template + static float3 generateH_impl(in surface_interactions::Anisotropic interaction, inout float3 u, out AnisotropicMicrofacetCache cache, in float _ax, in float _ay) + { + const float3 localV = interaction.getTangentSpaceV(); + + float3 V = normalize(float3(_ax * localV.x, _ay * localV.y, localV.z));//stretch view vector so that we're sampling as if roughness=1.0 + + float lensq = V.x * V.x + V.y * V.y; + float3 T1 = lensq > 0.0 ? float3(-V.y, V.x, 0.0) * rsqrt(lensq) : float3(1.0, 0.0, 0.0); + float3 T2 = cross(V, T1); + + float r = sqrt(u.x); + float phi = 2.0 * math::PI * u.y; + float t1 = r * cos(phi); + float t2 = r * sin(phi); + float s = 0.5 * (1.0 + V.z); + t2 = (1.0 - s) * sqrt(1.0 - t1 * t1) + s * t2; + + //reprojection onto hemisphere + //TODO try it wothout the& max(), not sure if -t1*t1-t2*t2>-1.0 + float3 H = t1 * T1 + t2 * T2 + sqrt(max(0.0, 1.0 - t1 * t1 - t2 * t2)) * V; + //unstretch + const float3 localH = normalize(float3(_ax * H.x, _ay * H.y, H.z)); + + return localH; + } + + template + float3 generateH(in surface_interactions::Anisotropic interaction, inout float3 u, out AnisotropicMicrofacetCache cache) + { + return generateH_impl(interaction, u, cache, a, a); + } + + float D(in float NdotH2) + { + float denom = NdotH2 * (a2 - 1.0) + 1.0; + return a2 * math::RECIPROCAL_PI / (denom * denom); + } + + float Lambda(in float NdotX2) + { + // TODO + // ggx is a little special... + return 0.f / 0.f;//nan + } +}; + +struct GGX : IsotropicGGX +{ + float ay; + float ay2; + + static GGX create(float _ax, float _ay, float _ax2, float _ay2) + { + GGX b; + b.a = _ax; + b.ay = _ay; + b.a2 = _ax2; + b.ay2 = _ay2; + return b; + } + static GGX create(float _ax, float _ay) + { + return create(_ax, _ay, _ax*_ax, _ay*_ay); + } + + template + float3 generateH(in surface_interactions::Anisotropic interaction, inout float3 u, out AnisotropicMicrofacetCache cache) + { + return generateH_impl(interaction, u, cache, a, ay); + } + + float D(in float TdotH2, in float BdotH2, in float NdotH2) + { + float aa = a * ay; + float denom = TdotH2 / a2 + BdotH2 / ay2 + NdotH2; + return math::RECIPROCAL_PI / (aa * denom * denom); + } + + float Lambda(in float TdotH2, in float BdotH2, in float NdotX2) + { + // TODO + // ggx is a little special... + return 0.f / 0.f; //nan + } +}; + +} +} +} +} +} + + + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/bxdf/reflection.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection.hlsl index 878cd7b6f3..65fef80c58 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection.hlsl @@ -28,9 +28,9 @@ LightSample cos_generate(const surface_interactions::Anisotropic -quotient_and_pdf cos_quotient_and_pdf() +quotient_and_pdf cos_quotient_and_pdf() { - return quotient_and_pdf::create(SpectralBins(1.f),nbl::hlsl::numeric_limits::inf()); + return quotient_and_pdf::create(SpectralBins(1.f),nbl::hlsl::numeric_limits::inf()); } } diff --git a/include/nbl/builtin/hlsl/common.hlsl b/include/nbl/builtin/hlsl/common.hlsl new file mode 100644 index 0000000000..fb381e4e7f --- /dev/null +++ b/include/nbl/builtin/hlsl/common.hlsl @@ -0,0 +1,85 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_COMMON_INCLUDED_ +#define _NBL_BUILTIN_HLSL_COMMON_INCLUDED_ + +namespace nbl +{ +namespace hlsl +{ + + +uint bitfieldExtract(uint value, int offset, int bits) +{ + uint mask = uint((1L << bits) - 1); + return uint(value >> offset) & mask; +} +uint bitfieldInsert(uint value, int insert, int offset, int bits) +{ + uint mask = ~(0xffffffffL << bits) << offset; + mask = ~mask; + value &= mask; + return value | (insert << offset); +} + + + + +uint packUnorm4x8(float4 value) +{ + uint4 Packed = uint4(round(saturate(value) * 255.0)); + return Packed.x | (Packed.y << 8) | (Packed.z << 16) | (Packed.w << 24); +} + +float4 unpackUnorm4x8(uint value) +{ + uint4 Packed = uint4(value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff, value >> 24); + return float4(Packed) / 255.0; +} + +uint PackSnorm4x8(float4 value) +{ + int4 Packed = int4(round(clamp(value, -1.0, 1.0) * 127.0)) & 0xff; + return uint(Packed.x | (Packed.y << 8) | (Packed.z << 16) | (Packed.w << 24)); +} + +float4 unpackSnorm4x8(uint value) +{ + int SignedValue = int(value); + int4 Packed = int4(SignedValue << 24, SignedValue << 16, SignedValue << 8, SignedValue) >> 24; + return clamp(float4(Packed) / 127.0, -1.0, 1.0); +} + +uint packUnorm2x16(float2 value) +{ + uint2 Packed = uint2(round(saturate(value) * 65535.0)); + return Packed.x | (Packed.y << 16); +} + +float2 unpackUnorm2x16(uint value) +{ + uint2 Packed = uint2(value & 0xffff, value >> 16); + return float2(Packed) / 65535.0; +} + +uint PackSnorm2x16(float2 value) +{ + int2 Packed = int2(round(clamp(value, -1.0, 1.0) * 32767.0)) & 0xffff; + return uint(Packed.x | (Packed.y << 16)); +} + +float2 unpackSnorm2x16(uint value) +{ + int SignedValue = int(value); + int2 Packed = int2(SignedValue << 16, SignedValue) >> 16; + return clamp(float2(Packed) / 32767.0, -1.0, 1.0); +} + + +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/ieee754.hlsl b/include/nbl/builtin/hlsl/ieee754.hlsl new file mode 100644 index 0000000000..bd9ba22f4e --- /dev/null +++ b/include/nbl/builtin/hlsl/ieee754.hlsl @@ -0,0 +1,142 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_IEE754_H_INCLUDED_ +#define _NBL_BUILTIN_HLSL_IEE754_H_INCLUDED_ + +#include + +namespace nbl +{ +namespace hlsl +{ + +namespace ieee754 +{ + +uint exponent_bias(in uint exponentBits) +{ + return (0x1u << (exponentBits - 1)) - 1; +} +uint extract_biased_exponent(float x) +{ + return bitfieldExtract(asuint(x), 23, 8); +} +int extract_exponent(float x) +{ + return int(extract_biased_exponent(x) - exponent_bias(8)); +} +uint compute_exponent_mask(in uint exponentBits, in uint mantissaBits) +{ + return ((1u << exponentBits) - 1) << mantissaBits; +} +float replace_biased_exponent(float x, uint exp_plus_bias) +{ + return asfloat(bitfieldInsert(asuint(x), exp_plus_bias, 23, 8)); +} +// performs no overflow tests, returns x*exp2(n) +float fast_mul_exp2(float x, int n) +{ + return replace_biased_exponent(x, extract_biased_exponent(x) + uint(n)); +} +uint compute_mantissa_mask(in uint mantissaBits) +{ + return (0x1u << mantissaBits) - 1; +} +uint extract_mantissa(in float x) +{ + return (asuint(x) & 0x7fffffu); +} +float true_min(in uint exponentBits, in uint mantissaBits) +{ + return exp2(1 - int(exponent_bias(exponentBits)) - mantissaBits); +} +float min(in uint exponentBits, in uint mantissaBits) +{ + const float e = exp2(1 - int(exponent_bias(exponentBits))); + const uint m = 0x1u << (23 - mantissaBits); + return asfloat(asuint(e) | m); +} +float max(in uint exponentBits, in uint mantissaBits) +{ + const uint biasedMaxExp = (((1u << exponentBits) - 1) - 1); // `(1 << exponentBits) - 1` is reserved for Inf/NaN. + const float e = exp2(biasedMaxExp - int(exponent_bias(exponentBits))); + const uint m = 0x7fFFffu & (0x7fFFffu << (23 - mantissaBits)); + return asfloat(asuint(e) | m); +} +uint encode_ufloat_impl(in int exponent, in uint exponentBits, in uint mantissa, in uint mantissaBits) +{ + const uint expBias = exponent_bias(exponentBits); + const uint e = uint(exponent + expBias); + const uint m = mantissa >> (23 - mantissaBits); + const uint encodedValue = (e << mantissaBits) | m; + return encodedValue; +} + +float float_epsilon(float n) +{ + return ieee754::fast_mul_exp2(n, -24); +} + +float float_epsilon(int n) +{ + return float_epsilon(float(n)); +} + +float float_epsilon() +{ + return FLT_EPSILON; +} + + +float gamma(float n) +{ + const float a = float_epsilon(n); + return a / (1.f - a); +} +float rcpgamma(float n) +{ + const float a = float_epsilon(n); + return 1.f / a - 1.f; +} + +float gamma(uint n) +{ + return gamma(float(n)); +} +float rcpgamma(uint n) +{ + return rcpgamma(float(n)); +} + +float3 add_with_bounds_wo_gamma(out float3 error, in float3 a, in float3 a_error, in float3 b, in float3 b_error) +{ + error = (a_error + b_error) / float_epsilon(1u); + float3 sum = a + b; + error += abs(sum); + return sum; +} +float3 sub_with_bounds_wo_gamma(out float3 error, in float3 a, in float3 a_error, in float3 b, in float3 b_error) +{ + error = (a_error + b_error) / float_epsilon(1u); + float3 sum = a - b; + error += abs(sum); + return sum; +} +float3 mul_with_bounds_wo_gamma(out float3 error, in float3 a, in float3 a_error, in float b, in float b_error) +{ + float3 crossCorrelationA = abs(a) * b_error; + float3 crossCorrelationB = a_error * abs(b); + error = (crossCorrelationB + crossCorrelationA + crossCorrelationB * crossCorrelationA) / float_epsilon(1u); + float3 product = a * b; + error += abs(product); + return product; +} + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/math/complex.hlsl b/include/nbl/builtin/hlsl/math/complex.hlsl new file mode 100644 index 0000000000..0dea4b5c6f --- /dev/null +++ b/include/nbl/builtin/hlsl/math/complex.hlsl @@ -0,0 +1,132 @@ + +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_MATH_COMPLEX_INCLUDED_ +#define _NBL_BUILTIN_HLSL_MATH_COMPLEX_INCLUDED_ + +#include + + +namespace nbl +{ +namespace hlsl +{ +namespace math +{ + + +template +struct complex_t +{ + // TODO: do it properly as an `exp` and `exp2` overload/shadow func + complex_t expImaginary(in float _theta) + { + complex_t result; + result.real = cos(_theta); + result.imaginary = sin(_theta); + return result; + } + + complex_t operator+(const complex_t other) + { + complex_t result; + result.real = real + other.real; + result.imaginary = imaginary + other.imaginary; + return result; + } + + complex_t operator-(const complex_t other) + { + complex_t result; + result.real = real - other.real; + result.imaginary = imaginary - other.imaginary; + return result; + } + + complex_t operator*(const complex_t other) + { + complex_t result; + result.real = real * other.real - imaginary * other.imaginary; + result.imaginary = real * other.real + imaginary * other.imaginary; + return result; + } + + complex_t conjugate() + { + complex_t result; + result.real = real; + result.imaginary = -imaginary; + return result; + } + + vector_t real, imaginary; +}; + + +// TODO: move to its own header +namespace fft +{ + +template +complex_t twiddle(in uint k, in float N) +{ + complex_t retval; + retval.x = cos(-2.f*PI*float(k)/N); + retval.y = sqrt(1.f-retval.x*retval.x); // twiddle is always half the range, so no conditional -1.f needed + return retval; +} + +template +complex_t twiddle(in uint k, in uint logTwoN) +{ + return twiddle(k,float(1u< +complex_t twiddle(in bool is_inverse, in uint k, in float N) +{ + complex_t twiddle = twiddle(k,N); + if (is_inverse) + return twiddle.conjugate; + return twiddle; +} + +template +complex_t twiddle(in bool is_inverse, in uint k, in uint logTwoN) +{ + return twiddle(is_inverse,k,float(1u< +void DIT_radix2(in complex_t twiddle, inout complex_t lo, inout complex_t hi) +{ + complex_t wHi = hi * twiddle; + hi = lo-wHi; + lo += wHi; +} + +// decimation in frequency +template +void DIF_radix2(in complex_t twiddle, inout complex_t lo, inout complex_t hi) +{ + complex_t diff = lo-hi; + lo += hi; + hi = diff * twiddle; +} + + +} + +// TODO: radices 4,8 and 16 + +} +} +} + + + +#endif diff --git a/include/nbl/builtin/hlsl/math/functions.hlsl b/include/nbl/builtin/hlsl/math/functions.hlsl new file mode 100644 index 0000000000..fafea5f457 --- /dev/null +++ b/include/nbl/builtin/hlsl/math/functions.hlsl @@ -0,0 +1,609 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_MATH_FUNCTIONS_INCLUDED_ +#define _NBL_BUILTIN_HLSL_MATH_FUNCTIONS_INCLUDED_ + +#include +#include + + +namespace nbl +{ +namespace hlsl +{ +namespace math +{ + +namespace impl +{ + +static float3 reflect_refract(in bool _refract, in float3 I, in float3 N, in float NdotI, in float NdotTorR, in float rcpOrientedEta) +{ + return N*(NdotI*(_refract ? rcpOrientedEta:1.0)+NdotTorR) - I*(_refract ? rcpOrientedEta:1.0); +} + +static uint bitfieldInsert(in uint base, in uint shifted_masked_value, in uint lo, in uint count) +{ + const uint hi = base^lo; + return (hi< +vector_t erf(in vector_t _x) +{ + const float a1 = 0.254829592; + const float a2 = -0.284496736; + const float a3 = 1.421413741; + const float a4 = -1.453152027; + const float a5 = 1.061405429; + const float p = 0.3275911; + + vector_t _sign = sign(_x); + vector_t x = abs(_x); + + vector_t t = 1.0 / (1.0 + p*x); + vector_t y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * exp(-x * x); + + return _sign*y; +} + + + +float erfInv(in float _x) +{ + float x = clamp(_x, -0.99999, 0.99999); + float w = -log((1.0-x) * (1.0+x)); + float p; + if (w<5.0) + { + w -= 2.5; + p = 2.81022636e-08; + p = 3.43273939e-07 + p*w; + p = -3.5233877e-06 + p*w; + p = -4.39150654e-06 + p*w; + p = 0.00021858087 + p*w; + p = -0.00125372503 + p*w; + p = -0.00417768164 + p*w; + p = 0.246640727 + p*w; + p = 1.50140941 + p*w; + } + else + { + w = sqrt(w) - 3.0; + p = -0.000200214257; + p = 0.000100950558 + p*w; + p = 0.00134934322 + p*w; + p = -0.00367342844 + p*w; + p = 0.00573950773 + p*w; + p = -0.0076224613 + p*w; + p = 0.00943887047 + p*w; + p = 1.00167406 + p*w; + p = 2.83297682 + p*w; + } + return p*x; +} + +float lengthManhattan(float v) +{ + return abs(v); +} +float lengthManhattan(float2 v) +{ + v = abs(v); + return v.x + v.y; +} +float lengthManhattan(float3 v) +{ + v = abs(v); + return v.x + v.y + v.z; +} +float lengthManhattan(float4 v) +{ + v = abs(v); + return v.x + v.y + v.z + v.w; +} + + +float lengthSq(in float v) +{ + return v * v; +} +template +float lengthSq(in vector_t v) +{ + return dot(v, v); +} + + +float3 reflect(in float3 I, in float3 N, in float NdotI) +{ + return N*2.0*NdotI - I; +} +float3 reflect(in float3 I, in float3 N) +{ + float NdotI = dot(N,I); + return reflect(I, N, NdotI); +} + +template +// for refraction the orientation of the normal matters, because a different IoR will be used +bool getOrientedEtas(out vector_t orientedEta, out vector_t rcpOrientedEta, in float NdotI, in vector_t eta) +{ + const bool backside = NdotI<0.0; + const vector_t rcpEta = 1.0/eta; + orientedEta = backside ? rcpEta:eta; + rcpOrientedEta = backside ? eta:rcpEta; + return backside; +} + + +float refract_compute_NdotT2(in float NdotI2, in float rcpOrientedEta2) +{ + return rcpOrientedEta2*NdotI2 + 1.0 - rcpOrientedEta2; +} +float refract_compute_NdotT(in bool backside, in float NdotI2, in float rcpOrientedEta2) +{ + const float abs_NdotT = sqrt(refract_compute_NdotT2(NdotI2,rcpOrientedEta2)); + return backside ? abs_NdotT:(-abs_NdotT); +} +float3 refract(in float3 I, in float3 N, in bool backside, in float NdotI, in float NdotI2, in float rcpOrientedEta, in float rcpOrientedEta2) +{ + return N*(NdotI*rcpOrientedEta + refract_compute_NdotT(backside,NdotI2,rcpOrientedEta2)) - rcpOrientedEta*I; +} +float3 refract(in float3 I, in float3 N, in float NdotI, in float eta) +{ + float orientedEta, rcpOrientedEta; + const bool backside = getOrientedEtas(orientedEta, rcpOrientedEta, NdotI, eta); + return refract(I, N, backside, NdotI, NdotI*NdotI, rcpOrientedEta, rcpOrientedEta*rcpOrientedEta); +} +float3 refract(in float3 I, in float3 N, in float eta) +{ + const float NdotI = dot(N, I); + return refract(I, N, NdotI, eta); +} + + + +float3 reflect_refract(in bool _refract, in float3 I, in float3 N, in bool backside, in float NdotI, in float NdotI2, in float rcpOrientedEta, in float rcpOrientedEta2) +{ + const float NdotTorR = _refract ? refract_compute_NdotT(backside,NdotI2,rcpOrientedEta2):NdotI; + return impl::reflect_refract(_refract,I,N,NdotI,NdotTorR,rcpOrientedEta); +} +float3 reflect_refract(in bool _refract, in float3 I, in float3 N, in float NdotI, in float NdotI2, in float eta) +{ + float orientedEta, rcpOrientedEta; + const bool backside = getOrientedEtas(orientedEta, rcpOrientedEta, NdotI, eta); + return reflect_refract(_refract, I, N, backside, NdotI, NdotI2, rcpOrientedEta, rcpOrientedEta*rcpOrientedEta); +} + +// returns unnormalized floattor +float3 computeUnnormalizedMicrofacetNormal(in bool _refract, in float3 V, in float3 L, in float orientedEta) +{ + const float etaFactor = (_refract ? orientedEta:1.0); + const float3 tmpH = V+L*etaFactor; + return _refract ? (-tmpH):tmpH; +} +// returns normalized floattor, but NaN when +float3 computeMicrofacetNormal(in bool _refract, in float3 V, in float3 L, in float orientedEta) +{ + const float3 H = computeUnnormalizedMicrofacetNormal(_refract,V,L,orientedEta); + const float unnormRcpLen = rsqrt(dot(H,H)); + return H*unnormRcpLen; +} + +// if V and L are on different sides of the surface normal, then their dot product sign bits will differ, hence XOR will yield 1 at last bit +bool isTransmissionPath(in float NdotV, in float NdotL) +{ + return bool((asuint(NdotV)^asuint(NdotL)) & 0x80000000u); +} + +// valid only for `theta` in [-PI,PI] +void sincos(in float theta, out float s, out float c) +{ + c = cos(theta); + s = sqrt(1.0-c*c); + s = theta<0.0 ? -s:s; // TODO: test with XOR + //s = asfloat(asuint(s)^(asuint(theta)&0x80000000u)); +} + +float3x2 frisvad(in float3 n) +{ + const float a = 1.0/(1.0 + n.z); + const float b = -n.x*n.y*a; + return (n.z<-0.9999999) ? float3x2(float2(0.0,-1.0),float2(-1.0,0.0),float2(0.0,0.0)):float3x2(float2(1.0-n.x*n.x*a, b), float2(b, 1.0-n.y*n.y*a), float2(-n.x, -n.y)); +} + +// @return if picked right choice +bool partitionRandVariable(in float leftProb, inout float xi, out float rcpChoiceProb) +{ + const float NEXT_ULP_AFTER_UNITY = asfloat(0x3f800001u); + const bool pickRight = xi>=leftProb*NEXT_ULP_AFTER_UNITY; + + // This is all 100% correct taking into account the above NEXT_ULP_AFTER_UNITY + xi -= pickRight ? leftProb:0.0; + + rcpChoiceProb = 1.0/(pickRight ? (1.0-leftProb):leftProb); + xi *= rcpChoiceProb; + + return pickRight; +} + +// @ return abs(x) if cond==true, max(x,0.0) otherwise +template +vector_t conditionalAbsOrMax(in bool cond, in vector_t x, in vector_t limit) +{ + const vector_t condAbs = asfloat(asuint(x) & (cond ? 0x7fFFffFFu:0xffFFffFFu)); + return max(condAbs,limit); +} + + +//! Integer +uint rotl(in uint x, in uint k) +{ + return (x<>(32u-k)); +} + +// Count Leading Zeroes +uint clz(in uint x) +{ + return 31u - firstbithigh(x); +} + +// GLSL's builtin is badly named +uint bitfieldOverwrite(in uint base, in uint value, in uint offset, in uint count) +{ + return impl::bitfieldInsert(base,value,int(offset),int(count)); +} + + +uint bitfieldInsert(in uint base, uint value, in uint offset, in uint count) +{ + const uint shifted_masked_value = (value&((0x1u< max(a,b); + const float c = a*b - sqrt((1.0f-a*a)*(1.0f-b*b)); + return reverse ? float2(-c, bias + PI) : float2(c, bias); +} + +// returns acos(a) + acos(b) +float getSumofArccosAB(in float cosA, in float cosB) +{ + const float2 combinedCos = combineCosForSumOfAcos(float2(cosA, 0.0f), float2(cosB, 0.0f)); + return acos(combinedCos.x) + combinedCos.y; +} + +// returns acos(a) + acos(b) + acos(c) + acos(d) +float getSumofArccosABCD(in float cosA, in float cosB, in float cosC, in float cosD) +{ + const float2 combinedCos0 = combineCosForSumOfAcos(float2(cosA, 0.0f), float2(cosB, 0.0f)); + const float2 combinedCos1 = combineCosForSumOfAcos(float2(cosC, 0.0f), float2(cosD, 0.0f)); + const float2 combinedCos = combineCosForSumOfAcos(combinedCos0, combinedCos1); + return acos(combinedCos.x) + combinedCos.y; +} + +//! MVC + + +// return dFdR (TODO: a completely separate include for this) +float applyChainRule1D(in float dFdG, in float dGdR) +{ + return dFdG*dGdR; +} +float2 applyChainRule1D(in float2 dFdG, in float dGdR) +{ + return dFdG*dGdR; +} +float3 applyChainRule1D(in float3 dFdG, in float dGdR) +{ + return dFdG*dGdR; +} +float4 applyChainRule1D(in float4 dFdG, in float dGdR) +{ + return dFdG*dGdR; +} +/* TODO*/ +float1x2 applyChainRule1D(in float dFdG, in float1x2 dGdR) +{ + return mul(dFdG, dGdR); +} +float2x2 applyChainRule1D(in float2x1 dFdG, in float1x2 dGdR) +{ + return mul(dFdG, dGdR); +} +float3x2 applyChainRule1D(in float3x1 dFdG, in float1x2 dGdR) +{ + return mul(dFdG, dGdR); +} +float4x2 applyChainRule1D(in float4x1 dFdG, in float1x2 dGdR) +{ + return mul(dFdG, dGdR); +} +float1x3 applyChainRule1D(in float1x1 dFdG, in float1x3 dGdR) +{ + return mul(dFdG, dGdR); +} +float2x3 applyChainRule1D(in float2x1 dFdG, in float1x3 dGdR) +{ + return mul(dFdG, dGdR); +} +float3x3 applyChainRule1D(in float3x1 dFdG, in float1x3 dGdR) +{ + return mul(dFdG, dGdR); +} +float4x3 applyChainRule1D(in float4x1 dFdG, in float1x3 dGdR) +{ + return mul(dFdG, dGdR); +} +float1x4 applyChainRule1D(in float1x1 dFdG, in float1x4 dGdR) +{ + return mul(dFdG, dGdR); +} +float2x4 applyChainRule1D(in float2x1 dFdG, in float1x4 dGdR) +{ + return mul(dFdG, dGdR); +} +float3x4 applyChainRule1D(in float3x1 dFdG, in float1x4 dGdR) +{ + return mul(dFdG, dGdR); +} +float4x4 applyChainRule1D(in float4x1 dFdG, in float1x4 dGdR) +{ + return mul(dFdG, dGdR); +} +float applyChainRule2D(in float1x2 dFdG, in float2 dGdR) +{ + return mul(dFdG, dGdR); +} +float2 applyChainRule2D(in float2x2 dFdG, in float2 dGdR) +{ + return mul(dFdG, dGdR); +} +float3 applyChainRule2D(in float3x2 dFdG, in float2 dGdR) +{ + return mul(dFdG, dGdR); +} +float4 applyChainRule2D(in float4x2 dFdG, in float2 dGdR) +{ + return mul(dFdG, dGdR); +} + +float1x2 applyChainRule2D(in float1x2 dFdG, in float2x2 dGdR) // needed for deriv map +{ + return mul(dFdG, dGdR); +} + +float2x2 applyChainRule2D(in float2x2 dFdG, in float2x2 dGdR) +{ + return mul(dFdG, dGdR); +} +float3x2 applyChainRule2D(in float3x2 dFdG, in float2x2 dGdR) +{ + return mul(dFdG, dGdR); +} +float4x2 applyChainRule2D(in float4x2 dFdG, in float2x2 dGdR) +{ + return mul(dFdG, dGdR); +} + +float1x3 applyChainRule2D(in float1x2 dFdG, in float2x3 dGdR) +{ + return mul(dFdG, dGdR); +} + +float2x3 applyChainRule2D(in float2x2 dFdG, in float2x3 dGdR) +{ + return mul(dFdG, dGdR); +} +float3x3 applyChainRule2D(in float3x2 dFdG, in float2x3 dGdR) +{ + return mul(dFdG, dGdR); +} +float4x4 applyChainRule2D(in float4x2 dFdG, in float2x4 dGdR) +{ + return mul(dFdG, dGdR); +} + +float1x4 applyChainRule2D(in float1x2 dFdG, in float2x4 dGdR) +{ + return mul(dFdG, dGdR); +} + +float2x4 applyChainRule2D(in float2x2 dFdG, in float2x4 dGdR) +{ + return mul(dFdG, dGdR); +} +float3x4 applyChainRule2D(in float3x2 dFdG, in float2x4 dGdR) +{ + return mul(dFdG, dGdR); +} + + + +float applyChainRule3D(in float1x3 dFdG, in float3 dGdR) +{ + return mul(dFdG, dGdR); +} + +float2 applyChainRule3D(in float2x3 dFdG, in float3 dGdR) +{ + return mul(dFdG, dGdR); +} +float3 applyChainRule3D(in float3x3 dFdG, in float3 dGdR) +{ + return mul(dFdG, dGdR); +} +float4 applyChainRule3D(in float4x3 dFdG, in float3 dGdR) +{ + return mul(dFdG, dGdR); +} + +float1x2 applyChainRule3D(in float1x3 dFdG, in float3x2 dGdR) +{ + return mul(dFdG, dGdR); +} + +float2x2 applyChainRule3D(in float2x3 dFdG, in float3x2 dGdR) +{ + return mul(dFdG, dGdR); +} +float3x2 applyChainRule3D(in float3x3 dFdG, in float3x2 dGdR) +{ + return mul(dFdG, dGdR); +} +float4x2 applyChainRule3D(in float4x3 dFdG, in float3x2 dGdR) +{ + return mul(dFdG, dGdR); +} + +float1x3 applyChainRule3D(in float1x3 dFdG, in float3x3 dGdR) +{ + return mul(dFdG, dGdR); +} + +float2x3 applyChainRule3D(in float2x3 dFdG, in float3x3 dGdR) +{ + return mul(dFdG, dGdR); +} +float3x3 applyChainRule3D(in float3x3 dFdG, in float3x3 dGdR) +{ + return mul(dFdG, dGdR); +} +float4x3 applyChainRule3D(in float4x3 dFdG, in float3x3 dGdR) +{ + return mul(dFdG, dGdR); +} + +float1x4 applyChainRule3D(in float1x3 dFdG, in float3x4 dGdR) +{ + return mul(dFdG, dGdR); +} + +float2x4 applyChainRule3D(in float2x3 dFdG, in float3x4 dGdR) +{ + return mul(dFdG, dGdR); +} +float3x4 applyChainRule3D(in float3x3 dFdG, in float3x4 dGdR) +{ + return mul(dFdG, dGdR); +} +float4x4 applyChainRule3D(in float4x3 dFdG, in float3x4 dGdR) +{ + return mul(dFdG, dGdR); +} + + + +float applyChainRule4D(in float1x4 dFdG, in float4 dGdR) +{ + return mul(dFdG, dGdR); +} + +float2 applyChainRule4D(in float2x4 dFdG, in float4 dGdR) +{ + return mul(dFdG, dGdR); +} +float3 applyChainRule4D(in float3x4 dFdG, in float4 dGdR) +{ + return mul(dFdG, dGdR); +} +float4 applyChainRule4D(in float4x4 dFdG, in float4 dGdR) +{ + return mul(dFdG, dGdR); +} + +float1x2 applyChainRule4D(in float1x4 dFdG, in float4x2 dGdR) +{ + return mul(dFdG, dGdR); +} + +float2x2 applyChainRule4D(in float2x4 dFdG, in float4x2 dGdR) +{ + return mul(dFdG, dGdR); +} +float3x2 applyChainRule4D(in float3x4 dFdG, in float4x2 dGdR) +{ + return mul(dFdG, dGdR); +} +float4x2 applyChainRule4D(in float4x4 dFdG, in float4x2 dGdR) +{ + return mul(dFdG, dGdR); +} + +float1x3 applyChainRule4D(in float1x4 dFdG, in float4x3 dGdR) +{ + return mul(dFdG, dGdR); +} + +float2x3 applyChainRule4D(in float2x4 dFdG, in float4x3 dGdR) +{ + return mul(dFdG, dGdR); +} +float3x3 applyChainRule4D(in float3x4 dFdG, in float4x3 dGdR) +{ + return mul(dFdG, dGdR); +} +float4x3 applyChainRule4D(in float4x4 dFdG, in float4x3 dGdR) +{ + return mul(dFdG, dGdR); +} + +float1x4 applyChainRule4D(in float1x4 dFdG, in float4x4 dGdR) +{ + return mul(dFdG, dGdR); +} + +float2x4 applyChainRule4D(in float2x4 dFdG, in float4x4 dGdR) +{ + return mul(dFdG, dGdR); +} +float3x4 applyChainRule4D(in float3x4 dFdG, in float4x4 dGdR) +{ + return mul(dFdG, dGdR); +} +float4x4 applyChainRule4D(in float4x4 dFdG, in float4x4 dGdR) +{ + return mul(dFdG, dGdR); +} + + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/math/quaternions.hlsl b/include/nbl/builtin/hlsl/math/quaternions.hlsl new file mode 100644 index 0000000000..6cf712c8a2 --- /dev/null +++ b/include/nbl/builtin/hlsl/math/quaternions.hlsl @@ -0,0 +1,104 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_GLSL_MATH_QUATERNIONS_INCLUDED_ +#define _NBL_BUILTIN_GLSL_MATH_QUATERNIONS_INCLUDED_ + + +namespace nbl +{ +namespace hlsl +{ +namespace math +{ + + +struct quaternion_t +{ + float4 data; + + + static quaternion_t constructFromTruncated(in float3 first3Components) + { + quaternion_t quat; + quat.data.xyz = first3Components; + quat.data.w = sqrt(1.0-dot(first3Components,first3Components)); + return quat; + } + + static quaternion_t lerp(in quaternion_t start, in quaternion_t end, in float fraction, in float totalPseudoAngle) + { + const uint negationMask = asuint(totalPseudoAngle) & 0x80000000u; + const float4 adjEnd = asfloat(asuint(end.data)^negationMask); + + quaternion_t quat; + quat.data = lerp(start.data, adjEnd, fraction); + return quat; + } + static quaternion_t lerp(in quaternion_t start, in quaternion_t end, in float fraction) + { + return lerp(start,end,fraction,dot(start.data,end.data)); + } + + static float flerp_impl_adj_interpolant(in float angle, in float fraction, in float interpolantPrecalcTerm2, in float interpolantPrecalcTerm3) + { + const float A = 1.0904f + angle * (-3.2452f + angle * (3.55645f - angle * 1.43519f)); + const float B = 0.848013f + angle * (-1.06021f + angle * 0.215638f); + const float k = A * interpolantPrecalcTerm2 + B; + return fraction+interpolantPrecalcTerm3*k; + } + + static quaternion_t flerp(in quaternion_t start, in quaternion_t end, in float fraction) + { + const float pseudoAngle = dot(start.data,end.data); + + const float interpolantPrecalcTerm = fraction-0.5f; + const float interpolantPrecalcTerm3 = fraction*interpolantPrecalcTerm*(fraction-1.f); + const float adjFrac = quaternion_t::flerp_impl_adj_interpolant(abs(pseudoAngle),fraction,interpolantPrecalcTerm*interpolantPrecalcTerm,interpolantPrecalcTerm3); + quaternion_t quat = quaternion_t::lerp(start,end,adjFrac,pseudoAngle); + quat.data = normalize(quat.data); + return quat; + } + + static float3x3 constructMatrix(in quaternion_t quat) + { + float3x3 mat; + mat[0] = quat.data.yzx*quat.data.ywz+quat.data.zxy*quat.data.zyw*float3( 1.f, 1.f,-1.f); + mat[1] = quat.data.yzx*quat.data.xzw+quat.data.zxy*quat.data.wxz*float3(-1.f, 1.f, 1.f); + mat[2] = quat.data.yzx*quat.data.wyx+quat.data.zxy*quat.data.xwy*float3( 1.f,-1.f, 1.f); + mat[0][0] = 0.5f-mat[0][0]; + mat[1][1] = 0.5f-mat[1][1]; + mat[2][2] = 0.5f-mat[2][2]; + mat *= 2.f; + return mat; + } +}; + +float3 slerp_delta_impl(in float3 start, in float3 preScaledWaypoint, in float cosAngleFromStart) +{ + float3 planeNormal = cross(start,preScaledWaypoint); + + cosAngleFromStart *= 0.5; + const float sinAngle = sqrt(0.5-cosAngleFromStart); + const float cosAngle = sqrt(0.5+cosAngleFromStart); + + planeNormal *= sinAngle; + const float3 precompPart = cross(planeNormal,start)*2.0; + + return precompPart*cosAngle+cross(planeNormal,precompPart); +} + +float3 slerp_impl_impl(in float3 start, in float3 preScaledWaypoint, in float cosAngleFromStart) +{ + return start + slerp_delta_impl(start,preScaledWaypoint,cosAngleFromStart); +} + + + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/property_pool/transfer.hlsl b/include/nbl/builtin/hlsl/property_pool/transfer.hlsl new file mode 100644 index 0000000000..46f490317c --- /dev/null +++ b/include/nbl/builtin/hlsl/property_pool/transfer.hlsl @@ -0,0 +1,40 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + + +#ifndef _NBL_HLSL_PROPERTY_POOL_TRANSFER_HLSL_INCLUDED_ +#define _NBL_HLSL_PROPERTY_POOL_TRANSFER_HLSL_INCLUDED_ + + +namespace nbl +{ +namespace hlsl +{ +namespace property_pool +{ + + +struct transfer_t +{ + int propertyDWORDsize_flags; + int elementCount; + uint srcIndexOffset; + uint dstIndexOffset; +}; + +#define TRANSFER_EF_DOWNLOAD 0x1u +#define TRANSFER_EF_SRC_FILL 0x2u +#define TRANSFER_EF_BIT_COUNT 2 + +#define INVALID 0xdeadbeef + +#define MAX_PROPERTIES_PER_DISPATCH 128 + + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/random/xoroshiro.hlsl b/include/nbl/builtin/hlsl/random/xoroshiro.hlsl index 04c2b61fc7..920048e6bb 100644 --- a/include/nbl/builtin/hlsl/random/xoroshiro.hlsl +++ b/include/nbl/builtin/hlsl/random/xoroshiro.hlsl @@ -12,56 +12,56 @@ namespace nbl { namespace hlsl -{ - -struct Xoroshiro64StateHolder -{ +{ + +struct Xoroshiro64StateHolder +{ void xoroshiro64_state_advance() { state[1] ^= state[0]; state[0] = rotl(state[0], 26u) ^ state[1] ^ (state[1]<<9u); // a, b state[1] = rotl(state[1], 13u); // c - } - - uint32_t2 state; + } + + uint32_t2 state; }; -struct Xoroshiro64Star -{ - static Xoroshiro64Star construct(NBL_CONST_REF_ARG(uint32_t2) initialState) - { - Xoroshiro64StateHolder stateHolder = {initialState}; - return Xoroshiro64Star(stateHolder); - } - - uint32_t operator()() - { +struct Xoroshiro64Star +{ + static Xoroshiro64Star construct(NBL_CONST_REF_ARG(uint32_t2) initialState) + { + Xoroshiro64StateHolder stateHolder = {initialState}; + return Xoroshiro64Star(stateHolder); + } + + uint32_t operator()() + { const uint32_t result = stateHolder.state[0]*0x9E3779BBu; stateHolder.xoroshiro64_state_advance(); - return result; - } - - Xoroshiro64StateHolder stateHolder; + return result; + } + + Xoroshiro64StateHolder stateHolder; }; -struct Xoroshiro64StarStar -{ - static Xoroshiro64StarStar construct(NBL_CONST_REF_ARG(uint32_t2) initialState) - { - Xoroshiro64StateHolder stateHolder = {initialState}; - return Xoroshiro64StarStar(stateHolder); - } - - uint32_t operator()() - { +struct Xoroshiro64StarStar +{ + static Xoroshiro64StarStar construct(NBL_CONST_REF_ARG(uint32_t2) initialState) + { + Xoroshiro64StateHolder stateHolder = {initialState}; + return Xoroshiro64StarStar(stateHolder); + } + + uint32_t operator()() + { const uint32_t result = rotl(stateHolder.state[0]*0x9E3779BBu,5u)*5u; stateHolder.xoroshiro64_state_advance(); - return result; - } - - Xoroshiro64StateHolder stateHolder; + return result; + } + + Xoroshiro64StateHolder stateHolder; }; } diff --git a/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl b/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl new file mode 100644 index 0000000000..85978432d3 --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl @@ -0,0 +1,47 @@ +// 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 +#ifndef _NBL_BUILTIN_HLSL_SAMPLING_CONCENTRIC_MAPPING_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_CONCENTRIC_MAPPING_INCLUDED_ + +#include + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +float2 concentricMapping(in float2 _u) +{ + //map [0;1]^2 to [-1;1]^2 + float2 u = 2.0f * _u - 1.0f; + + float2 p; + if (all(u == float2(0.0,0.0))) + p = float2(0.0,0.0); + else + { + float r; + float theta; + if (abs(u.x) > abs(u.y)) { + r = u.x; + theta = 0.25f * math::PI * (u.y / u.x); + } + else { + r = u.y; + theta = 0.5f * math::PI - 0.25f * math::PI * (u.x / u.y); + } + // TODO: use nbl_glsl_sincos, but check that theta is in [-PI,PI] + p = r * float2(cos(theta), sin(theta)); + } + + return p; +} + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/sampling/cos_weighted.hlsl b/include/nbl/builtin/hlsl/sampling/cos_weighted.hlsl new file mode 100644 index 0000000000..fdfaea3f9c --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/cos_weighted.hlsl @@ -0,0 +1,70 @@ +// 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 +#ifndef _NBL_BUILTIN_HLSL_SAMPLING_COS_WEIGHTED_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_COS_WEIGHTED_INCLUDED_ + +#include + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +float3 projected_hemisphere_generate(in float2 _sample) +{ + float2 p = concentricMapping(_sample*0.99999f+0.000005f); + + float z = sqrt(max(0.0f, 1.0f - p.x*p.x - p.y*p.y)); + + return float3(p.x,p.y,z); +} + +float projected_hemisphere_pdf(in float L_z) +{ + return L_z * math::RECIPROCAL_PI; +} + +float projected_hemisphere_quotient_and_pdf(out float pdf, in float L_z) +{ + pdf = projected_hemisphere_pdf(L_z); + return 1.0f; +} +float projected_hemisphere_quotient_and_pdf(out float pdf, in float3 L) +{ + return projected_hemisphere_quotient_and_pdf(pdf,L.z); +} + +float3 projected_sphere_generate(inout float3 _sample) // TODO, it should be `inout`, right? +{ + float3 retval = projected_hemisphere_generate(_sample.xy); + const bool chooseLower = _sample.z>0.5f; + retval.z = chooseLower ? (-retval.z):retval.z; + if (chooseLower) + _sample.z -= 0.5f; + _sample.z *= 2.f; + return retval; +} + +float projected_sphere_quotient_and_pdf(out float pdf, in float L_z) +{ + float retval = projected_hemisphere_quotient_and_pdf(pdf,L_z); + pdf *= 0.5f; + return retval; +} +float projected_sphere_quotient_and_pdf(out float pdf, in float3 L) +{ + return projected_sphere_quotient_and_pdf(pdf,L.z); +} + +float projected_sphere_pdf(in float L_z) +{ + return 0.5f*projected_hemisphere_pdf(L_z); +} +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/shapes/aabb.hlsl b/include/nbl/builtin/hlsl/shapes/aabb.hlsl index 9c24eff0a5..6c9aa1bd45 100644 --- a/include/nbl/builtin/hlsl/shapes/aabb.hlsl +++ b/include/nbl/builtin/hlsl/shapes/aabb.hlsl @@ -31,7 +31,7 @@ struct AABB_t // float getVolume() { - const float3 extent = AABB_t::getExtent(); + const float3 extent = getExtent(); return extent.x * extent.y * extent.z; } @@ -45,7 +45,7 @@ struct AABB_t float3 maxVx; }; -struct nbl_glsl_shapes_CompressedAABB_t +struct CompressedAABB_t { // AABB_t decompress() diff --git a/include/nbl/builtin/hlsl/shapes/frustum.hlsl b/include/nbl/builtin/hlsl/shapes/frustum.hlsl new file mode 100644 index 0000000000..5ca185e2f4 --- /dev/null +++ b/include/nbl/builtin/hlsl/shapes/frustum.hlsl @@ -0,0 +1,74 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_SHAPES_FRUSTUM_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SHAPES_FRUSTUM_INCLUDED_ + +#include + + +namespace nbl +{ +namespace hlsl +{ +namespace shapes +{ + + +struct Frustum_t +{ + float3x4 minPlanes; + float3x4 maxPlanes; + + + // gives false negatives + static bool fastestDoesNotIntersectAABB(in Frustum_t frust, in AABB_t aabb) + { + #define getClosestDP(R) (dot(aabb.getFarthestPointInFront(R.xyz), R.xyz) + R.w) + if (getClosestDP(frust.minPlanes[0])<=0.f) + return true; + if (getClosestDP(frust.minPlanes[1])<=0.f) + return true; + if (getClosestDP(frust.minPlanes[2])<=0.f) + return true; + + if (getClosestDP(frust.maxPlanes[0])<=0.f) + return true; + if (getClosestDP(frust.maxPlanes[1])<=0.f) + return true; + + return getClosestDP(frust.maxPlanes[2])<=0.f; + #undef getClosestDP + } + + + + // will place planes which correspond to the bounds in NDC + static Frustum_t extract(in float4x4 proj, in AABB_t bounds) + { + const float4x4 pTpose = transpose(proj); + + Frustum_t frust; + frust.minPlanes = (float3x4)(pTpose) - float3x4(pTpose[3]*bounds.minVx[0], pTpose[3]*bounds.minVx[1], pTpose[3]*bounds.minVx[2]); + frust.maxPlanes = float3x4(pTpose[3]*bounds.maxVx[0], pTpose[3]*bounds.maxVx[1], pTpose[3]*bounds.maxVx[2]) - (float3x4)(pTpose); + return frust; + } + + // assuming an NDC of [-1,1]^2 x [0,1] + static Frustum_t extract(in float4x4 proj) + { + AABB_t bounds; + bounds.minVx = float3(-1.f,-1.f,0.f); + bounds.maxVx = float3(1.f,1.f,1.f); + return extract(proj, bounds); + } +}; + + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/shapes/rectangle.hlsl b/include/nbl/builtin/hlsl/shapes/rectangle.hlsl new file mode 100644 index 0000000000..3fe5cb4fec --- /dev/null +++ b/include/nbl/builtin/hlsl/shapes/rectangle.hlsl @@ -0,0 +1,43 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_SHAPES_RECTANGLE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SHAPES_RECTANGLE_INCLUDED_ + +#include + + +namespace nbl +{ +namespace hlsl +{ +namespace shapes +{ + + +float3 getSphericalRectangle(in float3 observer, in float3 rectangleOrigin, in float3x3 rectangleNormalBasis) +{ + return mul((rectangleOrigin-observer), rectangleNormalBasis); +} + +float SolidAngleOfRectangle(in float3 r0, in float2 rectangleExtents) +{ + const float4 denorm_n_z = float4(-r0.y, r0.x+rectangleExtents.x, r0.y+rectangleExtents.y, -r0.x); + const float4 n_z = denorm_n_z*rsqrt(float4(r0.z*r0.z,r0.z*r0.z,r0.z*r0.z,r0.z*r0.z)+denorm_n_z*denorm_n_z); + const float4 cosGamma = float4( + -n_z[0]*n_z[1], + -n_z[1]*n_z[2], + -n_z[2]*n_z[3], + -n_z[3]*n_z[0] + ); + return math::getSumofArccosABCD(cosGamma[0], cosGamma[1], cosGamma[2], cosGamma[3]) - 2 * math::PI; +} + + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/shapes/triangle.hlsl b/include/nbl/builtin/hlsl/shapes/triangle.hlsl new file mode 100644 index 0000000000..134a482739 --- /dev/null +++ b/include/nbl/builtin/hlsl/shapes/triangle.hlsl @@ -0,0 +1,116 @@ + +#ifndef _NBL_BUILTIN_HLSL_SHAPES_TRIANGLE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SHAPES_TRIANGLE_INCLUDED_ + +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ + +namespace shapes +{ + +// +float3x3 getSphericalTriangle(in float3x3 vertices, in float3 origin) +{ + float3x3 tp_vertices = transpose(vertices); + + // the `normalize` cannot be optimized out + return float3x3(normalize(tp_vertices[0]-origin), normalize(tp_vertices[1]-origin), normalize(tp_vertices[2]-origin)); +} + +// returns true if pyramid degenerated into a line +bool SphericalTrianglePyramidAngles(in float3x3 sphericalVertices, out float3 cos_sides, out float3 csc_sides) +{ + float3x3 tp_sphericalVertices = transpose(sphericalVertices); + // The sides are denoted by lower-case letters a, b, and c. + // On the unit sphere their lengths are numerically equal to the radian measure of the angles that the great circle arcs subtend at the centre. + // The sides of proper spherical triangles are (by convention) less than PI + cos_sides = float3(dot(tp_sphericalVertices[1],tp_sphericalVertices[2]), + dot(tp_sphericalVertices[2],tp_sphericalVertices[0]), + dot(tp_sphericalVertices[0],tp_sphericalVertices[1])); + csc_sides = rsqrt(float3(1.f,1.f,1.f)-cos_sides*cos_sides); + return any((csc_sides >= float3(FLT_MAX,FLT_MAX,FLT_MAX))); +} + +// returns solid angle of a spherical triangle, this function is beyond optimized. +float SolidAngleOfTriangle(in float3x3 sphericalVertices, out float3 cos_vertices, out float3 sin_vertices, out float cos_a, out float cos_c, out float csc_b, out float csc_c) +{ + float3 cos_sides,csc_sides; + if (SphericalTrianglePyramidAngles(sphericalVertices,cos_sides,csc_sides)) + return 0.f; + + // these variables might eventually get optimized out + cos_a = cos_sides[0]; + cos_c = cos_sides[2]; + csc_b = csc_sides[1]; + csc_c = csc_sides[2]; + + // Both vertices and angles at the vertices are denoted by the same upper case letters A, B, and C. The angles A, B, C of the triangle are equal to the angles between the planes that intersect the surface of the sphere or, equivalently, the angles between the tangent vectors of the great circle arcs where they meet at the vertices. Angles are in radians. The angles of proper spherical triangles are (by convention) less than PI + cos_vertices = clamp((cos_sides-cos_sides.yzx*cos_sides.zxy)*csc_sides.yzx*csc_sides.zxy,float3(-1.f,-1.f,-1.f),float3(1.f,1.f,1.f)); + // using Spherical Law of Cosines (TODO: do we need to clamp anymore? since the pyramid angles method introduction?) + sin_vertices = sqrt(float3(1.f,1.f,1.f)-cos_vertices*cos_vertices); + + // the solid angle of a triangle is the sum of its planar vertices' angles minus PI + return math::getArccosSumofABC_minus_PI(cos_vertices[0],cos_vertices[1],cos_vertices[2],sin_vertices[0],sin_vertices[1],sin_vertices[2]); +} +float SolidAngleOfTriangle(in float3x3 sphericalVertices) +{ + float3 dummy0,dummy1; + float dummy2,dummy3,dummy4,dummy5; + return SolidAngleOfTriangle(sphericalVertices,dummy0,dummy1,dummy2,dummy3,dummy4,dummy5); +} +// returns solid angle of a triangle given by its world-space vertices and world-space viewing position +float SolidAngleOfTriangle(in float3x3 vertices, in float3 origin) +{ + return SolidAngleOfTriangle(getSphericalTriangle(vertices,origin)); +} + + +// return projected solid angle of a spherical triangle +float ProjectedSolidAngleOfTriangle(in float3x3 sphericalVertices, in float3 receiverNormal, out float3 cos_sides, out float3 csc_sides, out float3 cos_vertices) +{ + if (SphericalTrianglePyramidAngles(sphericalVertices,cos_sides,csc_sides)) + return 0.f; + + float3x3 tp_sphericalVertices = transpose(sphericalVertices); + + const float3x3 awayFromEdgePlane = float3x3( + cross(tp_sphericalVertices[1],tp_sphericalVertices[2])*csc_sides[0], + cross(tp_sphericalVertices[2],tp_sphericalVertices[0])*csc_sides[1], + cross(tp_sphericalVertices[0],tp_sphericalVertices[1])*csc_sides[2] + ); + + float3x3 tp_awayFromEdgePlane = transpose(awayFromEdgePlane); + + // useless here but could be useful somewhere else + cos_vertices[0] = dot(tp_awayFromEdgePlane[1],tp_awayFromEdgePlane[2]); + cos_vertices[1] = dot(tp_awayFromEdgePlane[2],tp_awayFromEdgePlane[0]); + cos_vertices[2] = dot(tp_awayFromEdgePlane[0],tp_awayFromEdgePlane[1]); + // TODO: above dot products are in the wrong order, either work out which is which, or try all 6 permutations till it works + cos_vertices = clamp((cos_sides-cos_sides.yzx*cos_sides.zxy)*csc_sides.yzx*csc_sides.zxy,float3(-1.f,-1.f,-1.f),float3(1.f,1.f,1.f)); + + const float3 externalProducts = abs(mul(tp_awayFromEdgePlane, receiverNormal)); + + const float3 pyramidAngles = acos(cos_sides); + return dot(pyramidAngles,externalProducts)/(2.f * math::PI); +} +float ProjectedSolidAngleOfTriangle(in float3x3 sphericalVertices, in float3 receiverNormal, out float3 cos_sides, out float3 csc_sides) +{ + float3 cos_vertices; + return ProjectedSolidAngleOfTriangle(sphericalVertices,receiverNormal,cos_sides,csc_sides,cos_vertices); +} +float ProjectedSolidAngleOfTriangle(in float3x3 sphericalVertices, in float3 receiverNormal) +{ + float3 cos_sides,csc_sides; + return ProjectedSolidAngleOfTriangle(sphericalVertices,receiverNormal,cos_sides,csc_sides); +} + +} +} +} +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/utils/acceleration_structures.hlsl b/include/nbl/builtin/hlsl/utils/acceleration_structures.hlsl new file mode 100644 index 0000000000..14331c2bfe --- /dev/null +++ b/include/nbl/builtin/hlsl/utils/acceleration_structures.hlsl @@ -0,0 +1,32 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_UTILS_ACCELERATION_STRUCTURES_INCLUDED_ +#define _NBL_BUILTIN_HLSL_UTILS_ACCELERATION_STRUCTURES_INCLUDED_ + + +namespace nbl +{ +namespace hlsl +{ + + +// Use for Indirect Builds +struct BuildRangeInfo +{ + uint primitiveCount; + uint primitiveOffset; + uint firstVertex; + uint transformOffset; + + #ifdef __cplusplus + auto operator<=>(const BuildRangeInfo&) const = default; + #endif // __cplusplus +}; + + +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/utils/common.hlsl b/include/nbl/builtin/hlsl/utils/common.hlsl new file mode 100644 index 0000000000..d8b4018dc0 --- /dev/null +++ b/include/nbl/builtin/hlsl/utils/common.hlsl @@ -0,0 +1,38 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_UTILS_COMMON_INCLUDED_ +#define _NBL_BUILTIN_HLSL_UTILS_COMMON_INCLUDED_ + + +namespace nbl +{ +namespace hlsl +{ + + +struct SBasicViewParameters +{ + float4x4 MVP; + float4x3 MV; + float4x3 NormalMatAndEyePos; + + float3x3 GetNormalMat(in float4x3 _NormalMatAndEyePos) + { + return (float3x3)(_NormalMatAndEyePos); + } + float3 GetEyePos(in float4x3 _NormalMatAndEyePos) + { + return _NormalMatAndEyePos[3]; + } +}; + + +} +} + + + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/utils/compressed_normal_matrix_t.hlsl b/include/nbl/builtin/hlsl/utils/compressed_normal_matrix_t.hlsl new file mode 100644 index 0000000000..6614ea4b83 --- /dev/null +++ b/include/nbl/builtin/hlsl/utils/compressed_normal_matrix_t.hlsl @@ -0,0 +1,41 @@ + +#ifndef _NBL_BUILTIN_HLSL_UTILS_COMPRESSED_NORMAL_MATRIX_T_INCLUDED_ +#define _NBL_BUILTIN_HLSL_UTILS_COMPRESSED_NORMAL_MATRIX_T_INCLUDED_ + +#include "nbl/builtin/hlsl/common.hlsl" +#include "nbl/builtin/hlsl/math/constants.hlsl" + +namespace nbl +{ +namespace hlsl +{ + +struct CompressedNormalMatrix_t +{ + uint4 data; + + + float3x3 decode(in CompressedNormalMatrix_t compr) + { + float3x3 m; + + const uint4 bottomBits = compr.data & (0x00030003u).xxxx; + const uint firstComp = (bottomBits[3]<<6u)|(bottomBits[2]<<4u)|(bottomBits[1]<<2u)|bottomBits[0]; + m[0].x = unpackSnorm2x16((firstComp>>8u)|firstComp).x; + + const uint4 remaining8Comp = compr.data & (0xFFFCFFFCu).xxxx; + m[0].yz = unpackSnorm2x16(remaining8Comp[0]); + m[1].xy = unpackSnorm2x16(remaining8Comp[1]); + const float2 tmp = unpackSnorm2x16(remaining8Comp[2]); + m[1].z = tmp[0]; + m[2].x = tmp[1]; + m[2].yz = unpackSnorm2x16(remaining8Comp[3]); + + return m; + } +}; + +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/utils/culling.hlsl b/include/nbl/builtin/hlsl/utils/culling.hlsl new file mode 100644 index 0000000000..092d2ba539 --- /dev/null +++ b/include/nbl/builtin/hlsl/utils/culling.hlsl @@ -0,0 +1,129 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_UTILS_CULLING_INCLUDED_ +#define _NBL_BUILTIN_HLSL_UTILS_CULLING_INCLUDED_ + +#include +#include + + +namespace nbl +{ +namespace hlsl +{ + + +// gives false negatives +bool fastestFrustumCullAABB(in float4x4 proj, in shapes::AABB_t aabb) +{ + const shapes::Frustum_t frust = shapes::Frustum_t::extract(proj); + return shapes::Frustum_t::fastestDoesNotIntersectAABB(frust, aabb); +} + +// gives very few false negatives +bool fastFrustumCullAABB(in float4x4 proj, in float4x4 invProj, in shapes::AABB_t aabb) +{ + if (fastestFrustumCullAABB(proj,aabb)) + return true; + + const shapes::Frustum_t boxInvFrustum = shapes::Frustum_t::extract(invProj); + shapes::AABB_t ndc; + ndc.minVx = float3(-1.f,-1.f,0.f); + ndc.maxVx = float3(1.f,1.f,1.f); + return shapes::Frustum_t::fastestDoesNotIntersectAABB(boxInvFrustum,ndc); +} + +// perfect Separating Axis Theorem, needed for Clustered/Tiled Lighting +bool preciseFrustumCullAABB(in float4x4 proj, in float4x4 invProj, in shapes::AABB_t aabb) +{ + const shapes::Frustum_t viewFrust = shapes::Frustum_t::extract(proj); + if (shapes::Frustum_t::fastestDoesNotIntersectAABB(viewFrust,aabb)) + return true; + + const shapes::Frustum_t boxInvFrustum = shapes::Frustum_t::extract(invProj); + shapes::AABB_t ndc; + ndc.minVx = float3(-1.f,-1.f,0.f); + ndc.maxVx = float3(1.f,1.f,1.f); + if (shapes::Frustum_t::fastestDoesNotIntersectAABB(boxInvFrustum,ndc)) + return true; + + float3 edges[12]; + edges[ 0] = cross(viewFrust.minPlanes[0].xyz,viewFrust.minPlanes[1].xyz); + edges[ 1] = cross(viewFrust.minPlanes[0].xyz,viewFrust.minPlanes[2].xyz); + edges[ 2] = cross(viewFrust.minPlanes[0].xyz,viewFrust.maxPlanes[1].xyz); + edges[ 3] = cross(viewFrust.minPlanes[0].xyz,viewFrust.maxPlanes[2].xyz); + edges[ 4] = cross(viewFrust.minPlanes[1].xyz,viewFrust.minPlanes[0].xyz); + edges[ 5] = cross(viewFrust.minPlanes[1].xyz,viewFrust.minPlanes[2].xyz); + edges[ 6] = cross(viewFrust.minPlanes[1].xyz,viewFrust.maxPlanes[0].xyz); + edges[ 7] = cross(viewFrust.minPlanes[1].xyz,viewFrust.maxPlanes[2].xyz); + edges[ 8] = cross(viewFrust.minPlanes[2].xyz,viewFrust.minPlanes[0].xyz); + edges[ 0] = cross(viewFrust.minPlanes[2].xyz,viewFrust.minPlanes[1].xyz); + edges[10] = cross(viewFrust.minPlanes[2].xyz,viewFrust.maxPlanes[0].xyz); + edges[11] = cross(viewFrust.minPlanes[2].xyz,viewFrust.maxPlanes[1].xyz); + for (int i=0; i<12; i++) + { +#define getClosestDP(R) (dot(ndc.getFarthestPointInFront(R.xyz),R.xyz)+R.w) + /* TODO: These are buggy! + // cross(e_0,edges[i]) + { + const float2 normal = float2(-edges[i].z,edges[i].y); + const bool2 negMask = lessThan(normal,float2(0.f)); + const float4 planeBase = normal.x*invProj[1]+normal.y*invProj[2]; + + const float minAABB = dot(lerp(aabb.minVx.yz,aabb.maxVx.yz,negMask),normal); + const float4 minPlane = planeBase-invProj[3]*minAABB; + if (getClosestDP(minPlane)<=0.f) + return true; + const float maxAABB = dot(lerp(aabb.maxVx.yz,aabb.minVx.yz,negMask),normal); + const float4 maxPlane = invProj[3]*maxAABB-planeBase; + if (getClosestDP(maxPlane)<=0.f) + return true; + } + // cross(e_1,edges[i]) + { + const float2 normal = float2(-edges[i].x,edges[i].z); + const bool2 negMask = lessThan(normal,float2(0.f)); + const float4 planeBase = normal.x*invProj[0]+normal.y*invProj[2]; + const float minAABB = dot(lerp(aabb.minVx.xz,aabb.maxVx.xz,negMask),normal); + const float4 minPlane = planeBase-invProj[3]*minAABB; + if (getClosestDP(minPlane)<=0.f) + return true; + const float maxAABB = dot(lerp(aabb.maxVx.xz,aabb.minVx.xz,negMask),normal); + const float4 maxPlane = invProj[3]*maxAABB-planeBase; + if (getClosestDP(maxPlane)<=0.f) + return true; + } the last one is probably buggy too*/ + // cross(e_2,edges[i]) + { + const float2 normal = float2(-edges[i].y,edges[i].x); + const bool2 negMask = normal < (0.0f).xx; + const float4 planeBase = normal.x*invProj[0]+normal.y*invProj[1]; + + const float minAABB = dot(lerp(aabb.minVx.xy,aabb.maxVx.xy,negMask),normal); + const float4 minPlane = planeBase-invProj[3]*minAABB; + if (getClosestDP(minPlane)<=0.f) + return true; + const float maxAABB = dot(lerp(aabb.maxVx.xy,aabb.minVx.xy,negMask),normal); + const float4 maxPlane = invProj[3]*maxAABB-planeBase; + if (getClosestDP(maxPlane)<=0.f) + return true; + } +#undef getClosestDP + } + return false; +} + +// TODO: Other culls useful for clustered lighting +// - Sphere vs Frustum +// - Convex Infinite Cone vs Frustum +// - Concave Infinite Cone vs Frustum (! is frustum inside of an convex infinite cone with half angle PI-theta) + + +} +} + + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/utils/indirect_commands.hlsl b/include/nbl/builtin/hlsl/utils/indirect_commands.hlsl new file mode 100644 index 0000000000..a66aa60828 --- /dev/null +++ b/include/nbl/builtin/hlsl/utils/indirect_commands.hlsl @@ -0,0 +1,53 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_UTILS_INDIRECT_COMMANDS_HLSL_INCLUDED_ +#define _NBL_BUILTIN_UTILS_INDIRECT_COMMANDS_HLSL_INCLUDED_ + +namespace nbl +{ +namespace hlsl +{ + +struct DrawArraysIndirectCommand_t +{ + uint count; + uint instanceCount; + uint first; + uint baseInstance; +}; + +struct DrawElementsIndirectCommand_t +{ + uint count; + uint instanceCount; + uint firstIndex; + uint baseVertex; + uint baseInstance; +}; + +struct DispatchIndirectCommand_t +{ + uint num_groups_x; + uint num_groups_y; + uint num_groups_z; +}; + + +uint computeOptimalPersistentWorkgroupDispatchSize(in uint elementCount, in uint workgroupSize, in uint workgroupSpinningProtection) +{ + const uint infinitelyWideDeviceWGCount = (elementCount-1u)/(workgroupSize*workgroupSpinningProtection)+1u; + return min(infinitelyWideDeviceWGCount,NBL_HLSL_LIMIT_MAX_RESIDENT_INVOCATIONS/NBL_HLSL_LIMIT_MAX_OPTIMALLY_RESIDENT_WORKGROUP_INVOCATIONS); +} +uint computeOptimalPersistentWorkgroupDispatchSize(in uint elementCount, in uint workgroupSize) +{ + return computeOptimalPersistentWorkgroupDispatchSize(elementCount,workgroupSize,1u); +} + + +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/utils/morton.hlsl b/include/nbl/builtin/hlsl/utils/morton.hlsl new file mode 100644 index 0000000000..8757090c71 --- /dev/null +++ b/include/nbl/builtin/hlsl/utils/morton.hlsl @@ -0,0 +1,50 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_UTILS_MORTON_INCLUDED_ +#define _NBL_BUILTIN_HLSL_UTILS_MORTON_INCLUDED_ + + +namespace nbl +{ +namespace hlsl +{ +namespace morton +{ + + +uint decode2d4bComponent(in uint x) +{ + x &= 0x55555555u; + x = (x ^ (x >> 1u)) & 0x33333333u; + x = (x ^ (x >> 2u)) & 0x0f0f0f0fu; + return x; +} + +uint decode2d8bComponent(in uint x) +{ + x &= 0x55555555u; + x = (x ^ (x >> 1u)) & 0x33333333u; + x = (x ^ (x >> 2u)) & 0x0f0f0f0fu; + x = (x ^ (x >> 4u)) & 0x00ff00ffu; + return x; +} + +uint2 decode2d4b(in uint x) +{ + return uint2(decode2d4bComponent(x), decode2d4bComponent(x >> 1u)); +} + +uint2 decode2d8b(in uint x) +{ + return uint2(decode2d8bComponent(x), decode2d8bComponent(x >> 1u)); +} + + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/utils/normal_decode.hlsl b/include/nbl/builtin/hlsl/utils/normal_decode.hlsl new file mode 100644 index 0000000000..0d9a1bd8ae --- /dev/null +++ b/include/nbl/builtin/hlsl/utils/normal_decode.hlsl @@ -0,0 +1,33 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_UTILS_NORMAL_DECODE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_UTILS_NORMAL_DECODE_INCLUDED_ + +#include "nbl/builtin/hlsl/math/constants.hlsl" + +#include "nbl/builtin/hlsl/utils/compressed_normal_matrix_t.hlsl" + + +namespace nbl +{ +namespace hlsl +{ +namespace normal_decode +{ + + +float3 signedSpherical(in float2 enc) +{ + float ang = enc.x * math::PI; + return float3(float2(cos(ang),sin(ang))*sqrt(1.0-enc.y*enc.y), enc.y); +} + + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/utils/normal_encode.hlsl b/include/nbl/builtin/hlsl/utils/normal_encode.hlsl new file mode 100644 index 0000000000..585d91425f --- /dev/null +++ b/include/nbl/builtin/hlsl/utils/normal_encode.hlsl @@ -0,0 +1,27 @@ + +#ifndef _NBL_BUILTIN_HLSL_UTILS_NORMAL_ENCODE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_UTILS_NORMAL_ENCODE_INCLUDED_ + +#include "nbl/builtin/hlsl/math/constants.hlsl" +#include "nbl/builtin/hlsl/utils/compressed_normal_matrix_t.hlsl" + + +namespace nbl +{ +namespace hlsl +{ +namespace normal_encode +{ + + +float2 signedSpherical(in float3 n) +{ + return float2(atan2(n.y,n.x)/math::PI, n.z); +} + + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/utils/surface_transform.hlsl b/include/nbl/builtin/hlsl/utils/surface_transform.hlsl new file mode 100644 index 0000000000..2bc7a7573d --- /dev/null +++ b/include/nbl/builtin/hlsl/utils/surface_transform.hlsl @@ -0,0 +1,177 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_SURFACE_TRANSFORM_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SURFACE_TRANSFORM_INCLUDED_ + +#include "nbl/builtin/hlsl/utils/surface_transform_e.h" + + +namespace nbl +{ +namespace hlsl +{ +namespace surface_transform +{ + + +//! Use this function to apply the INVERSE of swapchain tranformation to the screenspace coordinate `coord` +//! For example when the device orientation is 90°CW then this transforms the point 90°CCW. +//! Usecase = [Gather]: +//! Applications such as raytracing in shaders where you would want to generate rays from screen space coordinates. +//! Warnings: +//! - You don't need to consider this using in your raytracing shaders if you apply the forward transformation to your projection matrix. +//! - Be aware that almost always you'd want to do a single transform in your rendering pipeline. +int2 applyInverseToScreenSpaceCoordinate(in uint swapchainTransform, in int2 coord, in int2 screenSize) +{ + int2 lastTexel = screenSize - (1).xx; + switch (swapchainTransform) + { + case IDENTITY: + return coord; + case ROTATE_90: + return int2(lastTexel.y - coord.y, coord.x); + case ROTATE_180: + return int2(lastTexel) - coord; + case ROTATE_270: + return int2(coord.y, lastTexel.x - coord.x); + case HORIZONTAL_MIRROR: + return int2(lastTexel.x - coord.x, coord.y); + case HORIZONTAL_MIRROR_ROTATE_90: + return lastTexel - coord.yx; + case HORIZONTAL_MIRROR_ROTATE_180: + return int2(coord.x, lastTexel.y - coord.y); + case HORIZONTAL_MIRROR_ROTATE_270: + return coord.yx; + default: + return (0).xx; + } +} + +//! Use this function to apply the swapchain tranformation to the screenspace coordinate `coord` +//! Usecase = [Scatter]: +//! When directly writing to your swapchain using `imageStore` in order to match the orientation of the device relative to it's natural orientation. +//! Warning: Be aware that almost always you'd want to do a single transform in your rendering pipeline. +int2 applyToScreenSpaceCoordinate(in uint swapchainTransform, in int2 coord, in int2 screenSize) +{ + int2 lastTexel = screenSize - (1).xx; + switch (swapchainTransform) + { + case IDENTITY: + return coord; + case ROTATE_90: + return int2(coord.y, lastTexel.x - coord.x); + case ROTATE_180: + return int2(lastTexel) - coord; + case ROTATE_270: + return int2(lastTexel.y - coord.y, coord.x); + case HORIZONTAL_MIRROR: + return int2(lastTexel.x - coord.x, coord.y); + case HORIZONTAL_MIRROR_ROTATE_90: + return coord.yx; + case HORIZONTAL_MIRROR_ROTATE_180: + return int2(coord.x, lastTexel.y - coord.y); + case HORIZONTAL_MIRROR_ROTATE_270: + return lastTexel - coord.yx; + default: + return (0).xx; + } +} + +//! [width,height] might switch to [height, width] in orientations such as 90°CW +//! Usecase: Currently none in the shaders +int2 transformedExtents(in uint swapchainTransform, in int2 screenSize) +{ + switch (swapchainTransform) + { + case IDENTITY: + case HORIZONTAL_MIRROR: + case HORIZONTAL_MIRROR_ROTATE_180: + case ROTATE_180: + return screenSize; + case ROTATE_90: + case ROTATE_270: + case HORIZONTAL_MIRROR_ROTATE_90: + case HORIZONTAL_MIRROR_ROTATE_270: + return screenSize.yx; + default: + return (0).xx; + } +} + +// TODO: surface_transform::transformedDerivatives implementations are untested + +// If rendering directly to the swapchain, dFdx/dFdy operations may be incorrect due to the swapchain +// transform. Use these helper functions to transform the dFdx or dFdy accordingly. + +float2 transformedDerivatives(in uint swapchainTransform, in float2 ddxDdy) +{ + #define OUTPUT_TYPE float2 + #include "nbl/builtin/hlsl/utils/surface_transform_transformedDerivatives.hlsl" + #undef OUTPUT_TYPE +} +float2x2 transformedDerivatives(in uint swapchainTransform, in float2x2 ddxDdy) +{ + #define OUTPUT_TYPE float2x2 + #include "nbl/builtin/hlsl/utils/surface_transform_transformedDerivatives.hlsl" + #undef OUTPUT_TYPE +} +float2x3 transformedDerivatives(in uint swapchainTransform, in float2x3 ddxDdy) +{ + #define OUTPUT_TYPE float2x3 + #include "nbl/builtin/hlsl/utils/surface_transform_transformedDerivatives.hlsl" + #undef OUTPUT_TYPE +} +float2x4 transformedDerivatives(in uint swapchainTransform, in float2x4 ddxDdy) +{ + #define OUTPUT_TYPE float2x4 + #include "nbl/builtin/hlsl/utils/surface_transform_transformedDerivatives.hlsl" + #undef OUTPUT_TYPE +} + +//! Same as `surface_transform::applyToScreenSpaceCoordinate` but in NDC space +//! If rendering to the swapchain, you may use this function to transform the NDC coordinates directly +//! to be fed into gl_Position in vertex shading +//! Warning: Be aware that almost always you'd want to do a single transform in your rendering pipeline. +float2 applyToNDC(in uint swapchainTransform, in float2 ndc) +{ + const float sin90 = 1.0, cos90 = 0.0, + sin180 = 0.0, cos180 = -1.0, + sin270 = -1.0, cos270 = 0.0; + switch (swapchainTransform) + { + case ROTATE_90: + return mul(ndc, float2x2(cos90, -sin90, sin90, cos90)); + + case ROTATE_180: + return mul(ndc, float2x2(cos180, -sin180, sin180, cos180)); + + case ROTATE_270: + return mul(ndc, float2x2(cos270, -sin270, sin270, cos270)); + + case HORIZONTAL_MIRROR: + return mul(ndc, float2x2(-1, 0, 0, 1)); + + case HORIZONTAL_MIRROR_ROTATE_90: + return mul(ndc, float2x2(-cos90, sin90, sin90, cos90)); + + case HORIZONTAL_MIRROR_ROTATE_180: + return mul(ndc, float2x2(-cos180, sin180, sin180, cos180)); + + case HORIZONTAL_MIRROR_ROTATE_270: + return mul(ndc, float2x2(-cos270, sin270, sin270, cos270)); + + default: + return ndc; + } +} + + + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/utils/surface_transform_e.h b/include/nbl/builtin/hlsl/utils/surface_transform_e.h new file mode 100644 index 0000000000..2024ae7c73 --- /dev/null +++ b/include/nbl/builtin/hlsl/utils/surface_transform_e.h @@ -0,0 +1,31 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_SURFACE_TRANSFORM_E_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SURFACE_TRANSFORM_E_INCLUDED_ + +namespace nbl +{ +namespace hlsl +{ +namespace surface_transform +{ + + +static const uint IDENTITY = 0x00000001; +static const uint ROTATE_90 = 0x00000002; +static const uint ROTATE_180 = 0x00000004; +static const uint ROTATE_270 = 0x00000008; +static const uint HORIZONTAL_MIRROR = 0x00000010; +static const uint HORIZONTAL_MIRROR_ROTATE_90 = 0x00000020; +static const uint HORIZONTAL_MIRROR_ROTATE_180 = 0x00000040; +static const uint HORIZONTAL_MIRROR_ROTATE_270 = 0x00000080; + + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/utils/surface_transform_transformedDerivatives.hlsl b/include/nbl/builtin/hlsl/utils/surface_transform_transformedDerivatives.hlsl new file mode 100644 index 0000000000..e23523f786 --- /dev/null +++ b/include/nbl/builtin/hlsl/utils/surface_transform_transformedDerivatives.hlsl @@ -0,0 +1,29 @@ + +// Copyright (C) 2018-2022 - 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 + + + +switch (swapchainTransform) +{ + case IDENTITY: + return ddxDdy; + case HORIZONTAL_MIRROR: + return OUTPUT_TYPE(-ddxDdy[0], ddxDdy[1]); + case HORIZONTAL_MIRROR_ROTATE_180: + return OUTPUT_TYPE(ddxDdy[0], -ddxDdy[1]); + case ROTATE_180: + return OUTPUT_TYPE(-ddxDdy[0], -ddxDdy[1]); + case ROTATE_90: + return OUTPUT_TYPE(ddxDdy[1], -ddxDdy[0]); + case ROTATE_270: + return OUTPUT_TYPE(-ddxDdy[1], ddxDdy[0]); + case HORIZONTAL_MIRROR_ROTATE_90: + return OUTPUT_TYPE(ddxDdy[1], ddxDdy[0]); + case HORIZONTAL_MIRROR_ROTATE_270: + return OUTPUT_TYPE(-ddxDdy[1], -ddxDdy[0]); + default: + return (0); +} + diff --git a/include/nbl/builtin/hlsl/utils/transform.hlsl b/include/nbl/builtin/hlsl/utils/transform.hlsl new file mode 100644 index 0000000000..8fabe1fa3a --- /dev/null +++ b/include/nbl/builtin/hlsl/utils/transform.hlsl @@ -0,0 +1,117 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_UTILS_TRANSFORM_INCLUDED_ +#define _NBL_BUILTIN_HLSL_UTILS_TRANSFORM_INCLUDED_ + +#include + + +namespace nbl +{ +namespace hlsl +{ +namespace transform +{ + + +// move to ieee754 header? +float3x3 mul_with_bounds_wo_gamma(out float3x3 error, in float3x3 a, in float3x3 b, in float b_relative_error) +{ + float3x3 retval; + for (int i=0; i<3; i++) + { + float3 tmp = a[0]*b[i][0]; + retval[i] = tmp; + error[i] = abs(tmp); + for (int j=1; j<3; j++) + { + tmp = a[j]*b[i][j]; + retval[i] += tmp; + error[i] += abs(tmp); + } + } + const float error_factor = 1.f+b_relative_error/numeric_limits::float_epsilon(2); + error *= error_factor; + return retval; +} +float3x3 mul_with_bounds(out float3x3 error, in float3x3 a, in float3x3 b, in float b_relative_error) +{ + float3x3 retval = mul_with_bounds_wo_gamma(error,a,b,b_relative_error); + error *= ieee754::gamma(2u); + return retval; +} + + +/* +float4 pseudoMul4x4with3x1(in float4x4 m, in float3 v) +{ + return m[0] * v.x + m[1] * v.y + m[2] * v.z + m[3]; +} +float3 pseudoMul3x4with3x1(in float4x3 m, in float3 v) +{ + return m[0] * v.x + m[1] * v.y + m[2] * v.z + m[3]; +} +float4x3 pseudoMul4x3with4x3(in float4x3 lhs, in float4x3 rhs) // TODO: change name to 3x4with3x4 +{ + float4x3 result; + for (int i = 0; i < 4; i++) + result[i] = lhs[0] * rhs[i][0] + lhs[1] * rhs[i][1] + lhs[2] * rhs[i][2]; + result[3] += lhs[3]; + return result; +} +float4 pseudoMul4x4with4x3(in float4 proj, in float4x3 tform) +{ + float4 result; + for (int i = 0; i < 4; i++) + result[i] = proj[0] * tform[i][0] + proj[1] * tform[i][1] + proj[2] * tform[i][2]; + result[3] += proj[3]; + return result; +} +*/ + + +// useful for fast computation of a Normal Matrix (you just need to remember to normalize the transformed normal because of the missing divide by the determinant) +float3x3 sub3x3TransposeCofactors_fn(in float3x3 sub3x3) +{ + return float3x3( + cross(sub3x3[1],sub3x3[2]), + cross(sub3x3[2],sub3x3[0]), + cross(sub3x3[0],sub3x3[1]) + ); +} +// returns a signflip mask +uint sub3x3TransposeCofactors_fn(in float3x3 sub3x3, out float3x3 sub3x3TransposeCofactors) +{ + sub3x3TransposeCofactors = sub3x3TransposeCofactors_fn(sub3x3); + return asuint(dot(sub3x3[0], sub3x3TransposeCofactors[0])) & 0x80000000u; +} + +// use this if you anticipate flipped/mirrored models +float3 fastNormalTransform(in uint signFlipMask, in float3x3 sub3x3TransposeCofactors, in float3 normal) +{ + float3 tmp = mul(sub3x3TransposeCofactors, normal); + const float tmpLenRcp = rsqrt(dot(tmp,tmp)); + return tmp * asfloat(asuint(tmpLenRcp)^signFlipMask); +} + +// +float4x3 pseudoInverse3x4(in float4x3 tform) +{ + const float3x3 sub3x3Inv = transpose(float3x3(tform[0], tform[1], tform[2])); + float4x3 retval; + retval[0] = sub3x3Inv[0]; + retval[1] = sub3x3Inv[1]; + retval[2] = sub3x3Inv[2]; + retval[3] = mul(-sub3x3Inv, tform[3]); + return retval; +} + + +} +} +} + +#endif \ No newline at end of file diff --git a/src/nbl/CMakeLists.txt b/src/nbl/CMakeLists.txt index 89f219f92c..ce0932025e 100755 --- a/src/nbl/CMakeLists.txt +++ b/src/nbl/CMakeLists.txt @@ -520,6 +520,7 @@ if(_NBL_COMPILE_WITH_GLI_) endif() # DXC + if(_NBL_ENABLE_DXC_COMPILE_TESTS_) add_dependencies(Nabla HLSL_NABLA_COMPILE_TEST) endif()