67312b79 |
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 | } |
bc73b006 |
352 | |
353 | //======================================================================= |
354 | //function : DumpJson |
355 | //purpose : |
356 | //======================================================================= |
357 | void Graphic3d_PBRMaterial::DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth) const |
358 | { |
359 | OCCT_DUMP_CLASS_BEGIN (theOStream, Graphic3d_PBRMaterial) |
360 | |
361 | OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myColor) |
362 | |
363 | OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myMetallic) |
364 | OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myRoughness) |
365 | OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myEmission) |
366 | OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myIOR) |
367 | } |