// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
-#include <OpenGl_GlCore11.hxx>
+#include <OpenGl_GlCore15.hxx>
+#include <OpenGl_FrameBuffer.hxx>
#include <OpenGl_LayerList.hxx>
+#include <OpenGl_ShaderManager.hxx>
#include <OpenGl_Structure.hxx>
+#include <OpenGl_VertexBuffer.hxx>
+#include <OpenGl_View.hxx>
#include <OpenGl_Workspace.hxx>
#include <Graphic3d_GraphicDriver.hxx>
myNbPriorities (theNbPriorities),
myNbStructures (0),
myImmediateNbStructures (0),
- myModifStateOfRaytraceable (0)
+ myModifStateOfRaytraceable (0),
+ myRenderOpaqueFilter (new OpenGl_OpaqueFilter()),
+ myRenderTranspFilter (new OpenGl_TransparentFilter())
{
// insert default priority layers
myLayers.Append (OpenGl_Layer (myNbPriorities));
myLayerIds.Bind (Graphic3d_ZLayerId_TopOSD, myLayers.Upper());
myDefaultLayerIndex = myLayerIds.Find (Graphic3d_ZLayerId_Default);
+
+ myTransparentToProcess.Allocate (myLayers.Length());
}
//=======================================================================
// add the new layer
myLayers.Append (OpenGl_Layer (myNbPriorities));
myLayerIds.Bind (theLayerId, myLayers.Length());
+
+ myTransparentToProcess.Allocate (myLayers.Length());
}
//=======================================================================
}
myDefaultLayerIndex = myLayerIds.Find (Graphic3d_ZLayerId_Default);
+
+ myTransparentToProcess.Allocate (myLayers.Length());
}
//=======================================================================
//=======================================================================
void OpenGl_LayerList::Render (const Handle(OpenGl_Workspace)& theWorkspace,
const Standard_Boolean theToDrawImmediate,
- const OpenGl_LayerFilter theLayersToProcess) const
+ const OpenGl_LayerFilter theLayersToProcess,
+ OpenGl_FrameBuffer* theReadDrawFbo,
+ OpenGl_FrameBuffer* theOitAccumFbo) const
{
+ // Remember global settings for glDepth function and write mask.
OpenGl_GlobalLayerSettings aDefaultSettings;
const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext();
aCtx->core11fwd->glGetIntegerv (GL_DEPTH_FUNC, &aDefaultSettings.DepthFunc);
aCtx->core11fwd->glGetBooleanv (GL_DEPTH_WRITEMASK, &aDefaultSettings.DepthMask);
+ // Two render filters are used to support transparency draw. Opaque filter accepts
+ // only non-transparent OpenGl elements of a layer and counts number of skipped
+ // transparent ones. If the counter has positive value the layer is added into
+ // transparency post-processing stack. At the end of drawing or once the depth
+ // buffer is to be cleared the layers in the stack should be drawn using
+ // blending and depth mask settings and another transparency filter which accepts
+ // only transparent OpenGl elements of a layer. The stack <myTransparentToProcess>
+ // was preallocated before going into this method and has enough space to keep
+ // maximum number of references to layers, therefore it will not increase memory
+ // fragmentation during regular rendering.
+ const Handle(OpenGl_RenderFilter) aPrevFilter = theWorkspace->GetRenderFilter();
+ myRenderOpaqueFilter->SetPreviousFilter (aPrevFilter);
+ myRenderTranspFilter->SetPreviousFilter (aPrevFilter);
+ theWorkspace->SetRenderFilter (myRenderOpaqueFilter);
+
+ myTransparentToProcess.Clear();
+
+ OpenGl_LayerStack::iterator aStackIter (myTransparentToProcess.Origin());
Standard_Integer aSeqId = myLayers.Lower();
bool toClearDepth = false;
- for (OpenGl_SequenceOfLayers::Iterator anIts (myLayers); anIts.More(); anIts.Next(), ++aSeqId)
+ for (OpenGl_SequenceOfLayers::Iterator aLayerIter (myLayers); aLayerIter.More(); aLayerIter.Next(), ++aSeqId)
{
if (theLayersToProcess == OpenGl_LF_Bottom)
{
if (aSeqId != myDefaultLayerIndex) continue;
}
- const OpenGl_Layer& aLayer = anIts.Value();
+ const OpenGl_Layer& aLayer = aLayerIter.Value();
if (aLayer.IsImmediate() != theToDrawImmediate)
{
continue;
}
else if (aLayer.NbStructures() < 1)
{
- // make sure to clear depth of previous layers even if layer has no structures
+ // Make sure to clear depth of previous layers even if layer has no structures.
toClearDepth = toClearDepth || aLayer.LayerSettings().ToClearDepth();
continue;
}
- // depth buffers
+ // At this point the depth buffer may be set to clear by
+ // previous configuration of layers or configuration of the
+ // current layer. Additional rendering pass to handle transparent
+ // elements of recently drawn layers require use of current depth
+ // buffer so we put remaining layers for processing as one bunch before
+ // erasing the depth buffer.
if (toClearDepth
|| aLayer.LayerSettings().ToClearDepth())
{
+ if (!myTransparentToProcess.IsEmpty())
+ {
+ renderTransparent (theWorkspace, aStackIter, aDefaultSettings, theReadDrawFbo, theOitAccumFbo);
+ }
+
toClearDepth = false;
glDepthMask (GL_TRUE);
glClear (GL_DEPTH_BUFFER_BIT);
}
+ // Render opaque OpenGl elements of a layer and count the number of skipped.
+ // If a layer has skipped (e.g. transparent) elements it should be added into
+ // the transparency post-processing stack.
+ myRenderOpaqueFilter->SetSkippedCounter (0);
+
aLayer.Render (theWorkspace, aDefaultSettings);
+
+ if (myRenderOpaqueFilter->NbSkipped() > 0)
+ {
+ myTransparentToProcess.Push (&aLayer);
+ }
+ }
+
+ // Before finishing process the remaining collected layers with transparency.
+ if (!myTransparentToProcess.IsEmpty())
+ {
+ renderTransparent (theWorkspace, aStackIter, aDefaultSettings, theReadDrawFbo, theOitAccumFbo);
}
if (toClearDepth)
aCtx->core11fwd->glDepthMask (aDefaultSettings.DepthMask);
aCtx->core11fwd->glDepthFunc (aDefaultSettings.DepthFunc);
+
+ theWorkspace->SetRenderFilter (aPrevFilter);
+}
+
+//=======================================================================
+//function : renderTransparent
+//purpose : Render transparent objects using blending operator.
+//=======================================================================
+void OpenGl_LayerList::renderTransparent (const Handle(OpenGl_Workspace)& theWorkspace,
+ OpenGl_LayerStack::iterator& theLayerIter,
+ const OpenGl_GlobalLayerSettings& theGlobalSettings,
+ OpenGl_FrameBuffer* theReadDrawFbo,
+ OpenGl_FrameBuffer* theOitAccumFbo) const
+{
+ // Blended order-independent transparency algorithm require several preconditions
+ // to be enabled. It should be requested by user, at least two outputs from
+ // fragment shader should be supported by GPU, so is the given framebuffer
+ // should contain two additional color buffers to handle accumulated color channels,
+ // blended alpha channel and weight factors - these accumulation buffers are required
+ // to implement commuting blend operator (at least OpenGl 2.0 should be available).
+ const bool isEnabledOit = theOitAccumFbo != NULL
+ && theOitAccumFbo->NbColorBuffers() >= 2
+ && theOitAccumFbo->ColorTexture (0)->IsValid()
+ && theOitAccumFbo->ColorTexture (1)->IsValid();
+
+ // Check if current iterator has already reached the end of the stack.
+ // This should happen if no additional layers has been added to
+ // the processing stack after last transparency pass.
+ if (theLayerIter == myTransparentToProcess.Back())
+ {
+ return;
+ }
+
+ const Handle(OpenGl_Context) aCtx = theWorkspace->GetGlContext();
+ const Handle(OpenGl_ShaderManager)& aManager = aCtx->ShaderManager();
+ OpenGl_View* aView = theWorkspace->View();
+ const float aDepthFactor = aView != NULL ? aView->RenderingParams().OitDepthFactor : 0.0f;
+
+ theWorkspace->SetRenderFilter (myRenderTranspFilter);
+
+ aCtx->core11fwd->glEnable (GL_BLEND);
+
+ if (isEnabledOit)
+ {
+ aManager->SetOitState (true, aDepthFactor);
+
+ theOitAccumFbo->BindBuffer (aCtx);
+
+ static const Standard_Integer aDrawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT0 + 1 };
+ aCtx->SetDrawBuffers (2, aDrawBuffers);
+ aCtx->core11fwd->glClearColor (0.0f, 0.0f, 0.0f, 1.0f);
+ aCtx->core11fwd->glClear (GL_COLOR_BUFFER_BIT);
+ aCtx->core15fwd->glBlendFuncSeparate (GL_ONE, GL_ONE, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ else
+ {
+ aCtx->core11fwd->glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ // During blended order-independent transparency pass the depth test
+ // should be enabled to discard fragments covered by opaque geometry
+ // and depth writing should be disabled, because transparent fragments
+ // overal each other with non unitary coverage factor.
+ OpenGl_GlobalLayerSettings aGlobalSettings = theGlobalSettings;
+ aGlobalSettings.DepthMask = GL_FALSE;
+ aCtx->core11fwd->glDepthMask (GL_FALSE);
+
+ for (; theLayerIter != myTransparentToProcess.Back(); ++theLayerIter)
+ {
+ (*theLayerIter)->Render (theWorkspace, aGlobalSettings);
+ }
+
+ // Revert state of rendering.
+ if (isEnabledOit)
+ {
+ aManager->SetOitState (false, aDepthFactor);
+ theOitAccumFbo->UnbindBuffer (aCtx);
+ if (theReadDrawFbo)
+ {
+ theReadDrawFbo->BindBuffer (aCtx);
+ }
+
+ static const Standard_Integer aDrawBuffers[] = { GL_COLOR_ATTACHMENT0 };
+ aCtx->SetDrawBuffers (1, aDrawBuffers);
+ }
+
+ theWorkspace->SetRenderFilter (myRenderOpaqueFilter);
+ if (isEnabledOit)
+ {
+ const Standard_Boolean isMSAA = theReadDrawFbo && theReadDrawFbo->NbSamples() > 0;
+ OpenGl_VertexBuffer* aVerts = theWorkspace->View()->initBlitQuad (Standard_False);
+ if (aVerts->IsValid() && aManager->BindOitCompositingProgram (isMSAA))
+ {
+ aCtx->core11fwd->glDepthFunc (GL_ALWAYS);
+ aCtx->core11fwd->glDepthMask (GL_FALSE);
+
+ // Bind full screen quad buffer and framebuffer resources.
+ aVerts->BindVertexAttrib (aCtx, Graphic3d_TOA_POS);
+
+ const Handle(OpenGl_Texture) aTextureBack = theWorkspace->DisableTexture();
+
+ theOitAccumFbo->ColorTexture (0)->Bind (aCtx, GL_TEXTURE0 + 0);
+ theOitAccumFbo->ColorTexture (1)->Bind (aCtx, GL_TEXTURE0 + 1);
+
+ // Draw full screen quad with special shader to compose the buffers.
+ aCtx->core11fwd->glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
+ aCtx->core11fwd->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
+
+ // Unbind OpenGL texture objects and shader program.
+ aVerts->UnbindVertexAttrib (aCtx, Graphic3d_TOA_POS);
+ theOitAccumFbo->ColorTexture (0)->Unbind (aCtx, GL_TEXTURE0 + 0);
+ theOitAccumFbo->ColorTexture (1)->Unbind (aCtx, GL_TEXTURE0 + 1);
+ aCtx->BindProgram (NULL);
+
+ if (!aTextureBack.IsNull())
+ {
+ theWorkspace->EnableTexture (aTextureBack);
+ }
+ }
+ else
+ {
+ aCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
+ "Initialization of OIT compositing pass has failed.\n"
+ " Blended order-independent transparency will not be available.\n");
+ if (aView != NULL)
+ {
+ Standard_Boolean& aOITFlag = isMSAA ? aView->myToDisableOITMSAA : aView->myToDisableOIT;
+ aOITFlag = Standard_True;
+ }
+ }
+ }
+
+ aCtx->core11fwd->glDisable (GL_BLEND);
+ aCtx->core11fwd->glBlendFunc (GL_ONE, GL_ZERO);
+ aCtx->core11fwd->glDepthMask (theGlobalSettings.DepthMask);
+ aCtx->core11fwd->glDepthFunc (theGlobalSettings.DepthFunc);
+}
+
+//=======================================================================
+//class : OpenGl_OpaqueFilter
+//function : ShouldRender
+//purpose : Checks whether the element should be rendered or skipped.
+//=======================================================================
+Standard_Boolean OpenGl_LayerList::OpenGl_OpaqueFilter::ShouldRender (const Handle(OpenGl_Workspace)& theWorkspace,
+ const OpenGl_Element* theGlElement)
+{
+ if (!myFilter.IsNull()
+ && !myFilter->ShouldRender (theWorkspace, theGlElement))
+ {
+ return Standard_False;
+ }
+
+ const OpenGl_PrimitiveArray* aPArray = dynamic_cast<const OpenGl_PrimitiveArray*> (theGlElement);
+ if (aPArray == NULL
+ || aPArray->DrawMode() < OpenGl_PrimitiveArray::THE_FILLPRIM_FROM
+ || aPArray->DrawMode() > OpenGl_PrimitiveArray::THE_FILLPRIM_TO)
+ {
+ return Standard_True;
+ }
+
+ Standard_ShortReal aFront = 0.f;
+ Standard_ShortReal aBack = 0.f;
+
+ if (OpenGl_Context::CheckIsTransparent (theWorkspace->AspectFace(),
+ theWorkspace->HighlightStyle(),
+ aFront, aBack))
+ {
+ ++mySkippedCounter;
+ return Standard_False;
+ }
+
+ return Standard_True;
+}
+
+//=======================================================================
+//class : OpenGl_TransparentFilter
+//function : ShouldRender
+//purpose : Checks whether the element should be rendered or skipped.
+//=======================================================================
+Standard_Boolean OpenGl_LayerList::OpenGl_TransparentFilter::ShouldRender (const Handle(OpenGl_Workspace)& theWorkspace,
+ const OpenGl_Element* theGlElement)
+{
+ if (!myFilter.IsNull()
+ && !myFilter->ShouldRender (theWorkspace, theGlElement))
+ {
+ return Standard_False;
+ }
+
+ const OpenGl_PrimitiveArray* aPArray = dynamic_cast<const OpenGl_PrimitiveArray*> (theGlElement);
+ if (aPArray == NULL
+ || aPArray->DrawMode() < OpenGl_PrimitiveArray::THE_FILLPRIM_FROM
+ || aPArray->DrawMode() > OpenGl_PrimitiveArray::THE_FILLPRIM_TO)
+ {
+ return dynamic_cast<const OpenGl_AspectFace*> (theGlElement) != NULL;
+ }
+
+ Standard_ShortReal aFront = 0.f;
+ Standard_ShortReal aBack = 0.f;
+
+ return OpenGl_Context::CheckIsTransparent (theWorkspace->AspectFace(),
+ theWorkspace->HighlightStyle(),
+ aFront, aBack);
}