// This file has been automatically generated from resource file src/Shaders/PathtraceBase.fs static const char Shaders_PathtraceBase_fs[] = "#ifdef _MSC_VER\n" " #define PATH_TRACING // just for editing in MS VS\n" "\n" " #define in\n" " #define out\n" " #define inout\n" "\n" " typedef struct { float x; float y; } vec2;\n" " typedef struct { float x; float y; float z; } vec3;\n" " typedef struct { float x; float y; float z; float w; } vec4;\n" "#endif\n" "\n" "#ifdef PATH_TRACING\n" "\n" "///////////////////////////////////////////////////////////////////////////////////////\n" "// Specific data types\n" "\n" "//! Describes local space at the hit point (visualization space).\n" "struct SLocalSpace\n" "{\n" " //! Local X axis.\n" " vec3 AxisX;\n" "\n" " //! Local Y axis.\n" " vec3 AxisY;\n" "\n" " //! Local Z axis.\n" " vec3 AxisZ;\n" "};\n" "\n" "//! Describes material properties (BSDF).\n" "struct SBSDF\n" "{\n" " //! Weight of coat specular/glossy BRDF.\n" " vec4 Kc;\n" "\n" " //! Weight of base diffuse BRDF + base color texture index in W.\n" " vec4 Kd;\n" "\n" " //! Weight of base specular/glossy BRDF.\n" " vec4 Ks;\n" "\n" " //! Weight of base specular/glossy BTDF + metallic-roughness texture index in W.\n" " vec4 Kt;\n" "\n" " //! Fresnel coefficients of coat layer.\n" " vec3 FresnelCoat;\n" "\n" " //! Fresnel coefficients of base layer.\n" " vec3 FresnelBase;\n" "};\n" "\n" "///////////////////////////////////////////////////////////////////////////////////////\n" "// Support subroutines\n" "\n" "//=======================================================================\n" "// function : buildLocalSpace\n" "// purpose : Generates local space for the given normal\n" "//=======================================================================\n" "SLocalSpace buildLocalSpace (in vec3 theNormal)\n" "{\n" " vec3 anAxisX = vec3 (theNormal.z, 0.f, -theNormal.x);\n" " vec3 anAxisY = vec3 (0.f, -theNormal.z, theNormal.y);\n" "\n" " float aSqrLenX = dot (anAxisX, anAxisX);\n" " float aSqrLenY = dot (anAxisY, anAxisY);\n" "\n" " if (aSqrLenX > aSqrLenY)\n" " {\n" " anAxisX *= inversesqrt (aSqrLenX);\n" " anAxisY = cross (anAxisX, theNormal);\n" " }\n" " else\n" " {\n" " anAxisY *= inversesqrt (aSqrLenY);\n" " anAxisX = cross (anAxisY, theNormal);\n" " }\n" "\n" " return SLocalSpace (anAxisX, anAxisY, theNormal);\n" "}\n" "\n" "//=======================================================================\n" "// function : toLocalSpace\n" "// purpose : Transforms the vector to local space from world space\n" "//=======================================================================\n" "vec3 toLocalSpace (in vec3 theVector, in SLocalSpace theSpace)\n" "{\n" " return vec3 (dot (theVector, theSpace.AxisX),\n" " dot (theVector, theSpace.AxisY),\n" " dot (theVector, theSpace.AxisZ));\n" "}\n" "\n" "//=======================================================================\n" "// function : fromLocalSpace\n" "// purpose : Transforms the vector from local space to world space\n" "//=======================================================================\n" "vec3 fromLocalSpace (in vec3 theVector, in SLocalSpace theSpace)\n" "{\n" " return theVector.x * theSpace.AxisX +\n" " theVector.y * theSpace.AxisY +\n" " theVector.z * theSpace.AxisZ;\n" "}\n" "\n" "//=======================================================================\n" "// function : convolve\n" "// purpose : Performs a linear convolution of the vector components\n" "//=======================================================================\n" "float convolve (in vec3 theVector, in vec3 theFactor)\n" "{\n" " return dot (theVector, theFactor) * (1.f / max (theFactor.x + theFactor.y + theFactor.z, 1e-15f));\n" "}\n" "\n" "//=======================================================================\n" "// function : fresnelSchlick\n" "// purpose : Computes the Fresnel reflection formula using\n" "// Schlick's approximation.\n" "//=======================================================================\n" "vec3 fresnelSchlick (in float theCosI, in vec3 theSpecularColor)\n" "{\n" " return theSpecularColor + (UNIT - theSpecularColor) * pow (1.f - theCosI, 5.f);\n" "}\n" "\n" "//=======================================================================\n" "// function : fresnelDielectric\n" "// purpose : Computes the Fresnel reflection formula for dielectric in\n" "// case of circularly polarized light (Based on PBRT code).\n" "//=======================================================================\n" "float fresnelDielectric (in float theCosI,\n" " in float theCosT,\n" " in float theEtaI,\n" " in float theEtaT)\n" "{\n" " float aParl = (theEtaT * theCosI - theEtaI * theCosT) /\n" " (theEtaT * theCosI + theEtaI * theCosT);\n" "\n" " float aPerp = (theEtaI * theCosI - theEtaT * theCosT) /\n" " (theEtaI * theCosI + theEtaT * theCosT);\n" "\n" " return (aParl * aParl + aPerp * aPerp) * 0.5f;\n" "}\n" "\n" "#define ENVIRONMENT_IOR 1.f\n" "\n" "//=======================================================================\n" "// function : fresnelDielectric\n" "// purpose : Computes the Fresnel reflection formula for dielectric in\n" "// case of circularly polarized light (based on PBRT code)\n" "//=======================================================================\n" "float fresnelDielectric (in float theCosI, in float theIndex)\n" "{\n" " float aFresnel = 1.f;\n" "\n" " float anEtaI = theCosI > 0.f ? 1.f : theIndex;\n" " float anEtaT = theCosI > 0.f ? theIndex : 1.f;\n" "\n" " float aSinT2 = (anEtaI * anEtaI) / (anEtaT * anEtaT) * (1.f - theCosI * theCosI);\n" "\n" " if (aSinT2 < 1.f)\n" " {\n" " aFresnel = fresnelDielectric (abs (theCosI), sqrt (1.f - aSinT2), anEtaI, anEtaT);\n" " }\n" "\n" " return aFresnel;\n" "}\n" "\n" "//=======================================================================\n" "// function : fresnelConductor\n" "// purpose : Computes the Fresnel reflection formula for conductor in case\n" "// of circularly polarized light (based on PBRT source code)\n" "//=======================================================================\n" "float fresnelConductor (in float theCosI, in float theEta, in float theK)\n" "{\n" " float aTmp = 2.f * theEta * theCosI;\n" "\n" " float aTmp1 = theEta * theEta + theK * theK;\n" "\n" " float aSPerp = (aTmp1 - aTmp + theCosI * theCosI) /\n" " (aTmp1 + aTmp + theCosI * theCosI);\n" "\n" " float aTmp2 = aTmp1 * theCosI * theCosI;\n" "\n" " float aSParl = (aTmp2 - aTmp + 1.f) /\n" " (aTmp2 + aTmp + 1.f);\n" "\n" " return (aSPerp + aSParl) * 0.5f;\n" "}\n" "\n" "#define FRESNEL_SCHLICK -0.5f\n" "#define FRESNEL_CONSTANT -1.5f\n" "#define FRESNEL_CONDUCTOR -2.5f\n" "#define FRESNEL_DIELECTRIC -3.5f\n" "\n" "//=======================================================================\n" "// function : fresnelMedia\n" "// purpose : Computes the Fresnel reflection formula for general medium\n" "// in case of circularly polarized light.\n" "//=======================================================================\n" "vec3 fresnelMedia (in float theCosI, in vec3 theFresnel)\n" "{\n" " vec3 aFresnel;\n" "\n" " if (theFresnel.x > FRESNEL_SCHLICK)\n" " {\n" " aFresnel = fresnelSchlick (abs (theCosI), theFresnel);\n" " }\n" " else if (theFresnel.x > FRESNEL_CONSTANT)\n" " {\n" " aFresnel = vec3 (theFresnel.z);\n" " }\n" " else if (theFresnel.x > FRESNEL_CONDUCTOR)\n" " {\n" " aFresnel = vec3 (fresnelConductor (abs (theCosI), theFresnel.y, theFresnel.z));\n" " }\n" " else\n" " {\n" " aFresnel = vec3 (fresnelDielectric (theCosI, theFresnel.y));\n" " }\n" "\n" " return aFresnel;\n" "}\n" "\n" "//=======================================================================\n" "// function : transmitted\n" "// purpose : Computes transmitted direction in tangent space\n" "// (in case of TIR returned result is undefined!)\n" "//=======================================================================\n" "void transmitted (in float theIndex, in vec3 theIncident, out vec3 theTransmit)\n" "{\n" " // Compute relative index of refraction\n" " float anEta = (theIncident.z > 0.f) ? 1.f / theIndex : theIndex;\n" "\n" " // Handle total internal reflection (TIR)\n" " float aSinT2 = anEta * anEta * (1.f - theIncident.z * theIncident.z);\n" "\n" " // Compute direction of transmitted ray\n" " float aCosT = sqrt (1.f - min (aSinT2, 1.f)) * sign (-theIncident.z);\n" "\n" " theTransmit = normalize (vec3 (-anEta * theIncident.x,\n" " -anEta * theIncident.y,\n" " aCosT));\n" "}\n" "\n" "//////////////////////////////////////////////////////////////////////////////////////////////\n" "// Handlers and samplers for materials\n" "//////////////////////////////////////////////////////////////////////////////////////////////\n" "\n" "//=======================================================================\n" "// function : EvalLambertianReflection\n" "// purpose : Evaluates Lambertian BRDF, with cos(N, PSI)\n" "//=======================================================================\n" "float EvalLambertianReflection (in vec3 theWi, in vec3 theWo)\n" "{\n" " return (theWi.z <= 0.f || theWo.z <= 0.f) ? 0.f : theWi.z * (1.f / M_PI);\n" "}\n" "\n" "#define FLT_EPSILON 1.0e-5f\n" "\n" "//=======================================================================\n" "// function : SmithG1\n" "// purpose :\n" "//=======================================================================\n" "float SmithG1 (in vec3 theDirection, in vec3 theM, in float theRoughness)\n" "{\n" " float aResult = 0.f;\n" "\n" " if (dot (theDirection, theM) * theDirection.z > 0.f)\n" " {\n" " float aTanThetaM = sqrt (1.f - theDirection.z * theDirection.z) / theDirection.z;\n" "\n" " if (aTanThetaM == 0.f)\n" " {\n" " aResult = 1.f;\n" " }\n" " else\n" " {\n" " float aVal = 1.f / (theRoughness * aTanThetaM);\n" "\n" " // Use rational approximation to shadowing-masking function (from Mitsuba)\n" " aResult = (3.535f + 2.181f * aVal) / (1.f / aVal + 2.276f + 2.577f * aVal);\n" " }\n" " }\n" "\n" " return min (aResult, 1.f);\n" "}\n" "\n" "//=======================================================================\n" "// function : EvalBlinnReflection\n" "// purpose : Evaluates Blinn glossy BRDF, with cos(N, PSI)\n" "//=======================================================================\n" "vec3 EvalBlinnReflection (in vec3 theWi, in vec3 theWo, in vec3 theFresnel, in float theRoughness)\n" "{\n" " // calculate the reflection half-vec\n" " vec3 aH = normalize (theWi + theWo);\n" "\n" " // roughness value -> Blinn exponent\n" " float aPower = max (2.f / (theRoughness * theRoughness) - 2.f, 0.f);\n" "\n" " // calculate microfacet distribution\n" " float aD = (aPower + 2.f) * (1.f / M_2_PI) * pow (aH.z, aPower);\n" "\n" " // calculate shadow-masking function\n" " float aG = SmithG1 (theWo, aH, theRoughness) *\n" " SmithG1 (theWi, aH, theRoughness);\n" "\n" " // return total amount of reflection\n" " return (theWi.z <= 0.f || theWo.z <= 0.f) ? ZERO :\n" " aD * aG / (4.f * theWo.z) * fresnelMedia (dot (theWo, aH), theFresnel);\n" "}\n" "\n" "//=======================================================================\n" "// function : EvalBsdfLayered\n" "// purpose : Evaluates BSDF for specified material, with cos(N, PSI)\n" "//=======================================================================\n" "vec3 EvalBsdfLayered (in SBSDF theBSDF, in vec3 theWi, in vec3 theWo)\n" "{\n" "#ifdef TWO_SIDED_BXDF\n" " theWi.z *= sign (theWi.z);\n" " theWo.z *= sign (theWo.z);\n" "#endif\n" "\n" " vec3 aBxDF = theBSDF.Kd.rgb * EvalLambertianReflection (theWi, theWo);\n" "\n" " if (theBSDF.Ks.w > FLT_EPSILON)\n" " {\n" " aBxDF += theBSDF.Ks.rgb * EvalBlinnReflection (theWi, theWo, theBSDF.FresnelBase, theBSDF.Ks.w);\n" " }\n" "\n" " aBxDF *= UNIT - fresnelMedia (theWo.z, theBSDF.FresnelCoat);\n" "\n" " if (theBSDF.Kc.w > FLT_EPSILON)\n" " {\n" " aBxDF += theBSDF.Kc.rgb * EvalBlinnReflection (theWi, theWo, theBSDF.FresnelCoat, theBSDF.Kc.w);\n" " }\n" "\n" " return aBxDF;\n" "}\n" "\n" "//=======================================================================\n" "// function : SampleLambertianReflection\n" "// purpose : Samples Lambertian BRDF, W = BRDF * cos(N, PSI) / PDF(PSI)\n" "//=======================================================================\n" "vec3 SampleLambertianReflection (in vec3 theWo, out vec3 theWi, inout float thePDF)\n" "{\n" " float aKsi1 = RandFloat();\n" " float aKsi2 = RandFloat();\n" "\n" " theWi = vec3 (cos (M_2_PI * aKsi1),\n" " sin (M_2_PI * aKsi1),\n" " sqrt (1.f - aKsi2));\n" "\n" " theWi.xy *= sqrt (aKsi2);\n" "\n" "#ifdef TWO_SIDED_BXDF\n" " theWi.z *= sign (theWo.z);\n" "#endif\n" "\n" " thePDF *= theWi.z * (1.f / M_PI);\n" "\n" "#ifdef TWO_SIDED_BXDF\n" " return UNIT;\n" "#else\n" " return UNIT * step (0.f, theWo.z);\n" "#endif\n" "}\n" "\n" "//=======================================================================\n" "// function : SampleGlossyBlinnReflection\n" "// purpose : Samples Blinn BRDF, W = BRDF * cos(N, PSI) / PDF(PSI)\n" "// The BRDF is a product of three main terms, D, G, and F,\n" "// which is then divided by two cosine terms. Here we perform\n" "// importance sample the D part of the Blinn model; trying to\n" "// develop a sampling procedure that accounted for all of the\n" "// terms would be complex, and it is the D term that accounts\n" "// for most of the variation.\n" "//=======================================================================\n" "vec3 SampleGlossyBlinnReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel, in float theRoughness, inout float thePDF)\n" "{\n" " float aKsi1 = RandFloat();\n" " float aKsi2 = RandFloat();\n" "\n" " // roughness value --> Blinn exponent\n" " float aPower = max (2.f / (theRoughness * theRoughness) - 2.f, 0.f);\n" "\n" " // normal from microface distribution\n" " float aCosThetaM = pow (aKsi1, 1.f / (aPower + 2.f));\n" "\n" " vec3 aM = vec3 (cos (M_2_PI * aKsi2),\n" " sin (M_2_PI * aKsi2),\n" " aCosThetaM);\n" "\n" " aM.xy *= sqrt (1.f - aCosThetaM * aCosThetaM);\n" "\n" " // calculate PDF of sampled direction\n" " thePDF *= (aPower + 2.f) * (1.f / M_2_PI) * pow (aCosThetaM, aPower + 1.f);\n" "\n" "#ifdef TWO_SIDED_BXDF\n" " bool toFlip = theWo.z < 0.f;\n" "\n" " if (toFlip)\n" " theWo.z = -theWo.z;\n" "#endif\n" "\n" " float aCosDelta = dot (theWo, aM);\n" "\n" " // pick input based on half direction\n" " theWi = -theWo + 2.f * aCosDelta * aM;\n" "\n" " if (theWi.z <= 0.f || theWo.z <= 0.f)\n" " {\n" " return ZERO;\n" " }\n" "\n" " // Jacobian of half-direction mapping\n" " thePDF /= 4.f * aCosDelta;\n" "\n" " // compute shadow-masking coefficient\n" " float aG = SmithG1 (theWo, aM, theRoughness) *\n" " SmithG1 (theWi, aM, theRoughness);\n" "\n" "#ifdef TWO_SIDED_BXDF\n" " if (toFlip)\n" " theWi.z = -theWi.z;\n" "#endif\n" "\n" " return (aG * aCosDelta) / (theWo.z * aM.z) * fresnelMedia (aCosDelta, theFresnel);\n" "}\n" "\n" "//=======================================================================\n" "// function : BsdfPdfLayered\n" "// purpose : Calculates BSDF of sampling input knowing output\n" "//=======================================================================\n" "float BsdfPdfLayered (in SBSDF theBSDF, in vec3 theWo, in vec3 theWi, in vec3 theWeight)\n" "{\n" " float aPDF = 0.f; // PDF of sampling input direction\n" "\n" " // We choose whether the light is reflected or transmitted\n" " // by the coating layer according to the Fresnel equations\n" " vec3 aCoatF = fresnelMedia (theWo.z, theBSDF.FresnelCoat);\n" "\n" " // Coat BRDF is scaled by its Fresnel reflectance term. For\n" " // reasons of simplicity we scale base BxDFs only by coat's\n" " // Fresnel transmittance term\n" " vec3 aCoatT = UNIT - aCoatF;\n" "\n" " float aPc = dot (theBSDF.Kc.rgb * aCoatF, theWeight);\n" " float aPd = dot (theBSDF.Kd.rgb * aCoatT, theWeight);\n" " float aPs = dot (theBSDF.Ks.rgb * aCoatT, theWeight);\n" " float aPt = dot (theBSDF.Kt.rgb * aCoatT, theWeight);\n" "\n" " if (theWi.z * theWo.z > 0.f)\n" " {\n" " vec3 aH = normalize (theWi + theWo);\n" "\n" " aPDF = aPd * abs (theWi.z / M_PI);\n" "\n" " if (theBSDF.Kc.w > FLT_EPSILON)\n" " {\n" " float aPower = max (2.f / (theBSDF.Kc.w * theBSDF.Kc.w) - 2.f, 0.f); // roughness --> exponent\n" "\n" " aPDF += aPc * (aPower + 2.f) * (0.25f / M_2_PI) * pow (abs (aH.z), aPower + 1.f) / dot (theWi, aH);\n" " }\n" "\n" " if (theBSDF.Ks.w > FLT_EPSILON)\n" " {\n" " float aPower = max (2.f / (theBSDF.Ks.w * theBSDF.Ks.w) - 2.f, 0.f); // roughness --> exponent\n" "\n" " aPDF += aPs * (aPower + 2.f) * (0.25f / M_2_PI) * pow (abs (aH.z), aPower + 1.f) / dot (theWi, aH);\n" " }\n" " }\n" "\n" " return aPDF / (aPc + aPd + aPs + aPt);\n" "}\n" "\n" "//! Tool macro to handle sampling of particular BxDF\n" "#define PICK_BXDF_LAYER(p, k) aPDF = p / aTotalR; theWeight *= k / aPDF;\n" "\n" "//=======================================================================\n" "// function : SampleBsdfLayered\n" "// purpose : Samples specified composite material (BSDF)\n" "//=======================================================================\n" "float SampleBsdfLayered (in SBSDF theBSDF, in vec3 theWo, out vec3 theWi, inout vec3 theWeight, inout bool theInside)\n" "{\n" " // NOTE: OCCT uses two-layer material model. We have base diffuse, glossy, or transmissive\n" " // layer, covered by one glossy/specular coat. In the current model, the layers themselves\n" " // have no thickness; they can simply reflect light or transmits it to the layer under it.\n" " // We use actual BRDF model only for direct reflection by the coat layer. For transmission\n" " // through this layer, we approximate it as a flat specular surface.\n" "\n" " float aPDF = 0.f; // PDF of sampled direction\n" "\n" " // We choose whether the light is reflected or transmitted\n" " // by the coating layer according to the Fresnel equations\n" " vec3 aCoatF = fresnelMedia (theWo.z, theBSDF.FresnelCoat);\n" "\n" " // Coat BRDF is scaled by its Fresnel term. According to\n" " // Wilkie-Weidlich layered BSDF model, transmission term\n" " // for light passing through the coat at direction I and\n" " // leaving it in O is T = ( 1 - F (O) ) x ( 1 - F (I) ).\n" " // For reasons of simplicity, we discard the second term\n" " // and scale base BxDFs only by the first term.\n" " vec3 aCoatT = UNIT - aCoatF;\n" "\n" " float aPc = dot (theBSDF.Kc.rgb * aCoatF, theWeight);\n" " float aPd = dot (theBSDF.Kd.rgb * aCoatT, theWeight);\n" " float aPs = dot (theBSDF.Ks.rgb * aCoatT, theWeight);\n" " float aPt = dot (theBSDF.Kt.rgb * aCoatT, theWeight);\n" "\n" " // Calculate total reflection probability\n" " float aTotalR = (aPc + aPd) + (aPs + aPt);\n" "\n" " // Generate random variable to select BxDF\n" " float aKsi = aTotalR * RandFloat();\n" "\n" " if (aKsi < aPc) // REFLECTION FROM COAT\n" " {\n" " PICK_BXDF_LAYER (aPc, theBSDF.Kc.rgb)\n" "\n" " if (theBSDF.Kc.w < FLT_EPSILON)\n" " {\n" " theWeight *= aCoatF;\n" "\n" " theWi = vec3 (-theWo.x,\n" " -theWo.y,\n" " theWo.z);\n" " }\n" " else\n" " {\n" " theWeight *= SampleGlossyBlinnReflection (theWo, theWi, theBSDF.FresnelCoat, theBSDF.Kc.w, aPDF);\n" " }\n" "\n" " aPDF = mix (aPDF, MAXFLOAT, theBSDF.Kc.w < FLT_EPSILON);\n" " }\n" " else if (aKsi < aTotalR) // REFLECTION FROM BASE\n" " {\n" " theWeight *= aCoatT;\n" "\n" " if (aKsi < aPc + aPd) // diffuse BRDF\n" " {\n" " PICK_BXDF_LAYER (aPd, theBSDF.Kd.rgb)\n" "\n" " theWeight *= SampleLambertianReflection (theWo, theWi, aPDF);\n" " }\n" " else if (aKsi < (aPc + aPd) + aPs) // specular/glossy BRDF\n" " {\n" " PICK_BXDF_LAYER (aPs, theBSDF.Ks.rgb)\n" "\n" " if (theBSDF.Ks.w < FLT_EPSILON)\n" " {\n" " theWeight *= fresnelMedia (theWo.z, theBSDF.FresnelBase);\n" "\n" " theWi = vec3 (-theWo.x,\n" " -theWo.y,\n" " theWo.z);\n" " }\n" " else\n" " {\n" " theWeight *= SampleGlossyBlinnReflection (theWo, theWi, theBSDF.FresnelBase, theBSDF.Ks.w, aPDF);\n" " }\n" "\n" " aPDF = mix (aPDF, MAXFLOAT, theBSDF.Ks.w < FLT_EPSILON);\n" " }\n" " else // specular transmission\n" " {\n" " PICK_BXDF_LAYER (aPt, theBSDF.Kt.rgb)\n" "\n" " // refracted direction should exist if we are here\n" " transmitted (theBSDF.FresnelCoat.y, theWo, theWi);\n" "\n" " theInside = !theInside; aPDF = MAXFLOAT;\n" " }\n" " }\n" "\n" " // path termination for extra small weights\n" " theWeight = mix (ZERO, theWeight, step (FLT_EPSILON, aTotalR));\n" "\n" " return aPDF;\n" "}\n" "\n" "//////////////////////////////////////////////////////////////////////////////////////////////\n" "// Handlers and samplers for light sources\n" "//////////////////////////////////////////////////////////////////////////////////////////////\n" "\n" "// =======================================================================\n" "// function : Latlong\n" "// purpose : Converts world direction to environment texture coordinates\n" "// =======================================================================\n" "vec2 Latlong (in vec3 thePoint)\n" "{\n" " float aPsi = acos (-thePoint.z);\n" "\n" " float aPhi = atan (thePoint.y, thePoint.x) + M_PI;\n" "\n" " return vec2 (aPhi * 0.1591549f,\n" " aPsi * 0.3183098f);\n" "}\n" "\n" "//=======================================================================\n" "// function : SampleLight\n" "// purpose : General sampling function for directional and point lights\n" "//=======================================================================\n" "vec3 SampleLight (in vec3 theToLight, inout float theDistance, in bool isInfinite, in float theSmoothness, inout float thePDF)\n" "{\n" " SLocalSpace aSpace = buildLocalSpace (theToLight * (1.f / theDistance));\n" "\n" " // for point lights smoothness defines radius\n" " float aCosMax = isInfinite ? theSmoothness :\n" " inversesqrt (1.f + theSmoothness * theSmoothness / (theDistance * theDistance));\n" "\n" " float aKsi1 = RandFloat();\n" " float aKsi2 = RandFloat();\n" "\n" " float aTmp = 1.f - aKsi2 * (1.f - aCosMax);\n" "\n" " vec3 anInput = vec3 (cos (M_2_PI * aKsi1),\n" " sin (M_2_PI * aKsi1),\n" " aTmp);\n" "\n" " anInput.xy *= sqrt (1.f - aTmp * aTmp);\n" "\n" " thePDF = (aCosMax < 1.f) ? (thePDF / M_2_PI) / (1.f - aCosMax) : MAXFLOAT;\n" "\n" " return normalize (fromLocalSpace (anInput, aSpace));\n" "}\n" "\n" "//=======================================================================\n" "// function : HandlePointLight\n" "// purpose :\n" "//=======================================================================\n" "float HandlePointLight (in vec3 theInput, in vec3 theToLight, in float theRadius, in float theDistance, inout float thePDF)\n" "{\n" " float aCosMax = inversesqrt (1.f + theRadius * theRadius / (theDistance * theDistance));\n" "\n" " float aVisibility = step (aCosMax, dot (theInput, theToLight));\n" "\n" " thePDF *= step (-1.f, -aCosMax) * aVisibility * (1.f / M_2_PI) / (1.f - aCosMax);\n" "\n" " return aVisibility;\n" "}\n" "\n" "//=======================================================================\n" "// function : HandleDistantLight\n" "// purpose :\n" "//=======================================================================\n" "float HandleDistantLight (in vec3 theInput, in vec3 theToLight, in float theCosMax, inout float thePDF)\n" "{\n" " float aVisibility = step (theCosMax, dot (theInput, theToLight));\n" "\n" " thePDF *= step (-1.f, -theCosMax) * aVisibility * (1.f / M_2_PI) / (1.f - theCosMax);\n" "\n" " return aVisibility;\n" "}\n" "\n" "// =======================================================================\n" "// function: IntersectLight\n" "// purpose : Checks intersections with light sources\n" "// =======================================================================\n" "vec3 IntersectLight (in SRay theRay, in int theDepth, in float theHitDistance, out float thePDF)\n" "{\n" " vec3 aTotalRadiance = ZERO;\n" "\n" " thePDF = 0.f; // PDF of sampling light sources\n" "\n" " for (int aLightIdx = 0; aLightIdx < uLightCount; ++aLightIdx)\n" " {\n" " vec4 aLight = texelFetch (\n" " uRaytraceLightSrcTexture, LIGHT_POS (aLightIdx));\n" " vec4 aParam = texelFetch (\n" " uRaytraceLightSrcTexture, LIGHT_PWR (aLightIdx));\n" "\n" " // W component: 0 for infinite light and 1 for point light\n" " aLight.xyz -= mix (ZERO, theRay.Origin, aLight.w);\n" "\n" " float aPDF = 1.f / uLightCount;\n" "\n" " if (aLight.w != 0.f) // point light source\n" " {\n" " float aCenterDst = length (aLight.xyz);\n" "\n" " if (aCenterDst < theHitDistance)\n" " {\n" " float aVisibility = HandlePointLight (\n" " theRay.Direct, normalize (aLight.xyz), aParam.w /* radius */, aCenterDst, aPDF);\n" "\n" " if (aVisibility > 0.f)\n" " {\n" " theHitDistance = aCenterDst;\n" " aTotalRadiance = aParam.rgb;\n" "\n" " thePDF = aPDF;\n" " }\n" " }\n" " }\n" " else if (theHitDistance == MAXFLOAT) // directional light source\n" " {\n" " aTotalRadiance += aParam.rgb * HandleDistantLight (\n" " theRay.Direct, aLight.xyz, aParam.w /* angle cosine */, aPDF);\n" "\n" " thePDF += aPDF;\n" " }\n" " }\n" "\n" " if (thePDF == 0.f && theHitDistance == MAXFLOAT) // light source not found\n" " {\n" " if (theDepth + uSphereMapForBack == 0) // view ray and map is hidden\n" " {\n" " aTotalRadiance = BackgroundColor().rgb;\n" " }\n" " else\n" " {\n" " aTotalRadiance = FetchEnvironment (Latlong (theRay.Direct)).rgb;\n" " }\n" " #ifdef THE_SHIFT_sRGB\n" " aTotalRadiance = pow (aTotalRadiance, vec3 (2.f));\n" " #endif\n" " }\n" " \n" " return aTotalRadiance;\n" "}\n" "\n" "#define MIN_THROUGHPUT vec3 (1.0e-3f)\n" "#define MIN_CONTRIBUTION vec3 (1.0e-2f)\n" "\n" "#define MATERIAL_KC(index) (19 * index + 11)\n" "#define MATERIAL_KD(index) (19 * index + 12)\n" "#define MATERIAL_KS(index) (19 * index + 13)\n" "#define MATERIAL_KT(index) (19 * index + 14)\n" "#define MATERIAL_LE(index) (19 * index + 15)\n" "#define MATERIAL_FRESNEL_COAT(index) (19 * index + 16)\n" "#define MATERIAL_FRESNEL_BASE(index) (19 * index + 17)\n" "#define MATERIAL_ABSORPT_BASE(index) (19 * index + 18)\n" "\n" "//! Enables experimental Russian roulette sampling path termination.\n" "//! In most cases, it provides faster image convergence with minimal\n" "//! bias, so it is enabled by default.\n" "#define RUSSIAN_ROULETTE\n" "\n" "//! Frame step to increase number of bounces. This mode is used\n" "//! for interaction with the model, when path length is limited\n" "//! for the first samples, and gradually increasing when camera\n" "//! is stabilizing.\n" "#ifdef ADAPTIVE_SAMPLING\n" " #define FRAME_STEP 4\n" "#else\n" " #define FRAME_STEP 5\n" "#endif\n" "\n" "//=======================================================================\n" "// function : IsNotZero\n" "// purpose : Checks whether BSDF reflects direct light\n" "//=======================================================================\n" "bool IsNotZero (in SBSDF theBSDF, in vec3 theThroughput)\n" "{\n" " vec3 aGlossy = theBSDF.Kc.rgb * step (FLT_EPSILON, theBSDF.Kc.w) +\n" " theBSDF.Ks.rgb * step (FLT_EPSILON, theBSDF.Ks.w);\n" "\n" " return convolve (theBSDF.Kd.rgb + aGlossy, theThroughput) > FLT_EPSILON;\n" "}\n" "\n" "//=======================================================================\n" "// function : PathTrace\n" "// purpose : Calculates radiance along the given ray\n" "//=======================================================================\n" "vec4 PathTrace (in SRay theRay, in vec3 theInverse, in int theNbSamples)\n" "{\n" " float aRaytraceDepth = MAXFLOAT;\n" "\n" " vec3 aRadiance = ZERO;\n" " vec3 aThroughput = UNIT;\n" "\n" " int aTransfID = 0; // ID of object transformation\n" " bool aInMedium = false; // is the ray inside an object\n" "\n" " float aExpPDF = 1.f;\n" " float aImpPDF = 1.f;\n" "\n" " for (int aDepth = 0; aDepth < NB_BOUNCES; ++aDepth)\n" " {\n" " SIntersect aHit = SIntersect (MAXFLOAT, vec2 (ZERO), ZERO);\n" "\n" " ivec4 aTriIndex = SceneNearestHit (theRay, theInverse, aHit, aTransfID);\n" "\n" " // check implicit path\n" " vec3 aLe = IntersectLight (theRay, aDepth, aHit.Time, aExpPDF);\n" "\n" " if (any (greaterThan (aLe, ZERO)) || aTriIndex.x == -1)\n" " {\n" " float aMIS = (aDepth == 0 || aImpPDF == MAXFLOAT) ? 1.f :\n" " aImpPDF * aImpPDF / (aExpPDF * aExpPDF + aImpPDF * aImpPDF);\n" "\n" " aRadiance += aThroughput * aLe * aMIS; break; // terminate path\n" " }\n" "\n" " vec3 aInvTransf0 = texelFetch (uSceneTransformTexture, aTransfID + 0).xyz;\n" " vec3 aInvTransf1 = texelFetch (uSceneTransformTexture, aTransfID + 1).xyz;\n" " vec3 aInvTransf2 = texelFetch (uSceneTransformTexture, aTransfID + 2).xyz;\n" "\n" " // compute geometrical normal\n" " aHit.Normal = normalize (vec3 (dot (aInvTransf0, aHit.Normal),\n" " dot (aInvTransf1, aHit.Normal),\n" " dot (aInvTransf2, aHit.Normal)));\n" "\n" " theRay.Origin += theRay.Direct * aHit.Time; // get new intersection point\n" "\n" " // evaluate depth on first hit\n" " if (aDepth == 0)\n" " {\n" " vec4 aNDCPoint = uViewMat * vec4 (theRay.Origin, 1.f);\n" "\n" " float aPolygonOffset = PolygonOffset (aHit.Normal, theRay.Origin);\n" " aRaytraceDepth = (aNDCPoint.z / aNDCPoint.w + aPolygonOffset * POLYGON_OFFSET_SCALE) * 0.5f + 0.5f;\n" " }\n" "\n" " SBSDF aBSDF;\n" "\n" " // fetch BxDF weights\n" " aBSDF.Kc = texelFetch (uRaytraceMaterialTexture, MATERIAL_KC (aTriIndex.w));\n" " aBSDF.Kd = texelFetch (uRaytraceMaterialTexture, MATERIAL_KD (aTriIndex.w));\n" " aBSDF.Ks = texelFetch (uRaytraceMaterialTexture, MATERIAL_KS (aTriIndex.w));\n" " aBSDF.Kt = texelFetch (uRaytraceMaterialTexture, MATERIAL_KT (aTriIndex.w));\n" "\n" " // fetch Fresnel reflectance for both layers\n" " aBSDF.FresnelCoat = texelFetch (uRaytraceMaterialTexture, MATERIAL_FRESNEL_COAT (aTriIndex.w)).xyz;\n" " aBSDF.FresnelBase = texelFetch (uRaytraceMaterialTexture, MATERIAL_FRESNEL_BASE (aTriIndex.w)).xyz;\n" "\n" " vec4 anLE = texelFetch (uRaytraceMaterialTexture, MATERIAL_LE (aTriIndex.w));\n" "\n" " // compute smooth normal (in parallel with fetch)\n" " vec3 aNormal = SmoothNormal (aHit.UV, aTriIndex);\n" " aNormal = normalize (vec3 (dot (aInvTransf0, aNormal),\n" " dot (aInvTransf1, aNormal),\n" " dot (aInvTransf2, aNormal)));\n" "\n" " SLocalSpace aSpace = buildLocalSpace (aNormal);\n" "\n" "#ifdef USE_TEXTURES\n" " if (aBSDF.Kd.w >= 0.0 || aBSDF.Kt.w >= 0.0 || anLE.w >= 0.0)\n" " {\n" " vec4 aTexCoord = vec4 (SmoothUV (aHit.UV, aTriIndex), 0.f, 1.f);\n" " vec4 aTrsfRow1 = texelFetch (uRaytraceMaterialTexture, MATERIAL_TRS1 (aTriIndex.w));\n" " vec4 aTrsfRow2 = texelFetch (uRaytraceMaterialTexture, MATERIAL_TRS2 (aTriIndex.w));\n" " aTexCoord.st = vec2 (dot (aTrsfRow1, aTexCoord),\n" " dot (aTrsfRow2, aTexCoord));\n" "\n" " if (anLE.w >= 0.0)\n" " {\n" " anLE.rgb *= textureLod (sampler2D (uTextureSamplers[int (anLE.w)]), aTexCoord.st, 0.0).rgb;\n" " }\n" " if (aBSDF.Kt.w >= 0.0)\n" " {\n" " vec2 aTexMetRough = textureLod (sampler2D (uTextureSamplers[int (aBSDF.Kt.w)]), aTexCoord.st, 0.0).bg;\n" " float aPbrMetal = aTexMetRough.x;\n" " float aPbrRough2 = aTexMetRough.y * aTexMetRough.y;\n" " aBSDF.Ks.a *= aPbrRough2;\n" " // when using metal-roughness texture, global metalness of material (encoded in FresnelBase) is expected to be 1.0 so that Kd will be 0.0\n" " aBSDF.Kd.rgb = aBSDF.FresnelBase * (1.0 - aPbrMetal);\n" " aBSDF.FresnelBase *= aPbrMetal;\n" " }\n" " if (aBSDF.Kd.w >= 0.0)\n" " {\n" " vec4 aTexColor = textureLod (sampler2D (uTextureSamplers[int (aBSDF.Kd.w)]), aTexCoord.st, 0.0);\n" " vec3 aDiff = aTexColor.rgb * aTexColor.a;\n" " aBSDF.Kd.rgb *= aDiff;\n" " aBSDF.FresnelBase *= aDiff;\n" " if (aTexColor.a != 1.0)\n" " {\n" " // mix transparency BTDF with texture alpha-channel\n" " aBSDF.Ks.rgb *= aTexColor.a;\n" " aBSDF.Kt.rgb = (UNIT - aTexColor.aaa) + aTexColor.a * aBSDF.Kt.rgb;\n" " }\n" " }\n" " }\n" "#endif\n" "\n" " if (uLightCount > 0 && IsNotZero (aBSDF, aThroughput))\n" " {\n" " aExpPDF = 1.f / uLightCount;\n" "\n" " int aLightIdx = min (int (floor (RandFloat() * uLightCount)), uLightCount - 1);\n" "\n" " vec4 aLight = texelFetch (\n" " uRaytraceLightSrcTexture, LIGHT_POS (aLightIdx));\n" " vec4 aParam = texelFetch (\n" " uRaytraceLightSrcTexture, LIGHT_PWR (aLightIdx));\n" "\n" " // 'w' component is 0 for infinite light and 1 for point light\n" " aLight.xyz -= mix (ZERO, theRay.Origin, aLight.w);\n" "\n" " float aDistance = length (aLight.xyz);\n" "\n" " aLight.xyz = SampleLight (aLight.xyz, aDistance,\n" " aLight.w == 0.f /* is infinite */, aParam.w /* max cos or radius */, aExpPDF);\n" "\n" " aImpPDF = BsdfPdfLayered (aBSDF,\n" " toLocalSpace (-theRay.Direct, aSpace), toLocalSpace (aLight.xyz, aSpace), aThroughput);\n" "\n" " // MIS weight including division by explicit PDF\n" " float aMIS = (aExpPDF == MAXFLOAT) ? 1.f : aExpPDF / (aExpPDF * aExpPDF + aImpPDF * aImpPDF);\n" "\n" " vec3 aContrib = aMIS * aParam.rgb /* Le */ * EvalBsdfLayered (\n" " aBSDF, toLocalSpace (aLight.xyz, aSpace), toLocalSpace (-theRay.Direct, aSpace));\n" "\n" " if (any (greaterThan (aContrib, MIN_CONTRIBUTION))) // check if light source is important\n" " {\n" " SRay aShadow = SRay (theRay.Origin + aLight.xyz * uSceneEpsilon, aLight.xyz);\n" "\n" " aShadow.Origin += aHit.Normal * mix (\n" " -uSceneEpsilon, uSceneEpsilon, step (0.f, dot (aHit.Normal, aLight.xyz)));\n" "\n" " float aVisibility = SceneAnyHit (aShadow,\n" " InverseDirection (aLight.xyz), aLight.w == 0.f ? MAXFLOAT : aDistance);\n" "\n" " aRadiance += aVisibility * (aThroughput * aContrib);\n" " }\n" " }\n" "\n" " // account for self-emission\n" " aRadiance += aThroughput * anLE.rgb;\n" "\n" " if (aInMedium) // handle attenuation\n" " {\n" " vec4 aScattering = texelFetch (uRaytraceMaterialTexture, MATERIAL_ABSORPT_BASE (aTriIndex.w));\n" "\n" " aThroughput *= exp (-aHit.Time * aScattering.w * (UNIT - aScattering.rgb));\n" " }\n" "\n" " vec3 anInput = UNIT; // sampled input direction\n" "\n" " aImpPDF = SampleBsdfLayered (aBSDF,\n" " toLocalSpace (-theRay.Direct, aSpace), anInput, aThroughput, aInMedium);\n" "\n" " float aSurvive = float (any (greaterThan (aThroughput, MIN_THROUGHPUT)));\n" "\n" "#ifdef RUSSIAN_ROULETTE\n" " aSurvive = aDepth < 3 ? aSurvive : min (dot (LUMA, aThroughput), 0.95f);\n" "#endif\n" "\n" " // here, we additionally increase path length for non-diffuse bounces\n" " if (RandFloat() > aSurvive || all (lessThan (aThroughput, MIN_THROUGHPUT)) || aDepth >= theNbSamples / FRAME_STEP + step (1.f / M_PI, aImpPDF))\n" " {\n" " aDepth = INVALID_BOUNCES; // terminate path\n" " }\n" "\n" "#ifdef RUSSIAN_ROULETTE\n" " aThroughput /= aSurvive;\n" "#endif\n" "\n" " anInput = normalize (fromLocalSpace (anInput, aSpace));\n" "\n" " theRay = SRay (theRay.Origin + anInput * uSceneEpsilon +\n" " aHit.Normal * mix (-uSceneEpsilon, uSceneEpsilon, step (0.f, dot (aHit.Normal, anInput))), anInput);\n" "\n" " theInverse = InverseDirection (anInput);\n" " }\n" "\n" " gl_FragDepth = aRaytraceDepth;\n" "\n" " return vec4 (aRadiance, aRaytraceDepth);\n" "}\n" "\n" "#endif\n";