diff --git a/.gitattributes b/.gitattributes index db61c819ba..0faa8baa73 100644 --- a/.gitattributes +++ b/.gitattributes @@ -37,6 +37,9 @@ *.vrml eol=lf *.md eol=lf *.natvis eol=lf +*.fs eol=lf +*.vs eol=lf +*.glsl eol=lf FILES eol=lf PACKAGES eol=lf EXTERNLIB eol=lf diff --git a/samples/tcl/pathtrace_cube.tcl b/samples/tcl/pathtrace_cube.tcl index 264ed7796e..6cd321efdc 100644 --- a/samples/tcl/pathtrace_cube.tcl +++ b/samples/tcl/pathtrace_cube.tcl @@ -51,8 +51,8 @@ psphere s 0.2 vdisplay -noupdate s vlocation -noupdate s -setLocation 0.21 0.3 0.2 vsetmaterial -noupdate s glass -vbsdf s -absorpcolor 0.8 0.8 1.0 -vbsdf s -absorpcoeff 6 +vbsdf s -absorpColor 0.8 0.8 1.0 +vbsdf s -absorpCoeff 6 # setup first inner box box c 0.3 0.3 0.2 @@ -68,15 +68,15 @@ vdisplay -noupdate g vlocation -noupdate g -setLocation 0.7 0.25 0.2 vlocation -noupdate g -rotate 0 0 0 0 0 1 10 vsetmaterial -noupdate g glass -vbsdf g -absorpcolor 0.8 1.0 0.8 -vbsdf g -absorpcoeff 6 +vbsdf g -absorpColor 0.8 1.0 0.8 +vbsdf g -absorpCoeff 6 # setup second inner sphere psphere r 0.1 vdisplay -noupdate r vsetmaterial -noupdate r plastic -vbsdf r -kd 0.5 0.9 0.3 -ks 0.0 -kr 0.3 -n -vbsdf r -fresnel Constant 1.0 +vbsdf r -kd 0.5 0.9 0.3 -ks 0.3 -baseRoughness 0.0 -n +vbsdf r -baseFresnel Constant 1.0 vlocation r -setLocation 0.5 0.65 0.1 puts "Trying path tracing mode..." diff --git a/samples/tcl/pathtrace_materials.tcl b/samples/tcl/pathtrace_materials.tcl new file mode 100644 index 0000000000..8f7d10ce82 --- /dev/null +++ b/samples/tcl/pathtrace_materials.tcl @@ -0,0 +1,226 @@ +# Script demonstrating Global illumination materials +# using path tracing rendering engine in 3D view + +#Category: Visualization +#Title: Path tracing - Materials + +set aBallPath [locate_data_file occ/Ball.brep] + +pload MODELING VISUALIZATION + +# Setup 3D viewer +vclear +vinit name=View1 w=512 h=512 +vglinfo +vvbo 0 +vsetdispmode 1 + +# Restore exported shapes +restore $aBallPath Ball0 +restore $aBallPath Ball1 +restore $aBallPath Ball2 +restore $aBallPath Ball3 +restore $aBallPath Ball4 +restore $aBallPath Ball5 +restore $aBallPath Ball6 +restore $aBallPath Ball7 +restore $aBallPath Ball8 + +# Create chessboard-style floor +box tile 10 10 0.1 +eval compound [lrepeat 144 tile] tiles +explode tiles +for {set i 0} {$i < 12} {incr i} { + for {set j 1} {$j <= 12} {incr j} { + ttranslate tiles_[expr 12 * $i + $j] [expr $i * 10 - 90] [expr $j * 10 - 70] -0.15 + vdisplay -noupdate tiles_[expr 12 * $i + $j] + + vsetmaterial -noupdate tiles_[expr 12 * $i + $j] plaster + + if {($i + $j) % 2 == 0} { + vbsdf tiles_[expr 12 * $i + $j] -kd 0.85 + } else { + vbsdf tiles_[expr 12 * $i + $j] -kd 0.45 + } + } +} + +# Setup object 'Ball1' +vdisplay Ball1 +vsetmaterial Ball1 Brass +vbsdf Ball1 -Kc 0 0 0 +vbsdf Ball1 -Kd 0.272798 0.746262 0.104794 +vbsdf Ball1 -Ks 0.253738 0.253738 0.253738 +vbsdf Ball1 -Kt 0 0 0 +vbsdf Ball1 -baseRoughness 0.045 +vbsdf Ball1 -coatRoughness 0 +vbsdf Ball1 -Le 0 0 0 +vbsdf Ball1 -absorpColor 0 0 0 +vbsdf Ball1 -absorpCoeff 0 +vbsdf Ball1 -coatFresnel Constant 0 +vbsdf Ball1 -baseFresnel Schlick 0.58 0.42 0.2 +vlocation Ball1 -rotation 0 0 0 1 +vlocation Ball1 -location 10 0 0 + +# Setup object 'Ball2' +vdisplay Ball2 +vsetmaterial Ball2 Brass +vbsdf Ball2 -Kc 0 0 0 +vbsdf Ball2 -Kd 0.8 0.8 0.8 +vbsdf Ball2 -Ks 0 0 0 +vbsdf Ball2 -Kt 0 0 0 +vbsdf Ball2 -baseRoughness 0 +vbsdf Ball2 -coatRoughness 0 +vbsdf Ball2 -Le 2.02 0.171915 0.171915 +vbsdf Ball2 -absorpColor 0 0 0 +vbsdf Ball2 -absorpCoeff 0 +vbsdf Ball2 -coatFresnel Constant 0 +vbsdf Ball2 -baseFresnel Constant 1 +vlocation Ball2 -rotation 0 0 0 1 +vlocation Ball2 -location 10 40 0 + +# Setup object 'Ball3' +vdisplay Ball3 +vsetmaterial Ball3 Glass +vbsdf Ball3 -Kc 1 1 1 +vbsdf Ball3 -Kd 0 0 0 +vbsdf Ball3 -Ks 0 0 0 +vbsdf Ball3 -Kt 1 1 1 +vbsdf Ball3 -baseRoughness 0 +vbsdf Ball3 -coatRoughness 0 +vbsdf Ball3 -Le 0 0 0 +vbsdf Ball3 -absorpColor 0.75 0.95 0.9 +vbsdf Ball3 -absorpCoeff 0.05 +vbsdf Ball3 -coatFresnel Dielectric 1.62 +vbsdf Ball3 -baseFresnel Constant 1 +vlocation Ball3 -rotation 0 0 0 1 +vlocation Ball3 -location -30 -40 0 + +# Setup object 'Ball4' +vdisplay Ball4 +vsetmaterial Ball4 Brass +vbsdf Ball4 -Kc 0 0 0 +vbsdf Ball4 -Kd 0 0 0 +vbsdf Ball4 -Ks 0.985 0.985 0.985 +vbsdf Ball4 -Kt 0 0 0 +vbsdf Ball4 -baseRoughness 0 +vbsdf Ball4 -coatRoughness 0 +vbsdf Ball4 -Le 0 0 0 +vbsdf Ball4 -absorpColor 0 0 0 +vbsdf Ball4 -absorpCoeff 0 +vbsdf Ball4 -coatFresnel Constant 0 +vbsdf Ball4 -baseFresnel Schlick 0.58 0.42 0.2 +vlocation Ball4 -rotation 0 0 0 1 +vlocation Ball4 -location -70 -40 0 + +# Setup object 'Ball5' +vdisplay Ball5 +vsetmaterial Ball5 Glass +vbsdf Ball5 -Kc 1 1 1 +vbsdf Ball5 -Kd 0 0 0 +vbsdf Ball5 -Ks 0 0 0 +vbsdf Ball5 -Kt 1 1 1 +vbsdf Ball5 -baseRoughness 0 +vbsdf Ball5 -coatRoughness 0 +vbsdf Ball5 -Le 0 0 0 +vbsdf Ball5 -absorpColor 0 0.288061 0.825532 +vbsdf Ball5 -absorpCoeff 0.3 +vbsdf Ball5 -coatFresnel Dielectric 1.62 +vbsdf Ball5 -baseFresnel Constant 1 +vlocation Ball5 -rotation 0 0 0 1 +vlocation Ball5 -location -30 0 0 + +# Setup object 'Ball6' +vdisplay Ball6 +vsetmaterial Ball6 Brass +vbsdf Ball6 -Kc 1 1 1 +vbsdf Ball6 -Kd 0 0.716033 0.884507 +vbsdf Ball6 -Ks 0.115493 0.115493 0.115493 +vbsdf Ball6 -Kt 0 0 0 +vbsdf Ball6 -baseRoughness 0.045 +vbsdf Ball6 -coatRoughness 0 +vbsdf Ball6 -Le 0 0 0 +vbsdf Ball6 -absorpColor 0 0 0 +vbsdf Ball6 -absorpCoeff 0 +vbsdf Ball6 -coatFresnel Dielectric 1.5 +vbsdf Ball6 -baseFresnel Schlick 0.58 0.42 0.2 +vlocation Ball6 -rotation 0 0 0 1 +vlocation Ball6 -location -30 40 0 + +# Setup object 'Ball7' +vdisplay Ball7 +vsetmaterial Ball7 Brass +vbsdf Ball7 -Kc 1 1 1 +vbsdf Ball7 -Kd 1e-06 9.9999e-07 9.9999e-07 +vbsdf Ball7 -Ks 0.0479573 0.804998 0 +vbsdf Ball7 -Kt 0 0 0 +vbsdf Ball7 -baseRoughness 0.447 +vbsdf Ball7 -coatRoughness 0 +vbsdf Ball7 -Le 0 0 0 +vbsdf Ball7 -absorpColor 0 0 0 +vbsdf Ball7 -absorpCoeff 0 +vbsdf Ball7 -coatFresnel Dielectric 1.5 +vbsdf Ball7 -baseFresnel Schlick 0.58 0.42 0.2 +vlocation Ball7 -rotation 0 0 0 1 +vlocation Ball7 -location -70 0 0 + +# Setup object 'Ball8' +vdisplay Ball8 +vsetmaterial Ball8 Aluminium +vbsdf Ball8 -Kc 0 0 0 +vbsdf Ball8 -Kd 0 0 0 +vbsdf Ball8 -Ks 0.985 0.985 0.985 +vbsdf Ball8 -Kt 0 0 0 +vbsdf Ball8 -baseRoughness 0.026 +vbsdf Ball8 -coatRoughness 0 +vbsdf Ball8 -Le 0 0 0 +vbsdf Ball8 -absorpColor 0 0 0 +vbsdf Ball8 -absorpCoeff 0 +vbsdf Ball8 -coatFresnel Constant 0 +vbsdf Ball8 -baseFresnel Schlick 0.913183 0.921494 0.924524 +vlocation Ball8 -rotation 0 0 0 1 +vlocation Ball8 -location -70 40 0 + +# Setup object 'Ball0' +vdisplay Ball0 +vsetmaterial Ball0 Glass +vbsdf Ball0 -Kc 0 0 0 +vbsdf Ball0 -Kd 0.723404 0.166229 0.166229 +vbsdf Ball0 -Ks 0 0 0 +vbsdf Ball0 -Kt 0 0 0 +vbsdf Ball0 -baseRoughness 0 +vbsdf Ball0 -coatRoughness 0 +vbsdf Ball0 -Le 0 0 0 +vbsdf Ball0 -absorpColor 0 0 0 +vbsdf Ball0 -absorpCoeff 0 +vbsdf Ball0 -coatFresnel Constant 0 +vbsdf Ball0 -baseFresnel Constant 1 +vlocation Ball0 -rotation 0 0 0 1 +vlocation Ball0 -location 10 -40 0 + +# Restore view parameters +vcamera -perspective -fovy 25 +vcamera -distance 238.089 +vviewparams -proj 0.679219 -0.00724546 0.7339 +vviewparams -up -0.733931 -0.00311795 0.679217 +vviewparams -at -22.3025 0.0986351 3.30327 +vviewparams -eye 139.412 -1.62643 178.037 +vviewparams -size 170.508 + +# Restore light source parameters +vlight clear +vlight add directional direction -0.303949 -0.434084 -0.848048 smoothness 0.3 intensity 12 + +# Load environment map +vtextureenv on 1 + +puts "Trying path tracing mode..." +vrenderparams -ray -gi -rayDepth 10 + +# Start progressive refinement mode +#vprogressive + +puts "Make several path tracing iterations to refine the picture, please wait..." +vfps 512 +puts "Done. To improve the image further, or after view manipulations, give command:" +puts "vfps \[nb_iteratons\]" diff --git a/src/Graphic3d/Graphic3d_BSDF.cxx b/src/Graphic3d/Graphic3d_BSDF.cxx index 57840080a8..4421e9c2d5 100644 --- a/src/Graphic3d/Graphic3d_BSDF.cxx +++ b/src/Graphic3d/Graphic3d_BSDF.cxx @@ -27,7 +27,7 @@ Graphic3d_Vec4 Graphic3d_Fresnel::Serialize() const if (myFresnelType != Graphic3d_FM_SCHLICK) { - aData.x() = -static_cast (myFresnelType); + aData.x() = -static_cast (myFresnelType); } return aData; @@ -37,8 +37,8 @@ Graphic3d_Vec4 Graphic3d_Fresnel::Serialize() const // function : fresnelNormal // purpose : // ======================================================================= -inline Standard_ShortReal fresnelNormal (Standard_ShortReal theN, - Standard_ShortReal theK) +inline float fresnelNormal (float theN, + float theK) { return ((theN - 1.f) * (theN - 1.f) + theK * theK) / ((theN + 1.f) * (theN + 1.f) + theK * theK); @@ -51,10 +51,37 @@ inline Standard_ShortReal fresnelNormal (Standard_ShortReal theN, Graphic3d_Fresnel Graphic3d_Fresnel::CreateConductor (const Graphic3d_Vec3& theRefractionIndex, const Graphic3d_Vec3& theAbsorptionIndex) { - return Graphic3d_Fresnel (Graphic3d_FM_SCHLICK, - Graphic3d_Vec3 (fresnelNormal (theRefractionIndex.x(), theAbsorptionIndex.x()), - fresnelNormal (theRefractionIndex.y(), theAbsorptionIndex.y()), - fresnelNormal (theRefractionIndex.z(), theAbsorptionIndex.z()))); + const Graphic3d_Vec3 aFresnel (fresnelNormal (theRefractionIndex.x(), theAbsorptionIndex.x()), + fresnelNormal (theRefractionIndex.y(), theAbsorptionIndex.y()), + fresnelNormal (theRefractionIndex.z(), theAbsorptionIndex.z())); + + return Graphic3d_Fresnel (Graphic3d_FM_SCHLICK, aFresnel); +} + +// ======================================================================= +// function : Graphic3d_BSDF +// purpose : +// ======================================================================= +Graphic3d_BSDF::Graphic3d_BSDF() +{ + FresnelCoat = Graphic3d_Fresnel::CreateConstant (0.f); + FresnelBase = Graphic3d_Fresnel::CreateConstant (1.f); +} + +// ======================================================================= +// function : operator== +// purpose : +// ======================================================================= +bool Graphic3d_BSDF::operator== (const Graphic3d_BSDF& theOther) const +{ + return Kc == theOther.Kc + && Kd == theOther.Kd + && Kt == theOther.Kt + && Ks == theOther.Ks + && Le == theOther.Le + && Absorption == theOther.Absorption + && FresnelCoat == theOther.FresnelCoat + && FresnelBase == theOther.FresnelBase; } // ======================================================================= @@ -63,16 +90,21 @@ Graphic3d_Fresnel Graphic3d_Fresnel::CreateConductor (const Graphic3d_Vec3& theR // ======================================================================= void Graphic3d_BSDF::Normalize() { - Standard_ShortReal aMax = std::max (Kd.x() + Ks.x() + Kr.x() + Kt.x(), - std::max (Kd.y() + Ks.y() + Kr.y() + Kt.y(), - Kd.z() + Ks.z() + Kr.z() + Kt.z())); + float aMax = 0.f; + + for (int aChannelID = 0; aChannelID < 3; ++aChannelID) + { + aMax = std::max (aMax, Kd[aChannelID] + Ks[aChannelID] + Kt[aChannelID]); + } if (aMax > 1.f) { - Kd /= aMax; - Ks /= aMax; - Kr /= aMax; - Ks /= aMax; + for (int aChannelID = 0; aChannelID < 3; ++aChannelID) + { + Kd[aChannelID] /= aMax; + Ks[aChannelID] /= aMax; + Kt[aChannelID] /= aMax; + } } } @@ -93,26 +125,15 @@ Graphic3d_BSDF Graphic3d_BSDF::CreateDiffuse (const Graphic3d_Vec3& theWeight) // function : CreateMetallic // purpose : // ======================================================================= -Graphic3d_BSDF Graphic3d_BSDF::CreateMetallic (const Graphic3d_Vec3& theWeight, - const Graphic3d_Fresnel& theFresnel, - const Standard_ShortReal theRoughness) +Graphic3d_BSDF Graphic3d_BSDF::CreateMetallic (const Graphic3d_Vec3& theWeight, const Graphic3d_Fresnel& theFresnel, const float theRoughness) { Graphic3d_BSDF aBSDF; - aBSDF.Roughness = theRoughness; + aBSDF.FresnelBase = theFresnel; // Selecting between specular and glossy // BRDF depending on the given roughness - if (aBSDF.Roughness > 0.f) - { - aBSDF.Ks = theWeight; - } - else - { - aBSDF.Kr = theWeight; - } - - aBSDF.Fresnel = theFresnel; + aBSDF.Ks = Graphic3d_Vec4 (theWeight, theRoughness); return aBSDF; } @@ -121,18 +142,25 @@ Graphic3d_BSDF Graphic3d_BSDF::CreateMetallic (const Graphic3d_Vec3& theWeigh // function : CreateTransparent // purpose : // ======================================================================= -Graphic3d_BSDF Graphic3d_BSDF::CreateTransparent (const Graphic3d_Vec3& theWeight, - const Graphic3d_Vec3& theAbsorptionColor, - const Standard_ShortReal theAbsorptionCoeff) +Graphic3d_BSDF Graphic3d_BSDF::CreateTransparent (const Graphic3d_Vec3& theWeight, + const Graphic3d_Vec3& theAbsorptionColor, + const float theAbsorptionCoeff) { Graphic3d_BSDF aBSDF; + // Create Fresnel parameters for the coat layer; + // set it to 0 value to simulate ideal refractor + aBSDF.FresnelCoat = Graphic3d_Fresnel::CreateConstant (0.f); + aBSDF.Kt = theWeight; - aBSDF.AbsorptionColor = theAbsorptionColor; - aBSDF.AbsorptionCoeff = theAbsorptionCoeff; + // Link reflection and transmission coefficients + aBSDF.Kc.r() = aBSDF.Kt.r(); + aBSDF.Kc.g() = aBSDF.Kt.g(); + aBSDF.Kc.b() = aBSDF.Kt.b(); - aBSDF.Fresnel = Graphic3d_Fresnel::CreateConstant (0.f); + aBSDF.Absorption = Graphic3d_Vec4 (theAbsorptionColor, + theAbsorptionCoeff); return aBSDF; } @@ -141,19 +169,24 @@ Graphic3d_BSDF Graphic3d_BSDF::CreateTransparent (const Graphic3d_Vec3& theWe // function : CreateGlass // purpose : // ======================================================================= -Graphic3d_BSDF Graphic3d_BSDF::CreateGlass (const Graphic3d_Vec3& theWeight, - const Graphic3d_Vec3& theAbsorptionColor, - const Standard_ShortReal theAbsorptionCoeff, - const Standard_ShortReal theRefractionIndex) +Graphic3d_BSDF Graphic3d_BSDF::CreateGlass (const Graphic3d_Vec3& theWeight, + const Graphic3d_Vec3& theAbsorptionColor, + const float theAbsorptionCoeff, + const float theRefractionIndex) { Graphic3d_BSDF aBSDF; + // Create Fresnel parameters for the coat layer + aBSDF.FresnelCoat = Graphic3d_Fresnel::CreateDielectric (theRefractionIndex); + aBSDF.Kt = theWeight; - aBSDF.AbsorptionColor = theAbsorptionColor; - aBSDF.AbsorptionCoeff = theAbsorptionCoeff; + aBSDF.Kc.r() = aBSDF.Kt.r(); + aBSDF.Kc.g() = aBSDF.Kt.g(); + aBSDF.Kc.b() = aBSDF.Kt.b(); - aBSDF.Fresnel = Graphic3d_Fresnel::CreateDielectric (theRefractionIndex); + aBSDF.Absorption = Graphic3d_Vec4 (theAbsorptionColor, + theAbsorptionCoeff); return aBSDF; } \ No newline at end of file diff --git a/src/Graphic3d/Graphic3d_BSDF.hxx b/src/Graphic3d/Graphic3d_BSDF.hxx index 74e812f9a0..7919089dd9 100644 --- a/src/Graphic3d/Graphic3d_BSDF.hxx +++ b/src/Graphic3d/Graphic3d_BSDF.hxx @@ -34,8 +34,7 @@ class Graphic3d_Fresnel public: //! Creates uninitialized Fresnel factor. - Graphic3d_Fresnel() - : myFresnelType (Graphic3d_FM_CONSTANT) + Graphic3d_Fresnel() : myFresnelType (Graphic3d_FM_CONSTANT) { // ideal specular reflector myFresnelData = Graphic3d_Vec3 (0.f, 1.f, 0.f); @@ -111,36 +110,39 @@ private: //! for physically-based rendering (in path tracing engine). BSDF is represented as //! weighted mixture of basic BRDFs/BTDFs (Bidirectional Reflectance (Transmittance) //! Distribution Functions). +//! +//! NOTE: OCCT uses two-layer material model. We have base diffuse, glossy, or transmissive +//! layer, covered by one glossy/specular coat. In the current model, the layers themselves +//! have no thickness; they can simply reflect light or transmits it to the layer under it. +//! We use actual BRDF model only for direct reflection by the coat layer. For transmission +//! through this layer, we approximate it as a flat specular surface. class Graphic3d_BSDF { public: - //! Weight of the Lambertian BRDF. + //! Weight of coat specular/glossy BRDF. + Graphic3d_Vec4 Kc; + + //! Weight of base diffuse BRDF. Graphic3d_Vec3 Kd; - //! Weight of the reflection BRDF. - Graphic3d_Vec3 Kr; + //! Weight of base specular/glossy BRDF. + Graphic3d_Vec4 Ks; - //! Weight of the transmission BTDF. + //! Weight of base specular/glossy BTDF. Graphic3d_Vec3 Kt; - //! Weight of the Blinn's glossy BRDF. - Graphic3d_Vec3 Ks; - - //! Self-emitted radiance. + //! Radiance emitted by the surface. Graphic3d_Vec3 Le; - //! Parameters of Fresnel reflectance. - Graphic3d_Fresnel Fresnel; + //! Volume scattering color/density. + Graphic3d_Vec4 Absorption; - //! Roughness (exponent) of Blinn's BRDF. - Standard_ShortReal Roughness; + //! Parameters of Fresnel reflectance of coat layer. + Graphic3d_Fresnel FresnelCoat; - //! Absorption color of transparent media. - Graphic3d_Vec3 AbsorptionColor; - - //! Absorption intensity of transparent media. - Standard_ShortReal AbsorptionCoeff; + //! Parameters of Fresnel reflectance of base layer. + Graphic3d_Fresnel FresnelBase; public: @@ -170,39 +172,13 @@ public: public: //! Creates uninitialized BSDF. - Graphic3d_BSDF() : Roughness (1.f), AbsorptionCoeff (0.f) - { - Fresnel = Graphic3d_Fresnel::CreateConstant (1.f); - } + Standard_EXPORT Graphic3d_BSDF(); //! Normalizes BSDF components. Standard_EXPORT void Normalize(); - //! Performs mixing of two BSDFs. - Graphic3d_BSDF& operator+ (const Graphic3d_BSDF& theOther) - { - Kd += theOther.Kd; - Kr += theOther.Kr; - Kt += theOther.Kt; - Ks += theOther.Ks; - Le += theOther.Le; - - return *this; - } - //! Performs comparison of two BSDFs. - bool operator== (const Graphic3d_BSDF& theOther) const - { - return Kd == theOther.Kd - && Kr == theOther.Kr - && Kt == theOther.Kt - && Ks == theOther.Ks - && Le == theOther.Le - && Fresnel == theOther.Fresnel - && Roughness == theOther.Roughness - && AbsorptionCoeff == theOther.AbsorptionCoeff - && AbsorptionColor == theOther.AbsorptionColor; - } + Standard_EXPORT bool operator== (const Graphic3d_BSDF& theOther) const; }; diff --git a/src/Graphic3d/Graphic3d_MaterialAspect.cxx b/src/Graphic3d/Graphic3d_MaterialAspect.cxx index 2c4edbe05e..3b3f6db5a9 100644 --- a/src/Graphic3d/Graphic3d_MaterialAspect.cxx +++ b/src/Graphic3d/Graphic3d_MaterialAspect.cxx @@ -104,10 +104,9 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin ColorCoef[Graphic3d_TOR_DIFFUSE] = 0.24f; ColorCoef[Graphic3d_TOR_SPECULAR] = 0.06f; - BSDF.Kd = (Graphic3d_Vec3 )Colors[Graphic3d_TOR_DIFFUSE]; - BSDF.Ks = Graphic3d_Vec3 (0.00784314f, 0.00784314f, 0.00784314f); + BSDF.Kd = static_cast (Colors[Graphic3d_TOR_DIFFUSE]); + BSDF.Ks = Graphic3d_Vec4 (0.00784314f, 0.00784314f, 0.00784314f, 0.25f); BSDF.Normalize(); - BSDF.Roughness = 0.25f; break; case Graphic3d_NOM_SHINY_PLASTIC: Shininess = 1.00f; @@ -115,10 +114,9 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin ColorCoef[Graphic3d_TOR_DIFFUSE] = 0.50f; ColorCoef[Graphic3d_TOR_SPECULAR] = 1.00f; - BSDF.Kd = (Graphic3d_Vec3 )Colors[Graphic3d_TOR_DIFFUSE]; - BSDF.Ks = Graphic3d_Vec3 (0.145f, 0.145f, 0.145f); + BSDF.Kd = static_cast (Colors[Graphic3d_TOR_DIFFUSE]); + BSDF.Ks = Graphic3d_Vec4 (0.145f, 0.145f, 0.145f, 0.17f); BSDF.Normalize(); - BSDF.Roughness = 0.17f; break; case Graphic3d_NOM_SATIN: Shininess = 0.09375f; @@ -127,8 +125,7 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin ColorCoef[Graphic3d_TOR_SPECULAR] = 0.44f; BSDF.Kd = Graphic3d_Vec3 (0.2f); - BSDF.Ks = Graphic3d_Vec3 (0.6f); - BSDF.Roughness = 0.6f; + BSDF.Ks = Graphic3d_Vec4 (0.6f); break; case Graphic3d_NOM_NEON_GNC: @@ -141,9 +138,9 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin ReflActivity[Graphic3d_TOR_EMISSION] = Standard_True; BSDF.Kd = Graphic3d_Vec3 (0.0f); - BSDF.Kr = Graphic3d_Vec3 (0.5f); - BSDF.Le = (Graphic3d_Vec3 )Colors[Graphic3d_TOR_DIFFUSE]; - BSDF.Fresnel = Graphic3d_Fresnel::CreateDielectric (1.5f); + BSDF.Ks = Graphic3d_Vec4 (0.5f, 0.5f, 0.5f, 0.f); + BSDF.Le = static_cast (Colors[Graphic3d_TOR_DIFFUSE]); + BSDF.FresnelBase = Graphic3d_Fresnel::CreateDielectric (1.5f); break; case Graphic3d_NOM_METALIZED: Shininess = 0.13f; @@ -289,7 +286,7 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin Colors[Graphic3d_TOR_SPECULAR] = Quantity_Color (Graphic3d_Vec3 (0.98f, 1.0f, 0.60f)); BSDF.Kd = Graphic3d_Vec3 (0.243137f, 0.243137f, 0.243137f); - BSDF.Ks = Graphic3d_Vec3 (0.00392157f, 0.00392157f, 0.00392157f); + BSDF.Ks = Graphic3d_Vec4 (0.00392157f, 0.00392157f, 0.00392157f, 0.5f); break; // Ascending Compatibility of physical materials. Takes the same definition as in the next constructor. New materials @@ -341,9 +338,9 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin Colors[Graphic3d_TOR_EMISSION] = Quantity_Color (Graphic3d_Vec3 (0.0f, 1.0f, 0.46f)); BSDF.Kd = Graphic3d_Vec3 (0.0f); - BSDF.Kr = Graphic3d_Vec3 (0.5f); + BSDF.Ks = Graphic3d_Vec4 (0.5f, 0.5f, 0.5f, 0.f); BSDF.Le = Graphic3d_Vec3 (0.0f, 1.0f, 0.46f); - BSDF.Fresnel = Graphic3d_Fresnel::CreateDielectric (1.5f); + BSDF.FresnelBase = Graphic3d_Fresnel::CreateDielectric (1.5f); break; case Graphic3d_NOM_OBSIDIAN: MaterialType = Graphic3d_MATERIAL_PHYSIC; @@ -358,8 +355,7 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin Colors[Graphic3d_TOR_SPECULAR] = Quantity_Color (Graphic3d_Vec3 (0.333f, 0.329f, 0.346f)); BSDF.Kd = Graphic3d_Vec3 (0.023f, 0.f, 0.023f); - BSDF.Ks = Graphic3d_Vec3 (0.0156863f, 0.0156863f, 0.0156863f); - BSDF.Roughness = 0.1f; + BSDF.Ks = Graphic3d_Vec4 (0.0156863f, 0.0156863f, 0.0156863f, 0.1f); break; case Graphic3d_NOM_JADE: MaterialType = Graphic3d_MATERIAL_PHYSIC; @@ -373,10 +369,9 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin Colors[Graphic3d_TOR_DIFFUSE] = Quantity_Color (Graphic3d_Vec3 (0.540f, 0.890f, 0.630f)); Colors[Graphic3d_TOR_SPECULAR] = Quantity_Color (Graphic3d_Vec3 (0.316f, 0.316f, 0.316f)); - BSDF.Fresnel = Graphic3d_Fresnel::CreateDielectric (1.5f); + BSDF.FresnelBase = Graphic3d_Fresnel::CreateDielectric (1.5f); BSDF.Kd = Graphic3d_Vec3 (0.208658f, 0.415686f, 0.218401f); - BSDF.Ks = Graphic3d_Vec3 (0.611765f, 0.611765f, 0.611765f); - BSDF.Roughness = 0.06f; + BSDF.Ks = Graphic3d_Vec4 (0.611765f, 0.611765f, 0.611765f, 0.06f); break; case Graphic3d_NOM_CHARCOAL: MaterialType = Graphic3d_MATERIAL_PHYSIC; @@ -391,8 +386,7 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin Colors[Graphic3d_TOR_SPECULAR] = Quantity_Color (Graphic3d_Vec3 (0.000f, 0.000f, 0.000f)); BSDF.Kd = Graphic3d_Vec3 (0.02f, 0.02f, 0.02f); - BSDF.Ks = Graphic3d_Vec3 (0.1f, 0.1f, 0.1f); - BSDF.Roughness = 0.3f; + BSDF.Ks = Graphic3d_Vec4 (0.1f, 0.1f, 0.1f, 0.3f); break; case Graphic3d_NOM_WATER: MaterialType = Graphic3d_MATERIAL_PHYSIC; @@ -460,7 +454,7 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin BSDF.Kd = Graphic3d_Vec3 (0.1f); BSDF.Kt = Graphic3d_Vec3 (0.9f); - BSDF.Fresnel = Graphic3d_Fresnel::CreateConstant (0.0f); + BSDF.FresnelBase = Graphic3d_Fresnel::CreateConstant (0.0f); TransparencyCoef = 0.80f; Colors[Graphic3d_TOR_AMBIENT] = Quantity_Color (Graphic3d_Vec3 (0.550f, 0.550f, 0.550f)); diff --git a/src/OpenGl/OpenGl_SceneGeometry.hxx b/src/OpenGl/OpenGl_SceneGeometry.hxx index 17ff432f44..427ed303d5 100755 --- a/src/OpenGl/OpenGl_SceneGeometry.hxx +++ b/src/OpenGl/OpenGl_SceneGeometry.hxx @@ -74,25 +74,28 @@ public: //! Physically-based material properties (used in path tracing engine). struct Physical { - //! Weight of the diffuse BRDF. + //! Weight of coat specular/glossy BRDF. + BVH_Vec4f Kc; + + //! Weight of base diffuse BRDF. BVH_Vec4f Kd; - //! Weight of the reflection BRDF. - BVH_Vec4f Kr; - - //! Weight of the transmission BTDF. - BVH_Vec4f Kt; - - //! Weight of the Blinn's glossy BRDF. + //! Weight of base specular/glossy BRDF. BVH_Vec4f Ks; - //! Self-emitted radiance. + //! Weight of base specular/glossy BTDF. + BVH_Vec4f Kt; + + //! Radiance emitted by the surface. BVH_Vec4f Le; - //! Fresnel coefficients. - BVH_Vec4f Fresnel; + //! Fresnel coefficients of coat layer. + BVH_Vec4f FresnelCoat; - //! Absorption color for the transmission BSDF. + //! Fresnel coefficients of base layer. + BVH_Vec4f FresnelBase; + + //! Absorption color/intensity. BVH_Vec4f Absorption; } BSDF; diff --git a/src/OpenGl/OpenGl_View_Raytrace.cxx b/src/OpenGl/OpenGl_View_Raytrace.cxx index c078c546d5..793d4acf15 100644 --- a/src/OpenGl/OpenGl_View_Raytrace.cxx +++ b/src/OpenGl/OpenGl_View_Raytrace.cxx @@ -407,16 +407,16 @@ OpenGl_RaytraceMaterial OpenGl_View::convertMaterial (const OpenGl_AspectFace* // Serialize physically-based material properties const Graphic3d_BSDF& aBSDF = aSrcMat.BSDF(); - theMaterial.BSDF.Le = BVH_Vec4f (aBSDF.Le, 0.f); - theMaterial.BSDF.Kd = BVH_Vec4f (aBSDF.Kd, -1.f /* no tex */); - theMaterial.BSDF.Kr = BVH_Vec4f (aBSDF.Kr, 0.f); - theMaterial.BSDF.Kt = BVH_Vec4f (aBSDF.Kt, 0.f); - theMaterial.BSDF.Ks = BVH_Vec4f (aBSDF.Ks, aBSDF.Roughness); + theMaterial.BSDF.Kc = aBSDF.Kc; + theMaterial.BSDF.Ks = aBSDF.Ks; + theMaterial.BSDF.Kd = BVH_Vec4f (aBSDF.Kd, -1.f); // no texture + theMaterial.BSDF.Kt = BVH_Vec4f (aBSDF.Kt, 0.f); + theMaterial.BSDF.Le = BVH_Vec4f (aBSDF.Le, 0.f); - theMaterial.BSDF.Fresnel = aBSDF.Fresnel.Serialize(); + theMaterial.BSDF.Absorption = aBSDF.Absorption; - theMaterial.BSDF.Absorption = BVH_Vec4f (aBSDF.AbsorptionColor, - aBSDF.AbsorptionCoeff); + theMaterial.BSDF.FresnelCoat = aBSDF.FresnelCoat.Serialize (); + theMaterial.BSDF.FresnelBase = aBSDF.FresnelBase.Serialize (); // Handle material textures if (theAspect->Aspect()->ToMapTexture()) @@ -2291,7 +2291,7 @@ Standard_Boolean OpenGl_View::uploadRaytraceData (const Handle(OpenGl_Context)& if (myRaytraceGeometry.Materials.size() != 0) { aResult &= myRaytraceMaterialTexture->Init (theGlContext, 4, - GLsizei (myRaytraceGeometry.Materials.size() * 18), myRaytraceGeometry.Materials.front().Packed()); + GLsizei (myRaytraceGeometry.Materials.size() * 19), myRaytraceGeometry.Materials.front().Packed()); if (!aResult) { diff --git a/src/Shaders/PathtraceBase.fs b/src/Shaders/PathtraceBase.fs index 7088826599..8dfe1afcb3 100644 --- a/src/Shaders/PathtraceBase.fs +++ b/src/Shaders/PathtraceBase.fs @@ -29,25 +29,25 @@ struct SLocalSpace }; //! Describes material properties (BSDF). -struct SMaterial +struct SBSDF { - //! Weight of the Lambertian BRDF. + //! Weight of coat specular/glossy BRDF. + vec4 Kc; + + //! Weight of base diffuse BRDF. vec4 Kd; - //! Weight of the reflection BRDF. - vec3 Kr; - - //! Weight of the transmission BTDF. - vec3 Kt; - - //! Weight of the Blinn BRDF (and roughness). + //! Weight of base specular/glossy BRDF. vec4 Ks; - //! Fresnel coefficients. - vec3 Fresnel; + //! Weight of base specular/glossy BTDF. + vec3 Kt; - //! Absorption color and intensity of the media. - vec4 Absorption; + //! Fresnel coefficients of coat layer. + vec3 FresnelCoat; + + //! Fresnel coefficients of base layer. + vec3 FresnelBase; }; /////////////////////////////////////////////////////////////////////////////////////// @@ -148,19 +148,19 @@ float fresnelDielectric (in float theCosI, //======================================================================= float fresnelDielectric (in float theCosI, in float theIndex) { + float aFresnel = 1.f; + float anEtaI = theCosI > 0.f ? 1.f : theIndex; float anEtaT = theCosI > 0.f ? theIndex : 1.f; - float aSinT = (anEtaI / anEtaT) * sqrt (1.f - theCosI * theCosI); + float aSinT2 = (anEtaI * anEtaI) / (anEtaT * anEtaT) * (1.f - theCosI * theCosI); - if (aSinT >= 1.f) + if (aSinT2 < 1.f) { - return 1.f; + aFresnel = fresnelDielectric (abs (theCosI), sqrt (1.f - aSinT2), anEtaI, anEtaT); } - float aCosT = sqrt (1.f - aSinT * aSinT); - - return fresnelDielectric (abs (theCosI), aCosT, anEtaI, anEtaT); + return aFresnel; } //======================================================================= @@ -197,22 +197,26 @@ float fresnelConductor (in float theCosI, in float theEta, in float theK) //======================================================================= vec3 fresnelMedia (in float theCosI, in vec3 theFresnel) { + vec3 aFresnel; + if (theFresnel.x > FRESNEL_SCHLICK) { - return fresnelSchlick (abs (theCosI), theFresnel); + aFresnel = fresnelSchlick (abs (theCosI), theFresnel); } - - if (theFresnel.x > FRESNEL_CONSTANT) + else if (theFresnel.x > FRESNEL_CONSTANT) { - return vec3 (theFresnel.z); + aFresnel = vec3 (theFresnel.z); } - - if (theFresnel.x > FRESNEL_CONDUCTOR) + else if (theFresnel.x > FRESNEL_CONDUCTOR) { - return vec3 (fresnelConductor (abs (theCosI), theFresnel.y, theFresnel.z)); + aFresnel = vec3 (fresnelConductor (abs (theCosI), theFresnel.y, theFresnel.z)); + } + else + { + aFresnel = vec3 (fresnelDielectric (theCosI, theFresnel.y)); } - return vec3 (fresnelDielectric (theCosI, theFresnel.y)); + return aFresnel; } //======================================================================= @@ -249,36 +253,34 @@ float EvalLambertianReflection (in vec3 theWi, in vec3 theWo) return (theWi.z <= 0.f || theWo.z <= 0.f) ? 0.f : theWi.z * (1.f / M_PI); } +#define FLT_EPSILON 1.0e-5f + //======================================================================= // function : SmithG1 // purpose : //======================================================================= float SmithG1 (in vec3 theDirection, in vec3 theM, in float theRoughness) { - if (dot (theDirection, theM) * theDirection.z <= 0.f) + float aResult = 0.f; + + if (dot (theDirection, theM) * theDirection.z > 0.f) { - return 0.f; + float aTanThetaM = sqrt (1.f - theDirection.z * theDirection.z) / theDirection.z; + + if (aTanThetaM == 0.f) + { + aResult = 1.f; + } + else + { + float aVal = 1.f / (theRoughness * aTanThetaM); + + // Use rational approximation to shadowing-masking function (from Mitsuba) + aResult = (3.535f + 2.181f * aVal) / (1.f / aVal + 2.276f + 2.577f * aVal); + } } - float aTanThetaM = sqrt (1.f - theDirection.z * theDirection.z) / theDirection.z; - - if (aTanThetaM == 0.0f) - { - return 1.f; - } - - float aVal = 1.f / (theRoughness * aTanThetaM); - - if (aVal >= 1.6f) - { - return 1.f; - } - - // Use fast and accurate rational approximation to the - // shadowing-masking function (from Mitsuba renderer) - float aSqr = aVal * aVal; - - return (3.535f * aVal + 2.181f * aSqr) / (1.f + 2.276f * aVal + 2.577f * aSqr); + return min (aResult, 1.f); } //======================================================================= @@ -306,18 +308,31 @@ vec3 EvalBlinnReflection (in vec3 theWi, in vec3 theWo, in vec3 theFresnel, in f } //======================================================================= -// function : EvalMaterial +// function : EvalBsdfLayered // purpose : Evaluates BSDF for specified material, with cos(N, PSI) //======================================================================= -vec3 EvalMaterial (in SMaterial theBSDF, in vec3 theWi, in vec3 theWo) +vec3 EvalBsdfLayered (in SBSDF theBSDF, in vec3 theWi, in vec3 theWo) { #ifdef TWO_SIDED_BXDF theWi.z *= sign (theWi.z); theWo.z *= sign (theWo.z); #endif - return theBSDF.Kd.rgb * EvalLambertianReflection (theWi, theWo) + - theBSDF.Ks.rgb * EvalBlinnReflection (theWi, theWo, theBSDF.Fresnel, theBSDF.Ks.w); + vec3 aBxDF = theBSDF.Kd.rgb * EvalLambertianReflection (theWi, theWo); + + if (theBSDF.Ks.w > FLT_EPSILON) + { + aBxDF += theBSDF.Ks.rgb * EvalBlinnReflection (theWi, theWo, theBSDF.FresnelBase, theBSDF.Ks.w); + } + + aBxDF *= UNIT - fresnelMedia (theWo.z, theBSDF.FresnelCoat); + + if (theBSDF.Kc.w > FLT_EPSILON) + { + aBxDF += theBSDF.Kc.rgb * EvalBlinnReflection (theWi, theWo, theBSDF.FresnelCoat, theBSDF.Kc.w); + } + + return aBxDF; } //======================================================================= @@ -349,7 +364,7 @@ vec3 SampleLambertianReflection (in vec3 theWo, out vec3 theWi, inout float theP } //======================================================================= -// function : SampleBlinnReflection +// function : SampleGlossyBlinnReflection // purpose : Samples Blinn BRDF, W = BRDF * cos(N, PSI) / PDF(PSI) // The BRDF is a product of three main terms, D, G, and F, // which is then divided by two cosine terms. Here we perform @@ -358,7 +373,7 @@ vec3 SampleLambertianReflection (in vec3 theWo, out vec3 theWi, inout float theP // terms would be complex, and it is the D term that accounts // for most of the variation. //======================================================================= -vec3 SampleBlinnReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel, in float theRoughness, inout float thePDF) +vec3 SampleGlossyBlinnReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel, in float theRoughness, inout float thePDF) { float aKsi1 = RandFloat(); float aKsi2 = RandFloat(); @@ -396,7 +411,7 @@ vec3 SampleBlinnReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel, i } // Jacobian of half-direction mapping - thePDF /= 4.f * dot (theWi, aM); + thePDF /= 4.f * aCosDelta; // compute shadow-masking coefficient float aG = SmithG1 (theWo, aM, theRoughness) * @@ -411,139 +426,152 @@ vec3 SampleBlinnReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel, i } //======================================================================= -// function : SampleSpecularReflection -// purpose : Samples specular BRDF, W = BRDF * cos(N, PSI) / PDF(PSI) -//======================================================================= -vec3 SampleSpecularReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel) -{ - // Sample input direction - theWi = vec3 (-theWo.x, - -theWo.y, - theWo.z); - -#ifdef TWO_SIDED_BXDF - return fresnelMedia (theWo.z, theFresnel); -#else - return fresnelMedia (theWo.z, theFresnel) * step (0.f, theWo.z); -#endif -} - -//======================================================================= -// function : SampleSpecularTransmission -// purpose : Samples specular BTDF, W = BRDF * cos(N, PSI) / PDF(PSI) -//======================================================================= -vec3 SampleSpecularTransmission (in vec3 theWo, out vec3 theWi, in vec3 theWeight, in vec3 theFresnel, inout bool theInside) -{ - vec3 aFactor = fresnelMedia (theWo.z, theFresnel); - - float aReflection = convolve (aFactor, theWeight); - - // sample specular BRDF/BTDF - if (RandFloat() <= aReflection) - { - theWi = vec3 (-theWo.x, - -theWo.y, - theWo.z); - - theWeight = aFactor * (1.f / aReflection); - } - else - { - theInside = !theInside; - - transmitted (theFresnel.y, theWo, theWi); - - theWeight = (UNIT - aFactor) * (1.f / (1.f - aReflection)); - } - - return theWeight; -} - -#define FLT_EPSILON 1.0e-5F - -//======================================================================= -// function : BsdfPdf +// function : BsdfPdfLayered // purpose : Calculates BSDF of sampling input knowing output //======================================================================= -float BsdfPdf (in SMaterial theBSDF, in vec3 theWo, in vec3 theWi, in vec3 theWeight) +float BsdfPdfLayered (in SBSDF theBSDF, in vec3 theWo, in vec3 theWi, in vec3 theWeight) { - float aPd = convolve (theBSDF.Kd.rgb, theWeight); - float aPs = convolve (theBSDF.Ks.rgb, theWeight); - float aPr = convolve (theBSDF.Kr.rgb, theWeight); - float aPt = convolve (theBSDF.Kt.rgb, theWeight); - - float aReflection = aPd + aPs + aPr + aPt; - float aPDF = 0.f; // PDF of sampling input direction + // We choose whether the light is reflected or transmitted + // by the coating layer according to the Fresnel equations + vec3 aCoatF = fresnelMedia (theWo.z, theBSDF.FresnelCoat); + + // Coat BRDF is scaled by its Fresnel reflectance term. For + // reasons of simplicity we scale base BxDFs only by coat's + // Fresnel transmittance term + vec3 aCoatT = UNIT - aCoatF; + + float aPc = dot (theBSDF.Kc.rgb * aCoatF, theWeight); + float aPd = dot (theBSDF.Kd.rgb * aCoatT, theWeight); + float aPs = dot (theBSDF.Ks.rgb * aCoatT, theWeight); + float aPt = dot (theBSDF.Kt.rgb * aCoatT, theWeight); + if (theWi.z * theWo.z > 0.f) { vec3 aH = normalize (theWi + theWo); - // roughness value --> Blinn exponent - float aPower = max (2.f / (theBSDF.Ks.w * theBSDF.Ks.w) - 2.f, 0.f); + aPDF = aPd * abs (theWi.z / M_PI); - aPDF = aPd * abs (theWi.z / M_PI) + - aPs * (aPower + 2.f) * (1.f / M_2_PI) * pow (abs (aH.z), aPower + 1.f) / (4.f * dot (theWi, aH)); + if (theBSDF.Kc.w > FLT_EPSILON) + { + float aPower = max (2.f / (theBSDF.Kc.w * theBSDF.Kc.w) - 2.f, 0.f); // roughness --> exponent + + aPDF += aPc * (aPower + 2.f) * (0.25f / M_2_PI) * pow (abs (aH.z), aPower + 1.f) / dot (theWi, aH); + } + + if (theBSDF.Ks.w > FLT_EPSILON) + { + float aPower = max (2.f / (theBSDF.Ks.w * theBSDF.Ks.w) - 2.f, 0.f); // roughness --> exponent + + aPDF += aPs * (aPower + 2.f) * (0.25f / M_2_PI) * pow (abs (aH.z), aPower + 1.f) / dot (theWi, aH); + } } - return aPDF / aReflection; + return aPDF / (aPc + aPd + aPs + aPt); } //! Tool macro to handle sampling of particular BxDF -#define PICK_BXDF(p, k) aPDF = p / aReflection; theWeight *= k / aPDF; +#define PICK_BXDF_LAYER(p, k) aPDF = p / aTotalR; theWeight *= k / aPDF; //======================================================================= -// function : SampleBsdf +// function : SampleBsdfLayered // purpose : Samples specified composite material (BSDF) //======================================================================= -float SampleBsdf (in SMaterial theBSDF, in vec3 theWo, out vec3 theWi, inout vec3 theWeight, inout bool theInside) +float SampleBsdfLayered (in SBSDF theBSDF, in vec3 theWo, out vec3 theWi, inout vec3 theWeight, inout bool theInside) { - // compute probability of each reflection type (BxDF) - float aPd = convolve (theBSDF.Kd.rgb, theWeight); - float aPs = convolve (theBSDF.Ks.rgb, theWeight); - float aPr = convolve (theBSDF.Kr.rgb, theWeight); - float aPt = convolve (theBSDF.Kt.rgb, theWeight); + // NOTE: OCCT uses two-layer material model. We have base diffuse, glossy, or transmissive + // layer, covered by one glossy/specular coat. In the current model, the layers themselves + // have no thickness; they can simply reflect light or transmits it to the layer under it. + // We use actual BRDF model only for direct reflection by the coat layer. For transmission + // through this layer, we approximate it as a flat specular surface. - float aReflection = aPd + aPs + aPr + aPt; + float aPDF = 0.f; // PDF of sampled direction - // choose BxDF component to sample - float aKsi = aReflection * RandFloat(); + // We choose whether the light is reflected or transmitted + // by the coating layer according to the Fresnel equations + vec3 aCoatF = fresnelMedia (theWo.z, theBSDF.FresnelCoat); - // BxDF's PDF of sampled direction - float aPDF = 0.f; + // Coat BRDF is scaled by its Fresnel term. According to + // Wilkie-Weidlich layered BSDF model, transmission term + // for light passing through the coat at direction I and + // leaving it in O is T = ( 1 - F (O) ) x ( 1 - F (I) ). + // For reasons of simplicity, we discard the second term + // and scale base BxDFs only by the first term. + vec3 aCoatT = UNIT - aCoatF; - if (aKsi < aPd) // diffuse reflection + float aPc = dot (theBSDF.Kc.rgb * aCoatF, theWeight); + float aPd = dot (theBSDF.Kd.rgb * aCoatT, theWeight); + float aPs = dot (theBSDF.Ks.rgb * aCoatT, theWeight); + float aPt = dot (theBSDF.Kt.rgb * aCoatT, theWeight); + + // Calculate total reflection probability + float aTotalR = (aPc + aPd) + (aPs + aPt); + + // Generate random variable to select BxDF + float aKsi = aTotalR * RandFloat(); + + if (aKsi < aPc) // REFLECTION FROM COAT { - PICK_BXDF (aPd, theBSDF.Kd.rgb); + PICK_BXDF_LAYER (aPc, theBSDF.Kc.rgb) - theWeight *= SampleLambertianReflection (theWo, theWi, aPDF); + if (theBSDF.Kc.w < FLT_EPSILON) + { + theWeight *= aCoatF; + + theWi = vec3 (-theWo.x, + -theWo.y, + theWo.z); + } + else + { + theWeight *= SampleGlossyBlinnReflection (theWo, theWi, theBSDF.FresnelCoat, theBSDF.Kc.w, aPDF); + } + + aPDF = mix (aPDF, MAXFLOAT, theBSDF.Kc.w < FLT_EPSILON); } - else if (aKsi < aPd + aPs) // glossy reflection + else if (aKsi < aTotalR) // REFLECTION FROM BASE { - PICK_BXDF (aPs, theBSDF.Ks.rgb); + theWeight *= aCoatT; - theWeight *= SampleBlinnReflection (theWo, theWi, theBSDF.Fresnel, theBSDF.Ks.w, aPDF); - } - else if (aKsi < aPd + aPs + aPr) // specular reflection - { - PICK_BXDF (aPr, theBSDF.Kr.rgb); + if (aKsi < aPc + aPd) // diffuse BRDF + { + PICK_BXDF_LAYER (aPd, theBSDF.Kd.rgb) - aPDF = MAXFLOAT; + theWeight *= SampleLambertianReflection (theWo, theWi, aPDF); + } + else if (aKsi < (aPc + aPd) + aPs) // specular/glossy BRDF + { + PICK_BXDF_LAYER (aPs, theBSDF.Ks.rgb) - theWeight *= SampleSpecularReflection (theWo, theWi, theBSDF.Fresnel); - } - else if (aKsi < aReflection) // specular transmission - { - PICK_BXDF (aPt, theBSDF.Kt.rgb); + if (theBSDF.Ks.w < FLT_EPSILON) + { + theWeight *= fresnelMedia (theWo.z, theBSDF.FresnelBase); - aPDF = MAXFLOAT; + theWi = vec3 (-theWo.x, + -theWo.y, + theWo.z); + } + else + { + theWeight *= SampleGlossyBlinnReflection (theWo, theWi, theBSDF.FresnelBase, theBSDF.Ks.w, aPDF); + } - theWeight *= SampleSpecularTransmission (theWo, theWi, theWeight, theBSDF.Fresnel, theInside); + aPDF = mix (aPDF, MAXFLOAT, theBSDF.Ks.w < FLT_EPSILON); + } + else // specular transmission + { + PICK_BXDF_LAYER (aPt, theBSDF.Kt.rgb) + + // refracted direction should exist if we are here + transmitted (theBSDF.FresnelCoat.y, theWo, theWi); + + theInside = !theInside; aPDF = MAXFLOAT; + } } // path termination for extra small weights - theWeight = mix (ZERO, theWeight, step (FLT_EPSILON, aReflection)); + theWeight = mix (ZERO, theWeight, step (FLT_EPSILON, aTotalR)); return aPDF; } @@ -689,15 +717,16 @@ vec3 IntersectLight (in SRay theRay, in int theDepth, in float theHitDistance, o #define MIN_THROUGHPUT vec3 (1.0e-3f) #define MIN_CONTRIBUTION vec3 (1.0e-2f) -#define MATERIAL_KD(index) (18 * index + 11) -#define MATERIAL_KR(index) (18 * index + 12) -#define MATERIAL_KT(index) (18 * index + 13) -#define MATERIAL_KS(index) (18 * index + 14) -#define MATERIAL_LE(index) (18 * index + 15) -#define MATERIAL_FRESNEL(index) (18 * index + 16) -#define MATERIAL_ABSORPT(index) (18 * index + 17) +#define MATERIAL_KC(index) (19 * index + 11) +#define MATERIAL_KD(index) (19 * index + 12) +#define MATERIAL_KS(index) (19 * index + 13) +#define MATERIAL_KT(index) (19 * index + 14) +#define MATERIAL_LE(index) (19 * index + 15) +#define MATERIAL_FRESNEL_COAT(index) (19 * index + 16) +#define MATERIAL_FRESNEL_BASE(index) (19 * index + 17) +#define MATERIAL_ABSORPT_BASE(index) (19 * index + 18) -//! Enables experimental russian roulette sampling path termination. +//! Enables experimental Russian roulette sampling path termination. //! In most cases, it provides faster image convergence with minimal //! bias, so it is enabled by default. #define RUSSIAN_ROULETTE @@ -712,6 +741,18 @@ vec3 IntersectLight (in SRay theRay, in int theDepth, in float theHitDistance, o #define FRAME_STEP 5 #endif +//======================================================================= +// function : IsNotZero +// purpose : Checks whether BSDF reflects direct light +//======================================================================= +bool IsNotZero (in SBSDF theBSDF, in vec3 theThroughput) +{ + vec3 aGlossy = theBSDF.Kc.rgb * step (FLT_EPSILON, theBSDF.Kc.w) + + theBSDF.Ks.rgb * step (FLT_EPSILON, theBSDF.Ks.w); + + return convolve (theBSDF.Kd.rgb + aGlossy, theThroughput) > FLT_EPSILON; +} + //======================================================================= // function : PathTrace // purpose : Calculates radiance along the given ray @@ -766,17 +807,25 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse, in int theNbSamples) aRaytraceDepth = (aNDCPoint.z / aNDCPoint.w + aPolygonOffset * POLYGON_OFFSET_SCALE) * 0.5f + 0.5f; } - // fetch material (BSDF) - SMaterial aMaterial = SMaterial ( - vec4 (texelFetch (uRaytraceMaterialTexture, MATERIAL_KD (aTriIndex.w))), - vec3 (texelFetch (uRaytraceMaterialTexture, MATERIAL_KR (aTriIndex.w))), - vec3 (texelFetch (uRaytraceMaterialTexture, MATERIAL_KT (aTriIndex.w))), - vec4 (texelFetch (uRaytraceMaterialTexture, MATERIAL_KS (aTriIndex.w))), - vec3 (texelFetch (uRaytraceMaterialTexture, MATERIAL_FRESNEL (aTriIndex.w))), - vec4 (texelFetch (uRaytraceMaterialTexture, MATERIAL_ABSORPT (aTriIndex.w)))); + SBSDF aBSDF; + + // fetch BxDF weights + aBSDF.Kc = texelFetch (uRaytraceMaterialTexture, MATERIAL_KC (aTriIndex.w)); + aBSDF.Kd = texelFetch (uRaytraceMaterialTexture, MATERIAL_KD (aTriIndex.w)); + aBSDF.Ks = texelFetch (uRaytraceMaterialTexture, MATERIAL_KS (aTriIndex.w)); + aBSDF.Kt = texelFetch (uRaytraceMaterialTexture, MATERIAL_KT (aTriIndex.w)).rgb; + + // compute smooth normal (in parallel with fetch) + vec3 aNormal = SmoothNormal (aHit.UV, aTriIndex); + + aNormal = normalize (vec3 (dot (aInvTransf0, aNormal), + dot (aInvTransf1, aNormal), + dot (aInvTransf2, aNormal))); + + SLocalSpace aSpace = buildLocalSpace (aNormal); #ifdef USE_TEXTURES - if (aMaterial.Kd.w >= 0.f) + if (aBSDF.Kd.w >= 0.f) { vec4 aTexCoord = vec4 (SmoothUV (aHit.UV, aTriIndex), 0.f, 1.f); @@ -789,32 +838,23 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse, in int theNbSamples) dot (aTrsfRow2, aTexCoord)); vec4 aTexColor = textureLod ( - sampler2D (uTextureSamplers[int (aMaterial.Kd.w)]), aTexCoord.st, 0.f); + sampler2D (uTextureSamplers[int (aBSDF.Kd.w)]), aTexCoord.st, 0.f); - aMaterial.Kd.rgb *= (aTexColor.rgb * aTexColor.rgb) * aTexColor.w; // de-gamma correction (for gamma = 2) + aBSDF.Kd.rgb *= (aTexColor.rgb * aTexColor.rgb) * aTexColor.w; // de-gamma correction (for gamma = 2) if (aTexColor.w != 1.0f) { // mix transparency BTDF with texture alpha-channel - aMaterial.Kt = (UNIT - aTexColor.www) + aTexColor.w * aMaterial.Kt; + aBSDF.Kt = (UNIT - aTexColor.www) + aTexColor.w * aBSDF.Kt; } } #endif - // compute smooth normal - vec3 aNormal = SmoothNormal (aHit.UV, aTriIndex); + // fetch Fresnel reflectance for both layers + aBSDF.FresnelCoat = texelFetch (uRaytraceMaterialTexture, MATERIAL_FRESNEL_COAT (aTriIndex.w)).xyz; + aBSDF.FresnelBase = texelFetch (uRaytraceMaterialTexture, MATERIAL_FRESNEL_BASE (aTriIndex.w)).xyz; - aNormal = normalize (vec3 (dot (aInvTransf0, aNormal), - dot (aInvTransf1, aNormal), - dot (aInvTransf2, aNormal))); - - SLocalSpace aSpace = buildLocalSpace (aNormal); - - // account for self-emission (not stored in the material) - aRadiance += aThroughput * texelFetch ( - uRaytraceMaterialTexture, MATERIAL_LE (aTriIndex.w)).rgb; - - if (uLightCount > 0 && convolve (aMaterial.Kd.rgb + aMaterial.Ks.rgb, aThroughput) > 0.f) + if (uLightCount > 0 && IsNotZero (aBSDF, aThroughput)) { aExpPDF = 1.f / uLightCount; @@ -833,14 +873,14 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse, in int theNbSamples) aLight.xyz = SampleLight (aLight.xyz, aDistance, aLight.w == 0.f /* is infinite */, aParam.w /* max cos or radius */, aExpPDF); - aImpPDF = BsdfPdf (aMaterial, + aImpPDF = BsdfPdfLayered (aBSDF, toLocalSpace (-theRay.Direct, aSpace), toLocalSpace (aLight.xyz, aSpace), aThroughput); // MIS weight including division by explicit PDF float aMIS = (aExpPDF == MAXFLOAT) ? 1.f : aExpPDF / (aExpPDF * aExpPDF + aImpPDF * aImpPDF); - vec3 aContrib = aMIS * aParam.rgb /* Le */ * EvalMaterial ( - aMaterial, toLocalSpace (aLight.xyz, aSpace), toLocalSpace (-theRay.Direct, aSpace)); + vec3 aContrib = aMIS * aParam.rgb /* Le */ * EvalBsdfLayered ( + aBSDF, toLocalSpace (aLight.xyz, aSpace), toLocalSpace (-theRay.Direct, aSpace)); if (any (greaterThan (aContrib, MIN_CONTRIBUTION))) // check if light source is important { @@ -852,25 +892,29 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse, in int theNbSamples) float aVisibility = SceneAnyHit (aShadow, InverseDirection (aLight.xyz), aLight.w == 0.f ? MAXFLOAT : aDistance); - aRadiance += aVisibility * aThroughput * aContrib; + aRadiance += aVisibility * (aThroughput * aContrib); } } + // account for self-emission + aRadiance += aThroughput * texelFetch (uRaytraceMaterialTexture, MATERIAL_LE (aTriIndex.w)).rgb; + if (aInMedium) // handle attenuation { - aThroughput *= exp (-aHit.Time * - aMaterial.Absorption.w * (UNIT - aMaterial.Absorption.rgb)); + vec4 aScattering = texelFetch (uRaytraceMaterialTexture, MATERIAL_ABSORPT_BASE (aTriIndex.w)); + + aThroughput *= exp (-aHit.Time * aScattering.w * (UNIT - aScattering.rgb)); } vec3 anInput = UNIT; // sampled input direction - aImpPDF = SampleBsdf (aMaterial, + aImpPDF = SampleBsdfLayered (aBSDF, toLocalSpace (-theRay.Direct, aSpace), anInput, aThroughput, aInMedium); - float aSurvive = 1.f; + float aSurvive = float (any (greaterThan (aThroughput, MIN_THROUGHPUT))); #ifdef RUSSIAN_ROULETTE - aSurvive = aDepth < 3 ? 1.f : min (dot (LUMA, aThroughput), 0.95f); + aSurvive = aDepth < 3 ? aSurvive : min (dot (LUMA, aThroughput), 0.95f); #endif // here, we additionally increase path length for non-diffuse bounces diff --git a/src/Shaders/RaytraceBase.fs b/src/Shaders/RaytraceBase.fs index f38a132291..4f08b8333d 100644 --- a/src/Shaders/RaytraceBase.fs +++ b/src/Shaders/RaytraceBase.fs @@ -353,16 +353,16 @@ struct SSubTree ivec4 SubData; }; -#define MATERIAL_AMBN(index) (18 * index + 0) -#define MATERIAL_DIFF(index) (18 * index + 1) -#define MATERIAL_SPEC(index) (18 * index + 2) -#define MATERIAL_EMIS(index) (18 * index + 3) -#define MATERIAL_REFL(index) (18 * index + 4) -#define MATERIAL_REFR(index) (18 * index + 5) -#define MATERIAL_TRAN(index) (18 * index + 6) -#define MATERIAL_TRS1(index) (18 * index + 7) -#define MATERIAL_TRS2(index) (18 * index + 8) -#define MATERIAL_TRS3(index) (18 * index + 9) +#define MATERIAL_AMBN(index) (19 * index + 0) +#define MATERIAL_DIFF(index) (19 * index + 1) +#define MATERIAL_SPEC(index) (19 * index + 2) +#define MATERIAL_EMIS(index) (19 * index + 3) +#define MATERIAL_REFL(index) (19 * index + 4) +#define MATERIAL_REFR(index) (19 * index + 5) +#define MATERIAL_TRAN(index) (19 * index + 6) +#define MATERIAL_TRS1(index) (19 * index + 7) +#define MATERIAL_TRS2(index) (19 * index + 8) +#define MATERIAL_TRS3(index) (19 * index + 9) #define TRS_OFFSET(treelet) treelet.SubData.x #define BVH_OFFSET(treelet) treelet.SubData.y diff --git a/src/Shaders/Shaders_PathtraceBase_fs.pxx b/src/Shaders/Shaders_PathtraceBase_fs.pxx index 04cd95aa08..f33cc5bb39 100644 --- a/src/Shaders/Shaders_PathtraceBase_fs.pxx +++ b/src/Shaders/Shaders_PathtraceBase_fs.pxx @@ -32,25 +32,25 @@ static const char Shaders_PathtraceBase_fs[] = "};\n" "\n" "//! Describes material properties (BSDF).\n" - "struct SMaterial\n" + "struct SBSDF\n" "{\n" - " //! Weight of the Lambertian BRDF.\n" + " //! Weight of coat specular/glossy BRDF.\n" + " vec4 Kc;\n" + "\n" + " //! Weight of base diffuse BRDF.\n" " vec4 Kd;\n" "\n" - " //! Weight of the reflection BRDF.\n" - " vec3 Kr;\n" - "\n" - " //! Weight of the transmission BTDF.\n" - " vec3 Kt;\n" - "\n" - " //! Weight of the Blinn BRDF (and roughness).\n" + " //! Weight of base specular/glossy BRDF.\n" " vec4 Ks;\n" "\n" - " //! Fresnel coefficients.\n" - " vec3 Fresnel;\n" + " //! Weight of base specular/glossy BTDF.\n" + " vec3 Kt;\n" "\n" - " //! Absorption color and intensity of the media.\n" - " vec4 Absorption;\n" + " //! Fresnel coefficients of coat layer.\n" + " vec3 FresnelCoat;\n" + "\n" + " //! Fresnel coefficients of base layer.\n" + " vec3 FresnelBase;\n" "};\n" "\n" "///////////////////////////////////////////////////////////////////////////////////////\n" @@ -151,19 +151,19 @@ static const char Shaders_PathtraceBase_fs[] = "//=======================================================================\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 aSinT = (anEtaI / anEtaT) * sqrt (1.f - theCosI * theCosI);\n" + " float aSinT2 = (anEtaI * anEtaI) / (anEtaT * anEtaT) * (1.f - theCosI * theCosI);\n" "\n" - " if (aSinT >= 1.f)\n" + " if (aSinT2 < 1.f)\n" " {\n" - " return 1.f;\n" + " aFresnel = fresnelDielectric (abs (theCosI), sqrt (1.f - aSinT2), anEtaI, anEtaT);\n" " }\n" "\n" - " float aCosT = sqrt (1.f - aSinT * aSinT);\n" - "\n" - " return fresnelDielectric (abs (theCosI), aCosT, anEtaI, anEtaT);\n" + " return aFresnel;\n" "}\n" "\n" "//=======================================================================\n" @@ -200,22 +200,26 @@ static const char Shaders_PathtraceBase_fs[] = "//=======================================================================\n" "vec3 fresnelMedia (in float theCosI, in vec3 theFresnel)\n" "{\n" + " vec3 aFresnel;\n" + "\n" " if (theFresnel.x > FRESNEL_SCHLICK)\n" " {\n" - " return fresnelSchlick (abs (theCosI), theFresnel);\n" + " aFresnel = fresnelSchlick (abs (theCosI), theFresnel);\n" " }\n" - "\n" - " if (theFresnel.x > FRESNEL_CONSTANT)\n" + " else if (theFresnel.x > FRESNEL_CONSTANT)\n" " {\n" - " return vec3 (theFresnel.z);\n" + " aFresnel = vec3 (theFresnel.z);\n" " }\n" - "\n" - " if (theFresnel.x > FRESNEL_CONDUCTOR)\n" + " else if (theFresnel.x > FRESNEL_CONDUCTOR)\n" " {\n" - " return vec3 (fresnelConductor (abs (theCosI), theFresnel.y, theFresnel.z));\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 vec3 (fresnelDielectric (theCosI, theFresnel.y));\n" + " return aFresnel;\n" "}\n" "\n" "//=======================================================================\n" @@ -252,36 +256,34 @@ static const char Shaders_PathtraceBase_fs[] = " 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" - " if (dot (theDirection, theM) * theDirection.z <= 0.f)\n" + " float aResult = 0.f;\n" + "\n" + " if (dot (theDirection, theM) * theDirection.z > 0.f)\n" " {\n" - " return 0.f;\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" - " float aTanThetaM = sqrt (1.f - theDirection.z * theDirection.z) / theDirection.z;\n" - "\n" - " if (aTanThetaM == 0.0f)\n" - " {\n" - " return 1.f;\n" - " }\n" - "\n" - " float aVal = 1.f / (theRoughness * aTanThetaM);\n" - "\n" - " if (aVal >= 1.6f)\n" - " {\n" - " return 1.f;\n" - " }\n" - "\n" - " // Use fast and accurate rational approximation to the\n" - " // shadowing-masking function (from Mitsuba renderer)\n" - " float aSqr = aVal * aVal;\n" - "\n" - " return (3.535f * aVal + 2.181f * aSqr) / (1.f + 2.276f * aVal + 2.577f * aSqr);\n" + " return min (aResult, 1.f);\n" "}\n" "\n" "//=======================================================================\n" @@ -309,18 +311,31 @@ static const char Shaders_PathtraceBase_fs[] = "}\n" "\n" "//=======================================================================\n" - "// function : EvalMaterial\n" + "// function : EvalBsdfLayered\n" "// purpose : Evaluates BSDF for specified material, with cos(N, PSI)\n" "//=======================================================================\n" - "vec3 EvalMaterial (in SMaterial theBSDF, in vec3 theWi, in vec3 theWo)\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" - " return theBSDF.Kd.rgb * EvalLambertianReflection (theWi, theWo) +\n" - " theBSDF.Ks.rgb * EvalBlinnReflection (theWi, theWo, theBSDF.Fresnel, theBSDF.Ks.w);\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" @@ -352,7 +367,7 @@ static const char Shaders_PathtraceBase_fs[] = "}\n" "\n" "//=======================================================================\n" - "// function : SampleBlinnReflection\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" @@ -361,7 +376,7 @@ static const char Shaders_PathtraceBase_fs[] = "// terms would be complex, and it is the D term that accounts\n" "// for most of the variation.\n" "//=======================================================================\n" - "vec3 SampleBlinnReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel, in float theRoughness, inout float thePDF)\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" @@ -399,7 +414,7 @@ static const char Shaders_PathtraceBase_fs[] = " }\n" "\n" " // Jacobian of half-direction mapping\n" - " thePDF /= 4.f * dot (theWi, aM);\n" + " thePDF /= 4.f * aCosDelta;\n" "\n" " // compute shadow-masking coefficient\n" " float aG = SmithG1 (theWo, aM, theRoughness) *\n" @@ -414,139 +429,152 @@ static const char Shaders_PathtraceBase_fs[] = "}\n" "\n" "//=======================================================================\n" - "// function : SampleSpecularReflection\n" - "// purpose : Samples specular BRDF, W = BRDF * cos(N, PSI) / PDF(PSI)\n" - "//=======================================================================\n" - "vec3 SampleSpecularReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel)\n" - "{\n" - " // Sample input direction\n" - " theWi = vec3 (-theWo.x,\n" - " -theWo.y,\n" - " theWo.z);\n" - "\n" - "#ifdef TWO_SIDED_BXDF\n" - " return fresnelMedia (theWo.z, theFresnel);\n" - "#else\n" - " return fresnelMedia (theWo.z, theFresnel) * step (0.f, theWo.z);\n" - "#endif\n" - "}\n" - "\n" - "//=======================================================================\n" - "// function : SampleSpecularTransmission\n" - "// purpose : Samples specular BTDF, W = BRDF * cos(N, PSI) / PDF(PSI)\n" - "//=======================================================================\n" - "vec3 SampleSpecularTransmission (in vec3 theWo, out vec3 theWi, in vec3 theWeight, in vec3 theFresnel, inout bool theInside)\n" - "{\n" - " vec3 aFactor = fresnelMedia (theWo.z, theFresnel);\n" - "\n" - " float aReflection = convolve (aFactor, theWeight);\n" - "\n" - " // sample specular BRDF/BTDF\n" - " if (RandFloat() <= aReflection)\n" - " {\n" - " theWi = vec3 (-theWo.x,\n" - " -theWo.y,\n" - " theWo.z);\n" - "\n" - " theWeight = aFactor * (1.f / aReflection);\n" - " }\n" - " else\n" - " {\n" - " theInside = !theInside;\n" - "\n" - " transmitted (theFresnel.y, theWo, theWi);\n" - "\n" - " theWeight = (UNIT - aFactor) * (1.f / (1.f - aReflection));\n" - " }\n" - "\n" - " return theWeight;\n" - "}\n" - "\n" - "#define FLT_EPSILON 1.0e-5F\n" - "\n" - "//=======================================================================\n" - "// function : BsdfPdf\n" + "// function : BsdfPdfLayered\n" "// purpose : Calculates BSDF of sampling input knowing output\n" "//=======================================================================\n" - "float BsdfPdf (in SMaterial theBSDF, in vec3 theWo, in vec3 theWi, in vec3 theWeight)\n" + "float BsdfPdfLayered (in SBSDF theBSDF, in vec3 theWo, in vec3 theWi, in vec3 theWeight)\n" "{\n" - " float aPd = convolve (theBSDF.Kd.rgb, theWeight);\n" - " float aPs = convolve (theBSDF.Ks.rgb, theWeight);\n" - " float aPr = convolve (theBSDF.Kr.rgb, theWeight);\n" - " float aPt = convolve (theBSDF.Kt.rgb, theWeight);\n" - "\n" - " float aReflection = aPd + aPs + aPr + aPt;\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" - " // roughness value --> Blinn exponent\n" - " float aPower = max (2.f / (theBSDF.Ks.w * theBSDF.Ks.w) - 2.f, 0.f);\n" + " aPDF = aPd * abs (theWi.z / M_PI);\n" "\n" - " aPDF = aPd * abs (theWi.z / M_PI) +\n" - " aPs * (aPower + 2.f) * (1.f / M_2_PI) * pow (abs (aH.z), aPower + 1.f) / (4.f * dot (theWi, aH));\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 / aReflection;\n" + " return aPDF / (aPc + aPd + aPs + aPt);\n" "}\n" "\n" "//! Tool macro to handle sampling of particular BxDF\n" - "#define PICK_BXDF(p, k) aPDF = p / aReflection; theWeight *= k / aPDF;\n" + "#define PICK_BXDF_LAYER(p, k) aPDF = p / aTotalR; theWeight *= k / aPDF;\n" "\n" "//=======================================================================\n" - "// function : SampleBsdf\n" + "// function : SampleBsdfLayered\n" "// purpose : Samples specified composite material (BSDF)\n" "//=======================================================================\n" - "float SampleBsdf (in SMaterial theBSDF, in vec3 theWo, out vec3 theWi, inout vec3 theWeight, inout bool theInside)\n" + "float SampleBsdfLayered (in SBSDF theBSDF, in vec3 theWo, out vec3 theWi, inout vec3 theWeight, inout bool theInside)\n" "{\n" - " // compute probability of each reflection type (BxDF)\n" - " float aPd = convolve (theBSDF.Kd.rgb, theWeight);\n" - " float aPs = convolve (theBSDF.Ks.rgb, theWeight);\n" - " float aPr = convolve (theBSDF.Kr.rgb, theWeight);\n" - " float aPt = convolve (theBSDF.Kt.rgb, theWeight);\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 aReflection = aPd + aPs + aPr + aPt;\n" + " float aPDF = 0.f; // PDF of sampled direction\n" "\n" - " // choose BxDF component to sample\n" - " float aKsi = aReflection * RandFloat();\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" - " // BxDF's PDF of sampled direction\n" - " float aPDF = 0.f;\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" - " if (aKsi < aPd) // diffuse reflection\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 (aPd, theBSDF.Kd.rgb);\n" + " PICK_BXDF_LAYER (aPc, theBSDF.Kc.rgb)\n" "\n" - " theWeight *= SampleLambertianReflection (theWo, theWi, aPDF);\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 < aPd + aPs) // glossy reflection\n" + " else if (aKsi < aTotalR) // REFLECTION FROM BASE\n" " {\n" - " PICK_BXDF (aPs, theBSDF.Ks.rgb);\n" + " theWeight *= aCoatT;\n" "\n" - " theWeight *= SampleBlinnReflection (theWo, theWi, theBSDF.Fresnel, theBSDF.Ks.w, aPDF);\n" - " }\n" - " else if (aKsi < aPd + aPs + aPr) // specular reflection\n" - " {\n" - " PICK_BXDF (aPr, theBSDF.Kr.rgb);\n" + " if (aKsi < aPc + aPd) // diffuse BRDF\n" + " {\n" + " PICK_BXDF_LAYER (aPd, theBSDF.Kd.rgb)\n" "\n" - " aPDF = MAXFLOAT;\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" - " theWeight *= SampleSpecularReflection (theWo, theWi, theBSDF.Fresnel);\n" - " }\n" - " else if (aKsi < aReflection) // specular transmission\n" - " {\n" - " PICK_BXDF (aPt, theBSDF.Kt.rgb);\n" + " if (theBSDF.Ks.w < FLT_EPSILON)\n" + " {\n" + " theWeight *= fresnelMedia (theWo.z, theBSDF.FresnelBase);\n" "\n" - " aPDF = MAXFLOAT;\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" - " theWeight *= SampleSpecularTransmission (theWo, theWi, theWeight, theBSDF.Fresnel, theInside);\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, aReflection));\n" + " theWeight = mix (ZERO, theWeight, step (FLT_EPSILON, aTotalR));\n" "\n" " return aPDF;\n" "}\n" @@ -692,15 +720,16 @@ static const char Shaders_PathtraceBase_fs[] = "#define MIN_THROUGHPUT vec3 (1.0e-3f)\n" "#define MIN_CONTRIBUTION vec3 (1.0e-2f)\n" "\n" - "#define MATERIAL_KD(index) (18 * index + 11)\n" - "#define MATERIAL_KR(index) (18 * index + 12)\n" - "#define MATERIAL_KT(index) (18 * index + 13)\n" - "#define MATERIAL_KS(index) (18 * index + 14)\n" - "#define MATERIAL_LE(index) (18 * index + 15)\n" - "#define MATERIAL_FRESNEL(index) (18 * index + 16)\n" - "#define MATERIAL_ABSORPT(index) (18 * index + 17)\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" + "//! 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" @@ -716,6 +745,18 @@ static const char Shaders_PathtraceBase_fs[] = "#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" @@ -769,17 +810,25 @@ static const char Shaders_PathtraceBase_fs[] = " aRaytraceDepth = (aNDCPoint.z / aNDCPoint.w + aPolygonOffset * POLYGON_OFFSET_SCALE) * 0.5f + 0.5f;\n" " }\n" "\n" - " // fetch material (BSDF)\n" - " SMaterial aMaterial = SMaterial (\n" - " vec4 (texelFetch (uRaytraceMaterialTexture, MATERIAL_KD (aTriIndex.w))),\n" - " vec3 (texelFetch (uRaytraceMaterialTexture, MATERIAL_KR (aTriIndex.w))),\n" - " vec3 (texelFetch (uRaytraceMaterialTexture, MATERIAL_KT (aTriIndex.w))),\n" - " vec4 (texelFetch (uRaytraceMaterialTexture, MATERIAL_KS (aTriIndex.w))),\n" - " vec3 (texelFetch (uRaytraceMaterialTexture, MATERIAL_FRESNEL (aTriIndex.w))),\n" - " vec4 (texelFetch (uRaytraceMaterialTexture, MATERIAL_ABSORPT (aTriIndex.w))));\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)).rgb;\n" + "\n" + " // compute smooth normal (in parallel with fetch)\n" + " vec3 aNormal = SmoothNormal (aHit.UV, aTriIndex);\n" + "\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 (aMaterial.Kd.w >= 0.f)\n" + " if (aBSDF.Kd.w >= 0.f)\n" " {\n" " vec4 aTexCoord = vec4 (SmoothUV (aHit.UV, aTriIndex), 0.f, 1.f);\n" "\n" @@ -792,32 +841,23 @@ static const char Shaders_PathtraceBase_fs[] = " dot (aTrsfRow2, aTexCoord));\n" "\n" " vec4 aTexColor = textureLod (\n" - " sampler2D (uTextureSamplers[int (aMaterial.Kd.w)]), aTexCoord.st, 0.f);\n" + " sampler2D (uTextureSamplers[int (aBSDF.Kd.w)]), aTexCoord.st, 0.f);\n" "\n" - " aMaterial.Kd.rgb *= (aTexColor.rgb * aTexColor.rgb) * aTexColor.w; // de-gamma correction (for gamma = 2)\n" + " aBSDF.Kd.rgb *= (aTexColor.rgb * aTexColor.rgb) * aTexColor.w; // de-gamma correction (for gamma = 2)\n" "\n" " if (aTexColor.w != 1.0f)\n" " {\n" " // mix transparency BTDF with texture alpha-channel\n" - " aMaterial.Kt = (UNIT - aTexColor.www) + aTexColor.w * aMaterial.Kt;\n" + " aBSDF.Kt = (UNIT - aTexColor.www) + aTexColor.w * aBSDF.Kt;\n" " }\n" " }\n" "#endif\n" "\n" - " // compute smooth normal\n" - " vec3 aNormal = SmoothNormal (aHit.UV, aTriIndex);\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" - " aNormal = normalize (vec3 (dot (aInvTransf0, aNormal),\n" - " dot (aInvTransf1, aNormal),\n" - " dot (aInvTransf2, aNormal)));\n" - "\n" - " SLocalSpace aSpace = buildLocalSpace (aNormal);\n" - "\n" - " // account for self-emission (not stored in the material)\n" - " aRadiance += aThroughput * texelFetch (\n" - " uRaytraceMaterialTexture, MATERIAL_LE (aTriIndex.w)).rgb;\n" - "\n" - " if (uLightCount > 0 && convolve (aMaterial.Kd.rgb + aMaterial.Ks.rgb, aThroughput) > 0.f)\n" + " if (uLightCount > 0 && IsNotZero (aBSDF, aThroughput))\n" " {\n" " aExpPDF = 1.f / uLightCount;\n" "\n" @@ -836,14 +876,14 @@ static const char Shaders_PathtraceBase_fs[] = " aLight.xyz = SampleLight (aLight.xyz, aDistance,\n" " aLight.w == 0.f /* is infinite */, aParam.w /* max cos or radius */, aExpPDF);\n" "\n" - " aImpPDF = BsdfPdf (aMaterial,\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 */ * EvalMaterial (\n" - " aMaterial, toLocalSpace (aLight.xyz, aSpace), toLocalSpace (-theRay.Direct, aSpace));\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" @@ -855,25 +895,29 @@ static const char Shaders_PathtraceBase_fs[] = " float aVisibility = SceneAnyHit (aShadow,\n" " InverseDirection (aLight.xyz), aLight.w == 0.f ? MAXFLOAT : aDistance);\n" "\n" - " aRadiance += aVisibility * aThroughput * aContrib;\n" + " aRadiance += aVisibility * (aThroughput * aContrib);\n" " }\n" " }\n" "\n" + " // account for self-emission\n" + " aRadiance += aThroughput * texelFetch (uRaytraceMaterialTexture, MATERIAL_LE (aTriIndex.w)).rgb;\n" + "\n" " if (aInMedium) // handle attenuation\n" " {\n" - " aThroughput *= exp (-aHit.Time *\n" - " aMaterial.Absorption.w * (UNIT - aMaterial.Absorption.rgb));\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 = SampleBsdf (aMaterial,\n" + " aImpPDF = SampleBsdfLayered (aBSDF,\n" " toLocalSpace (-theRay.Direct, aSpace), anInput, aThroughput, aInMedium);\n" "\n" - " float aSurvive = 1.f;\n" + " float aSurvive = float (any (greaterThan (aThroughput, MIN_THROUGHPUT)));\n" "\n" "#ifdef RUSSIAN_ROULETTE\n" - " aSurvive = aDepth < 3 ? 1.f : min (dot (LUMA, aThroughput), 0.95f);\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" diff --git a/src/Shaders/Shaders_RaytraceBase_fs.pxx b/src/Shaders/Shaders_RaytraceBase_fs.pxx index 321a854f89..a25d6fc36a 100644 --- a/src/Shaders/Shaders_RaytraceBase_fs.pxx +++ b/src/Shaders/Shaders_RaytraceBase_fs.pxx @@ -356,16 +356,16 @@ static const char Shaders_RaytraceBase_fs[] = " ivec4 SubData;\n" "};\n" "\n" - "#define MATERIAL_AMBN(index) (18 * index + 0)\n" - "#define MATERIAL_DIFF(index) (18 * index + 1)\n" - "#define MATERIAL_SPEC(index) (18 * index + 2)\n" - "#define MATERIAL_EMIS(index) (18 * index + 3)\n" - "#define MATERIAL_REFL(index) (18 * index + 4)\n" - "#define MATERIAL_REFR(index) (18 * index + 5)\n" - "#define MATERIAL_TRAN(index) (18 * index + 6)\n" - "#define MATERIAL_TRS1(index) (18 * index + 7)\n" - "#define MATERIAL_TRS2(index) (18 * index + 8)\n" - "#define MATERIAL_TRS3(index) (18 * index + 9)\n" + "#define MATERIAL_AMBN(index) (19 * index + 0)\n" + "#define MATERIAL_DIFF(index) (19 * index + 1)\n" + "#define MATERIAL_SPEC(index) (19 * index + 2)\n" + "#define MATERIAL_EMIS(index) (19 * index + 3)\n" + "#define MATERIAL_REFL(index) (19 * index + 4)\n" + "#define MATERIAL_REFR(index) (19 * index + 5)\n" + "#define MATERIAL_TRAN(index) (19 * index + 6)\n" + "#define MATERIAL_TRS1(index) (19 * index + 7)\n" + "#define MATERIAL_TRS2(index) (19 * index + 8)\n" + "#define MATERIAL_TRS3(index) (19 * index + 9)\n" "\n" "#define TRS_OFFSET(treelet) treelet.SubData.x\n" "#define BVH_OFFSET(treelet) treelet.SubData.y\n" diff --git a/src/ViewerTest/ViewerTest.cxx b/src/ViewerTest/ViewerTest.cxx index 003a09a84e..8f356a7e69 100644 --- a/src/ViewerTest/ViewerTest.cxx +++ b/src/ViewerTest/ViewerTest.cxx @@ -5247,7 +5247,7 @@ static Standard_Integer vr(Draw_Interpretor& , Standard_Integer , const char** a //function : VBsdf //purpose : //=============================================================================================== -static int VBsdf (Draw_Interpretor& theDi, +static int VBsdf (Draw_Interpretor& theDI, Standard_Integer theArgsNb, const char** theArgVec) { @@ -5263,30 +5263,47 @@ static int VBsdf (Draw_Interpretor& theDi, ViewerTest_CmdParser aCmd; aCmd.AddDescription ("Adjusts parameters of material BSDF:"); - aCmd.AddOption ("print|echo|p", "Print BSDF"); - aCmd.AddOption ("kd", "Weight of the Lambertian BRDF"); - aCmd.AddOption ("kr", "Weight of the reflection BRDF"); - aCmd.AddOption ("kt", "Weight of the transmission BTDF"); - aCmd.AddOption ("ks", "Weight of the glossy Blinn BRDF"); - aCmd.AddOption ("le", "Self-emitted radiance"); + aCmd.AddOption ("print|echo|p", "Prints BSDF"); - aCmd.AddOption ("fresnel|f", "Fresnel coefficients; Allowed fresnel formats are: Constant x, Schlick x y z, Dielectric x, Conductor x y"); + aCmd.AddOption ("noupdate|update", "Suppresses viewer redraw call"); - aCmd.AddOption ("roughness|r", "Roughness of material (Blinn's exponent)"); - aCmd.AddOption ("absorpCoeff|af", "Absorption coeff (only for transparent material)"); - aCmd.AddOption ("absorpColor|ac", "Absorption color (only for transparent material)"); + aCmd.AddOption ("kc", "Weight of coat specular/glossy BRDF"); + aCmd.AddOption ("kd", "Weight of base diffuse BRDF"); + aCmd.AddOption ("ks", "Weight of base specular/glossy BRDF"); + aCmd.AddOption ("kt", "Weight of base specular/glossy BTDF"); + aCmd.AddOption ("le", "Radiance emitted by surface"); - aCmd.AddOption ("normalize|n", "Normalize BSDF coefficients"); + aCmd.AddOption ("coatFresnel|cf", "Fresnel reflectance of coat layer. Allowed formats: Constant R, Schlick R G B, Dielectric N, Conductor N K"); + aCmd.AddOption ("baseFresnel|bf", "Fresnel reflectance of base layer. Allowed formats: Constant R, Schlick R G B, Dielectric N, Conductor N K"); + + aCmd.AddOption ("coatRoughness|cr", "Roughness of coat glossy BRDF"); + aCmd.AddOption ("baseRoughness|br", "Roughness of base glossy BRDF"); + + aCmd.AddOption ("absorpCoeff|af", "Absorption coeff of base transmission BTDF"); + aCmd.AddOption ("absorpColor|ac", "Absorption color of base transmission BTDF"); + + aCmd.AddOption ("normalize|n", "Normalizes BSDF to ensure energy conservation"); aCmd.Parse (theArgsNb, theArgVec); if (aCmd.HasOption ("help")) { - theDi.PrintHelp (theArgVec[0]); + theDI.PrintHelp (theArgVec[0]); return 0; } + // check viewer update mode + ViewerTest_AutoUpdater anUpdateTool (ViewerTest::GetAISContext(), ViewerTest::CurrentView()); + + for (Standard_Integer anArgIter = 1; anArgIter < theArgsNb; ++anArgIter) + { + if (anUpdateTool.parseRedrawMode (theArgVec[anArgIter])) + { + break; + } + } + TCollection_AsciiString aName (aCmd.Arg ("", 0).c_str()); // find object @@ -5303,62 +5320,92 @@ static int VBsdf (Draw_Interpretor& theDi, if (aCmd.HasOption ("print")) { - Graphic3d_Vec4 aFresnel = aBSDF.Fresnel.Serialize(); - - std::cout << "\n" + theDI << "\n" + << "Kc: " << aBSDF.Kc.r() << ", " << aBSDF.Kc.g() << ", " << aBSDF.Kc.b() << "\n" << "Kd: " << aBSDF.Kd.r() << ", " << aBSDF.Kd.g() << ", " << aBSDF.Kd.b() << "\n" - << "Kr: " << aBSDF.Kr.r() << ", " << aBSDF.Kr.g() << ", " << aBSDF.Kr.b() << "\n" - << "Kt: " << aBSDF.Kt.r() << ", " << aBSDF.Kt.g() << ", " << aBSDF.Kt.b() << "\n" << "Ks: " << aBSDF.Ks.r() << ", " << aBSDF.Ks.g() << ", " << aBSDF.Ks.b() << "\n" - << "Le: " << aBSDF.Le.r() << ", " << aBSDF.Le.g() << ", " << aBSDF.Le.b() << "\n" - << "Fresnel: "; + << "Kt: " << aBSDF.Kt.r() << ", " << aBSDF.Kt.g() << ", " << aBSDF.Kt.b() << "\n" + << "Le: " << aBSDF.Le.r() << ", " << aBSDF.Le.g() << ", " << aBSDF.Le.b() << "\n"; - if (aFresnel.x() >= 0.f) + for (int aLayerID = 0; aLayerID < 2; ++aLayerID) { - std::cout - << "|Schlick| " << aFresnel.x() << ", " << aFresnel.y() << ", " << aFresnel.z() << "\n"; - } - else if (aFresnel.x() >= -1.5f) - { - std::cout - << "|Constant| " << aFresnel.z() << "\n"; - } - else if (aFresnel.x() >= -2.5f) - { - std::cout - << "|Conductor| " << aFresnel.y() << ", " << aFresnel.z() << "\n"; - } - else - { - std::cout - << "|Dielectric| " << aFresnel.y() << "\n"; + const Graphic3d_Vec4 aFresnel = aLayerID < 1 ? aBSDF.FresnelCoat.Serialize() + : aBSDF.FresnelBase.Serialize(); + + theDI << (aLayerID < 1 ? "Coat Fresnel: " + : "Base Fresnel: "); + + if (aFresnel.x() >= 0.f) + { + theDI << "Schlick " << "R = " << aFresnel.r() << ", " + << "G = " << aFresnel.g() << ", " + << "B = " << aFresnel.b() << "\n"; + } + else if (aFresnel.x() >= -1.5f) + { + theDI << "Constant " << aFresnel.z() << "\n"; + } + else if (aFresnel.x() >= -2.5f) + { + theDI << "Conductor " << "N = " << aFresnel.y() << ", " + << "K = " << aFresnel.z() << "\n"; + } + else + { + theDI << "Dielectric " << "N = " << aFresnel.y() << "\n"; + } } - - std::cout - << "Roughness: " << aBSDF.Roughness << "\n" - << "Absorption coeff: " << aBSDF.AbsorptionCoeff << "\n" - << "Absorption color: " << aBSDF.AbsorptionColor.r() << ", " - << aBSDF.AbsorptionColor.g() << ", " - << aBSDF.AbsorptionColor.b() << "\n"; + theDI << "Coat roughness: " << aBSDF.Kc.w() << "\n" + << "Base roughness: " << aBSDF.Ks.w() << "\n" + << "Absorption coeff: " << aBSDF.Absorption.w() << "\n" + << "Absorption color: " << aBSDF.Absorption.r() << ", " + << aBSDF.Absorption.g() << ", " + << aBSDF.Absorption.b() << "\n"; return 0; } - if (aCmd.HasOption ("roughness", 1, Standard_True)) + if (aCmd.HasOption ("coatRoughness", 1, Standard_True)) { - aCmd.Arg ("roughness", 0); - aBSDF.Roughness = aCmd.ArgFloat ("roughness"); + aBSDF.Kc.w() = aCmd.ArgFloat ("coatRoughness"); + } + + if (aCmd.HasOption ("baseRoughness", 1, Standard_True)) + { + aBSDF.Ks.w () = aCmd.ArgFloat ("baseRoughness"); } if (aCmd.HasOption ("absorpCoeff", 1, Standard_True)) { - aBSDF.AbsorptionCoeff = aCmd.ArgFloat ("absorpCoeff"); + aBSDF.Absorption.w() = aCmd.ArgFloat ("absorpCoeff"); } if (aCmd.HasOption ("absorpColor", 3, Standard_True)) { - aBSDF.AbsorptionColor = aCmd.ArgVec3f ("absorpColor"); + const Graphic3d_Vec3 aRGB = aCmd.ArgVec3f ("absorpColor"); + + aBSDF.Absorption.r() = aRGB.r(); + aBSDF.Absorption.g() = aRGB.g(); + aBSDF.Absorption.b() = aRGB.b(); + } + + if (aCmd.HasOption ("kc", 3) || aCmd.HasOption ("kc", 1, Standard_True)) + { + Graphic3d_Vec3 aKc; + + if (aCmd.HasOption ("kc", 3)) + { + aKc = aCmd.ArgVec3f ("kc"); + } + else + { + aKc = Graphic3d_Vec3 (aCmd.ArgFloat ("kc")); + } + + aBSDF.Kc.r() = aKc.r(); + aBSDF.Kc.g() = aKc.g(); + aBSDF.Kc.b() = aKc.b(); } if (aCmd.HasOption ("kd", 3)) @@ -5370,13 +5417,22 @@ static int VBsdf (Draw_Interpretor& theDi, aBSDF.Kd = Graphic3d_Vec3 (aCmd.ArgFloat ("kd")); } - if (aCmd.HasOption ("kr", 3)) + if (aCmd.HasOption ("ks", 3) || aCmd.HasOption ("ks", 1, Standard_True)) { - aBSDF.Kr = aCmd.ArgVec3f ("kr"); - } - else if (aCmd.HasOption ("kr", 1, Standard_True)) - { - aBSDF.Kr = Graphic3d_Vec3 (aCmd.ArgFloat ("kr")); + Graphic3d_Vec3 aKs; + + if (aCmd.HasOption ("ks", 3)) + { + aKs = aCmd.ArgVec3f ("ks"); + } + else + { + aKs = Graphic3d_Vec3 (aCmd.ArgFloat ("ks")); + } + + aBSDF.Ks.r() = aKs.r(); + aBSDF.Ks.g() = aKs.g(); + aBSDF.Ks.b() = aKs.b(); } if (aCmd.HasOption ("kt", 3)) @@ -5388,15 +5444,6 @@ static int VBsdf (Draw_Interpretor& theDi, aBSDF.Kt = Graphic3d_Vec3 (aCmd.ArgFloat ("kt")); } - if (aCmd.HasOption ("ks", 3)) - { - aBSDF.Ks = aCmd.ArgVec3f ("ks"); - } - else if (aCmd.HasOption ("ks", 1, Standard_True)) - { - aBSDF.Ks = Graphic3d_Vec3 (aCmd.ArgFloat ("ks")); - } - if (aCmd.HasOption ("le", 3)) { aBSDF.Le = aCmd.ArgVec3f ("le"); @@ -5407,59 +5454,73 @@ static int VBsdf (Draw_Interpretor& theDi, } const std::string aFresnelErrorMessage = - "Error! Wrong Fresnel type. Allowed types are: Constant x, Schlick x y z, Dielectric x, Conductor x y.\n"; + "Error! Wrong Fresnel type. Allowed types are: Constant F, Schlick R G B, Dielectric N, Conductor N K\n"; - if (aCmd.HasOption ("fresnel", 4)) // Schlick: type, x, y ,z + for (int aLayerID = 0; aLayerID < 2; ++aLayerID) { - std::string aFresnelType = aCmd.Arg ("fresnel", 0); - std::transform (aFresnelType.begin(), aFresnelType.end(), aFresnelType.begin(), ::tolower); + const std::string aFresnel = aLayerID < 1 ? "baseFresnel" + : "coatFresnel"; - if (aFresnelType == "schlick") + if (aCmd.HasOption (aFresnel, 4)) // Schlick: type R G B { - aBSDF.Fresnel = Graphic3d_Fresnel::CreateSchlick ( - Graphic3d_Vec3 (static_cast (Draw::Atof (aCmd.Arg ("fresnel", 1).c_str())), - static_cast (Draw::Atof (aCmd.Arg ("fresnel", 2).c_str())), - static_cast (Draw::Atof (aCmd.Arg ("fresnel", 3).c_str())))); - } - else - { - std::cout << aFresnelErrorMessage; - } - } - else if (aCmd.HasOption ("fresnel", 3)) // Conductor: type, x, y - { - std::string aFresnelType = aCmd.Arg ("fresnel", 0); - std::transform (aFresnelType.begin(), aFresnelType.end(), aFresnelType.begin(), ::tolower); + std::string aFresnelType = aCmd.Arg (aFresnel, 0); + std::transform (aFresnelType.begin (), aFresnelType.end (), aFresnelType.begin (), ::tolower); - if (aFresnelType == "conductor") - { - aBSDF.Fresnel = Graphic3d_Fresnel::CreateConductor ( - static_cast (Draw::Atof (aCmd.Arg ("fresnel", 1).c_str())), - static_cast (Draw::Atof (aCmd.Arg ("fresnel", 2).c_str()))); - } - else - { - std::cout << aFresnelErrorMessage; - } - } - else if (aCmd.HasOption ("fresnel", 2)) // Dielectric, Constant: type, x - { - std::string aFresnelType = aCmd.Arg ("fresnel", 0); - std::transform (aFresnelType.begin(), aFresnelType.end(), aFresnelType.begin(), ::tolower); + if (aFresnelType == "schlick") + { + Graphic3d_Vec3 aRGB (static_cast (Draw::Atof (aCmd.Arg (aFresnel, 1).c_str())), + static_cast (Draw::Atof (aCmd.Arg (aFresnel, 2).c_str())), + static_cast (Draw::Atof (aCmd.Arg (aFresnel, 3).c_str()))); - if (aFresnelType == "dielectric") - { - aBSDF.Fresnel = Graphic3d_Fresnel::CreateDielectric ( - static_cast (Draw::Atof (aCmd.Arg ("fresnel", 1).c_str()))); + aRGB.r() = std::min (std::max (aRGB.r(), 0.f), 1.f); + aRGB.g() = std::min (std::max (aRGB.g(), 0.f), 1.f); + aRGB.b() = std::min (std::max (aRGB.b(), 0.f), 1.f); + + (aLayerID < 1 ? aBSDF.FresnelBase : aBSDF.FresnelCoat) = Graphic3d_Fresnel::CreateSchlick (aRGB); + } + else + { + theDI << aFresnelErrorMessage.c_str() << "\n"; + } } - else if (aFresnelType == "constant") + else if (aCmd.HasOption (aFresnel, 3)) // Conductor: type N K { - aBSDF.Fresnel = Graphic3d_Fresnel::CreateConstant ( - static_cast (Draw::Atof (aCmd.Arg ("fresnel", 1).c_str()))); + std::string aFresnelType = aCmd.Arg (aFresnel, 0); + std::transform (aFresnelType.begin (), aFresnelType.end (), aFresnelType.begin (), ::tolower); + + if (aFresnelType == "conductor") + { + const float aN = static_cast (Draw::Atof (aCmd.Arg (aFresnel, 1).c_str())); + const float aK = static_cast (Draw::Atof (aCmd.Arg (aFresnel, 2).c_str())); + + (aLayerID < 1 ? aBSDF.FresnelBase : aBSDF.FresnelCoat) = Graphic3d_Fresnel::CreateConductor (aN, aK); + } + else + { + theDI << aFresnelErrorMessage.c_str() << "\n"; + } } - else + else if (aCmd.HasOption (aFresnel, 2)) // Dielectric or Constant: type N|C { - std::cout << aFresnelErrorMessage; + std::string aFresnelType = aCmd.Arg (aFresnel, 0); + std::transform (aFresnelType.begin (), aFresnelType.end (), aFresnelType.begin (), ::tolower); + + if (aFresnelType == "constant") + { + const float aR = static_cast (Draw::Atof (aCmd.Arg (aFresnel, 1).c_str())); + + (aLayerID < 1 ? aBSDF.FresnelBase : aBSDF.FresnelCoat) = Graphic3d_Fresnel::CreateConstant (aR); + } + else if (aFresnelType == "dielectric") + { + const float aN = static_cast (Draw::Atof (aCmd.Arg (aFresnel, 1).c_str())); + + (aLayerID < 1 ? aBSDF.FresnelBase : aBSDF.FresnelCoat) = Graphic3d_Fresnel::CreateDielectric (aN); + } + else + { + theDI << aFresnelErrorMessage.c_str() << "\n"; + } } } @@ -5471,8 +5532,6 @@ static int VBsdf (Draw_Interpretor& theDi, aMaterial.SetBSDF (aBSDF); anIObj->SetMaterial (aMaterial); - aView->Redraw(); - return 0; } diff --git a/tests/v3d/raytrace/sample_ball_alpha b/tests/v3d/raytrace/sample_ball_alpha index df43214efd..dfd7d58b71 100644 --- a/tests/v3d/raytrace/sample_ball_alpha +++ b/tests/v3d/raytrace/sample_ball_alpha @@ -7,7 +7,7 @@ source $env(CSF_OCCTSamplesPath)/tcl/pathtrace_ball.tcl vtexture ball 21 -scale 0.1 0.1 vsetmaterial ball plaster -vbsdf ball -fresnel Constant 0.0 +vbsdf ball -coatFresnel Constant 0.0 vfps 100 vdump $imagedir/${casename}_zoom.png diff --git a/tests/v3d/raytrace/sample_materials b/tests/v3d/raytrace/sample_materials new file mode 100644 index 0000000000..95e25ea36c --- /dev/null +++ b/tests/v3d/raytrace/sample_materials @@ -0,0 +1,7 @@ +puts "============" +puts "Visualization - Path Tracing, Materials sample" +puts "============" +puts "" + +source $env(CSF_OCCTSamplesPath)/tcl/pathtrace_materials.tcl +vdump $imagedir/${casename}_materials.png