diff --git a/src/Aspect/Aspect_SkydomeBackground.cxx b/src/Aspect/Aspect_SkydomeBackground.cxx new file mode 100644 index 0000000000..8fd3cc031f --- /dev/null +++ b/src/Aspect/Aspect_SkydomeBackground.cxx @@ -0,0 +1,99 @@ +// Created on: 2021-10-14 +// Created by: Artem CHESNOKOV +// Copyright (c) 2021 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include + +// ======================================================================= +// function : Constructor +// purpose : +// ======================================================================= +Aspect_SkydomeBackground::Aspect_SkydomeBackground() +: mySunDirection (0.0f, 1.0f, 0.0f), + myCloudiness (0.2f), + myTime (0.0f), + myFogginess (0.0f), + mySize (512) +{ + // +} + +// ======================================================================= +// function : Constructor +// purpose : +// ======================================================================= +Aspect_SkydomeBackground::Aspect_SkydomeBackground (const gp_Dir& theSunDirection, Standard_ShortReal theCloudiness, + Standard_ShortReal theTime, Standard_ShortReal theFogginess, Standard_Integer theSize) + : mySunDirection (theSunDirection), myCloudiness (theCloudiness), myTime (theTime), myFogginess (theFogginess), mySize (theSize) +{ + Standard_RangeError_Raise_if (theFogginess < 0, "Aspect_SkydomeBackground::Aspect_SkydomeBackground() theFoggines must be >= 0"); + Standard_RangeError_Raise_if (theCloudiness < 0, "Aspect_SkydomeBackground::Aspect_SkydomeBackground() theCloudiness must be >= 0"); + Standard_RangeError_Raise_if (theSize <= 0, "Aspect_SkydomeBackground::Aspect_SkydomeBackground() theSize must be > 0"); +} + +// ======================================================================= +// function : ~Aspect_SkydomeBackground +// purpose : +// ======================================================================= +Aspect_SkydomeBackground::~Aspect_SkydomeBackground() +{ + // +} + +// ======================================================================= +// function : SetCloudiness +// purpose : +// ======================================================================= +void Aspect_SkydomeBackground::SetCloudiness (Standard_ShortReal theCloudiness) +{ + Standard_RangeError_Raise_if (theCloudiness < 0, "Aspect_SkydomeBackground::SetCloudiness() theCloudiness must be >= 0"); + myCloudiness = theCloudiness; +} + +// ======================================================================= +// function : SetFogginess +// purpose : +// ======================================================================= +void Aspect_SkydomeBackground::SetFogginess (Standard_ShortReal theFogginess) +{ + Standard_RangeError_Raise_if (theFogginess < 0, "Aspect_SkydomeBackground::SetFogginess() theFoggines must be >= 0"); + myFogginess = theFogginess; +} + +// ======================================================================= +// function : SetSize +// purpose : +// ======================================================================= +void Aspect_SkydomeBackground::SetSize (Standard_Integer theSize) +{ + Standard_RangeError_Raise_if (theSize <= 0, "Aspect_SkydomeBackground::SetSize() theSize must be > 0"); + mySize = theSize; +} + +// ======================================================================= +// function : DumpJson +// purpose : +// ======================================================================= +void Aspect_SkydomeBackground::DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth) const +{ + OCCT_DUMP_CLASS_BEGIN (theOStream, Aspect_GradientBackground) + + OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &mySunDirection) + OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myTime) + OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myFogginess) + OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myCloudiness) + OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, mySize) +} diff --git a/src/Aspect/Aspect_SkydomeBackground.hxx b/src/Aspect/Aspect_SkydomeBackground.hxx new file mode 100644 index 0000000000..52eff71c17 --- /dev/null +++ b/src/Aspect/Aspect_SkydomeBackground.hxx @@ -0,0 +1,104 @@ +// Created on: 2021-10-14 +// Created by: Artem CHESNOKOV +// Copyright (c) 2021 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#ifndef _Aspect_SkydomeBackground_Header +#define _Aspect_SkydomeBackground_Header + +#include +#include +#include +#include +#include + +//! This class allows the definition of a window skydome background. +class Aspect_SkydomeBackground +{ +public: + + DEFINE_STANDARD_ALLOC + + //! Creates a window skydome background. + //! By default skydome is initialized with sun at its zenith (0.0, 1.0, 0.0), + //! average clody (0.2), zero time parameter, zero fogginess, 512x512 texture size. + Standard_EXPORT Aspect_SkydomeBackground(); + + //! Creates a window skydome background with given parameters. + //! @param[in] theSunDirection direction to the sun (moon). Sun direction with negative Y component + //! represents moon with (-X, -Y, -Z) direction. + //! @param[in] theCloudiness cloud intensity, 0.0 means no clouds at all and 1.0 - high clody. + //! @param[in] theTime time parameter of simulation. Might be tweaked to slightly change appearance. + //! @param[in] theFogginess fog intensity, 0.0 means no fog and 1.0 - high fogginess + //! @param[in] theSize size of cubemap side in pixels. + Standard_EXPORT Aspect_SkydomeBackground (const gp_Dir& theSunDirection, + Standard_ShortReal theCloudiness, + Standard_ShortReal theTime, + Standard_ShortReal theFogginess, + Standard_Integer theSize); + + //! Destructor. + Standard_EXPORT ~Aspect_SkydomeBackground(); + + //! Get sun direction. By default this value is (0, 1, 0) + //! Sun direction with negative Y component represents moon with (-X, -Y, -Z) direction. + const gp_Dir& SunDirection() const { return mySunDirection; } + + //! Get cloud intensity. By default this value is 0.2 + //! 0.0 means no clouds at all and 1.0 - high clody. + Standard_ShortReal Cloudiness() const { return myCloudiness; } + + //! Get time of cloud simulation. By default this value is 0.0 + //! This value might be tweaked to slightly change appearance of clouds. + Standard_ShortReal TimeParameter() const { return myTime; } + + //! Get fog intensity. By default this value is 0.0 + //! 0.0 means no fog and 1.0 - high fogginess + Standard_ShortReal Fogginess() const { return myFogginess; } + + //! Get size of cubemap. By default this value is 512 + Standard_Integer Size() const { return mySize; } + + //! Set sun direction. By default this value is (0, 1, 0) + //! Sun direction with negative Y component represents moon with (-X, -Y, -Z) direction. + void SetSunDirection (const gp_Dir& theSunDirection) { mySunDirection = theSunDirection; } + + //! Set cloud intensity. By default this value is 0.2 + //! 0.0 means no clouds at all and 1.0 - high clody. + Standard_EXPORT void SetCloudiness (Standard_ShortReal theCloudiness); + + //! Set time of cloud simulation. By default this value is 0.0 + //! This value might be tweaked to slightly change appearance of clouds. + void SetTimeParameter (Standard_ShortReal theTime) { myTime = theTime; } + + //! Set fog intensity. By default this value is 0.0 + //! 0.0 means no fog and 1.0 - high fogginess + Standard_EXPORT void SetFogginess (Standard_ShortReal theFogginess); + + //! Set size of cubemap. By default this value is 512 + Standard_EXPORT void SetSize (Standard_Integer theSize); + + //! Dumps the content of me into the stream + Standard_EXPORT void DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth = -1) const; + +private: + + gp_Dir mySunDirection; //!< Sun (moon) light direction. + Standard_ShortReal myCloudiness; //!< Cloud intensity. + Standard_ShortReal myTime; //!< Simulation time parameter. + Standard_ShortReal myFogginess; //!< Fog intensity + Standard_Integer mySize; //!< Size of cubemap in pixels + +}; + +#endif // _Aspect_SkydomeBackground_Header diff --git a/src/Aspect/FILES b/src/Aspect/FILES index 492dcb79a4..b1c7fa3f98 100755 --- a/src/Aspect/FILES +++ b/src/Aspect/FILES @@ -40,6 +40,8 @@ Aspect_RectangularGrid.hxx Aspect_RenderingContext.hxx Aspect_SequenceOfColor.hxx Aspect_ScrollDelta.hxx +Aspect_SkydomeBackground.cxx +Aspect_SkydomeBackground.hxx Aspect_Touch.hxx Aspect_TouchMap.hxx Aspect_TrackedDevicePose.hxx diff --git a/src/Graphic3d/Graphic3d_CView.cxx b/src/Graphic3d/Graphic3d_CView.cxx index a97efe8692..07077415ff 100644 --- a/src/Graphic3d/Graphic3d_CView.cxx +++ b/src/Graphic3d/Graphic3d_CView.cxx @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,7 @@ IMPLEMENT_STANDARD_RTTIEXT(Graphic3d_CView,Graphic3d_DataStructureManager) Graphic3d_CView::Graphic3d_CView (const Handle(Graphic3d_StructureManager)& theMgr) : myBgColor (Quantity_NOC_BLACK), myBackgroundType (Graphic3d_TOB_NONE), + myToUpdateSkydome (Standard_False), myStructureManager (theMgr), myCamera (new Graphic3d_Camera()), myHiddenObjects (new Graphic3d_NMapOfTransient()), @@ -53,6 +55,25 @@ Graphic3d_CView::~Graphic3d_CView() } } +// ======================================================================= +// function : SetBackgroundSkydome +// purpose : +// ======================================================================= +void Graphic3d_CView::SetBackgroundSkydome (const Aspect_SkydomeBackground& theAspect, + Standard_Boolean theToUpdatePBREnv) +{ + myToUpdateSkydome = true; + mySkydomeAspect = theAspect; + myCubeMapBackground = new Graphic3d_CubeMapPacked (""); + SetBackgroundType (Graphic3d_TOB_CUBEMAP); + if (theToUpdatePBREnv + && !myCubeMapIBL.IsNull()) + { + SetImageBasedLighting (false); + SetImageBasedLighting (true); + } +} + // ======================================================================= // function : Activate // purpose : diff --git a/src/Graphic3d/Graphic3d_CView.hxx b/src/Graphic3d/Graphic3d_CView.hxx index f7c0121a1e..43e3675fc3 100644 --- a/src/Graphic3d/Graphic3d_CView.hxx +++ b/src/Graphic3d/Graphic3d_CView.hxx @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -396,6 +397,13 @@ public: //! Sets background type. void SetBackgroundType (Graphic3d_TypeOfBackground theType) { myBackgroundType = theType; } + //! Returns skydome aspect; + const Aspect_SkydomeBackground& BackgroundSkydome() const { return mySkydomeAspect; } + + //! Sets skydome aspect + Standard_EXPORT void SetBackgroundSkydome (const Aspect_SkydomeBackground& theAspect, + Standard_Boolean theToUpdatePBREnv = Standard_True); + //! Enables or disables IBL (Image Based Lighting) from background cubemap. //! Has no effect if PBR is not used. //! @param[in] theToEnableIBL enable or disable IBL from background cubemap @@ -574,6 +582,8 @@ protected: Handle(Graphic3d_CubeMap) myCubeMapIBL; //!< Cubemap used for environment lighting Handle(Graphic3d_TextureEnv) myTextureEnvData; Graphic3d_TypeOfBackground myBackgroundType; //!< Current type of background + Aspect_SkydomeBackground mySkydomeAspect; + Standard_Boolean myToUpdateSkydome; Handle(Graphic3d_StructureManager) myStructureManager; Handle(Graphic3d_Camera) myCamera; diff --git a/src/Graphic3d/Graphic3d_ShaderManager.cxx b/src/Graphic3d/Graphic3d_ShaderManager.cxx index d6dfb378b5..b553462225 100644 --- a/src/Graphic3d/Graphic3d_ShaderManager.cxx +++ b/src/Graphic3d/Graphic3d_ShaderManager.cxx @@ -32,6 +32,7 @@ #include "../Shaders/Shaders_PhongPointLight_glsl.pxx" #include "../Shaders/Shaders_PhongSpotLight_glsl.pxx" #include "../Shaders/Shaders_PointLightAttenuation_glsl.pxx" +#include "../Shaders/Shaders_SkydomBackground_fs.pxx" #include "../Shaders/Shaders_TangentSpaceNormal_glsl.pxx" IMPLEMENT_STANDARD_RTTIEXT(Graphic3d_ShaderManager, Standard_Transient) @@ -2094,6 +2095,43 @@ Handle(Graphic3d_ShaderProgram) Graphic3d_ShaderManager::getBgCubeMapProgram() c return aProgSrc; } +// ======================================================================= +// function : getBgSkydomeProgram +// purpose : +// ======================================================================= +Handle(Graphic3d_ShaderProgram) Graphic3d_ShaderManager::getBgSkydomeProgram() const +{ + Handle(Graphic3d_ShaderProgram) aProgSrc = new Graphic3d_ShaderProgram(); + + Graphic3d_ShaderObject::ShaderVariableList aUniforms, aStageInOuts; + aStageInOuts.Append (Graphic3d_ShaderObject::ShaderVariable ("vec2 TexCoord", Graphic3d_TOS_VERTEX | Graphic3d_TOS_FRAGMENT)); + + TCollection_AsciiString aSrcVert = TCollection_AsciiString() + + EOL"void main()" + EOL"{" + EOL" gl_Position = vec4 (occVertex.xy, 0.0, 1.0);" + EOL" TexCoord = 0.5 * gl_Position.xy + vec2 (0.5);" + EOL"}"; + + TCollection_AsciiString aSrcFrag = Shaders_SkydomBackground_fs; + + if (myGapi == Aspect_GraphicsLibrary_OpenGL) + { + aProgSrc->SetHeader ("#version 130"); + } + else if (myGapi == Aspect_GraphicsLibrary_OpenGLES) + { + if (IsGapiGreaterEqual (3, 0)) + { + aProgSrc->SetHeader ("#version 300 es"); + } + } + aProgSrc->AttachShader (Graphic3d_ShaderObject::CreateFromSource (aSrcVert, Graphic3d_TOS_VERTEX, aUniforms, aStageInOuts)); + aProgSrc->AttachShader (Graphic3d_ShaderObject::CreateFromSource (aSrcFrag, Graphic3d_TOS_FRAGMENT, aUniforms, aStageInOuts)); + + return aProgSrc; +} + // ======================================================================= // function : getColoredQuadProgram // purpose : diff --git a/src/Graphic3d/Graphic3d_ShaderManager.hxx b/src/Graphic3d/Graphic3d_ShaderManager.hxx index 0619ea11e7..023b574260 100644 --- a/src/Graphic3d/Graphic3d_ShaderManager.hxx +++ b/src/Graphic3d/Graphic3d_ShaderManager.hxx @@ -142,6 +142,9 @@ protected: //! Generates shader program to render environment cubemap as background. Standard_EXPORT Handle(Graphic3d_ShaderProgram) getBgCubeMapProgram() const; + //! Generates shader program to render skydome background. + Standard_EXPORT Handle(Graphic3d_ShaderProgram) getBgSkydomeProgram() const; + //! Generates shader program to render correctly colored quad. Standard_EXPORT Handle(Graphic3d_ShaderProgram) getColoredQuadProgram() const; diff --git a/src/OpenGl/OpenGl_ShaderManager.cxx b/src/OpenGl/OpenGl_ShaderManager.cxx index 892cc020a5..0023cf89d4 100644 --- a/src/OpenGl/OpenGl_ShaderManager.cxx +++ b/src/OpenGl/OpenGl_ShaderManager.cxx @@ -1374,6 +1374,19 @@ const Handle(Graphic3d_ShaderProgram)& OpenGl_ShaderManager::GetBgCubeMapProgram return myBgCubeMapProgram; } +// ======================================================================= +// function : GetBgSkydomeProgram +// purpose : +// ======================================================================= +const Handle(Graphic3d_ShaderProgram)& OpenGl_ShaderManager::GetBgSkydomeProgram () +{ + if (myBgSkydomeProgram.IsNull()) + { + myBgSkydomeProgram = getBgSkydomeProgram(); + } + return myBgSkydomeProgram; +} + // ======================================================================= // function : GetColoredQuadProgram // purpose : diff --git a/src/OpenGl/OpenGl_ShaderManager.hxx b/src/OpenGl/OpenGl_ShaderManager.hxx index 22cc1b339a..9d9501fd81 100644 --- a/src/OpenGl/OpenGl_ShaderManager.hxx +++ b/src/OpenGl/OpenGl_ShaderManager.hxx @@ -229,6 +229,9 @@ public: //! Generates shader program to render environment cubemap as background. Standard_EXPORT const Handle(Graphic3d_ShaderProgram)& GetBgCubeMapProgram(); + //! Generates shader program to render skydome background. + Standard_EXPORT const Handle(Graphic3d_ShaderProgram)& GetBgSkydomeProgram(); + //! Generates shader program to render correctly colored quad. Standard_EXPORT const Handle(Graphic3d_ShaderProgram)& GetColoredQuadProgram(); @@ -772,6 +775,7 @@ protected: 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(Graphic3d_ShaderProgram) myBgSkydomeProgram; //!< program for background cubemap rendering Handle(Graphic3d_ShaderProgram) myColoredQuadProgram; //!< program for correct quad rendering Handle(OpenGl_ShaderProgram) myStereoPrograms[Graphic3d_StereoMode_NB]; //!< standard stereo programs diff --git a/src/OpenGl/OpenGl_View.cxx b/src/OpenGl/OpenGl_View.cxx index f352db0655..99f139e030 100644 --- a/src/OpenGl/OpenGl_View.cxx +++ b/src/OpenGl/OpenGl_View.cxx @@ -998,13 +998,17 @@ void OpenGl_View::drawBackground (const Handle(OpenGl_Workspace)& theWorkspace, if (myBackgroundType == Graphic3d_TOB_CUBEMAP) { - myCubeMapParams->Aspect()->ShaderProgram()->PushVariableInt ("uZCoeff", myCubeMapBackground->ZIsInverted() ? -1 : 1); - myCubeMapParams->Aspect()->ShaderProgram()->PushVariableInt ("uYCoeff", myCubeMapBackground->IsTopDown() ? 1 : -1); - const OpenGl_Aspects* anOldAspectFace = theWorkspace->SetAspects (myCubeMapParams); + updateSkydomeBg (aCtx); + if (!myCubeMapParams->Aspect()->ShaderProgram().IsNull()) + { + myCubeMapParams->Aspect()->ShaderProgram()->PushVariableInt ("uZCoeff", myCubeMapBackground->ZIsInverted() ? -1 : 1); + myCubeMapParams->Aspect()->ShaderProgram()->PushVariableInt ("uYCoeff", myCubeMapBackground->IsTopDown() ? 1 : -1); + const OpenGl_Aspects* anOldAspectFace = theWorkspace->SetAspects (myCubeMapParams); - myBackgrounds[Graphic3d_TOB_CUBEMAP]->Render (theWorkspace, theProjection); + myBackgrounds[Graphic3d_TOB_CUBEMAP]->Render (theWorkspace, theProjection); - theWorkspace->SetAspects (anOldAspectFace); + theWorkspace->SetAspects (anOldAspectFace); + } } else if (myBackgroundType == Graphic3d_TOB_GRADIENT || myBackgroundType == Graphic3d_TOB_TEXTURE) @@ -3080,6 +3084,97 @@ Standard_Boolean OpenGl_View::checkOitCompatibility (const Handle(OpenGl_Context return Standard_False; } +// ======================================================================= +// function : updateSkydomeBg +// purpose : +// ======================================================================= +void OpenGl_View::updateSkydomeBg (const Handle(OpenGl_Context)& theCtx) +{ + if (!myToUpdateSkydome) + { + return; + } + + myToUpdateSkydome = false; + + // Set custom shader + Handle(OpenGl_ShaderProgram) aProg; + Handle(Graphic3d_ShaderProgram) aProxy = theCtx->ShaderManager()->GetBgSkydomeProgram(); + TCollection_AsciiString anUnused; + theCtx->ShaderManager()->Create (aProxy, anUnused, aProg); + Handle(OpenGl_ShaderProgram) aPrevProgram = theCtx->ActiveProgram(); + theCtx->BindProgram (aProg); + + // Setup uniforms + aProg->SetUniform (theCtx, "uSunDir", OpenGl_Vec3((float )mySkydomeAspect.SunDirection().X(), + (float )mySkydomeAspect.SunDirection().Y(), + (float )mySkydomeAspect.SunDirection().Z())); + aProg->SetUniform (theCtx, "uCloudy", mySkydomeAspect.Cloudiness()); + aProg->SetUniform (theCtx, "uTime", mySkydomeAspect.TimeParameter()); + aProg->SetUniform (theCtx, "uFog", mySkydomeAspect.Fogginess()); + + // Create and prepare framebuffer + GLint aPrevFBO = 0; + theCtx->core11fwd->glGetIntegerv (GL_FRAMEBUFFER_BINDING, &aPrevFBO); + GLuint anFBO = 0; + theCtx->arbFBO->glGenFramebuffers (1, &anFBO); + theCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, anFBO); + + const Standard_Integer anOldViewport[4] = {theCtx->Viewport()[0], theCtx->Viewport()[1], theCtx->Viewport()[2], theCtx->Viewport()[3]}; + const Standard_Integer aViewport[4] = {0, 0, mySkydomeAspect.Size(), mySkydomeAspect.Size()}; + theCtx->ResizeViewport (aViewport); + + // Fullscreen triangle + Handle(OpenGl_VertexBuffer) aVBO = new OpenGl_VertexBuffer(); + const float aTriangle[] = {-1.0, -1.0, 3.0, -1.0, -1.0, 3.0}; + aVBO->Init (theCtx, 2, 3, aTriangle); + aVBO->BindAttribute (theCtx, Graphic3d_TypeOfAttribute::Graphic3d_TOA_POS); + aVBO->Bind (theCtx); + + if (mySkydomeTexture.IsNull()) + { + mySkydomeTexture = new OpenGl_Texture(); + mySkydomeTexture->Sampler()->Parameters()->SetFilter (Graphic3d_TOTF_BILINEAR); + } + if (mySkydomeTexture->SizeX() != mySkydomeAspect.Size()) + { + mySkydomeTexture->Release (theCtx.get()); + mySkydomeTexture->InitCubeMap (theCtx, NULL, mySkydomeAspect.Size(), + Image_Format_RGB, false, false); + } + + // init aspects if needed + if (myCubeMapParams->TextureSet (theCtx).IsNull()) + { + myCubeMapParams->Aspect()->SetInteriorStyle (Aspect_IS_SOLID); + myCubeMapParams->Aspect()->SetFaceCulling (Graphic3d_TypeOfBackfacingModel_DoubleSided); + myCubeMapParams->Aspect()->SetShadingModel (Graphic3d_TypeOfShadingModel_Unlit); + myCubeMapParams->Aspect()->SetShaderProgram (theCtx->ShaderManager()->GetBgCubeMapProgram()); + Handle(Graphic3d_TextureSet) aTextureSet = new Graphic3d_TextureSet (1); + myCubeMapParams->Aspect()->SetTextureSet (aTextureSet); + myCubeMapParams->Aspect()->SetTextureMapOn (true); + myCubeMapParams->SynchronizeAspects(); + } + + myCubeMapParams->Aspect()->ShaderProgram()->PushVariableInt ("uZCoeff", 1); + myCubeMapParams->Aspect()->ShaderProgram()->PushVariableInt ("uYCoeff", 1); + + for (Standard_Integer aSideIter = 0; aSideIter < 6; aSideIter++) + { + aProg->SetUniform (theCtx, "uSide", aSideIter); + theCtx->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + aSideIter, + mySkydomeTexture->TextureId(), 0); + theCtx->core15->glDrawArrays (GL_TRIANGLES, 0, 3); + } + theCtx->arbFBO->glDeleteFramebuffers (1, &anFBO); + aVBO->Release (theCtx.get()); + + myCubeMapParams->TextureSet (theCtx)->ChangeFirst() = mySkydomeTexture; + theCtx->BindProgram (aPrevProgram); + theCtx->ResizeViewport (anOldViewport); + theCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, aPrevFBO); +} + // ======================================================================= // function : checkPBRAvailability // purpose : @@ -3096,6 +3191,12 @@ Standard_Boolean OpenGl_View::checkPBRAvailability() const // ======================================================================= void OpenGl_View::updatePBREnvironment (const Handle(OpenGl_Context)& theCtx) { + if (myBackgroundType == Graphic3d_TOB_CUBEMAP + && myToUpdateSkydome) + { + updateSkydomeBg (theCtx); + } + if (myPBREnvState != OpenGl_PBREnvState_CREATED || !myPBREnvRequest) { diff --git a/src/OpenGl/OpenGl_View.hxx b/src/OpenGl/OpenGl_View.hxx index 78367fa9aa..4f5781aa64 100644 --- a/src/OpenGl/OpenGl_View.hxx +++ b/src/OpenGl/OpenGl_View.hxx @@ -379,7 +379,7 @@ protected: //! @name Rendering of GL graphics (with prepared drawing buffer). OpenGl_FrameBuffer* theOitAccumFbo, const Standard_Boolean theToDrawImmediate); - //! Draw background (gradient / image) + //! Draw background (gradient / image / cubemap) Standard_EXPORT virtual void drawBackground (const Handle(OpenGl_Workspace)& theWorkspace, Graphic3d_Camera::Projection theProjection); @@ -507,6 +507,12 @@ protected: //! @name Background parameters OpenGl_Aspects* myColoredQuadParams; //!< Stores parameters for gradient (corner mode) background OpenGl_BackgroundArray* myBackgrounds[Graphic3d_TypeOfBackground_NB]; //!< Array of primitive arrays of different background types Handle(OpenGl_TextureSet) myTextureEnv; + Handle(OpenGl_Texture) mySkydomeTexture; + +protected: //! @name methods related to skydome background + + //! Generates skydome cubemap. + Standard_EXPORT void updateSkydomeBg (const Handle(OpenGl_Context)& theCtx); protected: //! @name methods related to PBR diff --git a/src/Shaders/FILES b/src/Shaders/FILES index e4efc735ae..2a1d29017d 100644 --- a/src/Shaders/FILES +++ b/src/Shaders/FILES @@ -24,6 +24,7 @@ srcinc:::PathtraceBase.fs srcinc:::RaytraceBase.vs srcinc:::RaytraceSmooth.fs srcinc:::TangentSpaceNormal.glsl +srcinc:::SkydomBackground.fs Shaders_Declarations_glsl.pxx Shaders_DeclarationsImpl_glsl.pxx Shaders_DirectionalLightShadow_glsl.pxx @@ -48,3 +49,4 @@ Shaders_PathtraceBase_fs.pxx Shaders_RaytraceBase_vs.pxx Shaders_RaytraceSmooth_fs.pxx Shaders_TangentSpaceNormal_glsl.pxx +Shaders_SkydomBackground_fs.pxx diff --git a/src/Shaders/Shaders_SkydomBackground_fs.pxx b/src/Shaders/Shaders_SkydomBackground_fs.pxx new file mode 100644 index 0000000000..008d1885b0 --- /dev/null +++ b/src/Shaders/Shaders_SkydomBackground_fs.pxx @@ -0,0 +1,303 @@ +// This file has been automatically generated from resource file src/Shaders/SkydomBackground.fs + +static const char Shaders_SkydomBackground_fs[] = + "// Constants\n" + "const float M_PI = 3.1415926535;\n" + "const float THE_EARTH_RADIUS = 6360e3;\n" + "const vec3 THE_EARTH_CENTER = vec3 (0.0, -THE_EARTH_RADIUS, 0.0);\n" + "const float THE_ATMO_RADIUS = 6380e3; // atmosphere radius (6420e3?)\n" + "const float THE_G = 0.76; // anisotropy of the medium (papers use 0.76)\n" + "const float THE_G2 = THE_G * THE_G;\n" + "const float THE_HR = 8000.0; // Thickness of the atmosphere\n" + "const float THE_HM = 1000.0; // Same as above but for Mie\n" + "const vec3 THE_BETA_R = vec3 (5.8e-6, 13.5e-6, 33.1e-6); // Reyleigh scattering normal earth\n" + "const vec3 THE_BETA_M = vec3 (21e-6); // Normal Mie scattering\n" + "\n" + "// Parameters\n" + "const float THE_SunAttenuation = 1.0; // sun intensity\n" + "const float THE_EyeHeight = 100.0; // viewer height\n" + "const float THE_HorizonWidth = 0.002;\n" + "const int THE_NbSamples = 8;\n" + "const int THE_NbSamplesLight = 8; // integral sampling rate (might highly hit performance)\n" + "const float THE_SunPower = 5.0;\n" + "const float THE_StarTreshold = 0.98;\n" + "\n" + "// Uniforms\n" + "uniform vec3 uSunDir;\n" + "uniform float uTime;\n" + "uniform float uCloudy;\n" + "uniform float uFog;\n" + "\n" + "float hash13 (in vec3 p3)\n" + "{\n" + " p3 = fract (p3 * 0.1031);\n" + " p3 += dot (p3, p3.zyx + 31.32);\n" + " return fract ((p3.x + p3.y) * p3.z);\n" + "}\n" + "\n" + "float hash12 (in vec2 p)\n" + "{\n" + " vec3 p3 = fract (vec3(p.xyx) * .1031);\n" + " p3 += dot (p3, p3.yzx + 33.33);\n" + " return fract ((p3.x + p3.y) * p3.z);\n" + "}\n" + "\n" + "float smoothStarField (in vec2 theSamplePos)\n" + "{\n" + " vec2 aFract = fract (theSamplePos);\n" + " vec2 aFloorSample = floor (theSamplePos);\n" + " float v1 = hash12 (aFloorSample);\n" + " float v2 = hash12 (aFloorSample + vec2( 0.0, 1.0 ));\n" + " float v3 = hash12 (aFloorSample + vec2( 1.0, 0.0 ));\n" + " float v4 = hash12 (aFloorSample + vec2( 1.0, 1.0 ));\n" + "\n" + " vec2 u = aFract * aFract * (3.0 - 2.0 * aFract);\n" + "\n" + " return mix(v1, v2, u.x) +\n" + " (v3 - v1) * u.y * (1.0 - u.x) +\n" + " (v4 - v2) * u.x * u.y;\n" + "}\n" + "\n" + "float noisyStarField (in vec2 theSamplePos)\n" + "{\n" + " float aStarVal = smoothStarField (theSamplePos);\n" + " if (aStarVal >= THE_StarTreshold)\n" + " {\n" + " aStarVal = pow ((aStarVal - THE_StarTreshold) / (1.0 - THE_StarTreshold), 6.0);\n" + " }\n" + " else\n" + " {\n" + " aStarVal = 0.0;\n" + " }\n" + " return aStarVal;\n" + "}\n" + "\n" + "float smoothNoise (in vec3 theCoord)\n" + "{\n" + " vec3 anInt = floor (theCoord);\n" + " vec3 anFract = fract (theCoord);\n" + " anFract = anFract * anFract * (3.0 - (2.0 * anFract));\n" + " return mix(mix(mix(hash13(anInt ),\n" + " hash13(anInt + vec3(1.0, 0.0, 0.0)), anFract.x),\n" + " mix(hash13(anInt + vec3(0.0, 1.0, 0.0)),\n" + " hash13(anInt + vec3(1.0, 1.0, 0.0)), anFract.x), anFract.y),\n" + " mix(mix(hash13(anInt + vec3(0.0, 0.0, 1.0)),\n" + " hash13(anInt + vec3(1.0, 0.0, 1.0)), anFract.x),\n" + " mix(hash13(anInt + vec3(0.0, 1.0, 1.0)),\n" + " hash13(anInt + vec3(1.0, 1.0, 1.0)), anFract.x), anFract.y), anFract.z);\n" + "}\n" + "\n" + "float fnoise (in vec3 theCoord, in float theTime)\n" + "{\n" + " theCoord *= .25;\n" + " float aNoise;\n" + "\n" + " aNoise = 0.5000 * smoothNoise (theCoord);\n" + " theCoord = theCoord * 3.02; theCoord.y -= theTime * 0.2;\n" + " aNoise += 0.2500 * smoothNoise (theCoord);\n" + " theCoord = theCoord * 3.03; theCoord.y += theTime * 0.06;\n" + " aNoise += 0.1250 * smoothNoise (theCoord);\n" + " theCoord = theCoord * 3.01;\n" + " aNoise += 0.0625 * smoothNoise (theCoord);\n" + " theCoord = theCoord * 3.03;\n" + " aNoise += 0.03125 * smoothNoise (theCoord);\n" + " theCoord = theCoord * 3.02;\n" + " aNoise += 0.015625 * smoothNoise (theCoord);\n" + " return aNoise;\n" + "}\n" + "\n" + "float clouds (in vec3 theTs, in float theTime)\n" + "{\n" + " float aCloud = fnoise (theTs * 2e-4, theTime) + uCloudy * 0.1;\n" + " aCloud = smoothstep (0.44, 0.64, aCloud);\n" + " aCloud *= 70.0;\n" + " return aCloud + uFog;\n" + "}\n" + "\n" + "void densities (in vec3 thePos, out float theRayleigh, out float theMie, in float theTime)\n" + "{\n" + " float aHeight = length (thePos - THE_EARTH_CENTER) - THE_EARTH_RADIUS;\n" + " theRayleigh = exp (-aHeight / THE_HR);\n" + "\n" + " float aCloud = 0.0;\n" + " if (aHeight > 5000.0 && aHeight < 8000.0)\n" + " {\n" + " aCloud = clouds (thePos + vec3 (0.0, 0.,-theTime*3e3), theTime);\n" + " aCloud *= sin (M_PI*(aHeight - 5e3) / 5e3) * uCloudy;\n" + " }\n" + "\n" + " float aCloud2 = 0.0;\n" + " if (aHeight > 12e3 && aHeight < 15.5e3)\n" + " {\n" + " aCloud2 = fnoise (thePos * 3e-4, theTime) * clouds (thePos * 32.0, theTime);\n" + " aCloud2 *= sin (M_PI * (aHeight - 12e3) / 12e3) * 0.05;\n" + " aCloud2 = clamp (aCloud2, 0.0, 1.0);\n" + " }\n" + "\n" + " theMie = exp (-aHeight / THE_HM) + aCloud + uFog;\n" + " theMie += aCloud2;\n" + "}\n" + "\n" + "// ray with sphere intersection problem is reduced to solving the equation\n" + "// (P - C)^2 = r^2 <--- sphere equation\n" + "// where P is P(t) = A + t*B <--- point on ray\n" + "// t^2*dot(B, B) + t*2*dot(B, A-C) + dot(A-C, A-C) - r^2 = 0\n" + "// [ A ] [ B ] [ C ]\n" + "// We just need to solve the above quadratic equation\n" + "float raySphereIntersect (in vec3 theOrig, in vec3 theDir, in float theRadius)\n" + "{\n" + " theOrig = theOrig - THE_EARTH_CENTER;\n" + " // A coefficient will be always 1 (theDir is normalized)\n" + " float B = dot (theOrig, theDir);\n" + " float C = dot (theOrig, theOrig) - theRadius * theRadius;\n" + " // optimized version of classic (-b +- sqrt(b^2 - 4ac)) / 2a\n" + " float aDet2 = B * B - C;\n" + " if (aDet2 < 0.0) { return -1.0; }\n" + " float aDet = sqrt (aDet2);\n" + " float aT1 = -B - aDet;\n" + " float aT2 = -B + aDet;\n" + " return aT1 >= 0.0 ? aT1 : aT2;\n" + "}\n" + "\n" + "void scatter (in vec3 theEye, in vec3 theRay, in vec3 theSun,\n" + " out vec3 theCol, out float theScat, in float theTime)\n" + "{\n" + " float aRayLen = raySphereIntersect (theEye, theRay, THE_ATMO_RADIUS);\n" + " float aMu = dot (theRay, theSun);\n" + " float aMu2 = 1.0 + aMu*aMu;\n" + " // The Raleigh phase function looks like this:\n" + " float aPhaseR = 3.0/(16.0 * M_PI) * aMu2;\n" + " // And the Mie phase function equation is:\n" + " float aPhaseM = (3.0 / (8.0 * M_PI) * (1.0 - THE_G2) * aMu2)\n" + " / ((2.0 + THE_G2) * pow (1.0 + THE_G2 - 2.0 * THE_G * aMu, 1.5));\n" + "\n" + " float anOpticalDepthR = 0.0;\n" + " float anOpticalDepthM = 0.0;\n" + " vec3 aSumR = vec3 (0.0);\n" + " vec3 aSumM = vec3 (0.0); // Mie and Rayleigh contribution\n" + "\n" + " float dl = aRayLen / float (THE_NbSamples);\n" + " for (int i = 0; i < THE_NbSamples; ++i)\n" + " {\n" + " float l = float(i) * dl;\n" + " vec3 aSamplePos = theEye + theRay * l;\n" + "\n" + " float dR, dM;\n" + " densities (aSamplePos, dR, dM, theTime);\n" + " dR *= dl;\n" + " dM *= dl;\n" + " anOpticalDepthR += dR;\n" + " anOpticalDepthM += dM;\n" + "\n" + " float aSegmentLengthLight = raySphereIntersect (aSamplePos, theSun, THE_ATMO_RADIUS);\n" + " if (aSegmentLengthLight > 0.0)\n" + " {\n" + " float dls = aSegmentLengthLight / float (THE_NbSamplesLight);\n" + " float anOpticalDepthRs = 0.0;\n" + " float anOpticalDepthMs = 0.0;\n" + " for (int j = 0; j < THE_NbSamplesLight; ++j)\n" + " {\n" + " float ls = float (j) * dls;\n" + " vec3 aSamplePosS = aSamplePos + theSun * ls;\n" + " float dRs, dMs;\n" + " densities (aSamplePosS, dRs, dMs, theTime);\n" + " anOpticalDepthRs += dRs * dls;\n" + " anOpticalDepthMs += dMs * dls;\n" + " }\n" + "\n" + " vec3 anAttenuation = exp (-(THE_BETA_R * (anOpticalDepthR + anOpticalDepthRs)\n" + " + THE_BETA_M * (anOpticalDepthM + anOpticalDepthMs)));\n" + " aSumR += anAttenuation * dR;\n" + " aSumM += anAttenuation * dM;\n" + " }\n" + " }\n" + "\n" + " theCol = THE_SunPower * (aSumR * THE_BETA_R * aPhaseR + aSumM * THE_BETA_M * aPhaseM);\n" + " theScat = 1.0 - clamp (anOpticalDepthM*1e-5, 0.0, 1.0);\n" + "}\n" + "\n" + "// This is where all the magic happens. We first raymarch along the primary ray\n" + "// (from the camera origin to the point where the ray exits the atmosphere).\n" + "// For each sample along the primary ray,\n" + "// we then \"cast\" a light ray and raymarch along that ray as well.\n" + "// We basically shoot a ray in the direction of the sun.\n" + "vec4 computeIncidentLight (in vec3 theRayDirection, in vec2 theUv, in float theTime)\n" + "{\n" + " float aSunAttenuation = THE_SunAttenuation;\n" + " vec3 aSunDir = uSunDir;\n" + " // conversion to moon\n" + " float aStarAttenuation = 0.0;\n" + " if (aSunDir.y < 0.0)\n" + " {\n" + " aSunDir *= -1.0;\n" + " aSunAttenuation = aSunAttenuation * 0.1;\n" + " aStarAttenuation = sqrt (aSunDir.y);\n" + " }\n" + "\n" + " vec3 anEyePosition = vec3(0.0, THE_EyeHeight, 0.0);\n" + "\n" + " // draw a water surface horizontally symmetrically to the sky\n" + " if (theRayDirection.y <= -THE_HorizonWidth / 2.0)\n" + " {\n" + " theRayDirection.y = -THE_HorizonWidth - theRayDirection.y;\n" + " }\n" + "\n" + " float aScattering = 0.0;\n" + " vec3 aColor = vec3 (0.0);\n" + "\n" + " scatter (anEyePosition, theRayDirection, aSunDir, aColor, aScattering, theTime);\n" + " aColor *= aSunAttenuation;\n" + " float aStarIntensity = noisyStarField (theUv * 2048.0);\n" + " vec3 aStarColor = vec3 (aScattering * aStarIntensity * aStarAttenuation);\n" + " aColor += aStarColor;\n" + "\n" + " return vec4 (1.18 * pow (aColor, vec3(0.7)), 1.0);\n" + "}\n" + "\n" + "uniform int uSide;\n" + "\n" + "void main()\n" + "{\n" + " vec2 anUv = vec2 (2.0 * TexCoord.x - 1.0,\n" + " 2.0 * TexCoord.y - 1.0);\n" + " vec3 aPlanes[6];\n" + " aPlanes[0] = vec3 (+1.0, 0.0, 0.0);\n" + " aPlanes[1] = vec3 (-1.0, 0.0, 0.0);\n" + " aPlanes[2] = vec3 ( 0.0,+1.0, 0.0);\n" + " aPlanes[3] = vec3 ( 0.0,-1.0, 0.0);\n" + " aPlanes[4] = vec3 ( 0.0, 0.0,+1.0);\n" + " aPlanes[5] = vec3 ( 0.0, 0.0,-1.0);\n" + " vec3 aRayDirection;\n" + " if (uSide == 0)\n" + " {\n" + " // Positive X side\n" + " aRayDirection = aPlanes[0] + vec3 (0.0, +anUv.y, -anUv.x);\n" + " }\n" + " else if (uSide == 1)\n" + " {\n" + " // Negative X side\n" + " aRayDirection = aPlanes[1] + vec3 (0.0, +anUv.y, +anUv.x);\n" + " }\n" + " else if (uSide == 2)\n" + " {\n" + " // Positive Y side\n" + " aRayDirection = aPlanes[2] + vec3 (+anUv.x, 0.0, +anUv.y);\n" + " }\n" + " else if (uSide == 3)\n" + " {\n" + " // Negative Y side\n" + " aRayDirection = aPlanes[3] + vec3 (+anUv.x, 0.0, -anUv.y);\n" + " }\n" + " else if (uSide == 4)\n" + " {\n" + " // Positive Z side\n" + " aRayDirection = aPlanes[4] + vec3 (+anUv.x, +anUv.y, 0.0);\n" + " }\n" + " else if (uSide == 5)\n" + " {\n" + " // Negative Z side\n" + " aRayDirection = aPlanes[5] + vec3 (-anUv.x, +anUv.y, 0.0);\n" + " }\n" + "\n" + " occFragColor = computeIncidentLight (normalize (aRayDirection), anUv, uTime);\n" + "}\n"; diff --git a/src/Shaders/SkydomBackground.fs b/src/Shaders/SkydomBackground.fs new file mode 100644 index 0000000000..3afc3d0f08 --- /dev/null +++ b/src/Shaders/SkydomBackground.fs @@ -0,0 +1,300 @@ +// Constants +const float M_PI = 3.1415926535; +const float THE_EARTH_RADIUS = 6360e3; +const vec3 THE_EARTH_CENTER = vec3 (0.0, -THE_EARTH_RADIUS, 0.0); +const float THE_ATMO_RADIUS = 6380e3; // atmosphere radius (6420e3?) +const float THE_G = 0.76; // anisotropy of the medium (papers use 0.76) +const float THE_G2 = THE_G * THE_G; +const float THE_HR = 8000.0; // Thickness of the atmosphere +const float THE_HM = 1000.0; // Same as above but for Mie +const vec3 THE_BETA_R = vec3 (5.8e-6, 13.5e-6, 33.1e-6); // Reyleigh scattering normal earth +const vec3 THE_BETA_M = vec3 (21e-6); // Normal Mie scattering + +// Parameters +const float THE_SunAttenuation = 1.0; // sun intensity +const float THE_EyeHeight = 100.0; // viewer height +const float THE_HorizonWidth = 0.002; +const int THE_NbSamples = 8; +const int THE_NbSamplesLight = 8; // integral sampling rate (might highly hit performance) +const float THE_SunPower = 5.0; +const float THE_StarTreshold = 0.98; + +// Uniforms +uniform vec3 uSunDir; +uniform float uTime; +uniform float uCloudy; +uniform float uFog; + +float hash13 (in vec3 p3) +{ + p3 = fract (p3 * 0.1031); + p3 += dot (p3, p3.zyx + 31.32); + return fract ((p3.x + p3.y) * p3.z); +} + +float hash12 (in vec2 p) +{ + vec3 p3 = fract (vec3(p.xyx) * .1031); + p3 += dot (p3, p3.yzx + 33.33); + return fract ((p3.x + p3.y) * p3.z); +} + +float smoothStarField (in vec2 theSamplePos) +{ + vec2 aFract = fract (theSamplePos); + vec2 aFloorSample = floor (theSamplePos); + float v1 = hash12 (aFloorSample); + float v2 = hash12 (aFloorSample + vec2( 0.0, 1.0 )); + float v3 = hash12 (aFloorSample + vec2( 1.0, 0.0 )); + float v4 = hash12 (aFloorSample + vec2( 1.0, 1.0 )); + + vec2 u = aFract * aFract * (3.0 - 2.0 * aFract); + + return mix(v1, v2, u.x) + + (v3 - v1) * u.y * (1.0 - u.x) + + (v4 - v2) * u.x * u.y; +} + +float noisyStarField (in vec2 theSamplePos) +{ + float aStarVal = smoothStarField (theSamplePos); + if (aStarVal >= THE_StarTreshold) + { + aStarVal = pow ((aStarVal - THE_StarTreshold) / (1.0 - THE_StarTreshold), 6.0); + } + else + { + aStarVal = 0.0; + } + return aStarVal; +} + +float smoothNoise (in vec3 theCoord) +{ + vec3 anInt = floor (theCoord); + vec3 anFract = fract (theCoord); + anFract = anFract * anFract * (3.0 - (2.0 * anFract)); + return mix(mix(mix(hash13(anInt ), + hash13(anInt + vec3(1.0, 0.0, 0.0)), anFract.x), + mix(hash13(anInt + vec3(0.0, 1.0, 0.0)), + hash13(anInt + vec3(1.0, 1.0, 0.0)), anFract.x), anFract.y), + mix(mix(hash13(anInt + vec3(0.0, 0.0, 1.0)), + hash13(anInt + vec3(1.0, 0.0, 1.0)), anFract.x), + mix(hash13(anInt + vec3(0.0, 1.0, 1.0)), + hash13(anInt + vec3(1.0, 1.0, 1.0)), anFract.x), anFract.y), anFract.z); +} + +float fnoise (in vec3 theCoord, in float theTime) +{ + theCoord *= .25; + float aNoise; + + aNoise = 0.5000 * smoothNoise (theCoord); + theCoord = theCoord * 3.02; theCoord.y -= theTime * 0.2; + aNoise += 0.2500 * smoothNoise (theCoord); + theCoord = theCoord * 3.03; theCoord.y += theTime * 0.06; + aNoise += 0.1250 * smoothNoise (theCoord); + theCoord = theCoord * 3.01; + aNoise += 0.0625 * smoothNoise (theCoord); + theCoord = theCoord * 3.03; + aNoise += 0.03125 * smoothNoise (theCoord); + theCoord = theCoord * 3.02; + aNoise += 0.015625 * smoothNoise (theCoord); + return aNoise; +} + +float clouds (in vec3 theTs, in float theTime) +{ + float aCloud = fnoise (theTs * 2e-4, theTime) + uCloudy * 0.1; + aCloud = smoothstep (0.44, 0.64, aCloud); + aCloud *= 70.0; + return aCloud + uFog; +} + +void densities (in vec3 thePos, out float theRayleigh, out float theMie, in float theTime) +{ + float aHeight = length (thePos - THE_EARTH_CENTER) - THE_EARTH_RADIUS; + theRayleigh = exp (-aHeight / THE_HR); + + float aCloud = 0.0; + if (aHeight > 5000.0 && aHeight < 8000.0) + { + aCloud = clouds (thePos + vec3 (0.0, 0.,-theTime*3e3), theTime); + aCloud *= sin (M_PI*(aHeight - 5e3) / 5e3) * uCloudy; + } + + float aCloud2 = 0.0; + if (aHeight > 12e3 && aHeight < 15.5e3) + { + aCloud2 = fnoise (thePos * 3e-4, theTime) * clouds (thePos * 32.0, theTime); + aCloud2 *= sin (M_PI * (aHeight - 12e3) / 12e3) * 0.05; + aCloud2 = clamp (aCloud2, 0.0, 1.0); + } + + theMie = exp (-aHeight / THE_HM) + aCloud + uFog; + theMie += aCloud2; +} + +// ray with sphere intersection problem is reduced to solving the equation +// (P - C)^2 = r^2 <--- sphere equation +// where P is P(t) = A + t*B <--- point on ray +// t^2*dot(B, B) + t*2*dot(B, A-C) + dot(A-C, A-C) - r^2 = 0 +// [ A ] [ B ] [ C ] +// We just need to solve the above quadratic equation +float raySphereIntersect (in vec3 theOrig, in vec3 theDir, in float theRadius) +{ + theOrig = theOrig - THE_EARTH_CENTER; + // A coefficient will be always 1 (theDir is normalized) + float B = dot (theOrig, theDir); + float C = dot (theOrig, theOrig) - theRadius * theRadius; + // optimized version of classic (-b +- sqrt(b^2 - 4ac)) / 2a + float aDet2 = B * B - C; + if (aDet2 < 0.0) { return -1.0; } + float aDet = sqrt (aDet2); + float aT1 = -B - aDet; + float aT2 = -B + aDet; + return aT1 >= 0.0 ? aT1 : aT2; +} + +void scatter (in vec3 theEye, in vec3 theRay, in vec3 theSun, + out vec3 theCol, out float theScat, in float theTime) +{ + float aRayLen = raySphereIntersect (theEye, theRay, THE_ATMO_RADIUS); + float aMu = dot (theRay, theSun); + float aMu2 = 1.0 + aMu*aMu; + // The Raleigh phase function looks like this: + float aPhaseR = 3.0/(16.0 * M_PI) * aMu2; + // And the Mie phase function equation is: + float aPhaseM = (3.0 / (8.0 * M_PI) * (1.0 - THE_G2) * aMu2) + / ((2.0 + THE_G2) * pow (1.0 + THE_G2 - 2.0 * THE_G * aMu, 1.5)); + + float anOpticalDepthR = 0.0; + float anOpticalDepthM = 0.0; + vec3 aSumR = vec3 (0.0); + vec3 aSumM = vec3 (0.0); // Mie and Rayleigh contribution + + float dl = aRayLen / float (THE_NbSamples); + for (int i = 0; i < THE_NbSamples; ++i) + { + float l = float(i) * dl; + vec3 aSamplePos = theEye + theRay * l; + + float dR, dM; + densities (aSamplePos, dR, dM, theTime); + dR *= dl; + dM *= dl; + anOpticalDepthR += dR; + anOpticalDepthM += dM; + + float aSegmentLengthLight = raySphereIntersect (aSamplePos, theSun, THE_ATMO_RADIUS); + if (aSegmentLengthLight > 0.0) + { + float dls = aSegmentLengthLight / float (THE_NbSamplesLight); + float anOpticalDepthRs = 0.0; + float anOpticalDepthMs = 0.0; + for (int j = 0; j < THE_NbSamplesLight; ++j) + { + float ls = float (j) * dls; + vec3 aSamplePosS = aSamplePos + theSun * ls; + float dRs, dMs; + densities (aSamplePosS, dRs, dMs, theTime); + anOpticalDepthRs += dRs * dls; + anOpticalDepthMs += dMs * dls; + } + + vec3 anAttenuation = exp (-(THE_BETA_R * (anOpticalDepthR + anOpticalDepthRs) + + THE_BETA_M * (anOpticalDepthM + anOpticalDepthMs))); + aSumR += anAttenuation * dR; + aSumM += anAttenuation * dM; + } + } + + theCol = THE_SunPower * (aSumR * THE_BETA_R * aPhaseR + aSumM * THE_BETA_M * aPhaseM); + theScat = 1.0 - clamp (anOpticalDepthM*1e-5, 0.0, 1.0); +} + +// This is where all the magic happens. We first raymarch along the primary ray +// (from the camera origin to the point where the ray exits the atmosphere). +// For each sample along the primary ray, +// we then "cast" a light ray and raymarch along that ray as well. +// We basically shoot a ray in the direction of the sun. +vec4 computeIncidentLight (in vec3 theRayDirection, in vec2 theUv, in float theTime) +{ + float aSunAttenuation = THE_SunAttenuation; + vec3 aSunDir = uSunDir; + // conversion to moon + float aStarAttenuation = 0.0; + if (aSunDir.y < 0.0) + { + aSunDir *= -1.0; + aSunAttenuation = aSunAttenuation * 0.1; + aStarAttenuation = sqrt (aSunDir.y); + } + + vec3 anEyePosition = vec3(0.0, THE_EyeHeight, 0.0); + + // draw a water surface horizontally symmetrically to the sky + if (theRayDirection.y <= -THE_HorizonWidth / 2.0) + { + theRayDirection.y = -THE_HorizonWidth - theRayDirection.y; + } + + float aScattering = 0.0; + vec3 aColor = vec3 (0.0); + + scatter (anEyePosition, theRayDirection, aSunDir, aColor, aScattering, theTime); + aColor *= aSunAttenuation; + float aStarIntensity = noisyStarField (theUv * 2048.0); + vec3 aStarColor = vec3 (aScattering * aStarIntensity * aStarAttenuation); + aColor += aStarColor; + + return vec4 (1.18 * pow (aColor, vec3(0.7)), 1.0); +} + +uniform int uSide; + +void main() +{ + vec2 anUv = vec2 (2.0 * TexCoord.x - 1.0, + 2.0 * TexCoord.y - 1.0); + vec3 aPlanes[6]; + aPlanes[0] = vec3 (+1.0, 0.0, 0.0); + aPlanes[1] = vec3 (-1.0, 0.0, 0.0); + aPlanes[2] = vec3 ( 0.0,+1.0, 0.0); + aPlanes[3] = vec3 ( 0.0,-1.0, 0.0); + aPlanes[4] = vec3 ( 0.0, 0.0,+1.0); + aPlanes[5] = vec3 ( 0.0, 0.0,-1.0); + vec3 aRayDirection; + if (uSide == 0) + { + // Positive X side + aRayDirection = aPlanes[0] + vec3 (0.0, +anUv.y, -anUv.x); + } + else if (uSide == 1) + { + // Negative X side + aRayDirection = aPlanes[1] + vec3 (0.0, +anUv.y, +anUv.x); + } + else if (uSide == 2) + { + // Positive Y side + aRayDirection = aPlanes[2] + vec3 (+anUv.x, 0.0, +anUv.y); + } + else if (uSide == 3) + { + // Negative Y side + aRayDirection = aPlanes[3] + vec3 (+anUv.x, 0.0, -anUv.y); + } + else if (uSide == 4) + { + // Positive Z side + aRayDirection = aPlanes[4] + vec3 (+anUv.x, +anUv.y, 0.0); + } + else if (uSide == 5) + { + // Negative Z side + aRayDirection = aPlanes[5] + vec3 (-anUv.x, +anUv.y, 0.0); + } + + occFragColor = computeIncidentLight (normalize (aRayDirection), anUv, uTime); +} diff --git a/src/V3d/V3d_View.cxx b/src/V3d/V3d_View.cxx index c5e768b3df..ecd4c1d6b4 100644 --- a/src/V3d/V3d_View.cxx +++ b/src/V3d/V3d_View.cxx @@ -529,6 +529,16 @@ void V3d_View::SetBackgroundCubeMap (const Handle(Graphic3d_CubeMap)& theCubeMap } } +//============================================================================= +//function : SetBackgroundSkydome +//purpose : +//============================================================================= +void V3d_View::SetBackgroundSkydome (const Aspect_SkydomeBackground& theAspect, + Standard_Boolean theToUpdatePBREnv) +{ + myView->SetBackgroundSkydome (theAspect, theToUpdatePBREnv); +} + //============================================================================= //function : IsImageBasedLighting //purpose : diff --git a/src/V3d/V3d_View.hxx b/src/V3d/V3d_View.hxx index 222fb18ef0..aa4159e1a2 100644 --- a/src/V3d/V3d_View.hxx +++ b/src/V3d/V3d_View.hxx @@ -219,6 +219,15 @@ public: Standard_Boolean theToUpdatePBREnv = Standard_True, Standard_Boolean theToUpdate = Standard_False); + //! Returns skydome aspect; + const Aspect_SkydomeBackground& BackgroundSkydome() const { return myView->BackgroundSkydome(); } + + //! Sets skydome aspect + //! @param theAspect cubemap generation parameters + //! @param theToUpdatePBREnv defines whether IBL maps will be generated or not + Standard_EXPORT void SetBackgroundSkydome (const Aspect_SkydomeBackground& theAspect, + Standard_Boolean theToUpdatePBREnv = Standard_True); + //! Returns TRUE if IBL (Image Based Lighting) from background cubemap is enabled. Standard_EXPORT Standard_Boolean IsImageBasedLighting() const; diff --git a/src/ViewerTest/ViewerTest_ViewerCommands.cxx b/src/ViewerTest/ViewerTest_ViewerCommands.cxx index 68a0187699..77520fe13c 100644 --- a/src/ViewerTest/ViewerTest_ViewerCommands.cxx +++ b/src/ViewerTest/ViewerTest_ViewerCommands.cxx @@ -2692,6 +2692,9 @@ static int VBackground (Draw_Interpretor& theDI, Aspect_FillMethod anImageMode = Aspect_FM_CENTERED; bool hasImageMode = false; + bool isSkydomeBg = false; + Aspect_SkydomeBackground aSkydomeAspect; + NCollection_Sequence aCubeMapSeq; Graphic3d_CubeMapOrder aCubeOrder = Graphic3d_CubeMapOrder::Default(); bool isCubeZInverted = false; @@ -2722,6 +2725,48 @@ static int VBackground (Draw_Interpretor& theDI, { anImagePath = theArgVec[++anArgIter]; } + else if (anArg == "-skydome" + || anArg == "-sky") + { + isSkydomeBg = true; + } + else if (anArgIter + 3 < theNbArgs + && isSkydomeBg + && anArg == "-sundir") + { + float aX = (float) Draw::Atof (theArgVec[++anArgIter]); + float aY = (float) Draw::Atof (theArgVec[++anArgIter]); + float aZ = (float) Draw::Atof (theArgVec[++anArgIter]); + aSkydomeAspect.SetSunDirection (gp_Dir(aX, aY, aZ)); + } + else if (anArgIter + 1 < theNbArgs + && isSkydomeBg + && anArg == "-cloud") + { + float aCloudy = (float) Draw::Atof (theArgVec[++anArgIter]); + aSkydomeAspect.SetCloudiness (aCloudy); + } + else if (anArgIter + 1 < theNbArgs + && isSkydomeBg + && anArg == "-time") + { + float aTime = (float) Draw::Atof (theArgVec[++anArgIter]); + aSkydomeAspect.SetTimeParameter (aTime); + } + else if (anArgIter + 1 < theNbArgs + && isSkydomeBg + && anArg == "-fog") + { + float aFoggy = (float) Draw::Atof (theArgVec[++anArgIter]); + aSkydomeAspect.SetFogginess (aFoggy); + } + else if (anArgIter + 1 < theNbArgs + && isSkydomeBg + && anArg == "-size") + { + Standard_Integer aSize = Draw::Atoi (theArgVec[++anArgIter]); + aSkydomeAspect.SetSize (aSize); + } else if (anArgIter + 1 < theNbArgs && aCubeMapSeq.IsEmpty() && (anArg == "-cubemap" @@ -3003,6 +3048,11 @@ static int VBackground (Draw_Interpretor& theDI, aView->SetBgImageStyle (anImageMode); } + if (isSkydomeBg) + { + aView->SetBackgroundSkydome (aSkydomeAspect, toUseIBL != -1); + } + if (!aCubeMapSeq.IsEmpty()) { Handle(Graphic3d_CubeMap) aCubeMap; @@ -13809,6 +13859,8 @@ vbackground [-color Color [-default]] [-gradientMode {NONE|HORIZONTAL|VERTICAL|DIAG1|DIAG2|CORNER1|CORNER2|CORNER3|ELLIPTICAL}]=VERT] [-imageFile ImageFile [-imageMode {CENTERED|TILED|STRETCH|NONE}]=CENTERED [-srgb {0|1}]=1] [-cubemap CubemapFile1 [CubeMapFiles2-5] [-order TilesIndexes1-6] [-invertedz]=0] + [-skydome [-sunDir X Y Z=0 1 0] [-cloud Cloudy=0.2] [-time Time=0.0] + [-fog Haze=0.0] [-size SizePx=512]] [-pbrEnv {ibl|noibl|keep}] Changes background or some background settings. -color sets background color @@ -13824,6 +13876,13 @@ Changes background or some background settings. -order defines order of tiles in one image cubemap TileIndexi defubes an index in range [0, 5] for i tile of one image packed cubemap (has no effect in case of multi-image cubemaps). +Skydome background parameters (generated cubemap): + -skydome sets procedurally generated skydome as background + -sunDir sets direction to the sun, direction with negative y component represents moon direction (-x, -y, -z) + -cloud sets cloud intensity (0.0 - clear sky, 1.0 - very high cloudy) + -time might be tweaked to slightly change appearance of clouds + -fog sets mist intensity (0.0 - no mist at all, 1.0 - high mist) + -size sets size in pixels of cubemap side )" /* [vbackground] */); addCmd ("vsetbg", VBackground, /* [vsetbg] */ R"( diff --git a/tests/opengl/data/background/skydome b/tests/opengl/data/background/skydome new file mode 100644 index 0000000000..281bad22b6 --- /dev/null +++ b/tests/opengl/data/background/skydome @@ -0,0 +1,43 @@ +puts "============" +puts "0032606: Visualization - add a shader for sky" +puts "============" +puts "" + +set THE_DIM 256 + +pload MODELING VISUALIZATION +psphere s 1 + +vinit View1 -width 768 -height 512 +vcamera -persp -fovy 120 + +chrono t restart +vbackground -skydome -size $THE_DIM -cloud 0.3 -sunDir 1.0 0.5 0.0 -time 10 -fog 0.3 +chrono t show +vaxo +vdump $imagedir/${casename}_day.png + +chrono t restart +vbackground -skydome -size $THE_DIM -cloud 0.3 -sunDir 1.0 -0.5 0.0 -time -10 -fog 0.05 +chrono t show +vaxo +vdump $imagedir/${casename}_night.png + +chrono t restart +vbackground -skydome -size $THE_DIM -cloud 0.15 -sunDir 1.0 0.15 0.0 -time 10 +chrono t show +vaxo +vdump $imagedir/${casename}_sunset.png + +chrono t restart +vbackground -skydome -size $THE_DIM +chrono t show +vaxo +vdump $imagedir/${casename}_defaults.png + +vdisplay -dispMode 1 s +vfit +vaspects s -material SILVER +vrenderparams -shadingModel pbr +vlight headlight -enabled 0 +vdump $imagedir/${casename}_pbr.png