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