0030700: Visualization, TKOpenGl - support PBR Metallic-Roughness shading model
[occt.git] / src / Graphic3d / Graphic3d_PBRMaterial.cxx
1 // Author: Ilya Khramov
2 // Copyright (c) 2019 OPEN CASCADE SAS
3 //
4 // This file is part of Open CASCADE Technology software library.
5 //
6 // This library is free software; you can redistribute it and/or modify it under
7 // the terms of the GNU Lesser General Public License version 2.1 as published
8 // by the Free Software Foundation, with special exception defined in the file
9 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
10 // distribution for complete text of the license and disclaimer of any warranty.
11 //
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
14
15 #include <Graphic3d_PBRMaterial.hxx>
16
17 #include <Graphic3d_MaterialDefinitionError.hxx>
18
19 #include <limits>
20
21 // =======================================================================
22 // function : RoughnessFromSpecular
23 // purpose  :
24 // =======================================================================
25 Standard_ShortReal Graphic3d_PBRMaterial::RoughnessFromSpecular (const Quantity_Color& theSpecular,
26                                                                  const Standard_Real theShiness)
27 {
28   Standard_Real aRoughnessFactor = 1.0 - theShiness;
29   //Standard_Real aSpecIntens = theSpecular.Light() * theSpecular;
30   const Standard_Real aSpecIntens = theSpecular.Red()   * 0.2125
31                                   + theSpecular.Green() * 0.7154
32                                   + theSpecular.Blue()  * 0.0721;
33   if (aSpecIntens < 0.1)
34   {
35     // low specular intensity should produce a rough material even if shininess is high
36     aRoughnessFactor *= (1.0 - aSpecIntens);
37   }
38   return (Standard_ShortReal )aRoughnessFactor;
39 }
40
41 // =======================================================================
42 // function : Constructor
43 // purpose  :
44 // =======================================================================
45 Graphic3d_PBRMaterial::Graphic3d_PBRMaterial ()
46 : myColor     (0.f, 0.f, 0.f, 1.f),
47   myMetallic  (0.f),
48   myRoughness (1.f),
49   myEmission  (0.f),
50   myIOR       (1.5f)
51 {}
52
53 // =======================================================================
54 // function : Constructor
55 // purpose  :
56 // =======================================================================
57 Graphic3d_PBRMaterial::Graphic3d_PBRMaterial (const Graphic3d_BSDF& theBSDF)
58 {
59   SetBSDF (theBSDF);
60 }
61
62 // =======================================================================
63 // function : SetMetallic
64 // purpose  :
65 // =======================================================================
66 void Graphic3d_PBRMaterial::SetMetallic (Standard_ShortReal theMetallic)
67 {
68   Graphic3d_MaterialDefinitionError_Raise_if (theMetallic < 0.f || theMetallic > 1.f,
69     "'metallic' parameter of PBR material must be in range [0, 1]")
70   myMetallic = theMetallic;
71 }
72
73 // =======================================================================
74 // function : Roughness
75 // purpose  :
76 // =======================================================================
77 Standard_ShortReal Graphic3d_PBRMaterial::Roughness (Standard_ShortReal theNormalizedRoughness)
78 {
79   return theNormalizedRoughness * (1.f - MinRoughness()) + MinRoughness();
80 }
81
82 // =======================================================================
83 // function : SetRoughness
84 // purpose  :
85 // =======================================================================
86 void Graphic3d_PBRMaterial::SetRoughness (Standard_ShortReal theRoughness)
87 {
88   Graphic3d_MaterialDefinitionError_Raise_if (theRoughness < 0.f || theRoughness > 1.f,
89     "'roughness' parameter of PBR material must be in range [0, 1]")
90   myRoughness = theRoughness;
91 }
92
93 // =======================================================================
94 // function : SetIOR
95 // purpose  :
96 // =======================================================================
97 void Graphic3d_PBRMaterial::SetIOR (Standard_ShortReal theIOR)
98 {
99   Graphic3d_MaterialDefinitionError_Raise_if (theIOR < 1.f || theIOR > 3.f,
100     "'IOR' parameter of PBR material must be in range [1, 3]")
101   myIOR = theIOR;
102 }
103
104 // =======================================================================
105 // function : SetColor
106 // purpose  :
107 // =======================================================================
108 void Graphic3d_PBRMaterial::SetColor (const Quantity_ColorRGBA& theColor)
109 {
110   myColor.SetRGB (theColor.GetRGB());
111   SetAlpha (theColor.Alpha());
112 }
113
114 // =======================================================================
115 // function : SetColor
116 // purpose  :
117 // =======================================================================
118 void Graphic3d_PBRMaterial::SetColor (const Quantity_Color& theColor)
119 {
120   myColor.SetRGB (theColor);
121 }
122
123 // =======================================================================
124 // function : SetAlpha
125 // purpose  :
126 // =======================================================================
127 void Graphic3d_PBRMaterial::SetAlpha (Standard_ShortReal theAlpha)
128 {
129   Graphic3d_MaterialDefinitionError_Raise_if (theAlpha < 0.f || theAlpha > 1.f,
130     "'alpha' parameter of PBR material must be in range [0, 1]")
131   myColor.SetAlpha (theAlpha);
132 }
133
134 // =======================================================================
135 // function : SetEmission
136 // purpose  :
137 // =======================================================================
138 void Graphic3d_PBRMaterial::SetEmission (const Graphic3d_Vec3& theEmission)
139 {
140   Graphic3d_MaterialDefinitionError_Raise_if (theEmission.r() < 0.f
141                                            || theEmission.g() < 0.f
142                                            || theEmission.b() < 0.f,
143     "all components of 'emission' parameter of PBR material must be greater than 0")
144   myEmission = theEmission;
145 }
146
147 // =======================================================================
148 // function : SetBSDF
149 // purpose  :
150 // =======================================================================
151 void Graphic3d_PBRMaterial::SetBSDF (const Graphic3d_BSDF& theBSDF)
152 {
153   SetEmission (theBSDF.Le);
154
155   if (theBSDF.Absorption != Graphic3d_Vec4(0.f))
156   {
157     SetMetallic (0.f);
158     SetColor (Quantity_Color (theBSDF.Absorption.rgb()));
159     if (theBSDF.FresnelCoat.FresnelType() == Graphic3d_FM_DIELECTRIC)
160     {
161       SetIOR (theBSDF.FresnelCoat.Serialize().y());
162       SetRoughness (0.f);
163       SetAlpha (theBSDF.Absorption.a() * 4.f);
164     }
165     return;
166   }
167
168   if (theBSDF.FresnelBase.FresnelType() == Graphic3d_FM_CONSTANT
169    && theBSDF.Kt != Graphic3d_Vec3(0.f))
170   {
171     SetIOR (1.f);
172     SetRoughness (1.f);
173     SetMetallic (0.f);
174     SetColor (Quantity_Color (theBSDF.Kt));
175     SetAlpha (1.f - (theBSDF.Kt.r() + theBSDF.Kt.g() + theBSDF.Kt.b()) / 3.f);
176     return;
177   }
178
179   SetRoughness(sqrtf (theBSDF.Ks.w()));
180   if (theBSDF.FresnelBase.FresnelType() == Graphic3d_FM_DIELECTRIC
181    || theBSDF.FresnelBase.FresnelType() == Graphic3d_FM_CONSTANT)
182   {
183     SetIOR (1.5f);
184     SetColor (Quantity_Color (theBSDF.Kd));
185     SetMetallic (0.f);
186   }
187   else if (theBSDF.FresnelBase.FresnelType() == Graphic3d_FM_SCHLICK)
188   {
189     SetColor (Quantity_Color (theBSDF.FresnelBase.Serialize().rgb()));
190     SetMetallic (1.f);
191   }
192   else
193   {
194     SetColor (Quantity_Color (theBSDF.Ks.rgb()));
195     SetMetallic (1.f);
196   }
197 }
198
199 // =======================================================================
200 // function : GenerateEnvLUT
201 // purpose  :
202 // =======================================================================
203 void Graphic3d_PBRMaterial::GenerateEnvLUT (const Handle(Image_PixMap)& theLUT,
204                                             unsigned int theNbIntegralSamples)
205 {
206   if (theLUT->Format() != Image_Format_RGF)
207   {
208     throw Standard_ProgramError("LUT pix map format for PBR LUT generation must be Image_Format_RGF");
209   }
210
211   for (unsigned int y = 0; y < theLUT->SizeY(); ++y)
212   {
213     Standard_ShortReal aRoughness = Roughness(y / Standard_ShortReal(theLUT->SizeY() - 1));
214
215     for (unsigned int x = 0; x < theLUT->SizeX(); ++x)
216     {
217       Standard_ShortReal aCosV = x / Standard_ShortReal(theLUT->SizeX() - 1);
218       Graphic3d_Vec3 aView = lutGenView (aCosV);
219       unsigned int aCount = 0;
220       Graphic3d_Vec2 aResult = Graphic3d_Vec2 (0.f);
221       for (unsigned int i = 0; i < theNbIntegralSamples; ++i)
222       {
223         Graphic3d_Vec2 aHammersleyPoint = lutGenHammersley (i, theNbIntegralSamples);
224         Graphic3d_Vec3 aHalf = lutGenImportanceSample (aHammersleyPoint, aRoughness);
225         Graphic3d_Vec3 aLight = lutGenReflect (aView, aHalf);
226         if (aLight.z() >= 0.f)
227         {
228           ++aCount;
229           Standard_ShortReal aCosVH = aView.Dot (aHalf);
230           Standard_ShortReal aGeometryFactor = lutGenGeometryFactor (aLight.z(),
231                                                                      aCosV,
232                                                                      aRoughness);
233           Standard_ShortReal anIntermediateResult = 1.f - aCosVH;
234           anIntermediateResult *= anIntermediateResult;
235           anIntermediateResult *= anIntermediateResult;
236           anIntermediateResult *= 1.f - aCosVH;
237
238           aResult.x() += aGeometryFactor * (aCosVH / aHalf.z()) * (1.f - anIntermediateResult);
239           aResult.y() += aGeometryFactor * (aCosVH / aHalf.z()) * anIntermediateResult;
240         }
241       }
242
243       aResult = aResult / Standard_ShortReal(theNbIntegralSamples);
244       theLUT->ChangeValue<Graphic3d_Vec2> (theLUT->SizeY() - 1 - y, x) = aResult;
245     }
246   }
247 }
248
249 // =======================================================================
250 // function : SpecIBLMapSamplesFactor
251 // purpose  :
252 // =======================================================================
253 Standard_ShortReal Graphic3d_PBRMaterial::SpecIBLMapSamplesFactor (Standard_ShortReal theProbability,
254                                                                    Standard_ShortReal theRoughness)
255 {
256   return acosf (lutGenImportanceSampleCosTheta (theProbability, theRoughness)) * 2.f / Standard_ShortReal(M_PI);
257 }
258
259 // =======================================================================
260 // function : lutGenGeometryFactor
261 // purpose  :
262 // =======================================================================
263 Standard_ShortReal Graphic3d_PBRMaterial::lutGenGeometryFactor (Standard_ShortReal theCosL,
264                                                                 Standard_ShortReal theCosV,
265                                                                 Standard_ShortReal theRoughness)
266 {
267   Standard_ShortReal aK = theRoughness * theRoughness * 0.5f;
268
269   Standard_ShortReal aGeometryFactor = theCosL;
270   aGeometryFactor /= theCosL * (1.f - aK) + aK;
271   aGeometryFactor /= theCosV * (1.f - aK) + aK;
272
273   return aGeometryFactor;
274 }
275
276 // =======================================================================
277 // function : lutGenHammersley
278 // purpose  :
279 // =======================================================================
280 Graphic3d_Vec2 Graphic3d_PBRMaterial::lutGenHammersley (unsigned int theNumber, unsigned int theCount)
281 {
282   Standard_ShortReal aPhi2 = 0.f;
283   for (unsigned int i = 0; i < sizeof(unsigned int) * 8; ++i)
284   {
285     if ((theNumber >> i) == 0)
286     {
287       break;
288     }
289     aPhi2 += ((theNumber >> i) & 1) / Standard_ShortReal(1 << (i + 1));
290   }
291
292   return Graphic3d_Vec2(theNumber / Standard_ShortReal(theCount), aPhi2);
293 }
294
295 // =======================================================================
296 // function : lutGenImportanceSampleCosTheta
297 // purpose  :
298 // =======================================================================
299 Standard_ShortReal Graphic3d_PBRMaterial::lutGenImportanceSampleCosTheta (Standard_ShortReal theHammersleyPointComponent,
300                                                                           Standard_ShortReal theRoughness)
301 {
302   Standard_ShortReal aQuadRoughness = theRoughness * theRoughness;
303   aQuadRoughness *= aQuadRoughness;
304
305   Standard_ShortReal aTmp = 1.f + (aQuadRoughness - 1.f) * theHammersleyPointComponent;
306
307   if (aTmp != 0.f)
308   {
309     return sqrtf ((1.f - theHammersleyPointComponent) / aTmp);
310   }
311   else
312   {
313     return 0.f;
314   }
315 }
316
317 // =======================================================================
318 // function : lutGenImportanceSample
319 // purpose  :
320 // =======================================================================
321 Graphic3d_Vec3 Graphic3d_PBRMaterial::lutGenImportanceSample (const Graphic3d_Vec2 &theHammerslayPoint,
322                                                               Standard_ShortReal   theRoughness)
323 {
324   Standard_ShortReal aPhi = 2.f * Standard_ShortReal(M_PI) * theHammerslayPoint.y();
325
326   Standard_ShortReal aCosTheta = lutGenImportanceSampleCosTheta (theHammerslayPoint.x(), theRoughness);
327   Standard_ShortReal aSinTheta = sqrtf (1.f - aCosTheta * aCosTheta);
328
329   return Graphic3d_Vec3(aSinTheta * cosf (aPhi),
330                         aSinTheta * sinf (aPhi),
331                         aCosTheta);
332 }
333
334 // =======================================================================
335 // function : lutGenView
336 // purpose  :
337 // =======================================================================
338 Graphic3d_Vec3 Graphic3d_PBRMaterial::lutGenView (Standard_ShortReal theCosV)
339 {
340   return Graphic3d_Vec3(0.f, sqrtf(1.f - theCosV * theCosV), theCosV);
341 }
342
343 // =======================================================================
344 // function : lutGenReflect
345 // purpose  :
346 // =======================================================================
347 Graphic3d_Vec3 Graphic3d_PBRMaterial::lutGenReflect (const Graphic3d_Vec3 &theVector,
348                                                      const Graphic3d_Vec3 &theAxis)
349 {
350   return theAxis * theAxis.Dot(theVector) * 2.f - theVector;
351 }