Existing OCCT path tracing engine used very simple additive material (BSDF) model, so it was possible to reproduce
behavior only of very basic materials such as metal, glass, or plastic. However, some important in CAD industry
materials like car paint or ceramic could not be modeled well. In this patch, OCCT BSDF was significantly improved
by replacing additive model with two-layered scattering model. Therefore, we have base diffuse, glossy, or transmissive
layer, covered by one glossy/specular coat. The layers themselves have no thickness; they can simply reflect light or
transmits it to the layer under it. Balancing different combinations of layer properties can produce a wide range of
different effects. At the same time, disabling the first (coat) layer allows to keep full compatibility with previously
supported scattering model. All new parameters are available via 'vbsdf' command.
Location of new sample for few material examples:
samples\tcl\pathtrace_materials.tcl
Fix shader compilation issue.
Fix test case sample_ball_alpha.
Shaders_PathtraceBase_fs.pxx - regenerate resource from origin
*.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
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
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..."
--- /dev/null
+# 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\]"
if (myFresnelType != Graphic3d_FM_SCHLICK)
{
- aData.x() = -static_cast<Standard_ShortReal> (myFresnelType);
+ aData.x() = -static_cast<float> (myFresnelType);
}
return aData;
// 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);
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;
}
// =======================================================================
// =======================================================================
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;
+ }
}
}
// 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;
}
// 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;
}
// 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
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);
//! 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;
-
- //! Roughness (exponent) of Blinn's BRDF.
- Standard_ShortReal Roughness;
+ //! Volume scattering color/density.
+ Graphic3d_Vec4 Absorption;
- //! Absorption color of transparent media.
- Graphic3d_Vec3 AbsorptionColor;
+ //! Parameters of Fresnel reflectance of coat layer.
+ Graphic3d_Fresnel FresnelCoat;
- //! Absorption intensity of transparent media.
- Standard_ShortReal AbsorptionCoeff;
+ //! Parameters of Fresnel reflectance of base layer.
+ Graphic3d_Fresnel FresnelBase;
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;
};
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<Graphic3d_Vec3> (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;
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<Graphic3d_Vec3> (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;
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:
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<Graphic3d_Vec3> (Colors[Graphic3d_TOR_DIFFUSE]);
+ BSDF.FresnelBase = Graphic3d_Fresnel::CreateDielectric (1.5f);
break;
case Graphic3d_NOM_METALIZED:
Shininess = 0.13f;
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
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;
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;
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;
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;
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));
//! 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 base specular/glossy BRDF.
+ BVH_Vec4f Ks;
- //! Weight of the transmission BTDF.
+ //! Weight of base specular/glossy BTDF.
BVH_Vec4f Kt;
- //! Weight of the Blinn's glossy BRDF.
- BVH_Vec4f Ks;
-
- //! Self-emitted radiance.
+ //! Radiance emitted by the surface.
BVH_Vec4f Le;
- //! Fresnel coefficients.
- BVH_Vec4f Fresnel;
+ //! Fresnel coefficients of coat layer.
+ BVH_Vec4f FresnelCoat;
+
+ //! Fresnel coefficients of base layer.
+ BVH_Vec4f FresnelBase;
- //! Absorption color for the transmission BSDF.
+ //! Absorption color/intensity.
BVH_Vec4f Absorption;
} BSDF;
// 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())
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)
{
};
//! 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 base specular/glossy BRDF.
+ vec4 Ks;
- //! Weight of the transmission BTDF.
+ //! Weight of base specular/glossy BTDF.
vec3 Kt;
- //! Weight of the Blinn BRDF (and roughness).
- vec4 Ks;
-
- //! Fresnel coefficients.
- vec3 Fresnel;
+ //! Fresnel coefficients of coat layer.
+ vec3 FresnelCoat;
- //! Absorption color and intensity of the media.
- vec4 Absorption;
+ //! Fresnel coefficients of base layer.
+ vec3 FresnelBase;
};
///////////////////////////////////////////////////////////////////////////////////////
//=======================================================================
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;
}
//=======================================================================
//=======================================================================
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)
+ {
+ aFresnel = vec3 (fresnelConductor (abs (theCosI), theFresnel.y, theFresnel.z));
+ }
+ else
{
- return vec3 (fresnelConductor (abs (theCosI), theFresnel.y, theFresnel.z));
+ aFresnel = vec3 (fresnelDielectric (theCosI, theFresnel.y));
}
- return vec3 (fresnelDielectric (theCosI, theFresnel.y));
+ return aFresnel;
}
//=======================================================================
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)
- {
- return 0.f;
- }
+ float aResult = 0.f;
- float aTanThetaM = sqrt (1.f - theDirection.z * theDirection.z) / theDirection.z;
-
- if (aTanThetaM == 0.0f)
+ if (dot (theDirection, theM) * theDirection.z > 0.f)
{
- return 1.f;
- }
+ float aTanThetaM = sqrt (1.f - theDirection.z * theDirection.z) / theDirection.z;
- float aVal = 1.f / (theRoughness * aTanThetaM);
+ if (aTanThetaM == 0.f)
+ {
+ aResult = 1.f;
+ }
+ else
+ {
+ float aVal = 1.f / (theRoughness * aTanThetaM);
- if (aVal >= 1.6f)
- {
- return 1.f;
+ // Use rational approximation to shadowing-masking function (from Mitsuba)
+ aResult = (3.535f + 2.181f * aVal) / (1.f / aVal + 2.276f + 2.577f * aVal);
+ }
}
- // 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);
}
//=======================================================================
}
//=======================================================================
-// 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;
}
//=======================================================================
}
//=======================================================================
-// 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
// 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();
}
// 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) *
}
//=======================================================================
-// function : SampleSpecularReflection
-// purpose : Samples specular BRDF, W = BRDF * cos(N, PSI) / PDF(PSI)
+// function : BsdfPdfLayered
+// purpose : Calculates BSDF of sampling input knowing output
//=======================================================================
-vec3 SampleSpecularReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel)
+float BsdfPdfLayered (in SBSDF theBSDF, in vec3 theWo, in vec3 theWi, in vec3 theWeight)
{
- // 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
-}
+ float aPDF = 0.f; // PDF of sampling input direction
-//=======================================================================
-// 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);
+ // 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);
- float aReflection = convolve (aFactor, theWeight);
+ // 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;
- // sample specular BRDF/BTDF
- if (RandFloat() <= aReflection)
- {
- theWi = vec3 (-theWo.x,
- -theWo.y,
- theWo.z);
+ 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);
- theWeight = aFactor * (1.f / aReflection);
- }
- else
+ if (theWi.z * theWo.z > 0.f)
{
- 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
-// purpose : Calculates BSDF of sampling input knowing output
-//=======================================================================
-float BsdfPdf (in SMaterial 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);
+ vec3 aH = normalize (theWi + theWo);
- float aReflection = aPd + aPs + aPr + aPt;
+ aPDF = aPd * abs (theWi.z / M_PI);
- float aPDF = 0.f; // PDF of sampling input direction
+ if (theBSDF.Kc.w > FLT_EPSILON)
+ {
+ float aPower = max (2.f / (theBSDF.Kc.w * theBSDF.Kc.w) - 2.f, 0.f); // roughness --> exponent
- if (theWi.z * theWo.z > 0.f)
- {
- vec3 aH = normalize (theWi + theWo);
+ aPDF += aPc * (aPower + 2.f) * (0.25f / M_2_PI) * pow (abs (aH.z), aPower + 1.f) / dot (theWi, aH);
+ }
- // roughness value --> Blinn exponent
- float aPower = max (2.f / (theBSDF.Ks.w * theBSDF.Ks.w) - 2.f, 0.f);
+ if (theBSDF.Ks.w > FLT_EPSILON)
+ {
+ float aPower = max (2.f / (theBSDF.Ks.w * theBSDF.Ks.w) - 2.f, 0.f); // roughness --> exponent
- 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));
+ 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);
-
- float aReflection = aPd + aPs + aPr + aPt;
-
- // choose BxDF component to sample
- float aKsi = aReflection * RandFloat();
+ // 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 aPDF = 0.f; // PDF of sampled 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 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;
+
+ 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_LAYER (aPc, theBSDF.Kc.rgb)
- // BxDF's PDF of sampled direction
- float aPDF = 0.f;
+ if (theBSDF.Kc.w < FLT_EPSILON)
+ {
+ theWeight *= aCoatF;
- if (aKsi < aPd) // diffuse reflection
- {
- PICK_BXDF (aPd, theBSDF.Kd.rgb);
+ theWi = vec3 (-theWo.x,
+ -theWo.y,
+ theWo.z);
+ }
+ else
+ {
+ theWeight *= SampleGlossyBlinnReflection (theWo, theWi, theBSDF.FresnelCoat, theBSDF.Kc.w, aPDF);
+ }
- theWeight *= SampleLambertianReflection (theWo, theWi, 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)
+
+ theWeight *= SampleLambertianReflection (theWo, theWi, aPDF);
+ }
+ else if (aKsi < (aPc + aPd) + aPs) // specular/glossy BRDF
+ {
+ PICK_BXDF_LAYER (aPs, theBSDF.Ks.rgb)
- aPDF = MAXFLOAT;
+ if (theBSDF.Ks.w < FLT_EPSILON)
+ {
+ theWeight *= fresnelMedia (theWo.z, theBSDF.FresnelBase);
- theWeight *= SampleSpecularReflection (theWo, theWi, theBSDF.Fresnel);
- }
- else if (aKsi < aReflection) // specular transmission
- {
- PICK_BXDF (aPt, theBSDF.Kt.rgb);
+ theWi = vec3 (-theWo.x,
+ -theWo.y,
+ theWo.z);
+ }
+ else
+ {
+ theWeight *= SampleGlossyBlinnReflection (theWo, theWi, theBSDF.FresnelBase, theBSDF.Ks.w, aPDF);
+ }
- aPDF = MAXFLOAT;
+ aPDF = mix (aPDF, MAXFLOAT, theBSDF.Ks.w < FLT_EPSILON);
+ }
+ else // specular transmission
+ {
+ PICK_BXDF_LAYER (aPt, theBSDF.Kt.rgb)
- theWeight *= SampleSpecularTransmission (theWo, theWi, theWeight, theBSDF.Fresnel, theInside);
+ // 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;
}
#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
#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
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);
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);
-
- aNormal = normalize (vec3 (dot (aInvTransf0, aNormal),
- dot (aInvTransf1, aNormal),
- dot (aInvTransf2, aNormal)));
-
- SLocalSpace aSpace = buildLocalSpace (aNormal);
+ // 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;
- // 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;
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
{
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
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
"};\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"
+ " //! Weight of base specular/glossy BRDF.\n"
+ " vec4 Ks;\n"
"\n"
- " //! Weight of the transmission BTDF.\n"
+ " //! Weight of base specular/glossy BTDF.\n"
" vec3 Kt;\n"
"\n"
- " //! Weight of the Blinn BRDF (and roughness).\n"
- " vec4 Ks;\n"
- "\n"
- " //! Fresnel coefficients.\n"
- " vec3 Fresnel;\n"
+ " //! Fresnel coefficients of coat layer.\n"
+ " vec3 FresnelCoat;\n"
"\n"
- " //! Absorption color and intensity of the media.\n"
- " vec4 Absorption;\n"
+ " //! Fresnel coefficients of base layer.\n"
+ " vec3 FresnelBase;\n"
"};\n"
"\n"
"///////////////////////////////////////////////////////////////////////////////////////\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 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"
"//=======================================================================\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"
+ " aFresnel = vec3 (fresnelConductor (abs (theCosI), theFresnel.y, theFresnel.z));\n"
+ " }\n"
+ " else\n"
" {\n"
- " return vec3 (fresnelConductor (abs (theCosI), theFresnel.y, theFresnel.z));\n"
+ " aFresnel = vec3 (fresnelDielectric (theCosI, theFresnel.y));\n"
" }\n"
"\n"
- " return vec3 (fresnelDielectric (theCosI, theFresnel.y));\n"
+ " return aFresnel;\n"
"}\n"
"\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"
- " if (dot (theDirection, theM) * theDirection.z <= 0.f)\n"
- " {\n"
- " return 0.f;\n"
- " }\n"
+ " float aResult = 0.f;\n"
"\n"
- " float aTanThetaM = sqrt (1.f - theDirection.z * theDirection.z) / theDirection.z;\n"
- "\n"
- " if (aTanThetaM == 0.0f)\n"
+ " if (dot (theDirection, theM) * theDirection.z > 0.f)\n"
" {\n"
- " return 1.f;\n"
- " }\n"
+ " float aTanThetaM = sqrt (1.f - theDirection.z * theDirection.z) / theDirection.z;\n"
"\n"
- " float aVal = 1.f / (theRoughness * aTanThetaM);\n"
+ " if (aTanThetaM == 0.f)\n"
+ " {\n"
+ " aResult = 1.f;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " float aVal = 1.f / (theRoughness * aTanThetaM);\n"
"\n"
- " if (aVal >= 1.6f)\n"
- " {\n"
- " return 1.f;\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"
- " // 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"
"}\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"
"}\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"
"// 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"
" }\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"
"}\n"
"\n"
"//=======================================================================\n"
- "// function : SampleSpecularReflection\n"
- "// purpose : Samples specular BRDF, W = BRDF * cos(N, PSI) / PDF(PSI)\n"
+ "// function : BsdfPdfLayered\n"
+ "// purpose : Calculates BSDF of sampling input knowing output\n"
"//=======================================================================\n"
- "vec3 SampleSpecularReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel)\n"
+ "float BsdfPdfLayered (in SBSDF theBSDF, in vec3 theWo, in vec3 theWi, in vec3 theWeight)\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"
+ " float aPDF = 0.f; // PDF of sampling input direction\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"
+ " // 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"
- " float aReflection = convolve (aFactor, theWeight);\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"
- " // sample specular BRDF/BTDF\n"
- " if (RandFloat() <= aReflection)\n"
- " {\n"
- " theWi = vec3 (-theWo.x,\n"
- " -theWo.y,\n"
- " theWo.z);\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"
- " theWeight = aFactor * (1.f / aReflection);\n"
- " }\n"
- " else\n"
+ " if (theWi.z * theWo.z > 0.f)\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"
- "// 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"
- "{\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"
+ " vec3 aH = normalize (theWi + theWo);\n"
"\n"
- " float aReflection = aPd + aPs + aPr + aPt;\n"
+ " aPDF = aPd * abs (theWi.z / M_PI);\n"
"\n"
- " float aPDF = 0.f; // PDF of sampling input direction\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"
- " if (theWi.z * theWo.z > 0.f)\n"
- " {\n"
- " vec3 aH = normalize (theWi + theWo);\n"
+ " aPDF += aPc * (aPower + 2.f) * (0.25f / M_2_PI) * pow (abs (aH.z), aPower + 1.f) / dot (theWi, aH);\n"
+ " }\n"
"\n"
- " // roughness value --> Blinn exponent\n"
- " float aPower = max (2.f / (theBSDF.Ks.w * theBSDF.Ks.w) - 2.f, 0.f);\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 = 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"
+ " 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"
- "\n"
- " float aReflection = aPd + aPs + aPr + aPt;\n"
- "\n"
- " // choose BxDF component to sample\n"
- " float aKsi = aReflection * RandFloat();\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"
- " // BxDF's PDF of sampled direction\n"
- " float aPDF = 0.f;\n"
+ " if (theBSDF.Kc.w < FLT_EPSILON)\n"
+ " {\n"
+ " theWeight *= aCoatF;\n"
"\n"
- " if (aKsi < aPd) // diffuse reflection\n"
- " {\n"
- " PICK_BXDF (aPd, theBSDF.Kd.rgb);\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"
- " theWeight *= SampleLambertianReflection (theWo, theWi, aPDF);\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"
+ " 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"
- " aPDF = MAXFLOAT;\n"
+ " if (theBSDF.Ks.w < FLT_EPSILON)\n"
+ " {\n"
+ " theWeight *= fresnelMedia (theWo.z, theBSDF.FresnelBase);\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"
+ " 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 = MAXFLOAT;\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"
- " theWeight *= SampleSpecularTransmission (theWo, theWi, theWeight, theBSDF.Fresnel, theInside);\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"
"#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"
"#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"
" 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"
" 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"
- "\n"
- " aNormal = normalize (vec3 (dot (aInvTransf0, aNormal),\n"
- " dot (aInvTransf1, aNormal),\n"
- " dot (aInvTransf2, aNormal)));\n"
- "\n"
- " SLocalSpace aSpace = buildLocalSpace (aNormal);\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"
- " // 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"
" 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"
" 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"
" 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"
//function : VBsdf
//purpose :
//===============================================================================================
-static int VBsdf (Draw_Interpretor& theDi,
+static int VBsdf (Draw_Interpretor& theDI,
Standard_Integer theArgsNb,
const char** theArgVec)
{
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
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)
- {
- 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
+ for (int aLayerID = 0; aLayerID < 2; ++aLayerID)
{
- 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))
+ {
+ aBSDF.Kc.w() = aCmd.ArgFloat ("coatRoughness");
+ }
+
+ if (aCmd.HasOption ("baseRoughness", 1, Standard_True))
{
- aCmd.Arg ("roughness", 0);
- aBSDF.Roughness = aCmd.ArgFloat ("roughness");
+ 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))
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))
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");
}
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<Standard_ShortReal> (Draw::Atof (aCmd.Arg ("fresnel", 1).c_str())),
- static_cast<Standard_ShortReal> (Draw::Atof (aCmd.Arg ("fresnel", 2).c_str())),
- static_cast<Standard_ShortReal> (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<Standard_ShortReal> (Draw::Atof (aCmd.Arg ("fresnel", 1).c_str())),
- static_cast<Standard_ShortReal> (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<float> (Draw::Atof (aCmd.Arg (aFresnel, 1).c_str())),
+ static_cast<float> (Draw::Atof (aCmd.Arg (aFresnel, 2).c_str())),
+ static_cast<float> (Draw::Atof (aCmd.Arg (aFresnel, 3).c_str())));
- if (aFresnelType == "dielectric")
- {
- aBSDF.Fresnel = Graphic3d_Fresnel::CreateDielectric (
- static_cast<Standard_ShortReal> (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<Standard_ShortReal> (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<float> (Draw::Atof (aCmd.Arg (aFresnel, 1).c_str()));
+ const float aK = static_cast<float> (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<float> (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<float> (Draw::Atof (aCmd.Arg (aFresnel, 1).c_str()));
+
+ (aLayerID < 1 ? aBSDF.FresnelBase : aBSDF.FresnelCoat) = Graphic3d_Fresnel::CreateDielectric (aN);
+ }
+ else
+ {
+ theDI << aFresnelErrorMessage.c_str() << "\n";
+ }
}
}
aMaterial.SetBSDF (aBSDF);
anIObj->SetMaterial (aMaterial);
- aView->Redraw();
-
return 0;
}
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
--- /dev/null
+puts "============"
+puts "Visualization - Path Tracing, Materials sample"
+puts "============"
+puts ""
+
+source $env(CSF_OCCTSamplesPath)/tcl/pathtrace_materials.tcl
+vdump $imagedir/${casename}_materials.png