1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-04-10 18:51:21 +03:00
occt/src/OpenGl/OpenGl_ShadowMap.cxx
drochalo 48f8f1e6ea 0032173: Visualization, TKOpenGl - implement simple shadow mapping for a point light source
Modified shadowmap calculations to include multipass for point lights.
Added shader funtions to calculate point light shadows.
Added getter for default znear and zfar values in Graphic3d_Camera.
Added direction and up vector calulations on Graphic3d_CubeMap.
Added logical exception for opengles2.0 lack of support on some key features of cube shadow maps.
Added test case.
2024-03-05 16:16:19 +00:00

225 lines
8.7 KiB
C++

// 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 <OpenGl_ShadowMap.hxx>
#include <OpenGl_FrameBuffer.hxx>
#include <OpenGl_ShaderManager.hxx>
#include <Graphic3d_CView.hxx>
#include <Message_Messenger.hxx>
IMPLEMENT_STANDARD_RTTIEXT(OpenGl_ShadowMap, OpenGl_NamedResource)
// =======================================================================
// function : OpenGl_ShadowMap
// purpose :
// =======================================================================
OpenGl_ShadowMap::OpenGl_ShadowMap()
: OpenGl_NamedResource ("shadow_map"),
myShadowMapFbo (new OpenGl_FrameBuffer (myResourceId + ":fbo")),
myShadowCamera (new Graphic3d_Camera()),
myShadowMapBias (0.0f)
{
//
}
// =======================================================================
// function : Release
// purpose :
// =======================================================================
void OpenGl_ShadowMap::Release (OpenGl_Context* theCtx)
{
myShadowMapFbo->Release (theCtx);
}
// =======================================================================
// function : ~OpenGl_ShadowMap
// purpose :
// =======================================================================
OpenGl_ShadowMap::~OpenGl_ShadowMap()
{
Release (NULL);
}
// =======================================================================
// function : EstimatedDataSize
// purpose :
// =======================================================================
Standard_Size OpenGl_ShadowMap::EstimatedDataSize() const
{
return myShadowMapFbo->EstimatedDataSize();
}
// =======================================================================
// function : IsValid
// purpose :
// =======================================================================
bool OpenGl_ShadowMap::IsValid() const
{
return myShadowMapFbo->IsValid();
}
// =======================================================================
// function : Texture
// purpose :
// =======================================================================
const Handle(OpenGl_Texture)& OpenGl_ShadowMap::Texture() const
{
return myShadowMapFbo->DepthStencilTexture();
}
// =======================================================================
// function : UpdateCamera
// purpose :
// =======================================================================
bool OpenGl_ShadowMap::UpdateCamera (const Graphic3d_CView& theView,
const gp_XYZ* theOrigin,
const Standard_Integer theFace)
{
const Bnd_Box aMinMaxBox = theOrigin == NULL ? theView.MinMaxValues (false) : Bnd_Box(); // applicative min max boundaries
const Bnd_Box aGraphicBox = aMinMaxBox;
switch (myShadowLight->Type())
{
case Graphic3d_TypeOfLightSource_Ambient:
{
return false; // not applicable
}
case Graphic3d_TypeOfLightSource_Directional:
{
if (theOrigin != NULL)
{
Graphic3d_Mat4d aTrans;
aTrans.Translate (Graphic3d_Vec3d (theOrigin->X(), theOrigin->Y(), theOrigin->Z()));
Graphic3d_Mat4d anOrientMat = myShadowCamera->OrientationMatrix() * aTrans;
myLightMatrix = myShadowCamera->ProjectionMatrixF() * Graphic3d_Mat4 (anOrientMat);
return true;
}
Graphic3d_Vec4d aDir (myShadowLight->Direction().X(), myShadowLight->Direction().Y(), myShadowLight->Direction().Z(), 0.0);
if (myShadowLight->IsHeadlight())
{
Graphic3d_Mat4d anOrientInv;
theView.Camera()->OrientationMatrix().Inverted (anOrientInv);
aDir = anOrientInv * aDir;
}
myShadowCamera->SetZeroToOneDepth (theView.Camera()->IsZeroToOneDepth());
myShadowCamera->SetProjectionType (Graphic3d_Camera::Projection_Orthographic);
myShadowCamera->SetDirection (gp_Dir (aDir.x(), aDir.y(), aDir.z()));
myShadowCamera->SetUp (!myShadowCamera->Direction().IsParallel (gp::DY(), Precision::Angular())
? gp::DY()
: gp::DX());
myShadowCamera->OrthogonalizeUp();
// Fitting entire scene to the light might produce a shadow map of too low resolution.
// More reliable approach would be putting a center to a current eye position and limiting maximum range,
// so that shadow range will be limited to some reasonable distance from current eye.
if (myShadowCamera->FitMinMax (aMinMaxBox, 10.0 * Precision::Confusion(), false))
{
myShadowCamera->SetScale (Max (myShadowCamera->ViewDimensions().X() * 1.1, myShadowCamera->ViewDimensions().Y() * 1.1)); // add margin
}
myShadowCamera->ZFitAll (1.0, aMinMaxBox, aGraphicBox);
myLightMatrix = myShadowCamera->ProjectionMatrixF() * myShadowCamera->OrientationMatrixF();
return true;
}
case Graphic3d_TypeOfLightSource_Positional:
{
// render into cubemap shadowmap texture
myShadowCamera->SetZeroToOneDepth (theView.Camera()->IsZeroToOneDepth());
myShadowCamera->SetProjectionType (Graphic3d_Camera::Projection_Perspective);
myShadowCamera->SetFOVy (90.0);
const gp_Pnt& aLightPos = myShadowLight->Position();
myShadowCamera->MoveEyeTo (aLightPos);
// calculate direction and up vector for the given cubemap face
myShadowCamera->SetDirectionFromEye (Graphic3d_CubeMap::GetCubeDirection ((Graphic3d_CubeMapSide)theFace));
myShadowCamera->SetUp (Graphic3d_CubeMap::GetCubeUp ((Graphic3d_CubeMapSide)theFace));
// setup znear and zfar (default value)
myShadowCamera->SetZRange (1.0, myShadowCamera->GetDefaultZFar());
myLightMatrix = myShadowCamera->ProjectionMatrixF() * myShadowCamera->OrientationMatrixF();
return true;
}
case Graphic3d_TypeOfLightSource_Spot:
{
if (theOrigin != NULL)
{
Graphic3d_Mat4d aTrans;
aTrans.Translate (Graphic3d_Vec3d (theOrigin->X(), theOrigin->Y(), theOrigin->Z()));
Graphic3d_Mat4d anOrientMat = myShadowCamera->OrientationMatrix() * aTrans;
myLightMatrix = myShadowCamera->ProjectionMatrixF() * Graphic3d_Mat4 (anOrientMat);
return true;
}
Graphic3d_Vec4d aDir (myShadowLight->Direction().X(), myShadowLight->Direction().Y(), myShadowLight->Direction().Z(), 0.0);
if (myShadowLight->IsHeadlight())
{
Graphic3d_Mat4d anOrientInv;
theView.Camera()->OrientationMatrix().Inverted (anOrientInv);
aDir = anOrientInv * aDir;
}
myShadowCamera->SetZeroToOneDepth (theView.Camera()->IsZeroToOneDepth());
myShadowCamera->SetProjectionType (Graphic3d_Camera::Projection_Perspective);
const gp_Pnt& aLightPos = myShadowLight->Position();
Standard_Real aDistance (aMinMaxBox.Distance (Bnd_Box (aLightPos, aLightPos))
+ aMinMaxBox.CornerMin().Distance (aMinMaxBox.CornerMax()));
myShadowCamera->SetDistance (aDistance);
myShadowCamera->MoveEyeTo (aLightPos);
myShadowCamera->SetDirectionFromEye (myShadowLight->Direction());
myShadowCamera->SetUp (!myShadowCamera->Direction().IsParallel (gp::DY(), Precision::Angular())
? gp::DY()
: gp::DX());
myShadowCamera->OrthogonalizeUp();
myShadowCamera->SetZRange (1.0, aDistance);
myLightMatrix = myShadowCamera->ProjectionMatrixF() * myShadowCamera->OrientationMatrixF();
return true;
}
}
return false;
}
// =======================================================================
// function : Release
// purpose :
// =======================================================================
void OpenGl_ShadowMapArray::Release (OpenGl_Context* theCtx)
{
for (Standard_Integer anIter = Lower(); anIter <= Upper(); ++anIter)
{
if (const Handle(OpenGl_ShadowMap)& aShadow = ChangeValue (anIter))
{
aShadow->Release (theCtx);
}
}
}
// =======================================================================
// function : EstimatedDataSize
// purpose :
// =======================================================================
Standard_Size OpenGl_ShadowMapArray::EstimatedDataSize() const
{
Standard_Size aSize = 0;
for (Standard_Integer anIter = Lower(); anIter <= Upper(); ++anIter)
{
if (const Handle(OpenGl_ShadowMap)& aShadow = Value (anIter))
{
aSize += aShadow->EstimatedDataSize();
}
}
return aSize;
}