]> OCCT Git - occt.git/commitdiff
0032606: Visualization - add a shader for sky V3d_View::BackgroundSkydome() IR-2021-12-30
authorachesnok <achesnok@opencascade.com>
Thu, 14 Oct 2021 22:50:13 +0000 (01:50 +0300)
committersmoskvin <smoskvin@opencascade.com>
Wed, 29 Dec 2021 21:14:47 +0000 (00:14 +0300)
Introduced V3d_View::SkydomeAspect() property for generating skydome cubemap environment.
Skydome features: day/night cycle, 2 types of clouds, atmosphere, water surface, stars, fog.

18 files changed:
src/Aspect/Aspect_SkydomeBackground.cxx [new file with mode: 0644]
src/Aspect/Aspect_SkydomeBackground.hxx [new file with mode: 0644]
src/Aspect/FILES
src/Graphic3d/Graphic3d_CView.cxx
src/Graphic3d/Graphic3d_CView.hxx
src/Graphic3d/Graphic3d_ShaderManager.cxx
src/Graphic3d/Graphic3d_ShaderManager.hxx
src/OpenGl/OpenGl_ShaderManager.cxx
src/OpenGl/OpenGl_ShaderManager.hxx
src/OpenGl/OpenGl_View.cxx
src/OpenGl/OpenGl_View.hxx
src/Shaders/FILES
src/Shaders/Shaders_SkydomBackground_fs.pxx [new file with mode: 0644]
src/Shaders/SkydomBackground.fs [new file with mode: 0644]
src/V3d/V3d_View.cxx
src/V3d/V3d_View.hxx
src/ViewerTest/ViewerTest_ViewerCommands.cxx
tests/opengl/data/background/skydome [new file with mode: 0644]

diff --git a/src/Aspect/Aspect_SkydomeBackground.cxx b/src/Aspect/Aspect_SkydomeBackground.cxx
new file mode 100644 (file)
index 0000000..8fd3cc0
--- /dev/null
@@ -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 <Aspect_SkydomeBackground.hxx>
+
+#include <Standard_RangeError.hxx>
+
+// =======================================================================
+// 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 (file)
index 0000000..52eff71
--- /dev/null
@@ -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 <gp_Dir.hxx>
+#include <Graphic3d_Vec3.hxx>
+#include <Standard.hxx>
+#include <Standard_DefineAlloc.hxx>
+#include <Standard_Handle.hxx>
+
+//! 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
index 492dcb79a492a372a6958f3e28cf58f9303ba87b..b1c7fa3f980ad0fc43ea2169d3f1976f7b9647f7 100755 (executable)
@@ -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
index a97efe8692259193ad7fff85579f2de83e65a821..07077415ffc8630d5dbfcf2f3b0a23b3ad7ef4e7 100644 (file)
@@ -14,6 +14,7 @@
 #include <Graphic3d_CView.hxx>
 
 #include <Aspect_OpenVRSession.hxx>
+#include <Graphic3d_CubeMapPacked.hxx>
 #include <Graphic3d_Layer.hxx>
 #include <Graphic3d_MapIteratorOfMapOfStructure.hxx>
 #include <Graphic3d_StructureManager.hxx>
@@ -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  :
index f7c0121a1e20be115f0ffbddf661034a87a45d69..43e3675fc306522cdae42827cbd3e87a33be31ea 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <Aspect_Handle.hxx>
 #include <Aspect_RenderingContext.hxx>
+#include <Aspect_SkydomeBackground.hxx>
 #include <Aspect_Window.hxx>
 #include <Graphic3d_BufferType.hxx>
 #include <Graphic3d_Camera.hxx>
@@ -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;
index d6dfb378b527552cdce502a744c62b211a72d5c8..b553462225deafd248f1012505e7bafb7b05e012 100644 (file)
@@ -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  :
index 0619ea11e716cdfc2ea049bba45f9c4b2436a471..023b574260beb6282bb7d6af2f46926afbbd4fcc 100644 (file)
@@ -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;
 
index 892cc020a504c8e8d97769869924e3ee451480b8..0023cf89d4baf2ee80f946db6c988cecb672ba1b 100644 (file)
@@ -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  :
index 22cc1b339af65630960ec18fcab2f445a818ad00..9d9501fd8108e9b4cb5a02853a03cbc85c323691 100644 (file)
@@ -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
index f352db0655b7bdf1e0597f8ec32445d942a43154..99f139e030472e46c18acf35fab7ec33c33045a4 100644 (file)
@@ -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)
   {
index 78367fa9aa8cb7a76b1d6ea8929339e71403fcaf..4f5781aa64055d3b4f3268ebebb2dc5a4acc9e1b 100644 (file)
@@ -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
 
index e4efc735ae6105e0b52e488d9ca0cac31cdae9cb..2a1d29017dddc2c7074877bdee5d69645e2eb290 100644 (file)
@@ -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 (file)
index 0000000..008d188
--- /dev/null
@@ -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 (file)
index 0000000..3afc3d0
--- /dev/null
@@ -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);
+}
index c5e768b3dffaee0da58525df37ea40b3187224ea..ecd4c1d6b43b81fa0c0123fb03dec05b0dcabb7c 100644 (file)
@@ -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  :
index 222fb18ef0892296daacb3db53ac1bb157664c3e..aa4159e1a24661f428ed0ff45cd1f05d3e757343 100644 (file)
@@ -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;
 
index 68a01876991a9b06652d8a0670216ea7bfd77c31..77520fe13c991f6a1186207457cf881e0c5c2e66 100644 (file)
@@ -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<TCollection_AsciiString> 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 (file)
index 0000000..281bad2
--- /dev/null
@@ -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