0025304: Visualization, TKOpenGl - support texturing within built-in GLSL programs
[occt.git] / src / OpenGl / OpenGl_ShaderManager.cxx
old mode 100755 (executable)
new mode 100644 (file)
index 55dc4d4..890d85b
 #include <OpenGl_ShaderProgram.hxx>
 #include <OpenGl_Workspace.hxx>
 
+IMPLEMENT_STANDARD_HANDLE (OpenGl_SetOfShaderPrograms, Standard_Transient)
+IMPLEMENT_STANDARD_RTTIEXT(OpenGl_SetOfShaderPrograms, Standard_Transient)
+
 IMPLEMENT_STANDARD_HANDLE (OpenGl_ShaderManager, Standard_Transient)
 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_ShaderManager, Standard_Transient)
 
+namespace
+{
+
+#define EOL "\n"
+
+//! Definition of VertColor varying.
+const char THE_VARY_VertColor[] =
+  EOL"varying vec4 VertColor;";
+
+const char THE_VARY_TexCoord[] =
+  EOL"varying vec2 TexCoord;";
+
+//! Auxiliary function to transform normal
+const char THE_FUNC_transformNormal[] =
+  EOL"vec3 transformNormal (in vec3 theNormal)"
+  EOL"{"
+  EOL"  vec4 aResult = occWorldViewMatrixInverseTranspose"
+  EOL"               * occModelWorldMatrixInverseTranspose"
+  EOL"               * vec4 (theNormal, 0.0);"
+  EOL"  return normalize (aResult.xyz);"
+  EOL"}";
+
+//! Global shader variable for color definition with lighting enabled.
+const char THE_FUNC_lightDef[] =
+  EOL"vec3 Ambient;"   //!< Ambient  contribution of light sources
+  EOL"vec3 Diffuse;"   //!< Diffuse  contribution of light sources
+  EOL"vec3 Specular;"; //!< Specular contribution of light sources
+
+//! Function computes contribution of isotropic point light source
+const char THE_FUNC_pointLight[] =
+  EOL"void pointLight (in int  theId,"
+  EOL"                 in vec3 theNormal,"
+  EOL"                 in vec3 theView,"
+  EOL"                 in vec3 thePoint,"
+  EOL"                 in bool theIsFront)"
+  EOL"{"
+  EOL"  vec3 aLight = occLight_Position (theId).xyz;"
+  EOL"  if (occLight_IsHeadlight (theId) == 0)"
+  EOL"  {"
+  EOL"    aLight = vec3 (occWorldViewMatrix * occModelWorldMatrix * vec4 (aLight, 1.0));"
+  EOL"  }"
+  EOL"  aLight -= thePoint;"
+  EOL
+  EOL"  float aDist = length (aLight);"
+  EOL"  aLight = aLight * (1.0 / aDist);"
+  EOL
+  EOL"  float anAtten = 1.0 / (occLight_ConstAttenuation  (theId)"
+  EOL"                       + occLight_LinearAttenuation (theId) * aDist);"
+  EOL
+  EOL"  vec3 aHalf = normalize (aLight + theView);"
+  EOL
+  EOL"  vec3  aFaceSideNormal = theIsFront ? theNormal : -theNormal;"
+  EOL"  float aNdotL = max (0.0, dot (aFaceSideNormal, aLight));"
+  EOL"  float aNdotH = max (0.0, dot (aFaceSideNormal, aHalf ));"
+  EOL
+  EOL"  float aSpecl = 0.0;"
+  EOL"  if (aNdotL > 0.0)"
+  EOL"  {"
+  EOL"    aSpecl = pow (aNdotH, theIsFront ? occFrontMaterial_Shininess() : occBackMaterial_Shininess());"
+  EOL"  }"
+  EOL
+  EOL"Diffuse  += occLight_Diffuse  (theId).rgb * aNdotL * anAtten;"
+  EOL"Specular += occLight_Specular (theId).rgb * aSpecl * anAtten;"
+  EOL"}";
+
+//! Function computes contribution of spotlight source
+const char THE_FUNC_spotLight[] =
+  EOL"void spotLight (in int  theId,"
+  EOL"                in vec3 theNormal,"
+  EOL"                in vec3 theView,"
+  EOL"                in vec3 thePoint,"
+  EOL"                in bool theIsFront)"
+  EOL"{"
+  EOL"  vec3 aLight   = occLight_Position      (theId).xyz;"
+  EOL"  vec3 aSpotDir = occLight_SpotDirection (theId).xyz;"
+  EOL"  if (occLight_IsHeadlight (theId) == 0)"
+  EOL"  {"
+  EOL"    aLight   = vec3 (occWorldViewMatrix * occModelWorldMatrix * vec4 (aLight,   1.0));"
+  EOL"    aSpotDir = vec3 (occWorldViewMatrix * occModelWorldMatrix * vec4 (aSpotDir, 0.0));"
+  EOL"  }"
+  EOL"  aLight -= thePoint;"
+  EOL
+  EOL"  float aDist = length (aLight);"
+  EOL"  aLight = aLight * (1.0 / aDist);"
+  EOL
+  EOL"  aSpotDir = normalize (aSpotDir);"
+  // light cone
+  EOL"  float aCosA = dot (aSpotDir, -aLight);"
+  EOL"  if (aCosA >= 1.0 || aCosA < cos (occLight_SpotCutOff (theId)))"
+  EOL"  {"
+  EOL"    return;"
+  EOL"  }"
+  EOL
+  EOL"  float anExponent = occLight_SpotExponent (theId);"
+  EOL"  float anAtten    = 1.0 / (occLight_ConstAttenuation  (theId)"
+  EOL"                          + occLight_LinearAttenuation (theId) * aDist);"
+  EOL"  if (anExponent > 0.0)"
+  EOL"  {"
+  EOL"    anAtten *= pow (aCosA, anExponent * 128.0);"
+  EOL"  }"
+  EOL
+  EOL"  vec3 aHalf = normalize (aLight + theView);"
+  EOL
+  EOL"  vec3  aFaceSideNormal = theIsFront ? theNormal : -theNormal;"
+  EOL"  float aNdotL = max (0.0, dot (aFaceSideNormal, aLight));"
+  EOL"  float aNdotH = max (0.0, dot (aFaceSideNormal, aHalf ));"
+  EOL
+  EOL"  float aSpecl = 0.0;"
+  EOL"  if (aNdotL > 0.0)"
+  EOL"  {"
+  EOL"    aSpecl = pow (aNdotH, theIsFront ? occFrontMaterial_Shininess() : occBackMaterial_Shininess());"
+  EOL"  }"
+  EOL
+  EOL"  Diffuse  += occLight_Diffuse  (theId).rgb * aNdotL * anAtten;"
+  EOL"  Specular += occLight_Specular (theId).rgb * aSpecl * anAtten;"
+  EOL"}";
+
+//! Function computes contribution of directional light source
+const char THE_FUNC_directionalLight[] =
+  EOL"void directionalLight (in int  theId,"
+  EOL"                       in vec3 theNormal,"
+  EOL"                       in vec3 theView,"
+  EOL"                       in bool theIsFront)"
+  EOL"{"
+  EOL"  vec3 aLight = normalize (occLight_Position (theId).xyz);"
+  EOL"  if (occLight_IsHeadlight (theId) == 0)"
+  EOL"  {"
+  EOL"    aLight = vec3 (occWorldViewMatrix * occModelWorldMatrix * vec4 (aLight, 0.0));"
+  EOL"  }"
+  EOL
+  EOL"  vec3 aHalf = normalize (aLight + theView);"
+  EOL
+  EOL"  vec3  aFaceSideNormal = theIsFront ? theNormal : -theNormal;"
+  EOL"  float aNdotL = max (0.0, dot (aFaceSideNormal, aLight));"
+  EOL"  float aNdotH = max (0.0, dot (aFaceSideNormal, aHalf ));"
+  EOL
+  EOL"  float aSpecl = 0.0;"
+  EOL"  if (aNdotL > 0.0)"
+  EOL"  {"
+  EOL"    aSpecl = pow (aNdotH, theIsFront ? occFrontMaterial_Shininess() : occBackMaterial_Shininess());"
+  EOL"  }"
+  EOL
+  EOL"  Diffuse  += occLight_Diffuse  (theId).rgb * aNdotL;"
+  EOL"  Specular += occLight_Specular (theId).rgb * aSpecl;"
+  EOL"}";
+
+//! Process clipping planes in Fragment Shader.
+//! Should be added at the beginning of the main() function.
+const char THE_FRAG_CLIP_PLANES[] =
+  EOL"  for (int aPlaneIter = 0; aPlaneIter < occClipPlaneCount; ++aPlaneIter)"
+  EOL"  {"
+  EOL"    vec4 aClipEquation = occClipPlaneEquations[aPlaneIter];"
+  EOL"    int  aClipSpace    = occClipPlaneSpaces[aPlaneIter];"
+  EOL"    if (aClipSpace == OccEquationCoords_World)"
+  EOL"    {"
+  EOL"      if (dot (aClipEquation.xyz, PositionWorld.xyz) + aClipEquation.w < 0.0)"
+  EOL"      {"
+  EOL"        discard;"
+  EOL"      }"
+  EOL"    }"
+  EOL"    else if (aClipSpace == OccEquationCoords_View)"
+  EOL"    {"
+  EOL"      if (dot (aClipEquation.xyz, Position.xyz) + aClipEquation.w < 0.0)"
+  EOL"      {"
+  EOL"        discard;"
+  EOL"      }"
+  EOL"    }"
+  EOL"  }";
+
+}
+
 // =======================================================================
 // function : OpenGl_ShaderManager
 // purpose  : Creates new empty shader manager
 // =======================================================================
 OpenGl_ShaderManager::OpenGl_ShaderManager (OpenGl_Context* theContext)
-: myContext  (theContext),
-  myIsPP     (Standard_False),
+: myShadingModel (Visual3d_TOM_VERTEX),
+  myContext  (theContext),
   myLastView (NULL)
 {
   //
@@ -49,18 +223,32 @@ OpenGl_ShaderManager::~OpenGl_ShaderManager()
   myProgramList.Clear();
 }
 
+// =======================================================================
+// function : clear
+// purpose  :
+// =======================================================================
+void OpenGl_ShaderManager::clear()
+{
+  myProgramList.Clear();
+  myLightPrograms.Nullify();
+  myFlatPrograms = OpenGl_SetOfShaderPrograms();
+  myMapOfLightPrograms.Clear();
+  myFontProgram.Nullify();
+  switchLightPrograms();
+}
+
 // =======================================================================
 // function : Create
 // purpose  : Creates new shader program
 // =======================================================================
-void OpenGl_ShaderManager::Create (const Handle(Graphic3d_ShaderProgram)& theProxy,
-                                   TCollection_AsciiString&               theShareKey,
-                                   Handle(OpenGl_ShaderProgram)&          theProgram)
+Standard_Boolean OpenGl_ShaderManager::Create (const Handle(Graphic3d_ShaderProgram)& theProxy,
+                                               TCollection_AsciiString&               theShareKey,
+                                               Handle(OpenGl_ShaderProgram)&          theProgram)
 {
   theProgram.Nullify();
   if (theProxy.IsNull())
   {
-    return;
+    return Standard_False;
   }
 
   theShareKey = theProxy->GetId();
@@ -70,7 +258,7 @@ void OpenGl_ShaderManager::Create (const Handle(Graphic3d_ShaderProgram)& thePro
     {
       myProgramList.Append (theProgram);
     }
-    return;
+    return Standard_True;
   }
 
   theProgram = new OpenGl_ShaderProgram (theProxy);
@@ -79,11 +267,12 @@ void OpenGl_ShaderManager::Create (const Handle(Graphic3d_ShaderProgram)& thePro
     theProgram->Release (myContext);
     theShareKey.Clear();
     theProgram.Nullify();
-    return;
+    return Standard_False;
   }
 
   myProgramList.Append (theProgram);
   myContext->ShareResource (theShareKey, theProgram);
+  return Standard_True;
 }
 
 // =======================================================================
@@ -141,6 +330,42 @@ Standard_Boolean OpenGl_ShaderManager::IsEmpty() const
   return myProgramList.IsEmpty();
 }
 
+// =======================================================================
+// function : switchLightPrograms
+// purpose  :
+// =======================================================================
+void OpenGl_ShaderManager::switchLightPrograms()
+{
+  TCollection_AsciiString aKey (myShadingModel == Visual3d_TOM_FRAGMENT ? "p_" : "g_");
+  const OpenGl_ListOfLight* aLights = myLightSourceState.LightSources();
+  if (aLights != NULL)
+  {
+    for (OpenGl_ListOfLight::Iterator aLightIter (*aLights); aLightIter.More(); aLightIter.Next())
+    {
+      switch (aLightIter.Value().Type)
+      {
+        case Visual3d_TOLS_AMBIENT:
+          break; // skip ambient
+        case Visual3d_TOLS_DIRECTIONAL:
+          aKey += "d";
+          break;
+        case Visual3d_TOLS_POSITIONAL:
+          aKey += "p";
+          break;
+        case Visual3d_TOLS_SPOT:
+          aKey += "s";
+          break;
+      }
+    }
+  }
+
+  if (!myMapOfLightPrograms.Find (aKey, myLightPrograms))
+  {
+    myLightPrograms = new OpenGl_SetOfShaderPrograms();
+    myMapOfLightPrograms.Bind (aKey, myLightPrograms);
+  }
+}
+
 // =======================================================================
 // function : UpdateLightSourceStateTo
 // purpose  : Updates state of OCCT light sources
@@ -149,13 +374,24 @@ void OpenGl_ShaderManager::UpdateLightSourceStateTo (const OpenGl_ListOfLight* t
 {
   myLightSourceState.Set (theLights);
   myLightSourceState.Update();
+  switchLightPrograms();
+}
+
+// =======================================================================
+// function : SetShadingModel
+// purpose  :
+// =======================================================================
+void OpenGl_ShaderManager::SetShadingModel (const Visual3d_TypeOfModel theModel)
+{
+  myShadingModel = theModel;
+  switchLightPrograms();
 }
 
 // =======================================================================
 // function : SetProjectionState
 // purpose  : Sets new state of OCCT projection transform
 // =======================================================================
-void OpenGl_ShaderManager::UpdateProjectionStateTo (const Tmatrix3* theProjectionMatrix)
+void OpenGl_ShaderManager::UpdateProjectionStateTo (const OpenGl_Mat4& theProjectionMatrix)
 {
   myProjectionState.Set (theProjectionMatrix);
   myProjectionState.Update();
@@ -165,7 +401,7 @@ void OpenGl_ShaderManager::UpdateProjectionStateTo (const Tmatrix3* theProjectio
 // function : SetModelWorldState
 // purpose  : Sets new state of OCCT model-world transform
 // =======================================================================
-void OpenGl_ShaderManager::UpdateModelWorldStateTo (const Tmatrix3* theModelWorldMatrix)
+void OpenGl_ShaderManager::UpdateModelWorldStateTo (const OpenGl_Mat4& theModelWorldMatrix)
 {
   myModelWorldState.Set (theModelWorldMatrix);
   myModelWorldState.Update();
@@ -175,42 +411,12 @@ void OpenGl_ShaderManager::UpdateModelWorldStateTo (const Tmatrix3* theModelWorl
 // function : SetWorldViewState
 // purpose  : Sets new state of OCCT world-view transform
 // =======================================================================
-void OpenGl_ShaderManager::UpdateWorldViewStateTo (const Tmatrix3* theWorldViewMatrix)
+void OpenGl_ShaderManager::UpdateWorldViewStateTo (const OpenGl_Mat4& theWorldViewMatrix)
 {
   myWorldViewState.Set (theWorldViewMatrix);
   myWorldViewState.Update();
 }
 
-// =======================================================================
-// function : RevertProjectionStateTo
-// purpose  : Reverts state of OCCT projection transform
-// =======================================================================
-void OpenGl_ShaderManager::RevertProjectionStateTo (const Tmatrix3* theProjectionMatrix)
-{
-  myProjectionState.Set (theProjectionMatrix);
-  myProjectionState.Revert();
-}
-
-// =======================================================================
-// function : RevertModelWorldStateTo
-// purpose  : Reverts state of OCCT model-world transform
-// =======================================================================
-void OpenGl_ShaderManager::RevertModelWorldStateTo (const Tmatrix3* theModelWorldMatrix)
-{
-  myModelWorldState.Set (theModelWorldMatrix);
-  myModelWorldState.Revert();
-}
-
-// =======================================================================
-// function : RevertWorldViewStateTo
-// purpose  : Reverts state of OCCT world-view transform
-// =======================================================================
-void OpenGl_ShaderManager::RevertWorldViewStateTo (const Tmatrix3* theWorldViewMatrix)
-{
-  myWorldViewState.Set (theWorldViewMatrix);
-  myWorldViewState.Revert();
-}
-
 // =======================================================================
 // function : LightSourceState
 // purpose  : Returns current state of OCCT light sources
@@ -568,14 +774,15 @@ void OpenGl_ShaderManager::UpdateMaterialStateTo (const Handle(OpenGl_ShaderProg
 {
   if (myMaterialStates.IsBound (theProgram))
   {
-    myMaterialStates.ChangeFind (theProgram).Set (theAspect);
+    OpenGl_MaterialState& aState = myMaterialStates.ChangeFind (theProgram);
+    aState.Set (theAspect);
+    aState.Update();
   }
   else
   {
-    myMaterialStates.Bind (theProgram, OpenGl_MaterialState (theAspect));
+    myMaterialStates.Bind       (theProgram, OpenGl_MaterialState (theAspect));
+    myMaterialStates.ChangeFind (theProgram).Update();
   }
-
-  myMaterialStates.ChangeFind (theProgram).Update();
 }
 
 // =======================================================================
@@ -780,6 +987,465 @@ void OpenGl_ShaderManager::PushState (const Handle(OpenGl_ShaderProgram)& thePro
   PushMaterialState    (theProgram);
   PushWorldViewState   (theProgram);
   PushModelWorldState  (theProgram);
-  PushProjectionState  (theProgram);  
+  PushProjectionState  (theProgram);
   PushLightSourceState (theProgram);
 }
+
+// =======================================================================
+// function : prepareStdProgramFont
+// purpose  :
+// =======================================================================
+Standard_Boolean OpenGl_ShaderManager::prepareStdProgramFont()
+{
+  Handle(Graphic3d_ShaderProgram) aProgramSrc = new Graphic3d_ShaderProgram();
+  TCollection_AsciiString aSrcVert =
+      EOL"void main()"
+      EOL"{"
+      EOL"  gl_Position = occProjectionMatrix * occWorldViewMatrix * occModelWorldMatrix * occVertex;"
+      EOL"}";
+
+  TCollection_AsciiString aSrcFrag =
+      EOL"float getAlpha(void) { return texture2D(occActiveSampler, gl_PointCoord).a; }"
+      EOL"void main()"
+      EOL"{"
+      EOL"  vec4 aColor = occColor;"
+      EOL"  aColor.a *= getAlpha();"
+      EOL"  if (aColor.a <= 0.285) discard;"
+      EOL"  gl_FragColor = aColor;"
+      EOL"}";
+
+  aProgramSrc->AttachShader (Graphic3d_ShaderObject::CreateFromSource (Graphic3d_TOS_VERTEX,   aSrcVert));
+  aProgramSrc->AttachShader (Graphic3d_ShaderObject::CreateFromSource (Graphic3d_TOS_FRAGMENT, aSrcFrag));
+  TCollection_AsciiString aKey;
+  if (!Create (aProgramSrc, aKey, myFontProgram))
+  {
+    myFontProgram = new OpenGl_ShaderProgram(); // just mark as invalid
+    return Standard_False;
+  }
+  return Standard_True;
+}
+
+// =======================================================================
+// function : prepareStdProgramFlat
+// purpose  :
+// =======================================================================
+Standard_Boolean OpenGl_ShaderManager::prepareStdProgramFlat (Handle(OpenGl_ShaderProgram)& theProgram,
+                                                              const Standard_Integer        theBits)
+{
+  Handle(Graphic3d_ShaderProgram) aProgramSrc = new Graphic3d_ShaderProgram();
+  TCollection_AsciiString aSrcVert, aSrcVertExtraOut, aSrcVertExtraMain, aSrcFrag, aSrcFragExtraOut, aSrcFragExtraMain;
+  TCollection_AsciiString aSrcFragGetColor     = EOL"vec4 getColor(void) { return occColor; }";
+  TCollection_AsciiString aSrcFragMainGetColor = EOL"  gl_FragColor = getColor();";
+  if ((theBits & OpenGl_PO_Point) != 0)
+  {
+  #if defined(GL_ES_VERSION_2_0)
+    aSrcVertExtraMain += EOL"  gl_PointSize = occPointSize;";
+  #endif
+    if ((theBits & OpenGl_PO_TextureA) != 0)
+    {
+      aSrcFragGetColor =
+        EOL"float getAlpha(void) { return texture2D(occActiveSampler, gl_PointCoord).a; }"
+        EOL"vec4  getColor(void)"
+        EOL"{"
+        EOL"  vec4 aColor = occColor;"
+        EOL"  aColor.a *= getAlpha();"
+        EOL"  return aColor;"
+        EOL"}";
+
+      aSrcFragMainGetColor =
+        EOL"  vec4 aColor = getColor();"
+        EOL"  if (aColor.a <= 0.1) discard;"
+        EOL"  gl_FragColor = aColor;";
+    }
+    else if ((theBits & OpenGl_PO_TextureRGB) != 0)
+    {
+      aSrcFragGetColor =
+        EOL"vec4 getColor(void) { return texture2D(occActiveSampler, gl_PointCoord); }";
+      aSrcFragMainGetColor =
+        EOL"  vec4 aColor = getColor();"
+        EOL"  if (aColor.a <= 0.1) discard;"
+        EOL"  gl_FragColor = aColor;";
+    }
+  }
+  else
+  {
+    if ((theBits & OpenGl_PO_TextureRGB) != 0)
+    {
+      aSrcVertExtraOut  += THE_VARY_TexCoord;
+      aSrcFragExtraOut  += THE_VARY_TexCoord;
+      aSrcVertExtraMain +=
+        EOL"  TexCoord = occTexCoord.st;";
+
+      aSrcFragGetColor =
+        EOL"vec4 getColor(void) { return texture2D(occActiveSampler, TexCoord.st); }";
+    }
+  }
+  if ((theBits & OpenGl_PO_VertColor) != 0)
+  {
+    aSrcVertExtraOut  += THE_VARY_VertColor;
+    aSrcVertExtraMain += EOL"  VertColor = occVertColor;";
+    aSrcFragExtraOut  += THE_VARY_VertColor;
+    aSrcFragGetColor  =  EOL"vec4 getColor(void) { return VertColor; }";
+  }
+  if ((theBits & OpenGl_PO_ClipPlanes) != 0)
+  {
+    const char THE_POS_VARY[] =
+      EOL"varying vec4 PositionWorld;"
+      EOL"varying vec4 Position;";
+
+    aSrcVertExtraOut  += THE_POS_VARY;
+    aSrcFragExtraOut  += THE_POS_VARY;
+    aSrcVertExtraMain +=
+      EOL"  PositionWorld = occModelWorldMatrix * occVertex;"
+      EOL"  Position      = occWorldViewMatrix * PositionWorld;";
+    aSrcFragExtraMain += THE_FRAG_CLIP_PLANES;
+  }
+
+  aSrcVert =
+      aSrcVertExtraOut
+    + EOL"void main()"
+      EOL"{"
+    + aSrcVertExtraMain
+    + EOL"  gl_Position = occProjectionMatrix * occWorldViewMatrix * occModelWorldMatrix * occVertex;"
+      EOL"}";
+
+  aSrcFrag =
+      aSrcFragExtraOut
+    + aSrcFragGetColor
+    + EOL"void main()"
+      EOL"{"
+    + aSrcFragExtraMain
+    + aSrcFragMainGetColor
+    + EOL"}";
+
+  aProgramSrc->AttachShader (Graphic3d_ShaderObject::CreateFromSource (Graphic3d_TOS_VERTEX,   aSrcVert));
+  aProgramSrc->AttachShader (Graphic3d_ShaderObject::CreateFromSource (Graphic3d_TOS_FRAGMENT, aSrcFrag));
+
+  TCollection_AsciiString aKey;
+  if (!Create (aProgramSrc, aKey, theProgram))
+  {
+    theProgram = new OpenGl_ShaderProgram(); // just mark as invalid
+    return Standard_False;
+  }
+  return Standard_True;
+}
+
+// =======================================================================
+// function : stdComputeLighting
+// purpose  :
+// =======================================================================
+TCollection_AsciiString OpenGl_ShaderManager::stdComputeLighting (const Standard_Boolean theHasVertColor)
+{
+  bool aLightsMap[Visual3d_TOLS_SPOT + 1] = { false, false, false, false };
+  TCollection_AsciiString aLightsFunc, aLightsLoop;
+  const OpenGl_ListOfLight* aLights = myLightSourceState.LightSources();
+  if (aLights != NULL)
+  {
+    Standard_Integer anIndex = 0;
+    for (OpenGl_ListOfLight::Iterator aLightIter (*aLights); aLightIter.More(); aLightIter.Next(), ++anIndex)
+    {
+      switch (aLightIter.Value().Type)
+      {
+        case Visual3d_TOLS_AMBIENT:
+          --anIndex;
+          break; // skip ambient
+        case Visual3d_TOLS_DIRECTIONAL:
+          aLightsLoop = aLightsLoop + EOL"    directionalLight (" + anIndex + ", theNormal, theView, theIsFront);";
+          break;
+        case Visual3d_TOLS_POSITIONAL:
+          aLightsLoop = aLightsLoop + EOL"    pointLight (" + anIndex + ", theNormal, theView, aPoint, theIsFront);";
+          break;
+        case Visual3d_TOLS_SPOT:
+          aLightsLoop = aLightsLoop + EOL"    spotLight (" + anIndex + ", theNormal, theView, aPoint, theIsFront);";
+          break;
+      }
+
+      bool& aTypeBit = aLightsMap[aLightIter.Value().Type];
+      if (aTypeBit)
+      {
+        continue;
+      }
+
+      aTypeBit = true;
+      switch (aLightIter.Value().Type)
+      {
+        case Visual3d_TOLS_AMBIENT:     break;
+        case Visual3d_TOLS_DIRECTIONAL: aLightsFunc += THE_FUNC_directionalLight; break;
+        case Visual3d_TOLS_POSITIONAL:  aLightsFunc += THE_FUNC_pointLight;       break;
+        case Visual3d_TOLS_SPOT:        aLightsFunc += THE_FUNC_spotLight;        break;
+      }
+    }
+  }
+
+  TCollection_AsciiString aGetMatAmbient = "theIsFront ? occFrontMaterial_Ambient()  : occBackMaterial_Ambient();";
+  TCollection_AsciiString aGetMatDiffuse = "theIsFront ? occFrontMaterial_Diffuse()  : occBackMaterial_Diffuse();";
+  if (theHasVertColor)
+  {
+    aGetMatAmbient = "getVertColor();";
+    aGetMatDiffuse = "getVertColor();";
+  }
+
+  return TCollection_AsciiString()
+    + THE_FUNC_lightDef
+    + aLightsFunc
+    + EOL
+      EOL"vec4 computeLighting (in vec3 theNormal,"
+      EOL"                      in vec3 theView,"
+      EOL"                      in vec4 thePoint,"
+      EOL"                      in bool theIsFront)"
+      EOL"{"
+      EOL"  Ambient  = occLightAmbient.rgb;"
+      EOL"  Diffuse  = vec3 (0.0);"
+      EOL"  Specular = vec3 (0.0);"
+      EOL"  vec3 aPoint = thePoint.xyz / thePoint.w;"
+    + aLightsLoop
+    + EOL"  vec4 aMaterialAmbient  = " + aGetMatAmbient
+    + EOL"  vec4 aMaterialDiffuse  = " + aGetMatDiffuse
+    + EOL"  vec4 aMaterialSpecular = theIsFront ? occFrontMaterial_Specular() : occBackMaterial_Specular();"
+      EOL"  vec4 aMaterialEmission = theIsFront ? occFrontMaterial_Emission() : occBackMaterial_Emission();"
+      EOL"  return vec4 (Ambient,  1.0) * aMaterialAmbient"
+      EOL"       + vec4 (Diffuse,  1.0) * aMaterialDiffuse"
+      EOL"       + vec4 (Specular, 1.0) * aMaterialSpecular"
+      EOL"                              + aMaterialEmission;"
+      EOL"}";
+}
+
+// =======================================================================
+// function : prepareStdProgramGouraud
+// purpose  :
+// =======================================================================
+Standard_Boolean OpenGl_ShaderManager::prepareStdProgramGouraud (Handle(OpenGl_ShaderProgram)& theProgram,
+                                                                 const Standard_Integer        theBits)
+{
+  Handle(Graphic3d_ShaderProgram) aProgramSrc = new Graphic3d_ShaderProgram();
+  TCollection_AsciiString aSrcVert, aSrcVertColor, aSrcVertExtraOut, aSrcVertExtraMain, aSrcFrag, aSrcFragExtraOut, aSrcFragExtraMain;
+  TCollection_AsciiString aSrcFragGetColor = EOL"vec4 getColor(void) { return gl_FrontFacing ? FrontColor : BackColor; }";
+  if ((theBits & OpenGl_PO_Point) != 0)
+  {
+  #if defined(GL_ES_VERSION_2_0)
+    aSrcVertExtraMain += EOL"  gl_PointSize = occPointSize;";
+  #endif
+  }
+  if ((theBits & OpenGl_PO_VertColor) != 0)
+  {
+    aSrcVertColor = EOL"vec4 getVertColor(void) { return occVertColor; }";
+  }
+  if ((theBits & OpenGl_PO_Point) != 0)
+  {
+    if ((theBits & OpenGl_PO_TextureRGB) != 0)
+    {
+      aSrcFragGetColor =
+        EOL"vec4 getColor(void)"
+        EOL"{"
+        EOL"  vec4 aColor = gl_FrontFacing ? FrontColor : BackColor;"
+        EOL"  return texture2D(occActiveSampler, gl_PointCoord) * aColor;"
+        EOL"}";
+    }
+  }
+  else
+  {
+    if ((theBits & OpenGl_PO_TextureRGB) != 0)
+    {
+      aSrcVertExtraOut  += THE_VARY_TexCoord;
+      aSrcFragExtraOut  += THE_VARY_TexCoord;
+      aSrcVertExtraMain +=
+        EOL"  TexCoord = occTexCoord.st;";
+
+      aSrcFragGetColor =
+        EOL"vec4 getColor(void)"
+        EOL"{"
+        EOL"  vec4 aColor = gl_FrontFacing ? FrontColor : BackColor;"
+        EOL"  return texture2D(occActiveSampler, TexCoord.st) * aColor;"
+        EOL"}";
+    }
+  }
+  if ((theBits & OpenGl_PO_ClipPlanes) != 0)
+  {
+    const char THE_POS_VARY[] =
+      EOL"varying vec4 PositionWorld;"
+      EOL"varying vec4 Position;";
+
+    aSrcVertExtraOut  += THE_POS_VARY;
+    aSrcFragExtraOut  += THE_POS_VARY;
+    aSrcVertExtraMain +=
+      EOL"  PositionWorld = aPositionWorld;"
+      EOL"  Position      = aPosition;";
+    aSrcFragExtraMain += THE_FRAG_CLIP_PLANES;
+  }
+
+  const TCollection_AsciiString aLights = stdComputeLighting ((theBits & OpenGl_PO_VertColor) != 0);
+  aSrcVert = TCollection_AsciiString()
+    + THE_FUNC_transformNormal
+    + EOL
+    + aSrcVertColor
+    + aLights
+    + EOL
+      EOL"varying vec4 FrontColor;"
+      EOL"varying vec4 BackColor;"
+      EOL
+    + aSrcVertExtraOut
+    + EOL"void main()"
+      EOL"{"
+      EOL"  vec4 aPositionWorld = occModelWorldMatrix * occVertex;"
+      EOL"  vec4 aPosition      = occWorldViewMatrix * aPositionWorld;"
+      EOL"  vec3 aNormal        = transformNormal (occNormal);"
+      EOL"  vec3 aView          = vec3 (0.0, 0.0, 1.0);"
+      EOL"  FrontColor  = computeLighting (normalize (aNormal), normalize (aView), aPosition, true);"
+      EOL"  BackColor   = computeLighting (normalize (aNormal), normalize (aView), aPosition, false);"
+    + aSrcVertExtraMain
+    + EOL"  gl_Position = occProjectionMatrix * occWorldViewMatrix * occModelWorldMatrix * occVertex;"
+      EOL"}";
+
+  aSrcFrag = TCollection_AsciiString()
+    + EOL"varying vec4 FrontColor;"
+      EOL"varying vec4 BackColor;"
+    + aSrcFragExtraOut
+    + aSrcFragGetColor
+    + EOL"void main()"
+      EOL"{"
+    + aSrcFragExtraMain
+    + EOL"  gl_FragColor = getColor();"
+      EOL"}";
+
+  aProgramSrc->AttachShader (Graphic3d_ShaderObject::CreateFromSource (Graphic3d_TOS_VERTEX,   aSrcVert));
+  aProgramSrc->AttachShader (Graphic3d_ShaderObject::CreateFromSource (Graphic3d_TOS_FRAGMENT, aSrcFrag));
+  TCollection_AsciiString aKey;
+  if (!Create (aProgramSrc, aKey, theProgram))
+  {
+    theProgram = new OpenGl_ShaderProgram(); // just mark as invalid
+    return Standard_False;
+  }
+  return Standard_True;
+}
+
+// =======================================================================
+// function : prepareStdProgramPhong
+// purpose  :
+// =======================================================================
+Standard_Boolean OpenGl_ShaderManager::prepareStdProgramPhong (Handle(OpenGl_ShaderProgram)& theProgram,
+                                                               const Standard_Integer        theBits)
+{
+  #define thePhongCompLight "computeLighting (normalize (Normal), normalize (View), Position, gl_FrontFacing)"
+
+  Handle(Graphic3d_ShaderProgram) aProgramSrc = new Graphic3d_ShaderProgram();
+  TCollection_AsciiString aSrcVert, aSrcVertExtraOut, aSrcVertExtraMain, aSrcFrag, aSrcFragExtraOut, aSrcFragGetVertColor, aSrcFragExtraMain;
+  TCollection_AsciiString aSrcFragGetColor = EOL"vec4 getColor(void) { return " thePhongCompLight "; }";
+  if ((theBits & OpenGl_PO_Point) != 0)
+  {
+  #if defined(GL_ES_VERSION_2_0)
+    aSrcVertExtraMain += EOL"  gl_PointSize = occPointSize;";
+  #endif
+  }
+  if ((theBits & OpenGl_PO_VertColor) != 0)
+  {
+    aSrcVertExtraOut  += THE_VARY_VertColor;
+    aSrcVertExtraMain += EOL"  VertColor = occVertColor;";
+    aSrcFragGetColor   = EOL"varying vec4 VertColor;"
+                         EOL"vec4 getVertColor(void) { return VertColor; }";
+  }
+
+  if ((theBits & OpenGl_PO_Point) != 0)
+  {
+    if ((theBits & OpenGl_PO_TextureRGB) != 0)
+    {
+      aSrcFragGetColor =
+        EOL"vec4 getColor(void)"
+        EOL"{"
+        EOL"  vec4 aColor = " thePhongCompLight ";"
+        EOL"  return texture2D(occActiveSampler, gl_PointCoord) * aColor;"
+        EOL"}";
+    }
+  }
+  else
+  {
+    if ((theBits & OpenGl_PO_TextureRGB) != 0)
+    {
+      aSrcVertExtraOut  += THE_VARY_TexCoord;
+      aSrcFragExtraOut  += THE_VARY_TexCoord;
+      aSrcVertExtraMain +=
+        EOL"  TexCoord = occTexCoord.st;";
+
+      aSrcFragGetColor =
+        EOL"vec4 getColor(void)"
+        EOL"{"
+        EOL"  vec4 aColor = " thePhongCompLight ";"
+        EOL"  return texture2D(occActiveSampler, TexCoord.st) * aColor;"
+        EOL"}";
+    }
+  }
+
+  if ((theBits & OpenGl_PO_ClipPlanes) != 0)
+  {
+    aSrcFragExtraMain += THE_FRAG_CLIP_PLANES;
+  }
+
+  aSrcVert = TCollection_AsciiString()
+    + THE_FUNC_transformNormal
+    + EOL
+      EOL"varying vec4 PositionWorld;"
+      EOL"varying vec4 Position;"
+      EOL"varying vec3 Normal;"
+      EOL"varying vec3 View;"
+      EOL
+    + aSrcVertExtraOut
+    + EOL"void main()"
+      EOL"{"
+      EOL"  PositionWorld = occModelWorldMatrix * occVertex;"
+      EOL"  Position      = occWorldViewMatrix * PositionWorld;"
+      EOL"  Normal        = transformNormal (occNormal);"
+      EOL"  View          = vec3 (0.0, 0.0, 1.0);"
+    + aSrcVertExtraMain
+    + EOL"  gl_Position = occProjectionMatrix * occWorldViewMatrix * occModelWorldMatrix * occVertex;"
+      EOL"}";
+
+  const TCollection_AsciiString aLights = stdComputeLighting ((theBits & OpenGl_PO_VertColor) != 0);
+  aSrcFrag = TCollection_AsciiString()
+    + EOL"varying vec4 PositionWorld;"
+      EOL"varying vec4 Position;"
+      EOL"varying vec3 Normal;"
+      EOL"varying vec3 View;"
+    + EOL
+    + aSrcFragExtraOut
+    + aSrcFragGetVertColor
+    + aLights
+    + aSrcFragGetColor
+    + EOL
+      EOL"void main()"
+      EOL"{"
+    + aSrcFragExtraMain
+    + EOL"  gl_FragColor = getColor();"
+      EOL"}";
+
+  aProgramSrc->AttachShader (Graphic3d_ShaderObject::CreateFromSource (Graphic3d_TOS_VERTEX,   aSrcVert));
+  aProgramSrc->AttachShader (Graphic3d_ShaderObject::CreateFromSource (Graphic3d_TOS_FRAGMENT, aSrcFrag));
+  TCollection_AsciiString aKey;
+  if (!Create (aProgramSrc, aKey, theProgram))
+  {
+    theProgram = new OpenGl_ShaderProgram(); // just mark as invalid
+    return Standard_False;
+  }
+  return Standard_True;
+}
+
+// =======================================================================
+// function : bindProgramWithState
+// purpose  :
+// =======================================================================
+Standard_Boolean OpenGl_ShaderManager::bindProgramWithState (const Handle(OpenGl_ShaderProgram)& theProgram,
+                                                             const OpenGl_Element*               theAspect)
+{
+  if (!myContext->BindProgram (theProgram))
+  {
+    return Standard_False;
+  }
+  theProgram->ApplyVariables (myContext);
+
+  const OpenGl_MaterialState* aMaterialState = MaterialState (theProgram);
+  if (aMaterialState == NULL || aMaterialState->Aspect() != theAspect)
+  {
+    UpdateMaterialStateTo (theProgram, theAspect);
+  }
+
+  PushState (theProgram);
+  return Standard_True;
+}