hasTexSRGB (Standard_False),
hasFboSRGB (Standard_False),
hasSRGBControl (Standard_False),
+ hasFboRenderMipmap (Standard_False),
#if defined(GL_ES_VERSION_2_0)
hasFlatShading (OpenGl_FeatureNotAvailable),
#else
// 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)
{
|| 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
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)
// 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
);
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)
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());
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);
}
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);
}
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;
}
// =======================================================================
// 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 };
+ const OpenGl_TypeOfIBLMap aRendMapId = myCanRenderFloat ? OpenGl_TypeOfIBLMap_DiffuseSH : OpenGl_TypeOfIBLMap_DiffuseFallback;
+ Image_PixMap anImageF;
+ if (!myCanRenderFloat)
+ {
+ 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 (theIsDrawAction)
+ if (theDrawParams != NULL)
{
- theCtx->ActiveProgram()->SetUniform(theCtx, "occNbSpecIBLLevels", 0);
- theCtx->ActiveProgram()->SetUniform(theCtx, "uSamplesNum", static_cast<Standard_Integer>(theNbSamples));
+ 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<Graphic3d_Vec4> (0, aValIter);
+ }
+ else
+ {
+ const int32_t aPacked[3] = { anImageIn.Value<int32_t> (2, aValIter), anImageIn.Value<int32_t> (1, aValIter), anImageIn.Value<int32_t> (0, aValIter) };
+ aVal[0] = aPacked[0] / 2147483647.0f;
+ aVal[1] = aPacked[1] / 2147483647.0f;
+ aVal[2] = aPacked[2] / 2147483647.0f;
+ }
+ anImageF.ChangeValue<Graphic3d_Vec4> (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<Graphic3d_Vec4> (0, 0) = Graphic3d_Vec4 (1.0f);
+ for (Standard_Size aValIter = 1; aValIter < anImageF.SizeX(); ++aValIter)
+ {
+ anImageF.ChangeValue<Graphic3d_Vec4> (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;
// 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<Standard_Integer>(Graphic3d_PBRMaterial::SpecIBLMapSamplesFactor (theProbability, aLevelIter / float (mySpecMapLevelsNumber - 1)) * theNbSamples);
- theCtx->ActiveProgram()->SetUniform (theCtx, "uSamplesNum", static_cast<Standard_Integer>(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);
{
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)
}
}
+ if (theDrawParams != NULL)
+ {
+ myVBO.UnbindAttribute (theCtx, Graphic3d_TOA_POS);
+ }
+
return true;
}
// 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,
+ 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_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+ if (theCtx->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
+ Message::SendTrace() << "OpenGl_PBREnvironment, incomplete FBO for specular map " << aSideIter << " "<< aLevel;
return false;
}
}
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()
clear (theCtx, Graphic3d_Vec3(1.0f));
}
- myVBO.UnbindAttribute (theCtx, Graphic3d_TOA_POS);
theEnvMap->Unbind (theCtx, theCtx->PBREnvLUTTexUnit());
}
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);
}
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.
//! @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.
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
};
}
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
// 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;
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);
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;
}
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.
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
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
#include <OpenGl_ArbFBO.hxx>
#include <OpenGl_Context.hxx>
-#include <OpenGl_GlCore32.hxx>
+#include <OpenGl_GlCore45.hxx>
#include <OpenGl_Sampler.hxx>
#include <Graphic3d_TextureParams.hxx>
#include <TCollection_ExtendedString.hxx>
}
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
+}
//! 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")
bool theIsColorMap)
{
OpenGl_TextureFormat aFormat;
+ aFormat.SetImageFormat (theFormat);
switch (theFormat)
{
case Image_Format_GrayF:
aFormat.SetInternalFormat (theSizedFormat);
aFormat.SetPixelFormat (GL_RGBA);
aFormat.SetDataType (GL_FLOAT);
+ aFormat.SetImageFormat (Image_Format_RGBAF);
return aFormat;
}
case GL_R32F:
aFormat.SetInternalFormat (theSizedFormat);
aFormat.SetPixelFormat (GL_RED);
aFormat.SetDataType (GL_FLOAT);
+ aFormat.SetImageFormat (Image_Format_GrayF);
return aFormat;
}
case GL_RG32F:
aFormat.SetInternalFormat (theSizedFormat);
aFormat.SetPixelFormat (GL_RG);
aFormat.SetDataType (GL_FLOAT);
+ aFormat.SetImageFormat (Image_Format_RGF);
return aFormat;
}
case GL_RGBA16F:
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)
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)
aFormat.SetInternalFormat (theSizedFormat);
aFormat.SetPixelFormat (GL_RGBA);
aFormat.SetDataType (GL_UNSIGNED_BYTE);
+ aFormat.SetImageFormat (Image_Format_RGBA);
if (theSizedFormat == GL_SRGB8_ALPHA8
&& !theCtx->ToRenderSRGB())
{
aFormat.SetInternalFormat (theSizedFormat);
aFormat.SetPixelFormat (GL_RGB);
aFormat.SetDataType (GL_UNSIGNED_BYTE);
+ aFormat.SetImageFormat (Image_Format_RGB);
if (theSizedFormat == GL_SRGB8
&& !theCtx->ToRenderSRGB())
{
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
|| 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).
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
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<Image_ColorRGF> (aRowIter, aColIter);
+ Image_ColorRGBAF& aPixelRGBA = aPixMap->ChangeValue<Image_ColorRGBAF> (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()))
// 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
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;
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)
{
if (aNumber > 0)
{
- aVanDerCorput += float(aNumber % 2) / float(aDenominator);
+ aVanDerCorput += mod(float(aNumber), 2.0) / float(aDenominator);
aNumber /= 2;
aDenominator *= 2;
}
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,
0.315392 * 0.315392,
0.546274 * 0.546274
);
-
const float aSHCosCoeffs[9] = float[9]
(
3.141593,
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 aValue = occTextureCube (uEnvMap, cubemapVectorTransform (aDirection, uYCoeff, uZCoeff)).rgb;
-
- float aBasisFunc[9];
- aBasisFunc[0] = 1.0;
-
- aBasisFunc[1] = aDirection.x;
- aBasisFunc[2] = aDirection.y;
- aBasisFunc[3] = aDirection.z;
-
- aBasisFunc[4] = aDirection.x * aDirection.z;
- aBasisFunc[5] = aDirection.y * aDirection.z;
- aBasisFunc[6] = aDirection.x * aDirection.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];
+ vec3 aDir = sphereUniformSample (aHammersleyPoint);
+
+ vec3 aVal = occTextureCube (uEnvMap, cubemapVectorTransform (aDir, uYCoeff, uZCoeff)).rgb;
+ #if (__VERSION__ >= 120)
+ float aFunc[9];
+ aFunc[0] = 1.0;
+
+ aFunc[1] = aDir.x;
+ aFunc[2] = aDir.y;
+ aFunc[3] = aDir.z;
+
+ aFunc[4] = aDir.x * aDir.z;
+ aFunc[5] = aDir.y * aDir.z;
+ aFunc[6] = aDir.x * aDir.y;
+
+ aFunc[7] = 3.0 * aDir.z * aDir.z - 1.0;
+ aFunc[8] = aDir.x * aDir.x - aDir.y * aDir.y;
+
+ 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)
+ {
+ aResult = specularMapSample (theNormal, theRoughness, 0, 1);
+ }
+ else
{
- 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)
+ 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
}
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);
}
"// 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"
" 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"
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"
" {\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"
" 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"
" 0.315392 * 0.315392,\n"
" 0.546274 * 0.546274\n"
");\n"
- "\n"
"const float aSHCosCoeffs[9] = float[9]\n"
"(\n"
" 3.141593,\n"
" 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"
- "\n"
- " vec3 aValue = occTextureCube (uEnvMap, cubemapVectorTransform (aDirection, uYCoeff, uZCoeff)).rgb;\n"
- "\n"
- " float aBasisFunc[9];\n"
- " aBasisFunc[0] = 1.0;\n"
- "\n"
- " aBasisFunc[1] = aDirection.x;\n"
- " aBasisFunc[2] = aDirection.y;\n"
- " aBasisFunc[3] = aDirection.z;\n"
- "\n"
- " aBasisFunc[4] = aDirection.x * aDirection.z;\n"
- " aBasisFunc[5] = aDirection.y * aDirection.z;\n"
- " aBasisFunc[6] = aDirection.x * aDirection.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"
+ " vec3 aDir = sphereUniformSample (aHammersleyPoint);\n"
+ "\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"
+ " aFunc[1] = aDir.x;\n"
+ " aFunc[2] = aDir.y;\n"
+ " aFunc[3] = aDir.z;\n"
+ "\n"
+ " aFunc[4] = aDir.x * aDir.z;\n"
+ " aFunc[5] = aDir.y * aDir.z;\n"
+ " aFunc[6] = aDir.x * aDir.y;\n"
+ "\n"
+ " aFunc[7] = 3.0 * aDir.z * aDir.z - 1.0;\n"
+ " aFunc[8] = aDir.x * aDir.x - aDir.y * aDir.y;\n"
+ "\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"
+ " aResult = specularMapSample (theNormal, theRoughness, 0, 1);\n"
+ " }\n"
+ " else\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"
+ " 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";
"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";