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 <OpenGl_PBREnvironment.hxx> |
16 | |
17 | #include <Graphic3d_PBRMaterial.hxx> |
18 | #include <OpenGl_ArbFBO.hxx> |
19 | #include <OpenGl_FrameBuffer.hxx> |
20 | #include <OpenGl_ShaderManager.hxx> |
1220d98e |
21 | #include <OpenGl_ShaderProgram.hxx> |
67312b79 |
22 | #include <OSD_Timer.hxx> |
23 | #include <Message.hxx> |
24 | #include <Message_Messenger.hxx> |
25 | |
26 | #include <algorithm> |
27 | |
28 | IMPLEMENT_STANDARD_RTTIEXT(OpenGl_PBREnvironment, OpenGl_NamedResource) |
29 | |
30 | //! Constructor of this class saves necessary OpenGL states components which can be changed by OpenGl_PBREnvironment. |
31 | //! Destructor restores state back. |
32 | class OpenGl_PBREnvironmentSentry |
33 | { |
34 | public: |
35 | |
36 | OpenGl_PBREnvironmentSentry (const Handle(OpenGl_Context)& theCtx) |
37 | : myContext (theCtx) |
38 | { |
39 | backup(); |
40 | prepare(); |
41 | } |
42 | |
43 | ~OpenGl_PBREnvironmentSentry() |
44 | { |
45 | restore(); |
46 | } |
47 | |
48 | private: |
49 | |
50 | void backup() |
51 | { |
7106e4c8 |
52 | myColorMask = myContext->ColorMaskRGBA(); |
67312b79 |
53 | myContext->core11fwd->glGetIntegerv (GL_FRAMEBUFFER_BINDING, &myFBO); |
54 | myShaderProgram = myContext->ActiveProgram(); |
55 | for (unsigned int i = 0; i < 4; ++i) |
56 | { |
57 | myViewport[i] = myContext->Viewport()[i]; |
58 | } |
59 | myContext->core11fwd->glGetFloatv (GL_COLOR_CLEAR_VALUE, myClearColor); |
60 | |
61 | GLboolean aStatus = GL_TRUE; |
62 | myContext->core11fwd->glGetBooleanv (GL_DEPTH_TEST, &aStatus); |
63 | myDepthTestWasEnabled = aStatus ? Standard_True : Standard_False; |
64 | myContext->core11fwd->glGetBooleanv (GL_DEPTH_WRITEMASK, &aStatus); |
65 | myDepthWrirtingWasEnablig = aStatus ? Standard_True : Standard_False; |
66 | myContext->core11fwd->glGetBooleanv (GL_SCISSOR_TEST, &aStatus); |
67 | myScissorTestWasEnabled = aStatus ? Standard_True : Standard_False; |
68 | myContext->core11fwd->glGetIntegerv (GL_SCISSOR_BOX, myScissorBox); |
69 | } |
70 | |
71 | void prepare() |
72 | { |
73 | myContext->BindDefaultVao(); |
74 | myContext->core11fwd->glDisable (GL_DEPTH_TEST); |
75 | myContext->core11fwd->glDepthMask (GL_FALSE); |
76 | myContext->core11fwd->glDisable (GL_BLEND); |
77 | myContext->core11fwd->glDisable (GL_SCISSOR_TEST); |
7106e4c8 |
78 | myContext->SetColorMaskRGBA (NCollection_Vec4<bool> (true)); // force writes into all components, including alpha |
67312b79 |
79 | } |
80 | |
81 | void restore() |
82 | { |
7106e4c8 |
83 | myContext->SetColorMaskRGBA (myColorMask); |
8f8fe4a9 |
84 | myContext->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myFBO); |
67312b79 |
85 | myContext->BindProgram (myShaderProgram); |
86 | myContext->ResizeViewport (myViewport); |
87 | myContext->core11fwd->glClearColor (myClearColor.r(), myClearColor.g(), myClearColor.b(), myClearColor.a()); |
88 | if (myDepthTestWasEnabled) |
89 | { |
90 | myContext->core11fwd->glEnable (GL_DEPTH_TEST); |
91 | } |
92 | else |
93 | { |
94 | myContext->core11fwd->glDisable (GL_DEPTH_TEST); |
95 | } |
96 | myContext->core11fwd->glDepthMask (myDepthWrirtingWasEnablig ? GL_TRUE : GL_FALSE); |
97 | if (myScissorTestWasEnabled) |
98 | { |
99 | myContext->core11fwd->glEnable (GL_SCISSOR_TEST); |
100 | } |
101 | else |
102 | { |
103 | myContext->core11fwd->glDisable (GL_SCISSOR_TEST); |
104 | } |
105 | myContext->core11fwd->glScissor (myScissorBox[0], myScissorBox[1], myScissorBox[2], myScissorBox[3]); |
106 | } |
107 | |
108 | private: |
109 | |
110 | OpenGl_PBREnvironmentSentry (const OpenGl_PBREnvironmentSentry& ); |
111 | OpenGl_PBREnvironmentSentry& operator= (const OpenGl_PBREnvironmentSentry& ); |
112 | |
113 | private: |
114 | |
115 | const Handle(OpenGl_Context) myContext; |
116 | GLint myFBO; |
117 | Handle(OpenGl_ShaderProgram) myShaderProgram; |
7106e4c8 |
118 | NCollection_Vec4<bool> myColorMask; |
67312b79 |
119 | Standard_Boolean myDepthTestWasEnabled; |
120 | Standard_Boolean myDepthWrirtingWasEnablig; |
121 | Standard_Boolean myScissorTestWasEnabled; |
122 | Standard_Integer myScissorBox[4]; |
123 | Standard_Integer myViewport[4]; |
124 | Graphic3d_Vec4 myClearColor; |
125 | |
126 | }; |
127 | |
128 | // ======================================================================= |
129 | // function : Create |
130 | // purpose : |
131 | // ======================================================================= |
132 | Handle(OpenGl_PBREnvironment) OpenGl_PBREnvironment::Create (const Handle(OpenGl_Context)& theCtx, |
133 | unsigned int thePow2Size, |
134 | unsigned int theLevelsNumber, |
135 | const TCollection_AsciiString& theId) |
136 | { |
137 | if (theCtx->arbFBO == NULL) |
138 | { |
139 | return Handle(OpenGl_PBREnvironment)(); |
140 | } |
141 | |
142 | Handle(OpenGl_PBREnvironment) anEnvironment = new OpenGl_PBREnvironment (theCtx, thePow2Size, theLevelsNumber, theId); |
143 | if (!anEnvironment->IsComplete()) |
144 | { |
145 | theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_MEDIUM, |
146 | "Warning: PBR environment is not created. PBR material system will be ignored."); |
147 | anEnvironment->Release (theCtx.get()); |
148 | anEnvironment.Nullify(); |
149 | } |
150 | |
151 | return anEnvironment; |
152 | } |
153 | |
154 | // ======================================================================= |
155 | // function : OpenGl_PBREnvironment |
156 | // purpose : |
157 | // ======================================================================= |
158 | OpenGl_PBREnvironment::OpenGl_PBREnvironment (const Handle(OpenGl_Context)& theCtx, |
159 | unsigned int thePowOf2Size, |
160 | unsigned int theSpecMapLevelsNumber, |
161 | const TCollection_AsciiString& theId) |
162 | : OpenGl_NamedResource (theId), |
163 | myPow2Size (std::max (1u, thePowOf2Size)), |
164 | mySpecMapLevelsNumber (std::max (2u, std::min (theSpecMapLevelsNumber, std::max (1u, thePowOf2Size) + 1))), |
165 | myFBO (OpenGl_FrameBuffer::NO_FRAMEBUFFER), |
166 | myIsComplete (Standard_False), |
8f8fe4a9 |
167 | myIsNeededToBeBound (Standard_True), |
168 | myCanRenderFloat (Standard_True) |
67312b79 |
169 | { |
170 | OpenGl_PBREnvironmentSentry aSentry (theCtx); |
171 | |
172 | myIsComplete = initVAO (theCtx) |
173 | && initTextures (theCtx) |
174 | && initFBO (theCtx); |
175 | |
176 | if (myIsComplete) |
177 | { |
178 | clear (theCtx); |
179 | } |
180 | } |
181 | |
182 | // ======================================================================= |
183 | // function : Bind |
184 | // purpose : |
185 | // ======================================================================= |
186 | void OpenGl_PBREnvironment::Bind (const Handle(OpenGl_Context)& theCtx) |
187 | { |
188 | myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].Bind (theCtx); |
189 | myIBLMaps[OpenGl_TypeOfIBLMap_Specular] .Bind (theCtx); |
190 | myIsNeededToBeBound = Standard_False; |
191 | } |
192 | |
193 | // ======================================================================= |
194 | // function : Unbind |
195 | // purpose : |
196 | // ======================================================================= |
197 | void OpenGl_PBREnvironment::Unbind (const Handle(OpenGl_Context)& theCtx) |
198 | { |
199 | myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].Unbind (theCtx); |
200 | myIBLMaps[OpenGl_TypeOfIBLMap_Specular] .Unbind (theCtx); |
201 | myIsNeededToBeBound = Standard_True; |
202 | } |
203 | |
204 | // ======================================================================= |
205 | // function : Clear |
206 | // purpose : |
207 | // ======================================================================= |
208 | void OpenGl_PBREnvironment::Clear (const Handle(OpenGl_Context)& theCtx, |
209 | const Graphic3d_Vec3& theColor) |
210 | { |
211 | OpenGl_PBREnvironmentSentry aSentry (theCtx); |
212 | clear (theCtx, theColor); |
213 | } |
214 | |
215 | // ======================================================================= |
216 | // function : Bake |
217 | // purpose : |
218 | // ======================================================================= |
219 | void OpenGl_PBREnvironment::Bake (const Handle(OpenGl_Context)& theCtx, |
220 | const Handle(OpenGl_Texture)& theEnvMap, |
221 | Standard_Boolean theZIsInverted, |
222 | Standard_Boolean theIsTopDown, |
223 | Standard_Size theDiffMapNbSamples, |
224 | Standard_Size theSpecMapNbSamples, |
225 | Standard_ShortReal theProbability) |
226 | { |
227 | Standard_ProgramError_Raise_if (theEnvMap.IsNull(), "'Bake' function of 'OpenGl_PBREnvironment' can't work without source environment map") |
228 | Standard_RangeError_Raise_if (theProbability > 1.f || theProbability < 0.f, "'probability' parameter in 'Bake' function of 'OpenGl_PBREnvironment' must be in range [0, 1]") |
229 | Unbind (theCtx); |
230 | OpenGl_PBREnvironmentSentry aSentry (theCtx); |
231 | bake (theCtx, theEnvMap, theZIsInverted, theIsTopDown, theDiffMapNbSamples, theSpecMapNbSamples, theProbability); |
232 | } |
233 | |
234 | // ======================================================================= |
235 | // function : SizesAreDifferent |
236 | // purpose : |
237 | // ======================================================================= |
238 | bool OpenGl_PBREnvironment::SizesAreDifferent (unsigned int thePow2Size, |
239 | unsigned int theSpecMapLevelsNumber) const |
240 | { |
241 | thePow2Size = std::max (1u, thePow2Size); |
242 | theSpecMapLevelsNumber = std::max (2u, std::min (theSpecMapLevelsNumber, std::max (1u, thePow2Size) + 1)); |
243 | return myPow2Size != thePow2Size |
244 | || mySpecMapLevelsNumber != theSpecMapLevelsNumber; |
245 | } |
246 | |
247 | // ======================================================================= |
248 | // function : Release |
249 | // purpose : |
250 | // ======================================================================= |
251 | void OpenGl_PBREnvironment::Release (OpenGl_Context* theCtx) |
252 | { |
253 | if (myFBO != OpenGl_FrameBuffer::NO_FRAMEBUFFER) |
254 | { |
255 | if (theCtx != NULL |
256 | && theCtx->IsValid()) |
257 | { |
258 | theCtx->arbFBO->glDeleteFramebuffers (1, &myFBO); |
259 | } |
260 | myFBO = OpenGl_FrameBuffer::NO_FRAMEBUFFER; |
261 | } |
8f8fe4a9 |
262 | myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].Release (theCtx); |
67312b79 |
263 | myIBLMaps[OpenGl_TypeOfIBLMap_Specular] .Release (theCtx); |
8f8fe4a9 |
264 | myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseFallback].Release (theCtx); |
67312b79 |
265 | myVBO.Release (theCtx); |
266 | } |
267 | |
268 | // ======================================================================= |
269 | // function : ~OpenGl_PBREnvironment |
270 | // purpose : |
271 | // ======================================================================= |
272 | OpenGl_PBREnvironment::~OpenGl_PBREnvironment() |
273 | { |
274 | Release (NULL); |
275 | } |
276 | |
277 | // ======================================================================= |
278 | // function : initTextures |
279 | // purpose : |
280 | // ======================================================================= |
281 | bool OpenGl_PBREnvironment::initTextures (const Handle(OpenGl_Context)& theCtx) |
282 | { |
283 | myIBLMaps[OpenGl_TypeOfIBLMap_Specular] .Sampler()->Parameters()->SetTextureUnit (theCtx->PBRSpecIBLMapTexUnit()); |
284 | myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].Sampler()->Parameters()->SetTextureUnit (theCtx->PBRDiffIBLMapSHTexUnit()); |
285 | myIBLMaps[OpenGl_TypeOfIBLMap_Specular] .Sampler()->Parameters()->SetFilter (Graphic3d_TOTF_TRILINEAR); |
8f8fe4a9 |
286 | myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].Sampler()->Parameters()->SetFilter (Graphic3d_TOTF_NEAREST); |
67312b79 |
287 | myIBLMaps[OpenGl_TypeOfIBLMap_Specular] .Sampler()->Parameters()->SetLevelsRange (mySpecMapLevelsNumber - 1); |
8f8fe4a9 |
288 | myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseFallback].Sampler()->Parameters()->SetFilter (Graphic3d_TOTF_NEAREST); |
67312b79 |
289 | |
290 | // NVIDIA's driver didn't work properly with 3 channel texture for diffuse SH coefficients so that alpha channel has been added |
8f8fe4a9 |
291 | if (!myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].Init (theCtx, |
292 | OpenGl_TextureFormat::FindFormat (theCtx, Image_Format_RGBAF, false), |
293 | Graphic3d_Vec2i (9, 1), Graphic3d_TOT_2D)) |
294 | { |
295 | Message::SendFail() << "OpenGl_PBREnvironment, DiffuseSH texture creation failed"; |
296 | return false; |
297 | } |
298 | |
299 | if (!myIBLMaps[OpenGl_TypeOfIBLMap_Specular].InitCubeMap (theCtx, Handle(Graphic3d_CubeMap)(), |
300 | Standard_Size(1) << myPow2Size, Image_Format_RGB, true, false)) |
301 | { |
302 | Message::SendFail() << "OpenGl_PBREnvironment, Specular texture creation failed"; |
303 | return false; |
304 | } |
305 | |
306 | if (!myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseFallback].Init (theCtx, |
307 | OpenGl_TextureFormat::FindFormat (theCtx, Image_Format_RGBA, false), |
308 | Graphic3d_Vec2i (10, 4), Graphic3d_TOT_2D)) |
309 | { |
310 | Message::SendFail() << "OpenGl_PBREnvironment, DiffuseFallback texture creation failed"; |
311 | return false; |
312 | } |
313 | |
314 | return true; |
67312b79 |
315 | } |
316 | |
317 | // ======================================================================= |
318 | // function : initVAO |
319 | // purpose : |
320 | // ======================================================================= |
321 | bool OpenGl_PBREnvironment::initVAO (const Handle(OpenGl_Context)& theCtx) |
322 | { |
323 | const float aVertexPos[] = |
324 | { |
325 | -1.f, -1.f, 0.f, 0.f, |
326 | 1.f, -1.f, 0.f, 0.f, |
327 | -1.f, 1.f, 0.f, 0.f, |
328 | 1.f, 1.f, 0.f, 0.f |
329 | }; |
330 | return myVBO.Init (theCtx, 4, 4, aVertexPos); |
331 | } |
332 | |
333 | // ======================================================================= |
334 | // function : initFBO |
335 | // purpose : |
336 | // ======================================================================= |
337 | bool OpenGl_PBREnvironment::initFBO (const Handle(OpenGl_Context)& theCtx) |
338 | { |
339 | theCtx->arbFBO->glGenFramebuffers (1, &myFBO); |
340 | return checkFBOComplentess (theCtx); |
341 | } |
342 | |
343 | // ======================================================================= |
344 | // function : processDiffIBLMap |
345 | // purpose : |
346 | // ======================================================================= |
347 | bool OpenGl_PBREnvironment::processDiffIBLMap (const Handle(OpenGl_Context)& theCtx, |
8f8fe4a9 |
348 | const BakingParams* theDrawParams) |
67312b79 |
349 | { |
8f8fe4a9 |
350 | const OpenGl_TypeOfIBLMap aRendMapId = myCanRenderFloat ? OpenGl_TypeOfIBLMap_DiffuseSH : OpenGl_TypeOfIBLMap_DiffuseFallback; |
351 | Image_PixMap anImageF; |
352 | if (!myCanRenderFloat) |
353 | { |
354 | anImageF.InitZero (Image_Format_RGBAF, myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].SizeX(), myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].SizeY()); |
355 | } |
356 | |
357 | theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
358 | myIBLMaps[aRendMapId].TextureId(), 0); |
359 | const Standard_Integer aViewport[4] = { 0, 0, 9, myCanRenderFloat ? 1 : 3 }; |
67312b79 |
360 | theCtx->ResizeViewport(aViewport); |
8f8fe4a9 |
361 | if (theDrawParams != NULL) |
67312b79 |
362 | { |
8f8fe4a9 |
363 | if (!theCtx->ShaderManager()->BindPBREnvBakingProgram (aRendMapId)) |
364 | { |
365 | return false; |
366 | } |
67312b79 |
367 | |
8f8fe4a9 |
368 | const Handle(OpenGl_ShaderProgram)& aProg = theCtx->ActiveProgram(); |
369 | aProg->SetSampler (theCtx, "uEnvMap", theCtx->PBRSpecIBLMapTexUnit()); |
370 | aProg->SetUniform (theCtx, "uZCoeff", theDrawParams->IsZInverted ? -1 : 1); |
371 | aProg->SetUniform (theCtx, "uYCoeff", theDrawParams->IsTopDown ? 1 : -1); |
372 | aProg->SetUniform (theCtx, "uSamplesNum", Standard_Integer(theDrawParams->NbDiffSamples)); |
373 | |
374 | myVBO.BindAttribute (theCtx, Graphic3d_TOA_POS); |
67312b79 |
375 | theCtx->core11fwd->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); |
8f8fe4a9 |
376 | myVBO.UnbindAttribute (theCtx, Graphic3d_TOA_POS); |
377 | |
378 | if (!myCanRenderFloat) |
379 | { |
380 | // unpack RGBA8 values to floats |
381 | Image_PixMap anImageIn; |
382 | anImageIn.InitZero (myCanRenderFloat ? Image_Format_RGBAF : Image_Format_RGBA, aViewport[2], aViewport[3]); |
383 | theCtx->core11fwd->glReadPixels (0, 0, aViewport[2], aViewport[3], |
384 | GL_RGBA, myCanRenderFloat ? GL_FLOAT : GL_UNSIGNED_BYTE, anImageIn.ChangeData()); |
385 | const GLenum anErr = theCtx->core11fwd->glGetError(); |
386 | if (anErr != GL_NO_ERROR) |
387 | { |
388 | theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, |
389 | TCollection_AsciiString ("Unable to read PBR baking diffuse texture. Error ") + OpenGl_Context::FormatGlError (anErr)); |
390 | } |
391 | for (Standard_Size aValIter = 0; aValIter < anImageIn.SizeX(); ++aValIter) |
392 | { |
393 | Graphic3d_Vec4 aVal; |
394 | if (myCanRenderFloat) |
395 | { |
396 | aVal = anImageIn.Value<Graphic3d_Vec4> (0, aValIter); |
397 | } |
398 | else |
399 | { |
400 | const int32_t aPacked[3] = { anImageIn.Value<int32_t> (2, aValIter), anImageIn.Value<int32_t> (1, aValIter), anImageIn.Value<int32_t> (0, aValIter) }; |
401 | aVal[0] = aPacked[0] / 2147483647.0f; |
402 | aVal[1] = aPacked[1] / 2147483647.0f; |
403 | aVal[2] = aPacked[2] / 2147483647.0f; |
404 | } |
405 | anImageF.ChangeValue<Graphic3d_Vec4> (0, aValIter) = aVal; |
406 | } |
407 | } |
67312b79 |
408 | } |
409 | else |
410 | { |
8f8fe4a9 |
411 | if (myCanRenderFloat) |
412 | { |
413 | theCtx->core11fwd->glClear (GL_COLOR_BUFFER_BIT); |
67312b79 |
414 | |
8f8fe4a9 |
415 | theCtx->core11fwd->glEnable (GL_SCISSOR_TEST); |
416 | theCtx->core11fwd->glClearColor (0.f, 0.f, 0.f, 1.f); |
417 | theCtx->core11fwd->glScissor (1, 0, 8, 1); |
418 | theCtx->core11fwd->glClear (GL_COLOR_BUFFER_BIT); |
419 | theCtx->core11fwd->glDisable (GL_SCISSOR_TEST); |
420 | } |
421 | else |
422 | { |
423 | anImageF.ChangeValue<Graphic3d_Vec4> (0, 0) = Graphic3d_Vec4 (1.0f); |
424 | for (Standard_Size aValIter = 1; aValIter < anImageF.SizeX(); ++aValIter) |
425 | { |
426 | anImageF.ChangeValue<Graphic3d_Vec4> (0, aValIter) = Graphic3d_Vec4 (0.0f, 0.0f, 0.0f, 1.0f); |
427 | } |
428 | } |
429 | } |
430 | |
431 | if (!myCanRenderFloat) |
432 | { |
433 | if (!myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].Init (theCtx, |
434 | OpenGl_TextureFormat::FindFormat (theCtx, Image_Format_RGBAF, false), |
435 | Graphic3d_Vec2i (9, 1), Graphic3d_TOT_2D, &anImageF)) |
436 | { |
437 | Message::SendFail() << "OpenGl_PBREnvironment, DiffuseSH texture update failed"; |
438 | return false; |
439 | } |
67312b79 |
440 | } |
441 | |
442 | return true; |
443 | } |
444 | |
445 | // ======================================================================= |
446 | // function : processSpecIBLMap |
447 | // purpose : |
448 | // ======================================================================= |
449 | bool OpenGl_PBREnvironment::processSpecIBLMap (const Handle(OpenGl_Context)& theCtx, |
8f8fe4a9 |
450 | const BakingParams* theDrawParams) |
67312b79 |
451 | { |
8f8fe4a9 |
452 | if (theDrawParams != NULL) |
67312b79 |
453 | { |
8f8fe4a9 |
454 | if (!theCtx->ShaderManager()->BindPBREnvBakingProgram (OpenGl_TypeOfIBLMap_Specular)) |
455 | { |
456 | return false; |
457 | } |
458 | |
459 | const Handle(OpenGl_ShaderProgram)& aProg = theCtx->ActiveProgram(); |
460 | const float aSolidAngleSource = float(4.0 * M_PI / (6.0 * float(theDrawParams->EnvMapSize * theDrawParams->EnvMapSize))); |
461 | aProg->SetSampler (theCtx, "uEnvMap", theCtx->PBRSpecIBLMapTexUnit()); |
462 | aProg->SetUniform (theCtx, "uZCoeff", theDrawParams->IsZInverted ? -1 : 1); |
463 | aProg->SetUniform (theCtx, "uYCoeff", theDrawParams->IsTopDown ? 1 : -1); |
464 | aProg->SetUniform (theCtx, "occNbSpecIBLLevels", Standard_Integer(mySpecMapLevelsNumber)); |
465 | aProg->SetUniform (theCtx, "uEnvSolidAngleSource", aSolidAngleSource); |
466 | myVBO.BindAttribute (theCtx, Graphic3d_TOA_POS); |
67312b79 |
467 | } |
468 | |
8f8fe4a9 |
469 | const bool canRenderMipmaps = theCtx->hasFboRenderMipmap; |
470 | const OpenGl_TextureFormat aTexFormat = OpenGl_TextureFormat::FindSizedFormat (theCtx, myIBLMaps[OpenGl_TypeOfIBLMap_Specular].SizedFormat()); |
471 | #if !defined(GL_ES_VERSION_2_0) |
472 | const GLint anIntFormat = aTexFormat.InternalFormat(); |
473 | #else |
474 | // ES 2.0 does not support sized formats and format conversions - them detected from data type |
475 | const GLint anIntFormat = theCtx->IsGlGreaterEqual (3, 0) ? aTexFormat.InternalFormat() : aTexFormat.PixelFormat(); |
476 | #endif |
477 | |
67312b79 |
478 | for (int aLevelIter = mySpecMapLevelsNumber - 1;; --aLevelIter) |
479 | { |
480 | const Standard_Integer aSize = 1 << (myPow2Size - aLevelIter); |
481 | const Standard_Integer aViewport[4] = { 0, 0, aSize, aSize }; |
482 | theCtx->ResizeViewport (aViewport); |
8f8fe4a9 |
483 | if (theDrawParams != NULL) |
67312b79 |
484 | { |
8f8fe4a9 |
485 | const Standard_Integer aNbSamples = Standard_Integer(Graphic3d_PBRMaterial::SpecIBLMapSamplesFactor (theDrawParams->Probability, |
486 | aLevelIter / float (mySpecMapLevelsNumber - 1)) * theDrawParams->NbSpecSamples); |
487 | theCtx->ActiveProgram()->SetUniform (theCtx, "uSamplesNum", aNbSamples); |
67312b79 |
488 | theCtx->ActiveProgram()->SetUniform (theCtx, "uCurrentLevel", aLevelIter); |
489 | } |
490 | |
491 | for (Standard_Integer aSideIter = 0; aSideIter < 6; ++aSideIter) |
492 | { |
8f8fe4a9 |
493 | theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + aSideIter, |
494 | myIBLMaps[OpenGl_TypeOfIBLMap_Specular].TextureId(), canRenderMipmaps ? aLevelIter : 0); |
495 | if (theDrawParams != NULL) |
67312b79 |
496 | { |
497 | theCtx->ActiveProgram()->SetUniform(theCtx, "uCurrentSide", aSideIter); |
498 | theCtx->core11fwd->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
499 | } |
500 | else |
501 | { |
502 | theCtx->core11fwd->glClear(GL_COLOR_BUFFER_BIT); |
503 | } |
8f8fe4a9 |
504 | |
505 | if (!canRenderMipmaps |
506 | && aLevelIter != 0) |
507 | { |
508 | myIBLMaps[OpenGl_TypeOfIBLMap_Specular].Bind (theCtx, Graphic3d_TextureUnit_1); |
509 | theCtx->core20fwd->glCopyTexImage2D (GL_TEXTURE_CUBE_MAP_POSITIVE_X + aSideIter, aLevelIter, anIntFormat, 0, 0, (GLsizei )aSize, (GLsizei )aSize, 0); |
510 | myIBLMaps[OpenGl_TypeOfIBLMap_Specular].Unbind (theCtx, Graphic3d_TextureUnit_1); |
511 | const GLenum anErr = theCtx->core11fwd->glGetError(); |
512 | if (anErr != GL_NO_ERROR) |
513 | { |
514 | theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, |
515 | TCollection_AsciiString ("Unable to copy cubemap mipmap level. Error ") + OpenGl_Context::FormatGlError (anErr)); |
516 | } |
517 | } |
67312b79 |
518 | } |
519 | |
520 | if (aLevelIter == 0) |
521 | { |
522 | break; |
523 | } |
524 | } |
525 | |
8f8fe4a9 |
526 | if (theDrawParams != NULL) |
527 | { |
528 | myVBO.UnbindAttribute (theCtx, Graphic3d_TOA_POS); |
529 | } |
530 | |
67312b79 |
531 | return true; |
532 | } |
533 | |
534 | // ======================================================================= |
535 | // function : checkFBOCompletness |
536 | // purpose : |
537 | // ======================================================================= |
8f8fe4a9 |
538 | bool OpenGl_PBREnvironment::checkFBOComplentess (const Handle(OpenGl_Context)& theCtx) |
67312b79 |
539 | { |
8f8fe4a9 |
540 | myCanRenderFloat = true; |
541 | theCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myFBO); |
542 | theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
67312b79 |
543 | myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].TextureId(), 0); |
8f8fe4a9 |
544 | if (theCtx->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) |
67312b79 |
545 | { |
8f8fe4a9 |
546 | // Some WebGL 1.0 and OpenGL ES 2.0 implementations support float textures which cannot be used as render targets. |
547 | // This capability could be exposed by WEBGL_color_buffer_float/EXT_color_buffer_float extensions, |
548 | // but the simplest way is just to check FBO status. |
549 | // The fallback solution involves rendering into RGBA8 texture with floating values packed, |
550 | // and using glReadPixels() + conversion + texture upload of computed values. |
551 | myCanRenderFloat = false; |
552 | theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
553 | myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseFallback].TextureId(), 0); |
554 | if (theCtx->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) |
555 | { |
556 | Message::SendTrace() << "OpenGl_PBREnvironment, incomplete FBO for diffuse map"; |
557 | return false; |
558 | } |
67312b79 |
559 | } |
8f8fe4a9 |
560 | |
67312b79 |
561 | for (Standard_Integer aSideIter = 0; aSideIter < 6; ++aSideIter) |
562 | { |
563 | for (unsigned int aLevel = 0; aLevel < mySpecMapLevelsNumber; ++aLevel) |
564 | { |
8f8fe4a9 |
565 | if (!theCtx->hasFboRenderMipmap |
566 | && aLevel != 0) |
567 | { |
568 | continue; |
569 | } |
570 | |
571 | theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + aSideIter, |
67312b79 |
572 | myIBLMaps[OpenGl_TypeOfIBLMap_Specular].TextureId(), aLevel); |
8f8fe4a9 |
573 | if (theCtx->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) |
67312b79 |
574 | { |
8f8fe4a9 |
575 | Message::SendTrace() << "OpenGl_PBREnvironment, incomplete FBO for specular map " << aSideIter << " "<< aLevel; |
67312b79 |
576 | return false; |
577 | } |
578 | } |
579 | } |
580 | return true; |
581 | } |
582 | |
583 | // ======================================================================= |
584 | // function : bake |
585 | // purpose : |
586 | // ======================================================================= |
587 | void OpenGl_PBREnvironment::bake (const Handle(OpenGl_Context)& theCtx, |
588 | const Handle(OpenGl_Texture)& theEnvMap, |
589 | Standard_Boolean theZIsInverted, |
590 | Standard_Boolean theIsTopDown, |
591 | Standard_Size theDiffNbSamples, |
592 | Standard_Size theSpecNbSamples, |
593 | Standard_ShortReal theProbability) |
594 | { |
595 | myIsNeededToBeBound = Standard_True; |
8f8fe4a9 |
596 | |
67312b79 |
597 | theEnvMap->Bind (theCtx, theCtx->PBRSpecIBLMapTexUnit()); |
8f8fe4a9 |
598 | theCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myFBO); |
67312b79 |
599 | |
600 | OSD_Timer aTimer; |
601 | aTimer.Start(); |
8f8fe4a9 |
602 | BakingParams aDrawParams; |
603 | aDrawParams.NbSpecSamples = theSpecNbSamples; |
604 | aDrawParams.NbDiffSamples = theDiffNbSamples; |
605 | aDrawParams.EnvMapSize = theEnvMap->SizeX(); |
606 | aDrawParams.Probability = theProbability; |
607 | aDrawParams.IsZInverted = theZIsInverted; |
608 | aDrawParams.IsTopDown = theIsTopDown; |
609 | if (processSpecIBLMap (theCtx, &aDrawParams) |
610 | && processDiffIBLMap (theCtx, &aDrawParams)) |
67312b79 |
611 | { |
a87b1b37 |
612 | Message::SendTrace(TCollection_AsciiString() |
67312b79 |
613 | + "IBL " + myIBLMaps[OpenGl_TypeOfIBLMap_Specular].SizeX() + "x" + myIBLMaps[OpenGl_TypeOfIBLMap_Specular].SizeY() |
a87b1b37 |
614 | + " is baked in " + aTimer.ElapsedTime() + " s"); |
67312b79 |
615 | } |
616 | else |
617 | { |
618 | theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PERFORMANCE, 0, GL_DEBUG_SEVERITY_HIGH, |
619 | TCollection_AsciiString("Error: baking PBR environment ") + myIBLMaps[OpenGl_TypeOfIBLMap_Specular].SizeX() |
620 | + "x" + myIBLMaps[OpenGl_TypeOfIBLMap_Specular].SizeY() + " takes too much time!."); |
621 | clear (theCtx, Graphic3d_Vec3(1.0f)); |
622 | } |
623 | |
67312b79 |
624 | theEnvMap->Unbind (theCtx, theCtx->PBREnvLUTTexUnit()); |
625 | } |
626 | |
627 | // ======================================================================= |
628 | // function : clear |
629 | // purpose : |
630 | // ======================================================================= |
631 | void OpenGl_PBREnvironment::clear (const Handle(OpenGl_Context)& theCtx, |
632 | const Graphic3d_Vec3& theColor) |
633 | { |
634 | myIsNeededToBeBound = Standard_True; |
8f8fe4a9 |
635 | theCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myFBO); |
67312b79 |
636 | theCtx->core11fwd->glClearColor (theColor.r(), theColor.g(), theColor.b(), 1.f); |
637 | |
8f8fe4a9 |
638 | processSpecIBLMap (theCtx, NULL); |
639 | processDiffIBLMap (theCtx, NULL); |
67312b79 |
640 | } |