diff --git a/src/OpenGl/OpenGl_Context.cxx b/src/OpenGl/OpenGl_Context.cxx index 16c68647c4..9147d26bcf 100644 --- a/src/OpenGl/OpenGl_Context.cxx +++ b/src/OpenGl/OpenGl_Context.cxx @@ -158,6 +158,7 @@ OpenGl_Context::OpenGl_Context (const Handle(OpenGl_Caps)& theCaps) hasTexSRGB (Standard_False), hasFboSRGB (Standard_False), hasSRGBControl (Standard_False), + hasFboRenderMipmap (Standard_False), #if defined(GL_ES_VERSION_2_0) hasFlatShading (OpenGl_FeatureNotAvailable), #else @@ -1228,6 +1229,20 @@ void OpenGl_Context::ReadGlVersion (Standard_Integer& theGlVerMajor, // read numbers theGlVerMajor = atoi (aMajorStr); theGlVerMinor = atoi (aMinorStr); +#if defined(__EMSCRIPTEN__) + if (theGlVerMajor >= 3) + { + if (!toCheckVer3 + || ::strstr (aVerStr, "WebGL 1.0") != NULL) + { + Message::SendWarning() << "Warning! OpenGL context reports version " << theGlVerMajor << "." << theGlVerMinor + << " but WebGL 2.0 was unavailable\n" + << "Fallback to OpenGL ES 2.0 will be used instead of reported version"; + theGlVerMajor = 2; + theGlVerMinor = 0; + } + } +#endif if (theGlVerMajor <= 0) { @@ -1540,6 +1555,8 @@ void OpenGl_Context::init (const Standard_Boolean theIsCoreProfile) || CheckExtension ("GL_OES_rgb8_rgba8"); hasTexSRGB = IsGlGreaterEqual (3, 0); hasFboSRGB = IsGlGreaterEqual (3, 0); + hasFboRenderMipmap = IsGlGreaterEqual (3, 0) + || CheckExtension ("GL_OES_fbo_render_mipmap"); hasSRGBControl = CheckExtension ("GL_EXT_sRGB_write_control"); // NPOT textures has limited support within OpenGL ES 2.0 // which are relaxed by OpenGL ES 3.0 or some extensions @@ -1983,6 +2000,7 @@ void OpenGl_Context::init (const Standard_Boolean theIsCoreProfile) hasTexSRGB = IsGlGreaterEqual (2, 1); hasFboSRGB = IsGlGreaterEqual (2, 1); hasSRGBControl = hasFboSRGB; + hasFboRenderMipmap = Standard_True; arbDrawBuffers = CheckExtension ("GL_ARB_draw_buffers"); arbNPTW = CheckExtension ("GL_ARB_texture_non_power_of_two"); arbTexFloat = IsGlGreaterEqual (3, 0) @@ -3415,10 +3433,11 @@ void OpenGl_Context::init (const Standard_Boolean theIsCoreProfile) // check whether PBR shading model is supported myHasPBR = arbFBO != NULL && myMaxTexCombined >= 4 - && arbTexRG && arbTexFloat && (IsGlGreaterEqual (3, 0) - #if !defined(GL_ES_VERSION_2_0) + #if defined(GL_ES_VERSION_2_0) + || CheckExtension ("GL_EXT_shader_texture_lod") + #else || (IsGlGreaterEqual (2, 1) && CheckExtension ("GL_EXT_gpu_shader4")) #endif ); diff --git a/src/OpenGl/OpenGl_Context.hxx b/src/OpenGl/OpenGl_Context.hxx index 3c25ca97d4..8bde7aaea4 100644 --- a/src/OpenGl/OpenGl_Context.hxx +++ b/src/OpenGl/OpenGl_Context.hxx @@ -1085,6 +1085,7 @@ public: //! @name extensions Standard_Boolean hasTexSRGB; //!< sRGB texture formats (desktop OpenGL 2.1, OpenGL ES 3.0 or GL_EXT_texture_sRGB) Standard_Boolean hasFboSRGB; //!< sRGB FBO render targets (desktop OpenGL 2.1, OpenGL ES 3.0) Standard_Boolean hasSRGBControl; //!< sRGB write control (any desktop OpenGL, OpenGL ES + GL_EXT_sRGB_write_control extension) + Standard_Boolean hasFboRenderMipmap; //!< FBO render target could be non-zero mimap level of texture OpenGl_FeatureFlag hasFlatShading; //!< Complex flag indicating support of Flat shading (Graphic3d_TOSM_FACET) (always available on desktop; on OpenGL ES - since 3.0 or as extension GL_OES_standard_derivatives) OpenGl_FeatureFlag hasGlslBitwiseOps; //!< GLSL supports bitwise operations; OpenGL 3.0 / OpenGL ES 3.0 (GLSL 130 / GLSL ES 300) or OpenGL 2.1 + GL_EXT_gpu_shader4 OpenGl_FeatureFlag hasDrawBuffers; //!< Complex flag indicating support of multiple draw buffers (desktop OpenGL 2.0, OpenGL ES 3.0, GL_ARB_draw_buffers, GL_EXT_draw_buffers) diff --git a/src/OpenGl/OpenGl_PBREnvironment.cxx b/src/OpenGl/OpenGl_PBREnvironment.cxx index e68f7c714d..14188a7e42 100644 --- a/src/OpenGl/OpenGl_PBREnvironment.cxx +++ b/src/OpenGl/OpenGl_PBREnvironment.cxx @@ -77,7 +77,7 @@ private: void restore() { - myContext->arbFBO->glBindFramebuffer (GL_DRAW_FRAMEBUFFER, myFBO); + myContext->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myFBO); myContext->BindProgram (myShaderProgram); myContext->ResizeViewport (myViewport); myContext->core11fwd->glClearColor (myClearColor.r(), myClearColor.g(), myClearColor.b(), myClearColor.a()); @@ -159,7 +159,8 @@ OpenGl_PBREnvironment::OpenGl_PBREnvironment (const Handle(OpenGl_Context)& the mySpecMapLevelsNumber (std::max (2u, std::min (theSpecMapLevelsNumber, std::max (1u, thePowOf2Size) + 1))), myFBO (OpenGl_FrameBuffer::NO_FRAMEBUFFER), myIsComplete (Standard_False), - myIsNeededToBeBound (Standard_True) + myIsNeededToBeBound (Standard_True), + myCanRenderFloat (Standard_True) { OpenGl_PBREnvironmentSentry aSentry (theCtx); @@ -253,8 +254,9 @@ void OpenGl_PBREnvironment::Release (OpenGl_Context* theCtx) } myFBO = OpenGl_FrameBuffer::NO_FRAMEBUFFER; } - myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].Release(theCtx); + myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].Release (theCtx); myIBLMaps[OpenGl_TypeOfIBLMap_Specular] .Release (theCtx); + myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseFallback].Release (theCtx); myVBO.Release (theCtx); } @@ -276,16 +278,35 @@ bool OpenGl_PBREnvironment::initTextures (const Handle(OpenGl_Context)& theCtx) myIBLMaps[OpenGl_TypeOfIBLMap_Specular] .Sampler()->Parameters()->SetTextureUnit (theCtx->PBRSpecIBLMapTexUnit()); myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].Sampler()->Parameters()->SetTextureUnit (theCtx->PBRDiffIBLMapSHTexUnit()); myIBLMaps[OpenGl_TypeOfIBLMap_Specular] .Sampler()->Parameters()->SetFilter (Graphic3d_TOTF_TRILINEAR); - myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].Sampler()->Parameters()->SetFilter(Graphic3d_TOTF_NEAREST); + myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].Sampler()->Parameters()->SetFilter (Graphic3d_TOTF_NEAREST); myIBLMaps[OpenGl_TypeOfIBLMap_Specular] .Sampler()->Parameters()->SetLevelsRange (mySpecMapLevelsNumber - 1); + myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseFallback].Sampler()->Parameters()->SetFilter (Graphic3d_TOTF_NEAREST); // NVIDIA's driver didn't work properly with 3 channel texture for diffuse SH coefficients so that alpha channel has been added - return myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].Init (theCtx, - OpenGl_TextureFormat::FindFormat (theCtx, Image_Format_RGBAF, false), - Graphic3d_Vec2i (9, 1), - Graphic3d_TOT_2D) - && myIBLMaps[OpenGl_TypeOfIBLMap_Specular].InitCubeMap (theCtx, Handle(Graphic3d_CubeMap)(), - Standard_Size(1) << myPow2Size, Image_Format_RGB, true, false); + if (!myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].Init (theCtx, + OpenGl_TextureFormat::FindFormat (theCtx, Image_Format_RGBAF, false), + Graphic3d_Vec2i (9, 1), Graphic3d_TOT_2D)) + { + Message::SendFail() << "OpenGl_PBREnvironment, DiffuseSH texture creation failed"; + return false; + } + + if (!myIBLMaps[OpenGl_TypeOfIBLMap_Specular].InitCubeMap (theCtx, Handle(Graphic3d_CubeMap)(), + Standard_Size(1) << myPow2Size, Image_Format_RGB, true, false)) + { + Message::SendFail() << "OpenGl_PBREnvironment, Specular texture creation failed"; + return false; + } + + if (!myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseFallback].Init (theCtx, + OpenGl_TextureFormat::FindFormat (theCtx, Image_Format_RGBA, false), + Graphic3d_Vec2i (10, 4), Graphic3d_TOT_2D)) + { + Message::SendFail() << "OpenGl_PBREnvironment, DiffuseFallback texture creation failed"; + return false; + } + + return true; } // ======================================================================= @@ -319,28 +340,98 @@ bool OpenGl_PBREnvironment::initFBO (const Handle(OpenGl_Context)& theCtx) // purpose : // ======================================================================= bool OpenGl_PBREnvironment::processDiffIBLMap (const Handle(OpenGl_Context)& theCtx, - Standard_Boolean theIsDrawAction, - Standard_Size theNbSamples) + const BakingParams* theDrawParams) { - theCtx->arbFBO->glFramebufferTexture2D (GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].TextureId(), 0); - const Standard_Integer aViewport[4] = { 0, 0, 9, 1 }; - theCtx->ResizeViewport(aViewport); - if (theIsDrawAction) + const OpenGl_TypeOfIBLMap aRendMapId = myCanRenderFloat ? OpenGl_TypeOfIBLMap_DiffuseSH : OpenGl_TypeOfIBLMap_DiffuseFallback; + Image_PixMap anImageF; + if (!myCanRenderFloat) { - theCtx->ActiveProgram()->SetUniform(theCtx, "occNbSpecIBLLevels", 0); - theCtx->ActiveProgram()->SetUniform(theCtx, "uSamplesNum", static_cast(theNbSamples)); + anImageF.InitZero (Image_Format_RGBAF, myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].SizeX(), myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].SizeY()); + } + theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + myIBLMaps[aRendMapId].TextureId(), 0); + const Standard_Integer aViewport[4] = { 0, 0, 9, myCanRenderFloat ? 1 : 3 }; + theCtx->ResizeViewport(aViewport); + if (theDrawParams != NULL) + { + if (!theCtx->ShaderManager()->BindPBREnvBakingProgram (aRendMapId)) + { + return false; + } + + const Handle(OpenGl_ShaderProgram)& aProg = theCtx->ActiveProgram(); + aProg->SetSampler (theCtx, "uEnvMap", theCtx->PBRSpecIBLMapTexUnit()); + aProg->SetUniform (theCtx, "uZCoeff", theDrawParams->IsZInverted ? -1 : 1); + aProg->SetUniform (theCtx, "uYCoeff", theDrawParams->IsTopDown ? 1 : -1); + aProg->SetUniform (theCtx, "uSamplesNum", Standard_Integer(theDrawParams->NbDiffSamples)); + + myVBO.BindAttribute (theCtx, Graphic3d_TOA_POS); theCtx->core11fwd->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); + myVBO.UnbindAttribute (theCtx, Graphic3d_TOA_POS); + + if (!myCanRenderFloat) + { + // unpack RGBA8 values to floats + Image_PixMap anImageIn; + anImageIn.InitZero (myCanRenderFloat ? Image_Format_RGBAF : Image_Format_RGBA, aViewport[2], aViewport[3]); + theCtx->core11fwd->glReadPixels (0, 0, aViewport[2], aViewport[3], + GL_RGBA, myCanRenderFloat ? GL_FLOAT : GL_UNSIGNED_BYTE, anImageIn.ChangeData()); + const GLenum anErr = theCtx->core11fwd->glGetError(); + if (anErr != GL_NO_ERROR) + { + theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, + TCollection_AsciiString ("Unable to read PBR baking diffuse texture. Error ") + OpenGl_Context::FormatGlError (anErr)); + } + for (Standard_Size aValIter = 0; aValIter < anImageIn.SizeX(); ++aValIter) + { + Graphic3d_Vec4 aVal; + if (myCanRenderFloat) + { + aVal = anImageIn.Value (0, aValIter); + } + else + { + const int32_t aPacked[3] = { anImageIn.Value (2, aValIter), anImageIn.Value (1, aValIter), anImageIn.Value (0, aValIter) }; + aVal[0] = aPacked[0] / 2147483647.0f; + aVal[1] = aPacked[1] / 2147483647.0f; + aVal[2] = aPacked[2] / 2147483647.0f; + } + anImageF.ChangeValue (0, aValIter) = aVal; + } + } } else { - theCtx->core11fwd->glClear (GL_COLOR_BUFFER_BIT); + if (myCanRenderFloat) + { + theCtx->core11fwd->glClear (GL_COLOR_BUFFER_BIT); - theCtx->core11fwd->glEnable (GL_SCISSOR_TEST); - theCtx->core11fwd->glClearColor (0.f, 0.f, 0.f, 1.f); - theCtx->core11fwd->glScissor (1, 0, 8, 1); - theCtx->core11fwd->glClear (GL_COLOR_BUFFER_BIT); + theCtx->core11fwd->glEnable (GL_SCISSOR_TEST); + theCtx->core11fwd->glClearColor (0.f, 0.f, 0.f, 1.f); + theCtx->core11fwd->glScissor (1, 0, 8, 1); + theCtx->core11fwd->glClear (GL_COLOR_BUFFER_BIT); + theCtx->core11fwd->glDisable (GL_SCISSOR_TEST); + } + else + { + anImageF.ChangeValue (0, 0) = Graphic3d_Vec4 (1.0f); + for (Standard_Size aValIter = 1; aValIter < anImageF.SizeX(); ++aValIter) + { + anImageF.ChangeValue (0, aValIter) = Graphic3d_Vec4 (0.0f, 0.0f, 0.0f, 1.0f); + } + } + } + + if (!myCanRenderFloat) + { + if (!myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].Init (theCtx, + OpenGl_TextureFormat::FindFormat (theCtx, Image_Format_RGBAF, false), + Graphic3d_Vec2i (9, 1), Graphic3d_TOT_2D, &anImageF)) + { + Message::SendFail() << "OpenGl_PBREnvironment, DiffuseSH texture update failed"; + return false; + } } return true; @@ -351,34 +442,52 @@ bool OpenGl_PBREnvironment::processDiffIBLMap (const Handle(OpenGl_Context)& the // purpose : // ======================================================================= bool OpenGl_PBREnvironment::processSpecIBLMap (const Handle(OpenGl_Context)& theCtx, - Standard_Boolean theIsDrawAction, - Standard_Integer theEnvMapSize, - Standard_Size theNbSamples, - Standard_ShortReal theProbability) + const BakingParams* theDrawParams) { - if (theIsDrawAction) + if (theDrawParams != NULL) { - theCtx->ActiveProgram()->SetUniform (theCtx, "occNbSpecIBLLevels", Standard_Integer(mySpecMapLevelsNumber)); - theCtx->ActiveProgram()->SetUniform (theCtx, "uEnvMapSize", theEnvMapSize); + if (!theCtx->ShaderManager()->BindPBREnvBakingProgram (OpenGl_TypeOfIBLMap_Specular)) + { + return false; + } + + const Handle(OpenGl_ShaderProgram)& aProg = theCtx->ActiveProgram(); + const float aSolidAngleSource = float(4.0 * M_PI / (6.0 * float(theDrawParams->EnvMapSize * theDrawParams->EnvMapSize))); + aProg->SetSampler (theCtx, "uEnvMap", theCtx->PBRSpecIBLMapTexUnit()); + aProg->SetUniform (theCtx, "uZCoeff", theDrawParams->IsZInverted ? -1 : 1); + aProg->SetUniform (theCtx, "uYCoeff", theDrawParams->IsTopDown ? 1 : -1); + aProg->SetUniform (theCtx, "occNbSpecIBLLevels", Standard_Integer(mySpecMapLevelsNumber)); + aProg->SetUniform (theCtx, "uEnvSolidAngleSource", aSolidAngleSource); + myVBO.BindAttribute (theCtx, Graphic3d_TOA_POS); } + const bool canRenderMipmaps = theCtx->hasFboRenderMipmap; + const OpenGl_TextureFormat aTexFormat = OpenGl_TextureFormat::FindSizedFormat (theCtx, myIBLMaps[OpenGl_TypeOfIBLMap_Specular].SizedFormat()); +#if !defined(GL_ES_VERSION_2_0) + const GLint anIntFormat = aTexFormat.InternalFormat(); +#else + // ES 2.0 does not support sized formats and format conversions - them detected from data type + const GLint anIntFormat = theCtx->IsGlGreaterEqual (3, 0) ? aTexFormat.InternalFormat() : aTexFormat.PixelFormat(); +#endif + for (int aLevelIter = mySpecMapLevelsNumber - 1;; --aLevelIter) { const Standard_Integer aSize = 1 << (myPow2Size - aLevelIter); const Standard_Integer aViewport[4] = { 0, 0, aSize, aSize }; theCtx->ResizeViewport (aViewport); - if (theIsDrawAction) + if (theDrawParams != NULL) { - Standard_Integer aNbSamples = static_cast(Graphic3d_PBRMaterial::SpecIBLMapSamplesFactor (theProbability, aLevelIter / float (mySpecMapLevelsNumber - 1)) * theNbSamples); - theCtx->ActiveProgram()->SetUniform (theCtx, "uSamplesNum", static_cast(aNbSamples)); + const Standard_Integer aNbSamples = Standard_Integer(Graphic3d_PBRMaterial::SpecIBLMapSamplesFactor (theDrawParams->Probability, + aLevelIter / float (mySpecMapLevelsNumber - 1)) * theDrawParams->NbSpecSamples); + theCtx->ActiveProgram()->SetUniform (theCtx, "uSamplesNum", aNbSamples); theCtx->ActiveProgram()->SetUniform (theCtx, "uCurrentLevel", aLevelIter); } for (Standard_Integer aSideIter = 0; aSideIter < 6; ++aSideIter) { - theCtx->arbFBO->glFramebufferTexture2D (GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + aSideIter, - myIBLMaps[OpenGl_TypeOfIBLMap_Specular].TextureId(), aLevelIter); - if (theIsDrawAction) + theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + aSideIter, + myIBLMaps[OpenGl_TypeOfIBLMap_Specular].TextureId(), canRenderMipmaps ? aLevelIter : 0); + if (theDrawParams != NULL) { theCtx->ActiveProgram()->SetUniform(theCtx, "uCurrentSide", aSideIter); theCtx->core11fwd->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); @@ -387,6 +496,20 @@ bool OpenGl_PBREnvironment::processSpecIBLMap (const Handle(OpenGl_Context)& the { theCtx->core11fwd->glClear(GL_COLOR_BUFFER_BIT); } + + if (!canRenderMipmaps + && aLevelIter != 0) + { + myIBLMaps[OpenGl_TypeOfIBLMap_Specular].Bind (theCtx, Graphic3d_TextureUnit_1); + theCtx->core20fwd->glCopyTexImage2D (GL_TEXTURE_CUBE_MAP_POSITIVE_X + aSideIter, aLevelIter, anIntFormat, 0, 0, (GLsizei )aSize, (GLsizei )aSize, 0); + myIBLMaps[OpenGl_TypeOfIBLMap_Specular].Unbind (theCtx, Graphic3d_TextureUnit_1); + const GLenum anErr = theCtx->core11fwd->glGetError(); + if (anErr != GL_NO_ERROR) + { + theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, + TCollection_AsciiString ("Unable to copy cubemap mipmap level. Error ") + OpenGl_Context::FormatGlError (anErr)); + } + } } if (aLevelIter == 0) @@ -395,6 +518,11 @@ bool OpenGl_PBREnvironment::processSpecIBLMap (const Handle(OpenGl_Context)& the } } + if (theDrawParams != NULL) + { + myVBO.UnbindAttribute (theCtx, Graphic3d_TOA_POS); + } + return true; } @@ -402,23 +530,44 @@ bool OpenGl_PBREnvironment::processSpecIBLMap (const Handle(OpenGl_Context)& the // function : checkFBOCompletness // purpose : // ======================================================================= -bool OpenGl_PBREnvironment::checkFBOComplentess (const Handle(OpenGl_Context)& theCtx) const +bool OpenGl_PBREnvironment::checkFBOComplentess (const Handle(OpenGl_Context)& theCtx) { - theCtx->arbFBO->glBindFramebuffer (GL_DRAW_FRAMEBUFFER, myFBO); - theCtx->arbFBO->glFramebufferTexture2D (GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + myCanRenderFloat = true; + theCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myFBO); + theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseSH].TextureId(), 0); - if (theCtx->arbFBO->glCheckFramebufferStatus (GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + if (theCtx->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - return false; + // Some WebGL 1.0 and OpenGL ES 2.0 implementations support float textures which cannot be used as render targets. + // This capability could be exposed by WEBGL_color_buffer_float/EXT_color_buffer_float extensions, + // but the simplest way is just to check FBO status. + // The fallback solution involves rendering into RGBA8 texture with floating values packed, + // and using glReadPixels() + conversion + texture upload of computed values. + myCanRenderFloat = false; + theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + myIBLMaps[OpenGl_TypeOfIBLMap_DiffuseFallback].TextureId(), 0); + if (theCtx->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + Message::SendTrace() << "OpenGl_PBREnvironment, incomplete FBO for diffuse map"; + return false; + } } + for (Standard_Integer aSideIter = 0; aSideIter < 6; ++aSideIter) { for (unsigned int aLevel = 0; aLevel < mySpecMapLevelsNumber; ++aLevel) { - theCtx->arbFBO->glFramebufferTexture2D (GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + aSideIter, - myIBLMaps[OpenGl_TypeOfIBLMap_Specular].TextureId(), aLevel); - if (theCtx->arbFBO->glCheckFramebufferStatus (GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + if (!theCtx->hasFboRenderMipmap + && aLevel != 0) { + continue; + } + + theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + aSideIter, + myIBLMaps[OpenGl_TypeOfIBLMap_Specular].TextureId(), aLevel); + if (theCtx->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + Message::SendTrace() << "OpenGl_PBREnvironment, incomplete FBO for specular map " << aSideIter << " "<< aLevel; return false; } } @@ -439,21 +588,21 @@ void OpenGl_PBREnvironment::bake (const Handle(OpenGl_Context)& theCtx, Standard_ShortReal theProbability) { myIsNeededToBeBound = Standard_True; - if (!theCtx->ShaderManager()->BindPBREnvBakingProgram()) - { - return; - } + theEnvMap->Bind (theCtx, theCtx->PBRSpecIBLMapTexUnit()); - theCtx->ActiveProgram()->SetSampler (theCtx, "uEnvMap", theCtx->PBRSpecIBLMapTexUnit()); - theCtx->ActiveProgram()->SetUniform (theCtx, "uZCoeff", theZIsInverted ? -1 : 1); - theCtx->ActiveProgram()->SetUniform (theCtx, "uYCoeff", theIsTopDown ? 1 : -1); - theCtx->arbFBO->glBindFramebuffer (GL_DRAW_FRAMEBUFFER, myFBO); - myVBO.BindAttribute (theCtx, Graphic3d_TOA_POS); + theCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myFBO); OSD_Timer aTimer; aTimer.Start(); - if (processSpecIBLMap (theCtx, true, theEnvMap->SizeX(), theSpecNbSamples, theProbability) - && processDiffIBLMap (theCtx, true, theDiffNbSamples)) + BakingParams aDrawParams; + aDrawParams.NbSpecSamples = theSpecNbSamples; + aDrawParams.NbDiffSamples = theDiffNbSamples; + aDrawParams.EnvMapSize = theEnvMap->SizeX(); + aDrawParams.Probability = theProbability; + aDrawParams.IsZInverted = theZIsInverted; + aDrawParams.IsTopDown = theIsTopDown; + if (processSpecIBLMap (theCtx, &aDrawParams) + && processDiffIBLMap (theCtx, &aDrawParams)) { Message::SendTrace(TCollection_AsciiString() + "IBL " + myIBLMaps[OpenGl_TypeOfIBLMap_Specular].SizeX() + "x" + myIBLMaps[OpenGl_TypeOfIBLMap_Specular].SizeY() @@ -467,7 +616,6 @@ void OpenGl_PBREnvironment::bake (const Handle(OpenGl_Context)& theCtx, clear (theCtx, Graphic3d_Vec3(1.0f)); } - myVBO.UnbindAttribute (theCtx, Graphic3d_TOA_POS); theEnvMap->Unbind (theCtx, theCtx->PBREnvLUTTexUnit()); } @@ -479,9 +627,9 @@ void OpenGl_PBREnvironment::clear (const Handle(OpenGl_Context)& theCtx, const Graphic3d_Vec3& theColor) { myIsNeededToBeBound = Standard_True; - theCtx->arbFBO->glBindFramebuffer (GL_DRAW_FRAMEBUFFER, myFBO); + theCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myFBO); theCtx->core11fwd->glClearColor (theColor.r(), theColor.g(), theColor.b(), 1.f); - processSpecIBLMap (theCtx, false); - processDiffIBLMap (theCtx, false); + processSpecIBLMap (theCtx, NULL); + processDiffIBLMap (theCtx, NULL); } diff --git a/src/OpenGl/OpenGl_PBREnvironment.hxx b/src/OpenGl/OpenGl_PBREnvironment.hxx index 986883ba89..11607d86bf 100644 --- a/src/OpenGl/OpenGl_PBREnvironment.hxx +++ b/src/OpenGl/OpenGl_PBREnvironment.hxx @@ -134,7 +134,22 @@ private: enum OpenGl_TypeOfIBLMap { OpenGl_TypeOfIBLMap_DiffuseSH, - OpenGl_TypeOfIBLMap_Specular + OpenGl_TypeOfIBLMap_Specular, + OpenGl_TypeOfIBLMap_DiffuseFallback, + }; + + //! Parameters for baking IBL. + struct BakingParams + { + Standard_Size NbSpecSamples; + Standard_Size NbDiffSamples; + Standard_Integer EnvMapSize; + Standard_ShortReal Probability; + Standard_Boolean IsZInverted; + Standard_Boolean IsTopDown; + + BakingParams() + : NbSpecSamples (0), NbDiffSamples (0), EnvMapSize (1024), Probability (1.0f), IsZInverted (false), IsTopDown (false) {} }; //! Initializes all textures. @@ -156,23 +171,19 @@ private: //! @return false in case of failed baking or clearing //! Warning! Requires using of OpenGl_PBREnvironmentSentry. bool processDiffIBLMap (const Handle(OpenGl_Context)& theCtx, - Standard_Boolean theIsDrawAction, - Standard_Size theNbSamples = 0); + const BakingParams* theDrawParams); //! Responses for specular IBL map processing. //! @return false in case of failed baking or clearing //! Warning! Requires using of OpenGl_PBREnvironmentSentry. bool processSpecIBLMap (const Handle(OpenGl_Context)& theCtx, - Standard_Boolean theIsDrawAction, - Standard_Integer theEnvMapSize = 1024, - Standard_Size theNbSamples = 0, - Standard_ShortReal theProbability = 1.f); + const BakingParams* theDrawParams); //! Checks completeness of frame buffer object for all targets //! (all cube map sides and levels of IBL maps). //! @return false in case of uncompleted frame buffer object. //! Warning! Requires using of OpenGl_PBREnvironmentSentry. - bool checkFBOComplentess (const Handle(OpenGl_Context)& theCtx) const; + bool checkFBOComplentess (const Handle(OpenGl_Context)& theCtx); //! Version of 'Bake' without OpenGl_PBREnvironmentSetnry. //! Warning! Requires using of OpenGl_PBREnvironmentSentry. @@ -194,12 +205,13 @@ private: unsigned int myPow2Size; //!< size of IBL maps sides (real size can be calculated as 2^myPow2Size) unsigned int mySpecMapLevelsNumber; //!< number of mipmap levels used in specular IBL map - OpenGl_Texture myIBLMaps[2]; //!< IBL maps + OpenGl_Texture myIBLMaps[3]; //!< IBL maps OpenGl_VertexBuffer myVBO; //!< vertex buffer object of screen rectangular GLuint myFBO; //!< frame buffer object to generate or clear IBL maps Standard_Boolean myIsComplete; //!< completeness of PBR environment Standard_Boolean myIsNeededToBeBound; //!< indicates whether IBL map's textures have to be bound or it is not obligate + Standard_Boolean myCanRenderFloat; //!< indicates whether driver supports rendering into floating point texture or not }; diff --git a/src/OpenGl/OpenGl_ShaderManager.cxx b/src/OpenGl/OpenGl_ShaderManager.cxx index ae07acb046..a3e6f271a2 100644 --- a/src/OpenGl/OpenGl_ShaderManager.cxx +++ b/src/OpenGl/OpenGl_ShaderManager.cxx @@ -1764,10 +1764,17 @@ int OpenGl_ShaderManager::defaultGlslVersion (const Handle(Graphic3d_ShaderProgr } else { - if (theProgram->IsPBR() - && myContext->IsGlGreaterEqual (3, 0)) + if (theProgram->IsPBR()) { - theProgram->SetHeader ("#version 300 es"); + if (myContext->IsGlGreaterEqual (3, 0)) + { + theProgram->SetHeader ("#version 300 es"); + } + else if (myContext->CheckExtension ("GL_EXT_shader_texture_lod")) + { + theProgram->SetHeader ("#extension GL_EXT_shader_texture_lod : enable\n" + "#define textureCubeLod textureCubeLodEXT"); + } } if ((theBits & OpenGl_PO_WriteOit) != 0 || (theBits & OpenGl_PO_OitDepthPeeling) != 0 @@ -3165,8 +3172,9 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramBoundBox() // function : preparePBREnvBakingProgram // purpose : // ======================================================================= -Standard_Boolean OpenGl_ShaderManager::preparePBREnvBakingProgram() +Standard_Boolean OpenGl_ShaderManager::preparePBREnvBakingProgram (Standard_Integer theIndex) { + Standard_ASSERT_RAISE (theIndex >= 0 && theIndex <= 2,""); Handle(Graphic3d_ShaderProgram) aProgramSrc = new Graphic3d_ShaderProgram(); OpenGl_ShaderObject::ShaderVariableList aUniforms, aStageInOuts; @@ -3177,16 +3185,27 @@ Standard_Boolean OpenGl_ShaderManager::preparePBREnvBakingProgram() TCollection_AsciiString aSrcFrag = TCollection_AsciiString() + THE_FUNC_cubemap_vector_transform + Shaders_PBRDistribution_glsl + + ((theIndex == 0 || theIndex == 2) ? "\n#define THE_TO_BAKE_DIFFUSE\n" : "\n#define THE_TO_BAKE_SPECULAR\n") + + (theIndex == 2 ? "\n#define THE_TO_PACK_FLOAT\n" : "") + Shaders_PBREnvBaking_fs; // constant array definition requires OpenGL 2.1+ or OpenGL ES 3.0+ #if defined(GL_ES_VERSION_2_0) - aProgramSrc->SetHeader ("#version 300 es"); + if (myContext->IsGlGreaterEqual (3, 0)) + { + aProgramSrc->SetHeader ("#version 300 es"); + } + else if (myContext->CheckExtension ("GL_EXT_shader_texture_lod")) + { + aProgramSrc->SetHeader ("#extension GL_EXT_shader_texture_lod : enable\n" + "#define textureCubeLod textureCubeLodEXT"); + } #else aProgramSrc->SetHeader ("#version 120"); #endif - defaultGlslVersion (aProgramSrc, "pbr_env_baking", 0); + static const char* THE_BAKE_NAMES[3] = { "pbr_env_baking_diffuse", "pbr_env_baking_specular", "pbr_env_baking_difffallback" }; + defaultGlslVersion (aProgramSrc, THE_BAKE_NAMES[theIndex], 0); aProgramSrc->SetDefaultSampler (false); aProgramSrc->SetNbLightsMax (0); aProgramSrc->SetNbShadowMaps (0); @@ -3195,12 +3214,28 @@ Standard_Boolean OpenGl_ShaderManager::preparePBREnvBakingProgram() aProgramSrc->AttachShader (OpenGl_ShaderObject::CreateFromSource (aSrcVert, Graphic3d_TOS_VERTEX, aUniforms, aStageInOuts)); aProgramSrc->AttachShader (OpenGl_ShaderObject::CreateFromSource (aSrcFrag, Graphic3d_TOS_FRAGMENT, aUniforms, aStageInOuts)); TCollection_AsciiString aKey; - if (!Create (aProgramSrc, aKey, myPBREnvBakingProgram)) + if (!Create (aProgramSrc, aKey, myPBREnvBakingProgram[theIndex])) { - myPBREnvBakingProgram = new OpenGl_ShaderProgram(); // just mark as invalid + myPBREnvBakingProgram[theIndex] = new OpenGl_ShaderProgram(); // just mark as invalid return Standard_False; } + if (theIndex == 0 + || theIndex == 2) + { + // workaround for old GLSL - load constants as uniform + myContext->BindProgram (myPBREnvBakingProgram[theIndex]); + const float aSHBasisFuncCoeffs[9] = + { + 0.282095f * 0.282095f, 0.488603f * 0.488603f, 0.488603f * 0.488603f, 0.488603f * 0.488603f, + 1.092548f * 1.092548f, 1.092548f * 1.092548f, 1.092548f * 1.092548f, 0.315392f * 0.315392f, 0.546274f * 0.546274f + }; + const float aSHCosCoeffs[9] = { 3.141593f, 2.094395f, 2.094395f, 2.094395f, 0.785398f, 0.785398f, 0.785398f, 0.785398f, 0.785398f }; + myPBREnvBakingProgram[theIndex]->SetUniform (myContext, myPBREnvBakingProgram[theIndex]->GetUniformLocation (myContext, "aSHBasisFuncCoeffs"), 9, aSHBasisFuncCoeffs); + myPBREnvBakingProgram[theIndex]->SetUniform (myContext, myPBREnvBakingProgram[theIndex]->GetUniformLocation (myContext, "aSHCosCoeffs"), 9, aSHCosCoeffs); + myContext->BindProgram (NULL); + } + return Standard_True; } diff --git a/src/OpenGl/OpenGl_ShaderManager.hxx b/src/OpenGl/OpenGl_ShaderManager.hxx index 7635518ff0..44f2c2d3ac 100644 --- a/src/OpenGl/OpenGl_ShaderManager.hxx +++ b/src/OpenGl/OpenGl_ShaderManager.hxx @@ -277,13 +277,13 @@ public: const Handle(OpenGl_VertexBuffer)& BoundBoxVertBuffer() const { return myBoundBoxVertBuffer; } //! Bind program for IBL maps generation in PBR pipeline. - Standard_Boolean BindPBREnvBakingProgram() + Standard_Boolean BindPBREnvBakingProgram (Standard_Integer theIndex) { - if (myPBREnvBakingProgram.IsNull()) + if (myPBREnvBakingProgram[theIndex].IsNull()) { - preparePBREnvBakingProgram(); + preparePBREnvBakingProgram (theIndex); } - return myContext->BindProgram (myPBREnvBakingProgram); + return myContext->BindProgram (myPBREnvBakingProgram[theIndex]); } //! Generates shader program to render environment cubemap as background. @@ -814,7 +814,7 @@ protected: Standard_Integer theBits); //! Prepare GLSL source for IBL generation used in PBR pipeline. - Standard_EXPORT Standard_Boolean preparePBREnvBakingProgram(); + Standard_EXPORT Standard_Boolean preparePBREnvBakingProgram (Standard_Integer theIndex); //! Checks whether one of PBR shading models is set as default model. Standard_Boolean IsPbrAllowed() const { return myShadingModel == Graphic3d_TOSM_PBR @@ -883,7 +883,7 @@ protected: Handle(OpenGl_ShaderProgram) myOitDepthPeelingFlushProgram[2]; //!< standard program for OIT Depth Peeling flush (default and MSAA) OpenGl_MapOfShaderPrograms myMapOfLightPrograms; //!< map of lighting programs depending on lights configuration - Handle(OpenGl_ShaderProgram) myPBREnvBakingProgram;//!< program for IBL maps generation used in PBR pipeline + Handle(OpenGl_ShaderProgram) myPBREnvBakingProgram[3]; //!< programs for IBL maps generation used in PBR pipeline (0 for Diffuse; 1 for Specular; 2 for fallback) Handle(Graphic3d_ShaderProgram) myBgCubeMapProgram; //!< program for background cubemap rendering Handle(OpenGl_ShaderProgram) myStereoPrograms[Graphic3d_StereoMode_NB]; //!< standard stereo programs diff --git a/src/OpenGl/OpenGl_Texture.cxx b/src/OpenGl/OpenGl_Texture.cxx index 2bf73d553f..939e5bc991 100644 --- a/src/OpenGl/OpenGl_Texture.cxx +++ b/src/OpenGl/OpenGl_Texture.cxx @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include #include @@ -1235,3 +1235,72 @@ Standard_Size OpenGl_Texture::EstimatedDataSize() const } return aSize; } + +// ======================================================================= +// function : ImageDump +// purpose : +// ======================================================================= +bool OpenGl_Texture::ImageDump (Image_PixMap& theImage, + const Handle(OpenGl_Context)& theCtx, + Graphic3d_TextureUnit theTexUnit, + Standard_Integer theLevel, + Standard_Integer theCubeSide) const +{ +#if !defined(GL_ES_VERSION_2_0) + const OpenGl_TextureFormat aFormat = OpenGl_TextureFormat::FindSizedFormat (theCtx, mySizedFormat); + if (theCtx.IsNull() + || !IsValid() + || theLevel < 0 + || !aFormat.IsValid() + || aFormat.ImageFormat() == Image_Format_UNKNOWN + || (myTarget == GL_TEXTURE_CUBE_MAP + && (theCubeSide < 0 || theCubeSide > 5))) + { + return false; + } + + GLenum aTarget = myTarget; + Graphic3d_Vec2i aSize (mySizeX, mySizeY); + if (myTarget == GL_TEXTURE_CUBE_MAP) + { + aTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + theCubeSide; + } + for (Standard_Integer aMipIter = 0; aMipIter < theLevel; ++aMipIter) + { + aSize /= 2; + if (aSize.x() == 0) { aSize.x() = 1; } + if (aSize.y() == 0) { aSize.y() = 1; } + } + if (!theImage.InitTrash (aFormat.ImageFormat(), aSize.x(), aSize.y())) + { + return false; + } + + const GLint anAligment = Min (GLint(theImage.MaxRowAligmentBytes()), 8); // limit to 8 bytes for OpenGL + theCtx->core11fwd->glPixelStorei (GL_PACK_ALIGNMENT, anAligment); + theCtx->core11fwd->glPixelStorei (GL_PACK_ROW_LENGTH, 0); + // glGetTextureImage() allows avoiding to binding texture id, but apparently requires clean FBO binding state... + //if (theCtx->core45 != NULL) { theCtx->core45->glGetTextureImage (myTextureId, theLevel, aFormat.PixelFormat(), aFormat.DataType(), (GLsizei )theImage.SizeBytes(), theImage.ChangeData()); } else + { + Bind (theCtx, theTexUnit); + theCtx->core11fwd->glGetTexImage (aTarget, theLevel, aFormat.PixelFormat(), aFormat.DataType(), theImage.ChangeData()); + Unbind (theCtx, theTexUnit); + } + if (theImage.Format() != aFormat.ImageFormat()) + { + Image_PixMap::SwapRgbaBgra (theImage); + } + + const bool hasErrors = theCtx->ResetErrors (true); + theCtx->core11fwd->glPixelStorei (GL_PACK_ALIGNMENT, 1); + return !hasErrors; +#else + // glGetTexImage() is unavailable in OpenGL ES + (void )theImage; + (void )theCtx; + (void )theTexUnit; + (void )theLevel; + (void )theCubeSide; + return false; +#endif +} diff --git a/src/OpenGl/OpenGl_Texture.hxx b/src/OpenGl/OpenGl_Texture.hxx index a4b2ca42e2..03f57d959e 100644 --- a/src/OpenGl/OpenGl_Texture.hxx +++ b/src/OpenGl/OpenGl_Texture.hxx @@ -191,6 +191,19 @@ public: //! Returns TRUE for point sprite texture. virtual bool IsPointSprite() const { return false; } + //! Auxiliary method for making an image dump from texture data. + //! @param theImage [out] result image data (will be overridden) + //! @param theCtx [in] active GL context + //! @param theTexUnit [in] texture slot to use + //! @param theLevel [in] mipmap level to dump + //! @param theCubeSide [in] cubemap side to dump within [0, 5] range + //! @return FALSE on error + Standard_EXPORT bool ImageDump (Image_PixMap& theImage, + const Handle(OpenGl_Context)& theCtx, + Graphic3d_TextureUnit theTexUnit, + Standard_Integer theLevel = 0, + Standard_Integer theCubeSide = 0) const; + public: Standard_DEPRECATED("Deprecated method, OpenGl_TextureFormat::FindFormat() should be used instead") diff --git a/src/OpenGl/OpenGl_TextureFormat.cxx b/src/OpenGl/OpenGl_TextureFormat.cxx index b655d41add..ec07a2c2d2 100644 --- a/src/OpenGl/OpenGl_TextureFormat.cxx +++ b/src/OpenGl/OpenGl_TextureFormat.cxx @@ -122,6 +122,7 @@ OpenGl_TextureFormat OpenGl_TextureFormat::FindFormat (const Handle(OpenGl_Conte bool theIsColorMap) { OpenGl_TextureFormat aFormat; + aFormat.SetImageFormat (theFormat); switch (theFormat) { case Image_Format_GrayF: @@ -414,6 +415,7 @@ OpenGl_TextureFormat OpenGl_TextureFormat::FindSizedFormat (const Handle(OpenGl_ aFormat.SetInternalFormat (theSizedFormat); aFormat.SetPixelFormat (GL_RGBA); aFormat.SetDataType (GL_FLOAT); + aFormat.SetImageFormat (Image_Format_RGBAF); return aFormat; } case GL_R32F: @@ -422,6 +424,7 @@ OpenGl_TextureFormat OpenGl_TextureFormat::FindSizedFormat (const Handle(OpenGl_ aFormat.SetInternalFormat (theSizedFormat); aFormat.SetPixelFormat (GL_RED); aFormat.SetDataType (GL_FLOAT); + aFormat.SetImageFormat (Image_Format_GrayF); return aFormat; } case GL_RG32F: @@ -430,6 +433,7 @@ OpenGl_TextureFormat OpenGl_TextureFormat::FindSizedFormat (const Handle(OpenGl_ aFormat.SetInternalFormat (theSizedFormat); aFormat.SetPixelFormat (GL_RG); aFormat.SetDataType (GL_FLOAT); + aFormat.SetImageFormat (Image_Format_RGF); return aFormat; } case GL_RGBA16F: @@ -438,6 +442,7 @@ OpenGl_TextureFormat OpenGl_TextureFormat::FindSizedFormat (const Handle(OpenGl_ aFormat.SetInternalFormat (theSizedFormat); aFormat.SetPixelFormat (GL_RGBA); aFormat.SetDataType (GL_HALF_FLOAT); + aFormat.SetImageFormat (Image_Format_RGBAF); if (theCtx->hasHalfFloatBuffer == OpenGl_FeatureInExtensions) { #if defined(GL_ES_VERSION_2_0) @@ -454,6 +459,7 @@ OpenGl_TextureFormat OpenGl_TextureFormat::FindSizedFormat (const Handle(OpenGl_ aFormat.SetInternalFormat (theSizedFormat); aFormat.SetPixelFormat (GL_RED); aFormat.SetDataType (GL_HALF_FLOAT); + aFormat.SetImageFormat (Image_Format_GrayF); if (theCtx->hasHalfFloatBuffer == OpenGl_FeatureInExtensions) { #if defined(GL_ES_VERSION_2_0) @@ -472,6 +478,7 @@ OpenGl_TextureFormat OpenGl_TextureFormat::FindSizedFormat (const Handle(OpenGl_ aFormat.SetInternalFormat (theSizedFormat); aFormat.SetPixelFormat (GL_RGBA); aFormat.SetDataType (GL_UNSIGNED_BYTE); + aFormat.SetImageFormat (Image_Format_RGBA); if (theSizedFormat == GL_SRGB8_ALPHA8 && !theCtx->ToRenderSRGB()) { @@ -487,6 +494,7 @@ OpenGl_TextureFormat OpenGl_TextureFormat::FindSizedFormat (const Handle(OpenGl_ aFormat.SetInternalFormat (theSizedFormat); aFormat.SetPixelFormat (GL_RGB); aFormat.SetDataType (GL_UNSIGNED_BYTE); + aFormat.SetImageFormat (Image_Format_RGB); if (theSizedFormat == GL_SRGB8 && !theCtx->ToRenderSRGB()) { diff --git a/src/OpenGl/OpenGl_TextureFormat.hxx b/src/OpenGl/OpenGl_TextureFormat.hxx index d8bc04c658..592fc6128c 100644 --- a/src/OpenGl/OpenGl_TextureFormat.hxx +++ b/src/OpenGl/OpenGl_TextureFormat.hxx @@ -66,7 +66,7 @@ public: public: //! Empty constructor (invalid texture format). - OpenGl_TextureFormat() : myInternalFormat (0), myPixelFormat (0), myDataType (0), myNbComponents (0) {} + OpenGl_TextureFormat() : myImageFormat (Image_Format_UNKNOWN), myInternalFormat (0), myPixelFormat (0), myDataType (0), myNbComponents (0) {} //! Return TRUE if format is defined. bool IsValid() const @@ -107,6 +107,12 @@ public: || myInternalFormat == GL_SRGB8_ALPHA8; } + //! Returns image format (best match or Image_Format_UNKNOWN if no suitable fit). + Image_Format ImageFormat() const { return myImageFormat; } + + //! Sets image format. + void SetImageFormat (Image_Format theFormat) { myImageFormat = theFormat; } + public: //! Returns OpenGL internal format of the pixel data (example: GL_R32F). @@ -117,6 +123,7 @@ public: private: + Image_Format myImageFormat; //!< image format GLint myInternalFormat; //!< OpenGL internal format of the pixel data GLenum myPixelFormat; //!< OpenGL pixel format GLint myDataType; //!< OpenGL data type of input pixel data diff --git a/src/OpenGl/OpenGl_View.cxx b/src/OpenGl/OpenGl_View.cxx index cf258d9fef..4d58b9eb7b 100644 --- a/src/OpenGl/OpenGl_View.cxx +++ b/src/OpenGl/OpenGl_View.cxx @@ -1194,14 +1194,34 @@ bool OpenGl_View::prepareFrameBuffers (Graphic3d_Camera::Projection& theProj) aParams->SetTextureUnit (aCtx->PBREnvLUTTexUnit()); anEnvLUT = new OpenGl_Texture(THE_SHARED_ENV_LUT_KEY, aParams); Handle(Image_PixMap) aPixMap = new Image_PixMap(); - aPixMap->InitWrapper (Image_Format_RGF, (Standard_Byte*)Textures_EnvLUT, Textures_EnvLUTSize, Textures_EnvLUTSize); + if (aCtx->arbTexRG) + { + aPixMap->InitWrapper (Image_Format_RGF, (Standard_Byte*)Textures_EnvLUT, Textures_EnvLUTSize, Textures_EnvLUTSize); + } + else + { + Image_PixMap aPixMapRG; + aPixMapRG.InitWrapper (Image_Format_RGF, (Standard_Byte*)Textures_EnvLUT, Textures_EnvLUTSize, Textures_EnvLUTSize); + aPixMap->InitZero (Image_Format_RGBAF, Textures_EnvLUTSize, Textures_EnvLUTSize); + for (Standard_Size aRowIter = 0; aRowIter < aPixMapRG.SizeY(); ++aRowIter) + { + for (Standard_Size aColIter = 0; aColIter < aPixMapRG.SizeX(); ++aColIter) + { + const Image_ColorRGF& aPixelRG = aPixMapRG.Value (aRowIter, aColIter); + Image_ColorRGBAF& aPixelRGBA = aPixMap->ChangeValue (aRowIter, aColIter); + aPixelRGBA.r() = aPixelRG.r(); + aPixelRGBA.g() = aPixelRG.g(); + } + } + } + OpenGl_TextureFormat aTexFormat = OpenGl_TextureFormat::FindFormat (aCtx, aPixMap->Format(), false); #if defined(GL_ES_VERSION_2_0) // GL_RG32F is not texture-filterable format on OpenGL ES without OES_texture_float_linear extension. // GL_RG16F is texture-filterable since OpenGL ES 3.0 and can be initialized from 32-bit floats. // Note that it is expected that GL_RG16F has enough precision for this table, so that it can be used also on desktop OpenGL. //if (!aCtx->hasTexFloatLinear) - aTexFormat.SetInternalFormat (GL_RG16F); + aTexFormat.SetInternalFormat (aCtx->arbTexRG ? GL_RG16F : GL_RGBA16F); #endif if (!aTexFormat.IsValid() || !anEnvLUT->Init (aCtx, aTexFormat, Graphic3d_Vec2i((Standard_Integer)Textures_EnvLUTSize), Graphic3d_TOT_2D, aPixMap.get())) diff --git a/src/Shaders/Declarations.glsl b/src/Shaders/Declarations.glsl index 27418912ca..8b7fe60f49 100644 --- a/src/Shaders/Declarations.glsl +++ b/src/Shaders/Declarations.glsl @@ -143,44 +143,49 @@ const int OccLightType_Spot = 3; //!< spot light source // Light sources uniform vec4 occLightAmbient; //!< Cumulative ambient color #if defined(THE_MAX_LIGHTS) && (THE_MAX_LIGHTS > 0) +#if (THE_MAX_LIGHTS > 1) + #define occLight_Index(theId) theId +#else + #define occLight_Index(theId) 0 +#endif uniform THE_PREC_ENUM int occLightSourcesCount; //!< Total number of light sources //! Type of light source, int (see OccLightType enum). -#define occLight_Type(theId) occLightSourcesTypes[theId] +#define occLight_Type(theId) occLightSourcesTypes[occLight_Index(theId)] //! Specular intensity (equals to diffuse), vec3. -#define occLight_Specular(theId) occLightSources[theId * 4 + 0].rgb +#define occLight_Specular(theId) occLightSources[occLight_Index(theId) * 4 + 0].rgb //! Intensity of light source (>= 0), float. -#define occLight_Intensity(theId) occLightSources[theId * 4 + 0].a +#define occLight_Intensity(theId) occLightSources[occLight_Index(theId) * 4 + 0].a //! Is light a headlight, bool? DEPRECATED method. #define occLight_IsHeadlight(theId) false //! Position of specified light source or direction of directional light source, vec3. -#define occLight_Position(theId) occLightSources[theId * 4 + 1].xyz +#define occLight_Position(theId) occLightSources[occLight_Index(theId) * 4 + 1].xyz //! Direction of specified spot light source, vec3. -#define occLight_SpotDirection(theId) occLightSources[theId * 4 + 2].xyz +#define occLight_SpotDirection(theId) occLightSources[occLight_Index(theId) * 4 + 2].xyz //! Range on which point light source (positional or spot) can affect (>= 0), float. -#define occLight_Range(theId) occLightSources[theId * 4 + 2].w +#define occLight_Range(theId) occLightSources[occLight_Index(theId) * 4 + 2].w //! Maximum spread angle of the spot light (in radians), float. -#define occLight_SpotCutOff(theId) occLightSources[theId * 4 + 3].z +#define occLight_SpotCutOff(theId) occLightSources[occLight_Index(theId) * 4 + 3].z //! Attenuation of the spot light intensity (from 0 to 1), float. -#define occLight_SpotExponent(theId) occLightSources[theId * 4 + 3].w +#define occLight_SpotExponent(theId) occLightSources[occLight_Index(theId) * 4 + 3].w #if !defined(THE_IS_PBR) //! Diffuse intensity (equals to Specular), vec3. -#define occLight_Diffuse(theId) occLightSources[theId * 4 + 0].rgb +#define occLight_Diffuse(theId) occLightSources[occLight_Index(theId) * 4 + 0].rgb //! Const attenuation factor of positional light source, float. -#define occLight_ConstAttenuation(theId) occLightSources[theId * 4 + 3].x +#define occLight_ConstAttenuation(theId) occLightSources[occLight_Index(theId) * 4 + 3].x //! Linear attenuation factor of positional light source, float. -#define occLight_LinearAttenuation(theId) occLightSources[theId * 4 + 3].y +#define occLight_LinearAttenuation(theId) occLightSources[occLight_Index(theId) * 4 + 3].y #endif #endif diff --git a/src/Shaders/DirectionalLightShadow.glsl b/src/Shaders/DirectionalLightShadow.glsl index 80c9a1e27f..25a90a0c26 100644 --- a/src/Shaders/DirectionalLightShadow.glsl +++ b/src/Shaders/DirectionalLightShadow.glsl @@ -14,7 +14,7 @@ float occDirectionalLightShadow (in sampler2D theShadow, in int theId, in vec3 theNormal) { - vec4 aPosLightSpace = PosLightSpace[theId]; + vec4 aPosLightSpace = PosLightSpace[occLight_Index(theId)]; vec3 aLightDir = vec3 (occWorldViewMatrix * vec4 (occLight_Position (theId), 0.0)); vec3 aProjCoords = (aPosLightSpace.xyz / aPosLightSpace.w) * 0.5 + vec3 (0.5); float aCurrentDepth = aProjCoords.z; diff --git a/src/Shaders/PBREnvBaking.fs b/src/Shaders/PBREnvBaking.fs index e795c4607a..6ce89bcde5 100644 --- a/src/Shaders/PBREnvBaking.fs +++ b/src/Shaders/PBREnvBaking.fs @@ -1,12 +1,22 @@ THE_SHADER_IN vec3 ViewDirection; //!< direction of fetching from environment cubemap +#if (__VERSION__ >= 120) uniform int uSamplesNum; //!< number of samples in Monte-Carlo integration -uniform int uCurrentLevel; //!< current level of specular IBL map (ignored in case of diffuse map's processing) -uniform int uEnvMapSize; //!< one edge's size of source environtment map's zero mipmap level -uniform int uYCoeff; //!< coefficient of Y controlling horizontal flip of cubemap -uniform int uZCoeff; //!< coefficient of Z controlling vertical flip of cubemap +#else +const int uSamplesNum = 256; +#endif uniform samplerCube uEnvMap; //!< source of baking (environment cubemap) +#ifdef THE_TO_BAKE_DIFFUSE +uniform int uYCoeff; //!< coefficient of Y controlling horizontal flip of cubemap +uniform int uZCoeff; //!< coefficient of Z controlling vertical flip of cubemap +#endif + +#ifdef THE_TO_BAKE_SPECULAR +uniform int uCurrentLevel; //!< current level of specular IBL map (ignored in case of diffuse map's processing) +uniform float uEnvSolidAngleSource; //!< source solid angle sample computed from one edge's size of source environment map's zero mipmap level +#endif + //! Returns coordinates of point theNumber from Hammersley point set having size theSize. vec2 hammersley (in int theNumber, in int theSize) @@ -18,7 +28,7 @@ vec2 hammersley (in int theNumber, { if (aNumber > 0) { - aVanDerCorput += float(aNumber % 2) / float(aDenominator); + aVanDerCorput += mod(float(aNumber), 2.0) / float(aDenominator); aNumber /= 2; aDenominator *= 2; } @@ -62,6 +72,8 @@ vec3 fromTangentSpace (in vec3 theVector, return anX * theVector.x + anY * theVector.y + theNormal * theVector.z; } +#ifdef THE_TO_BAKE_DIFFUSE +#if (__VERSION__ >= 120) const float aSHBasisFuncCoeffs[9] = float[9] ( 0.282095 * 0.282095, @@ -74,7 +86,6 @@ const float aSHBasisFuncCoeffs[9] = float[9] 0.315392 * 0.315392, 0.546274 * 0.546274 ); - const float aSHCosCoeffs[9] = float[9] ( 3.141593, @@ -87,76 +98,129 @@ const float aSHCosCoeffs[9] = float[9] 0.785398, 0.785398 ); +#else +uniform float aSHBasisFuncCoeffs[9]; +uniform float aSHCosCoeffs[9]; +#endif //! Bakes diffuse IBL map's spherical harmonics coefficients. vec3 bakeDiffuseSH() { - int anIndex = int(gl_FragCoord.x); - vec3 aResult = vec3 (0.0); + int anId = int(gl_FragCoord.x); + float aCoef; +#if (__VERSION__ >= 120) + aCoef = aSHCosCoeffs[anId] * aSHBasisFuncCoeffs[anId]; +#else + if (anId == 0) { aCoef = aSHCosCoeffs[0] * aSHBasisFuncCoeffs[0]; } + else if (anId == 1) { aCoef = aSHCosCoeffs[1] * aSHBasisFuncCoeffs[1]; } + else if (anId == 2) { aCoef = aSHCosCoeffs[2] * aSHBasisFuncCoeffs[2]; } + else if (anId == 3) { aCoef = aSHCosCoeffs[3] * aSHBasisFuncCoeffs[3]; } + else if (anId == 4) { aCoef = aSHCosCoeffs[4] * aSHBasisFuncCoeffs[4]; } + else if (anId == 5) { aCoef = aSHCosCoeffs[5] * aSHBasisFuncCoeffs[5]; } + else if (anId == 6) { aCoef = aSHCosCoeffs[6] * aSHBasisFuncCoeffs[6]; } + else if (anId == 7) { aCoef = aSHCosCoeffs[7] * aSHBasisFuncCoeffs[7]; } + else { aCoef = aSHCosCoeffs[8] * aSHBasisFuncCoeffs[8]; } +#endif + vec3 aRes = vec3 (0.0); for (int aSampleIter = 0; aSampleIter < uSamplesNum; ++aSampleIter) { vec2 aHammersleyPoint = hammersley (aSampleIter, uSamplesNum); - vec3 aDirection = sphereUniformSample (aHammersleyPoint); + vec3 aDir = sphereUniformSample (aHammersleyPoint); - vec3 aValue = occTextureCube (uEnvMap, cubemapVectorTransform (aDirection, uYCoeff, uZCoeff)).rgb; + vec3 aVal = occTextureCube (uEnvMap, cubemapVectorTransform (aDir, uYCoeff, uZCoeff)).rgb; + #if (__VERSION__ >= 120) + float aFunc[9]; + aFunc[0] = 1.0; - float aBasisFunc[9]; - aBasisFunc[0] = 1.0; + aFunc[1] = aDir.x; + aFunc[2] = aDir.y; + aFunc[3] = aDir.z; - aBasisFunc[1] = aDirection.x; - aBasisFunc[2] = aDirection.y; - aBasisFunc[3] = aDirection.z; + aFunc[4] = aDir.x * aDir.z; + aFunc[5] = aDir.y * aDir.z; + aFunc[6] = aDir.x * aDir.y; - aBasisFunc[4] = aDirection.x * aDirection.z; - aBasisFunc[5] = aDirection.y * aDirection.z; - aBasisFunc[6] = aDirection.x * aDirection.y; + aFunc[7] = 3.0 * aDir.z * aDir.z - 1.0; + aFunc[8] = aDir.x * aDir.x - aDir.y * aDir.y; - aBasisFunc[7] = 3.0 * aDirection.z * aDirection.z - 1.0; - aBasisFunc[8] = aDirection.x * aDirection.x - aDirection.y * aDirection.y; - - aResult += aValue * aBasisFunc[anIndex]; + aRes += aVal * aFunc[anId]; + #else + if (anId == 0) { aRes += aVal * 1.0; } + else if (anId == 1) { aRes += aVal * aDir.x; } + else if (anId == 2) { aRes += aVal * aDir.y; } + else if (anId == 3) { aRes += aVal * aDir.z; } + else if (anId == 4) { aRes += aVal * (aDir.x * aDir.z); } + else if (anId == 5) { aRes += aVal * (aDir.y * aDir.z); } + else if (anId == 6) { aRes += aVal * (aDir.x * aDir.y); } + else if (anId == 7) { aRes += aVal * (3.0 * aDir.z * aDir.z - 1.0); } + else { aRes += aVal * (aDir.x * aDir.x - aDir.y * aDir.y); } + #endif } - aResult *= 4.0 * aSHCosCoeffs[anIndex] * aSHBasisFuncCoeffs[anIndex] / float(uSamplesNum); - return aResult; + return 4.0 * aRes * aCoef / float(uSamplesNum); +} +#endif + +#ifdef THE_TO_BAKE_SPECULAR +//! Computes a single sample for specular IBL map. +vec4 specularMapSample (in vec3 theNormal, + in float theRoughness, + in int theNumber, + in int theSize) +{ + vec2 aHammersleyPoint = hammersley (theNumber, theSize); + vec3 aHalf = importanceSample (aHammersleyPoint, occRoughness (theRoughness)); + float aHdotV = aHalf.z; + aHalf = fromTangentSpace (aHalf, theNormal); + vec3 aLight = -reflect (theNormal, aHalf); + float aNdotL = dot (aLight, theNormal); + if (aNdotL > 0.0) + { + float aSolidAngleSample = 1.0 / (float(theSize) * (occPBRDistribution (aHdotV, theRoughness) * 0.25 + 0.0001) + 0.0001); + float aLod = (theRoughness == 0.0) ? 0.0 : 0.5 * log2 (aSolidAngleSample / uEnvSolidAngleSource); + return vec4 (occTextureCubeLod (uEnvMap, aLight, aLod).rgb * aNdotL, aNdotL); + } + return vec4 (0.0); } //! Bakes specular IBL map. vec3 bakeSpecularMap (in vec3 theNormal, in float theRoughness) { - vec3 aResult = vec3(0.0); - float aWeightSum = 0.0; - int aSamplesNum = (theRoughness == 0.0) ? 1 : uSamplesNum; - float aSolidAngleSource = 4.0 * PI / (6.0 * float(uEnvMapSize * uEnvMapSize)); - for (int aSampleIter = 0; aSampleIter < aSamplesNum; ++aSampleIter) + vec4 aResult = vec4(0.0); + if (theRoughness == 0.0) { - vec2 aHammersleyPoint = hammersley (aSampleIter, aSamplesNum); - vec3 aHalf = importanceSample (aHammersleyPoint, occRoughness (theRoughness)); - float aHdotV = aHalf.z; - aHalf = fromTangentSpace (aHalf, theNormal); - vec3 aLight = -reflect (theNormal, aHalf); - float aNdotL = dot (aLight, theNormal); - if (aNdotL > 0.0) + aResult = specularMapSample (theNormal, theRoughness, 0, 1); + } + else + { + for (int aSampleIter = 0; aSampleIter < uSamplesNum; ++aSampleIter) { - float aSolidAngleSample = 1.0 / (float(aSamplesNum) * (occPBRDistribution (aHdotV, theRoughness) * 0.25 + 0.0001) + 0.0001); - float aLod = (theRoughness == 0.0) ? 0.0 : 0.5 * log2 (aSolidAngleSample / aSolidAngleSource); - aResult += occTextureCubeLod (uEnvMap, aLight, aLod).rgb * aNdotL; - aWeightSum += aNdotL; + aResult += specularMapSample (theNormal, theRoughness, aSampleIter, uSamplesNum); } } - return aResult / aWeightSum; + return aResult.xyz / aResult.w; } +#endif void main() { vec3 aViewDirection = normalize (ViewDirection); - if (occNbSpecIBLLevels == 0) - { - occSetFragColor (vec4 (bakeDiffuseSH (), 1.0)); - } - else - { - occSetFragColor (vec4 (bakeSpecularMap (aViewDirection, float(uCurrentLevel) / float(occNbSpecIBLLevels - 1)), 1.0)); - } +#ifdef THE_TO_BAKE_DIFFUSE + vec4 aRes = vec4 (bakeDiffuseSH(), 1.0); +#ifdef THE_TO_PACK_FLOAT + int aCompIndex = int(gl_FragCoord.y); + float aComp = aCompIndex == 0 ? aRes.x : (aCompIndex == 1 ? aRes.y : aRes.z); + int aFixedPrec = int(aComp * 2147483647.0); + int aFixedDiv1 = aFixedPrec / 256; + int aFixedDiv2 = aFixedDiv1 / 256; + int aFixedDiv3 = aFixedDiv2 / 256; + vec4 aPacked = vec4(float(aFixedPrec), float(aFixedDiv1), float(aFixedDiv2), float(aFixedDiv3)); + aRes = fract (aPacked * (1.0 / 256.0)); +#endif + occFragColor = aRes; +#else + float aRoughness = float(uCurrentLevel) / float(occNbSpecIBLLevels - 1); + occFragColor = vec4 (bakeSpecularMap (aViewDirection, aRoughness), 1.0); +#endif } diff --git a/src/Shaders/PBREnvBaking.vs b/src/Shaders/PBREnvBaking.vs index d9339aee25..f8f8f31bde 100644 --- a/src/Shaders/PBREnvBaking.vs +++ b/src/Shaders/PBREnvBaking.vs @@ -4,32 +4,52 @@ uniform int uCurrentSide; //!< current side of cubemap uniform int uYCoeff; //!< coefficient of Y controlling horizontal flip of cubemap uniform int uZCoeff; //!< coefficient of Z controlling vertical flip of cubemap -const mat2 cubemapDirectionMatrices[6] = mat2[] -( - mat2 ( 0, -1, -1, 0), - mat2 ( 0, 1, -1, 0), - mat2 ( 0, 1, 1, 0), - mat2 ( 0, 1, -1, 0), - mat2 ( 1, 0, 0, -1), - mat2 (-1, 0, 0, -1) -); - -//! Generates environment map fetching direction considering current index of side. -vec3 cubemapBakingViewDirection (in int theSide, - in vec2 theScreenCoord) -{ - int anAxis = theSide / 2; - vec3 aDirection = vec3(0.0); - aDirection[anAxis] = float(-(int(theSide) % 2) * 2 + 1); - theScreenCoord = cubemapDirectionMatrices[theSide] * theScreenCoord; - aDirection[(anAxis + 1) % 3] = theScreenCoord.x; - aDirection[(anAxis + 2) % 3] = theScreenCoord.y; - return aDirection; -} - void main() { - ViewDirection = cubemapBakingViewDirection (uCurrentSide, occVertex.xy); - ViewDirection = cubemapVectorTransform (ViewDirection, uYCoeff, uZCoeff); + vec3 aDir; + vec2 aCoord; + if (uCurrentSide == 0) + { + aCoord = mat2( 0,-1,-1, 0) * occVertex.xy; + aDir.x = 1.0; + aDir.y = aCoord.x; + aDir.z = aCoord.y; + } + else if (uCurrentSide == 1) + { + aCoord = mat2( 0, 1,-1, 0) * occVertex.xy; + aDir.x = -1.0; + aDir.y = aCoord.x; + aDir.z = aCoord.y; + } + else if (uCurrentSide == 2) + { + aCoord = mat2( 0, 1, 1, 0) * occVertex.xy; + aDir.x = aCoord.y; + aDir.y = 1.0; + aDir.z = aCoord.x; + } + else if (uCurrentSide == 3) + { + aCoord = mat2( 0, 1,-1, 0) * occVertex.xy; + aDir.x = aCoord.y; + aDir.y = -1.0; + aDir.z = aCoord.x; + } + else if (uCurrentSide == 4) + { + aCoord = mat2( 1, 0, 0,-1) * occVertex.xy; + aDir.x = aCoord.x; + aDir.y = aCoord.y; + aDir.z = 1.0; + } + else //if (uCurrentSide == 5) + { + aCoord = mat2(-1, 0, 0,-1) * occVertex.xy; + aDir.x = aCoord.x; + aDir.y = aCoord.y; + aDir.z = -1.0; + } + ViewDirection = cubemapVectorTransform (aDir, uYCoeff, uZCoeff); gl_Position = vec4 (occVertex.xy, 0.0, 1.0); } diff --git a/src/Shaders/Shaders_Declarations_glsl.pxx b/src/Shaders/Shaders_Declarations_glsl.pxx index c33fdf60c0..bd3be98367 100644 --- a/src/Shaders/Shaders_Declarations_glsl.pxx +++ b/src/Shaders/Shaders_Declarations_glsl.pxx @@ -146,44 +146,49 @@ static const char Shaders_Declarations_glsl[] = "// Light sources\n" "uniform vec4 occLightAmbient; //!< Cumulative ambient color\n" "#if defined(THE_MAX_LIGHTS) && (THE_MAX_LIGHTS > 0)\n" + "#if (THE_MAX_LIGHTS > 1)\n" + " #define occLight_Index(theId) theId\n" + "#else\n" + " #define occLight_Index(theId) 0\n" + "#endif\n" "uniform THE_PREC_ENUM int occLightSourcesCount; //!< Total number of light sources\n" "\n" "//! Type of light source, int (see OccLightType enum).\n" - "#define occLight_Type(theId) occLightSourcesTypes[theId]\n" + "#define occLight_Type(theId) occLightSourcesTypes[occLight_Index(theId)]\n" "\n" "//! Specular intensity (equals to diffuse), vec3.\n" - "#define occLight_Specular(theId) occLightSources[theId * 4 + 0].rgb\n" + "#define occLight_Specular(theId) occLightSources[occLight_Index(theId) * 4 + 0].rgb\n" "\n" "//! Intensity of light source (>= 0), float.\n" - "#define occLight_Intensity(theId) occLightSources[theId * 4 + 0].a\n" + "#define occLight_Intensity(theId) occLightSources[occLight_Index(theId) * 4 + 0].a\n" "\n" "//! Is light a headlight, bool? DEPRECATED method.\n" "#define occLight_IsHeadlight(theId) false\n" "\n" "//! Position of specified light source or direction of directional light source, vec3.\n" - "#define occLight_Position(theId) occLightSources[theId * 4 + 1].xyz\n" + "#define occLight_Position(theId) occLightSources[occLight_Index(theId) * 4 + 1].xyz\n" "\n" "//! Direction of specified spot light source, vec3.\n" - "#define occLight_SpotDirection(theId) occLightSources[theId * 4 + 2].xyz\n" + "#define occLight_SpotDirection(theId) occLightSources[occLight_Index(theId) * 4 + 2].xyz\n" "\n" "//! Range on which point light source (positional or spot) can affect (>= 0), float.\n" - "#define occLight_Range(theId) occLightSources[theId * 4 + 2].w\n" + "#define occLight_Range(theId) occLightSources[occLight_Index(theId) * 4 + 2].w\n" "\n" "//! Maximum spread angle of the spot light (in radians), float.\n" - "#define occLight_SpotCutOff(theId) occLightSources[theId * 4 + 3].z\n" + "#define occLight_SpotCutOff(theId) occLightSources[occLight_Index(theId) * 4 + 3].z\n" "\n" "//! Attenuation of the spot light intensity (from 0 to 1), float.\n" - "#define occLight_SpotExponent(theId) occLightSources[theId * 4 + 3].w\n" + "#define occLight_SpotExponent(theId) occLightSources[occLight_Index(theId) * 4 + 3].w\n" "\n" "#if !defined(THE_IS_PBR)\n" "//! Diffuse intensity (equals to Specular), vec3.\n" - "#define occLight_Diffuse(theId) occLightSources[theId * 4 + 0].rgb\n" + "#define occLight_Diffuse(theId) occLightSources[occLight_Index(theId) * 4 + 0].rgb\n" "\n" "//! Const attenuation factor of positional light source, float.\n" - "#define occLight_ConstAttenuation(theId) occLightSources[theId * 4 + 3].x\n" + "#define occLight_ConstAttenuation(theId) occLightSources[occLight_Index(theId) * 4 + 3].x\n" "\n" "//! Linear attenuation factor of positional light source, float.\n" - "#define occLight_LinearAttenuation(theId) occLightSources[theId * 4 + 3].y\n" + "#define occLight_LinearAttenuation(theId) occLightSources[occLight_Index(theId) * 4 + 3].y\n" "#endif\n" "#endif\n" "\n" diff --git a/src/Shaders/Shaders_DirectionalLightShadow_glsl.pxx b/src/Shaders/Shaders_DirectionalLightShadow_glsl.pxx index 1aff57dfba..aed61e598c 100644 --- a/src/Shaders/Shaders_DirectionalLightShadow_glsl.pxx +++ b/src/Shaders/Shaders_DirectionalLightShadow_glsl.pxx @@ -17,7 +17,7 @@ static const char Shaders_DirectionalLightShadow_glsl[] = " in int theId,\n" " in vec3 theNormal)\n" "{\n" - " vec4 aPosLightSpace = PosLightSpace[theId];\n" + " vec4 aPosLightSpace = PosLightSpace[occLight_Index(theId)];\n" " vec3 aLightDir = vec3 (occWorldViewMatrix * vec4 (occLight_Position (theId), 0.0));\n" " vec3 aProjCoords = (aPosLightSpace.xyz / aPosLightSpace.w) * 0.5 + vec3 (0.5);\n" " float aCurrentDepth = aProjCoords.z;\n" diff --git a/src/Shaders/Shaders_PBREnvBaking_fs.pxx b/src/Shaders/Shaders_PBREnvBaking_fs.pxx index bc995ac8e7..183773b278 100644 --- a/src/Shaders/Shaders_PBREnvBaking_fs.pxx +++ b/src/Shaders/Shaders_PBREnvBaking_fs.pxx @@ -3,13 +3,23 @@ static const char Shaders_PBREnvBaking_fs[] = "THE_SHADER_IN vec3 ViewDirection; //!< direction of fetching from environment cubemap\n" "\n" + "#if (__VERSION__ >= 120)\n" "uniform int uSamplesNum; //!< number of samples in Monte-Carlo integration\n" - "uniform int uCurrentLevel; //!< current level of specular IBL map (ignored in case of diffuse map's processing)\n" - "uniform int uEnvMapSize; //!< one edge's size of source environtment map's zero mipmap level\n" - "uniform int uYCoeff; //!< coefficient of Y controlling horizontal flip of cubemap\n" - "uniform int uZCoeff; //!< coefficient of Z controlling vertical flip of cubemap\n" + "#else\n" + "const int uSamplesNum = 256;\n" + "#endif\n" "uniform samplerCube uEnvMap; //!< source of baking (environment cubemap)\n" "\n" + "#ifdef THE_TO_BAKE_DIFFUSE\n" + "uniform int uYCoeff; //!< coefficient of Y controlling horizontal flip of cubemap\n" + "uniform int uZCoeff; //!< coefficient of Z controlling vertical flip of cubemap\n" + "#endif\n" + "\n" + "#ifdef THE_TO_BAKE_SPECULAR\n" + "uniform int uCurrentLevel; //!< current level of specular IBL map (ignored in case of diffuse map's processing)\n" + "uniform float uEnvSolidAngleSource; //!< source solid angle sample computed from one edge's size of source environment map's zero mipmap level\n" + "#endif\n" + "\n" "//! Returns coordinates of point theNumber from Hammersley point set having size theSize.\n" "vec2 hammersley (in int theNumber,\n" " in int theSize)\n" @@ -21,7 +31,7 @@ static const char Shaders_PBREnvBaking_fs[] = " {\n" " if (aNumber > 0)\n" " {\n" - " aVanDerCorput += float(aNumber % 2) / float(aDenominator);\n" + " aVanDerCorput += mod(float(aNumber), 2.0) / float(aDenominator);\n" " aNumber /= 2;\n" " aDenominator *= 2;\n" " }\n" @@ -65,6 +75,8 @@ static const char Shaders_PBREnvBaking_fs[] = " return anX * theVector.x + anY * theVector.y + theNormal * theVector.z;\n" "}\n" "\n" + "#ifdef THE_TO_BAKE_DIFFUSE\n" + "#if (__VERSION__ >= 120)\n" "const float aSHBasisFuncCoeffs[9] = float[9]\n" "(\n" " 0.282095 * 0.282095,\n" @@ -77,7 +89,6 @@ static const char Shaders_PBREnvBaking_fs[] = " 0.315392 * 0.315392,\n" " 0.546274 * 0.546274\n" ");\n" - "\n" "const float aSHCosCoeffs[9] = float[9]\n" "(\n" " 3.141593,\n" @@ -90,76 +101,129 @@ static const char Shaders_PBREnvBaking_fs[] = " 0.785398,\n" " 0.785398\n" ");\n" + "#else\n" + "uniform float aSHBasisFuncCoeffs[9];\n" + "uniform float aSHCosCoeffs[9];\n" + "#endif\n" "\n" "//! Bakes diffuse IBL map's spherical harmonics coefficients.\n" "vec3 bakeDiffuseSH()\n" "{\n" - " int anIndex = int(gl_FragCoord.x);\n" - " vec3 aResult = vec3 (0.0);\n" + " int anId = int(gl_FragCoord.x);\n" + " float aCoef;\n" + "#if (__VERSION__ >= 120)\n" + " aCoef = aSHCosCoeffs[anId] * aSHBasisFuncCoeffs[anId];\n" + "#else\n" + " if (anId == 0) { aCoef = aSHCosCoeffs[0] * aSHBasisFuncCoeffs[0]; }\n" + " else if (anId == 1) { aCoef = aSHCosCoeffs[1] * aSHBasisFuncCoeffs[1]; }\n" + " else if (anId == 2) { aCoef = aSHCosCoeffs[2] * aSHBasisFuncCoeffs[2]; }\n" + " else if (anId == 3) { aCoef = aSHCosCoeffs[3] * aSHBasisFuncCoeffs[3]; }\n" + " else if (anId == 4) { aCoef = aSHCosCoeffs[4] * aSHBasisFuncCoeffs[4]; }\n" + " else if (anId == 5) { aCoef = aSHCosCoeffs[5] * aSHBasisFuncCoeffs[5]; }\n" + " else if (anId == 6) { aCoef = aSHCosCoeffs[6] * aSHBasisFuncCoeffs[6]; }\n" + " else if (anId == 7) { aCoef = aSHCosCoeffs[7] * aSHBasisFuncCoeffs[7]; }\n" + " else { aCoef = aSHCosCoeffs[8] * aSHBasisFuncCoeffs[8]; }\n" + "#endif\n" + " vec3 aRes = vec3 (0.0);\n" " for (int aSampleIter = 0; aSampleIter < uSamplesNum; ++aSampleIter)\n" " {\n" " vec2 aHammersleyPoint = hammersley (aSampleIter, uSamplesNum);\n" - " vec3 aDirection = sphereUniformSample (aHammersleyPoint);\n" + " vec3 aDir = sphereUniformSample (aHammersleyPoint);\n" "\n" - " vec3 aValue = occTextureCube (uEnvMap, cubemapVectorTransform (aDirection, uYCoeff, uZCoeff)).rgb;\n" + " vec3 aVal = occTextureCube (uEnvMap, cubemapVectorTransform (aDir, uYCoeff, uZCoeff)).rgb;\n" + " #if (__VERSION__ >= 120)\n" + " float aFunc[9];\n" + " aFunc[0] = 1.0;\n" "\n" - " float aBasisFunc[9];\n" - " aBasisFunc[0] = 1.0;\n" + " aFunc[1] = aDir.x;\n" + " aFunc[2] = aDir.y;\n" + " aFunc[3] = aDir.z;\n" "\n" - " aBasisFunc[1] = aDirection.x;\n" - " aBasisFunc[2] = aDirection.y;\n" - " aBasisFunc[3] = aDirection.z;\n" + " aFunc[4] = aDir.x * aDir.z;\n" + " aFunc[5] = aDir.y * aDir.z;\n" + " aFunc[6] = aDir.x * aDir.y;\n" "\n" - " aBasisFunc[4] = aDirection.x * aDirection.z;\n" - " aBasisFunc[5] = aDirection.y * aDirection.z;\n" - " aBasisFunc[6] = aDirection.x * aDirection.y;\n" + " aFunc[7] = 3.0 * aDir.z * aDir.z - 1.0;\n" + " aFunc[8] = aDir.x * aDir.x - aDir.y * aDir.y;\n" "\n" - " aBasisFunc[7] = 3.0 * aDirection.z * aDirection.z - 1.0;\n" - " aBasisFunc[8] = aDirection.x * aDirection.x - aDirection.y * aDirection.y;\n" - "\n" - " aResult += aValue * aBasisFunc[anIndex];\n" + " aRes += aVal * aFunc[anId];\n" + " #else\n" + " if (anId == 0) { aRes += aVal * 1.0; }\n" + " else if (anId == 1) { aRes += aVal * aDir.x; }\n" + " else if (anId == 2) { aRes += aVal * aDir.y; }\n" + " else if (anId == 3) { aRes += aVal * aDir.z; }\n" + " else if (anId == 4) { aRes += aVal * (aDir.x * aDir.z); }\n" + " else if (anId == 5) { aRes += aVal * (aDir.y * aDir.z); }\n" + " else if (anId == 6) { aRes += aVal * (aDir.x * aDir.y); }\n" + " else if (anId == 7) { aRes += aVal * (3.0 * aDir.z * aDir.z - 1.0); }\n" + " else { aRes += aVal * (aDir.x * aDir.x - aDir.y * aDir.y); }\n" + " #endif\n" " }\n" "\n" - " aResult *= 4.0 * aSHCosCoeffs[anIndex] * aSHBasisFuncCoeffs[anIndex] / float(uSamplesNum);\n" - " return aResult;\n" + " return 4.0 * aRes * aCoef / float(uSamplesNum);\n" + "}\n" + "#endif\n" + "\n" + "#ifdef THE_TO_BAKE_SPECULAR\n" + "//! Computes a single sample for specular IBL map.\n" + "vec4 specularMapSample (in vec3 theNormal,\n" + " in float theRoughness,\n" + " in int theNumber,\n" + " in int theSize)\n" + "{\n" + " vec2 aHammersleyPoint = hammersley (theNumber, theSize);\n" + " vec3 aHalf = importanceSample (aHammersleyPoint, occRoughness (theRoughness));\n" + " float aHdotV = aHalf.z;\n" + " aHalf = fromTangentSpace (aHalf, theNormal);\n" + " vec3 aLight = -reflect (theNormal, aHalf);\n" + " float aNdotL = dot (aLight, theNormal);\n" + " if (aNdotL > 0.0)\n" + " {\n" + " float aSolidAngleSample = 1.0 / (float(theSize) * (occPBRDistribution (aHdotV, theRoughness) * 0.25 + 0.0001) + 0.0001);\n" + " float aLod = (theRoughness == 0.0) ? 0.0 : 0.5 * log2 (aSolidAngleSample / uEnvSolidAngleSource);\n" + " return vec4 (occTextureCubeLod (uEnvMap, aLight, aLod).rgb * aNdotL, aNdotL);\n" + " }\n" + " return vec4 (0.0);\n" "}\n" "\n" "//! Bakes specular IBL map.\n" "vec3 bakeSpecularMap (in vec3 theNormal,\n" " in float theRoughness)\n" "{\n" - " vec3 aResult = vec3(0.0);\n" - " float aWeightSum = 0.0;\n" - " int aSamplesNum = (theRoughness == 0.0) ? 1 : uSamplesNum;\n" - " float aSolidAngleSource = 4.0 * PI / (6.0 * float(uEnvMapSize * uEnvMapSize));\n" - " for (int aSampleIter = 0; aSampleIter < aSamplesNum; ++aSampleIter)\n" + " vec4 aResult = vec4(0.0);\n" + " if (theRoughness == 0.0)\n" " {\n" - " vec2 aHammersleyPoint = hammersley (aSampleIter, aSamplesNum);\n" - " vec3 aHalf = importanceSample (aHammersleyPoint, occRoughness (theRoughness));\n" - " float aHdotV = aHalf.z;\n" - " aHalf = fromTangentSpace (aHalf, theNormal);\n" - " vec3 aLight = -reflect (theNormal, aHalf);\n" - " float aNdotL = dot (aLight, theNormal);\n" - " if (aNdotL > 0.0)\n" + " aResult = specularMapSample (theNormal, theRoughness, 0, 1);\n" + " }\n" + " else\n" + " {\n" + " for (int aSampleIter = 0; aSampleIter < uSamplesNum; ++aSampleIter)\n" " {\n" - " float aSolidAngleSample = 1.0 / (float(aSamplesNum) * (occPBRDistribution (aHdotV, theRoughness) * 0.25 + 0.0001) + 0.0001);\n" - " float aLod = (theRoughness == 0.0) ? 0.0 : 0.5 * log2 (aSolidAngleSample / aSolidAngleSource);\n" - " aResult += occTextureCubeLod (uEnvMap, aLight, aLod).rgb * aNdotL;\n" - " aWeightSum += aNdotL;\n" + " aResult += specularMapSample (theNormal, theRoughness, aSampleIter, uSamplesNum);\n" " }\n" " }\n" - " return aResult / aWeightSum;\n" + " return aResult.xyz / aResult.w;\n" "}\n" + "#endif\n" "\n" "void main()\n" "{\n" " vec3 aViewDirection = normalize (ViewDirection);\n" - " if (occNbSpecIBLLevels == 0)\n" - " {\n" - " occSetFragColor (vec4 (bakeDiffuseSH (), 1.0));\n" - " }\n" - " else\n" - " {\n" - " occSetFragColor (vec4 (bakeSpecularMap (aViewDirection, float(uCurrentLevel) / float(occNbSpecIBLLevels - 1)), 1.0));\n" - " }\n" + "#ifdef THE_TO_BAKE_DIFFUSE\n" + " vec4 aRes = vec4 (bakeDiffuseSH(), 1.0);\n" + "#ifdef THE_TO_PACK_FLOAT\n" + " int aCompIndex = int(gl_FragCoord.y);\n" + " float aComp = aCompIndex == 0 ? aRes.x : (aCompIndex == 1 ? aRes.y : aRes.z);\n" + " int aFixedPrec = int(aComp * 2147483647.0);\n" + " int aFixedDiv1 = aFixedPrec / 256;\n" + " int aFixedDiv2 = aFixedDiv1 / 256;\n" + " int aFixedDiv3 = aFixedDiv2 / 256;\n" + " vec4 aPacked = vec4(float(aFixedPrec), float(aFixedDiv1), float(aFixedDiv2), float(aFixedDiv3));\n" + " aRes = fract (aPacked * (1.0 / 256.0));\n" + "#endif\n" + " occFragColor = aRes;\n" + "#else\n" + " float aRoughness = float(uCurrentLevel) / float(occNbSpecIBLLevels - 1);\n" + " occFragColor = vec4 (bakeSpecularMap (aViewDirection, aRoughness), 1.0);\n" + "#endif\n" "}\n"; diff --git a/src/Shaders/Shaders_PBREnvBaking_vs.pxx b/src/Shaders/Shaders_PBREnvBaking_vs.pxx index 89a37cb50d..1c004a5fc7 100644 --- a/src/Shaders/Shaders_PBREnvBaking_vs.pxx +++ b/src/Shaders/Shaders_PBREnvBaking_vs.pxx @@ -7,32 +7,52 @@ static const char Shaders_PBREnvBaking_vs[] = "uniform int uYCoeff; //!< coefficient of Y controlling horizontal flip of cubemap\n" "uniform int uZCoeff; //!< coefficient of Z controlling vertical flip of cubemap\n" "\n" - "const mat2 cubemapDirectionMatrices[6] = mat2[]\n" - "(\n" - " mat2 ( 0, -1, -1, 0),\n" - " mat2 ( 0, 1, -1, 0),\n" - " mat2 ( 0, 1, 1, 0),\n" - " mat2 ( 0, 1, -1, 0),\n" - " mat2 ( 1, 0, 0, -1),\n" - " mat2 (-1, 0, 0, -1)\n" - ");\n" - "\n" - "//! Generates environment map fetching direction considering current index of side.\n" - "vec3 cubemapBakingViewDirection (in int theSide,\n" - " in vec2 theScreenCoord)\n" - "{\n" - " int anAxis = theSide / 2;\n" - " vec3 aDirection = vec3(0.0);\n" - " aDirection[anAxis] = float(-(int(theSide) % 2) * 2 + 1);\n" - " theScreenCoord = cubemapDirectionMatrices[theSide] * theScreenCoord;\n" - " aDirection[(anAxis + 1) % 3] = theScreenCoord.x;\n" - " aDirection[(anAxis + 2) % 3] = theScreenCoord.y;\n" - " return aDirection;\n" - "}\n" - "\n" "void main()\n" "{\n" - " ViewDirection = cubemapBakingViewDirection (uCurrentSide, occVertex.xy);\n" - " ViewDirection = cubemapVectorTransform (ViewDirection, uYCoeff, uZCoeff);\n" + " vec3 aDir;\n" + " vec2 aCoord;\n" + " if (uCurrentSide == 0)\n" + " {\n" + " aCoord = mat2( 0,-1,-1, 0) * occVertex.xy;\n" + " aDir.x = 1.0;\n" + " aDir.y = aCoord.x;\n" + " aDir.z = aCoord.y;\n" + " }\n" + " else if (uCurrentSide == 1)\n" + " {\n" + " aCoord = mat2( 0, 1,-1, 0) * occVertex.xy;\n" + " aDir.x = -1.0;\n" + " aDir.y = aCoord.x;\n" + " aDir.z = aCoord.y;\n" + " }\n" + " else if (uCurrentSide == 2)\n" + " {\n" + " aCoord = mat2( 0, 1, 1, 0) * occVertex.xy;\n" + " aDir.x = aCoord.y;\n" + " aDir.y = 1.0;\n" + " aDir.z = aCoord.x;\n" + " }\n" + " else if (uCurrentSide == 3)\n" + " {\n" + " aCoord = mat2( 0, 1,-1, 0) * occVertex.xy;\n" + " aDir.x = aCoord.y;\n" + " aDir.y = -1.0;\n" + " aDir.z = aCoord.x;\n" + " }\n" + " else if (uCurrentSide == 4)\n" + " {\n" + " aCoord = mat2( 1, 0, 0,-1) * occVertex.xy;\n" + " aDir.x = aCoord.x;\n" + " aDir.y = aCoord.y;\n" + " aDir.z = 1.0;\n" + " }\n" + " else //if (uCurrentSide == 5)\n" + " {\n" + " aCoord = mat2(-1, 0, 0,-1) * occVertex.xy;\n" + " aDir.x = aCoord.x;\n" + " aDir.y = aCoord.y;\n" + " aDir.z = -1.0;\n" + " }\n" + " ViewDirection = cubemapVectorTransform (aDir, uYCoeff, uZCoeff);\n" " gl_Position = vec4 (occVertex.xy, 0.0, 1.0);\n" "}\n";