You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/3rd/VTK7.1/source/VTK-7.1.1/Rendering/OpenGL/vtkShadowMapBakerPass.cxx

785 lines
23 KiB
C++

/*=========================================================================
Program: Visualization Toolkit
Module: vtkShadowMapBakerPass.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkShadowMapBakerPass.h"
#include "vtkObjectFactory.h"
#include <cassert>
#include "vtkRenderState.h"
#include "vtkOpenGLRenderer.h"
#include "vtkgl.h"
#include "vtkFrameBufferObject.h"
#include "vtkTextureObject.h"
#include "vtkShaderProgram2.h"
#include "vtkOpenGLRenderWindow.h"
#include "vtkOpenGLError.h"
#include "vtkInformationIntegerKey.h"
#include "vtkMath.h"
// to be able to dump intermediate passes into png files for debugging.
// only for vtkShadowMapBakerPass developers.
//#define VTK_SHADOW_MAP_BAKER_PASS_DEBUG
//#define DONT_DUPLICATE_LIGHTS
#include "vtkImageImport.h"
#include "vtkPixelBufferObject.h"
#include "vtkImageExtractComponents.h"
#include "vtkLightCollection.h"
#include "vtkLight.h"
#include "vtkInformation.h"
#include "vtkCamera.h"
#include "vtkAbstractTransform.h" // for helper classes stack and concatenation
#include "vtkPerspectiveTransform.h"
#include "vtkTransform.h"
#include <sstream>
#include "vtkStdString.h"
// For vtkShadowMapBakerPassTextures, vtkShadowMapBakerPassLightCameras
#include "vtkShadowMapPassInternal.h"
// debugging
#include "vtkOpenGLState.h"
#include "vtkTimerLog.h"
vtkStandardNewMacro(vtkShadowMapBakerPass);
vtkCxxSetObjectMacro(vtkShadowMapBakerPass,OpaquePass,vtkRenderPass);
vtkCxxSetObjectMacro(vtkShadowMapBakerPass,CompositeZPass,vtkRenderPass);
vtkInformationKeyMacro(vtkShadowMapBakerPass,OCCLUDER,Integer);
vtkInformationKeyMacro(vtkShadowMapBakerPass,RECEIVER,Integer);
// ----------------------------------------------------------------------------
// helper function to compute the mNearest point in a given direction.
// To be called several times, with initialized = false the first time.
void vtkShadowMapBakerPass::PointNearFar(double *v,
double *pt,
double *dir,
double &mNear,
double &mFar,
bool initialized)
{
double diff[3];
diff[0] = v[0] - pt[0]; diff[1] = v[1] - pt[1]; diff[2] = v[2] - pt[2];
double dot = vtkMath::Dot(diff, dir);
if(initialized)
{
if(dot < mNear)
{
mNear = dot;
}
if(dot > mFar)
{
mFar = dot;
}
}
else
{
mNear = dot;
mFar = dot;
}
}
// ----------------------------------------------------------------------------
// compute the min/max of the projection of a box in a given direction.
void vtkShadowMapBakerPass::BoxNearFar(double *bb,
double *pt,
double *dir,
double &mNear,
double &mFar)
{
double v[3];
v[0] = bb[0]; v[1] = bb[2]; v[2] = bb[4];
PointNearFar(v, pt, dir, mNear, mFar, false);
v[0] = bb[1]; v[1] = bb[2]; v[2] = bb[4];
PointNearFar(v, pt, dir, mNear, mFar, true);
v[0] = bb[0]; v[1] = bb[3]; v[2] = bb[4];
PointNearFar(v, pt, dir, mNear, mFar, true);
v[0] = bb[1]; v[1] = bb[3]; v[2] = bb[4];
PointNearFar(v, pt, dir, mNear, mFar, true);
v[0] = bb[0]; v[1] = bb[2]; v[2] = bb[5];
PointNearFar(v, pt, dir, mNear, mFar, true);
v[0] = bb[1]; v[1] = bb[2]; v[2] = bb[5];
PointNearFar(v, pt, dir, mNear, mFar, true);
v[0] = bb[0]; v[1] = bb[3]; v[2] = bb[5];
PointNearFar(v, pt, dir, mNear, mFar, true);
v[0] = bb[1]; v[1] = bb[3]; v[2] = bb[5];
PointNearFar(v, pt, dir, mNear, mFar, true);
}
// ----------------------------------------------------------------------------
vtkShadowMapBakerPass::vtkShadowMapBakerPass()
{
this->OpaquePass=0;
this->CompositeZPass=0;
this->Resolution=256;
this->PolygonOffsetFactor=1.1f;
this->PolygonOffsetUnits=4.0f;
this->FrameBufferObject=0;
this->ShadowMaps=0;
this->LightCameras=0;
this->HasShadows=false;
this->NeedUpdate=true;
}
// ----------------------------------------------------------------------------
vtkShadowMapBakerPass::~vtkShadowMapBakerPass()
{
if(this->OpaquePass!=0)
{
this->OpaquePass->Delete();
}
if(this->CompositeZPass!=0)
{
this->CompositeZPass->Delete();
}
if(this->FrameBufferObject!=0)
{
vtkErrorMacro(<<"FrameBufferObject should have been deleted in ReleaseGraphicsResources().");
}
if(this->ShadowMaps!=0)
{
vtkErrorMacro(<<"ShadowMaps should have been deleted in ReleaseGraphicsResources().");
}
if(this->LightCameras!=0)
{
vtkErrorMacro(<<"LightCameras should have been deleted in ReleaseGraphicsResources().");
}
}
// ----------------------------------------------------------------------------
void vtkShadowMapBakerPass::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os,indent);
os << indent << "OpaquePass: ";
if(this->OpaquePass!=0)
{
this->OpaquePass->PrintSelf(os,indent);
}
else
{
os << "(none)" <<endl;
}
os << indent << "CompositeZPass: ";
if(this->CompositeZPass!=0)
{
this->CompositeZPass->PrintSelf(os,indent);
}
else
{
os << "(none)" <<endl;
}
os << indent << "Resolution: " << this->Resolution << endl;
os << indent << "PolygonOffsetFactor: " << this->PolygonOffsetFactor
<< endl;
os << indent << "PolygonOffsetUnits: " << this->PolygonOffsetUnits << endl;
}
// ----------------------------------------------------------------------------
bool vtkShadowMapBakerPass::GetHasShadows()
{
return this->HasShadows;
}
// ----------------------------------------------------------------------------
bool vtkShadowMapBakerPass::LightCreatesShadow(vtkLight *l)
{
assert("pre: l_exists" && l!=0);
return !l->LightTypeIsHeadlight() &&
(!l->GetPositional() || l->GetConeAngle()<180.0);
}
// ----------------------------------------------------------------------------
vtkShadowMapBakerPassTextures *vtkShadowMapBakerPass::GetShadowMaps()
{
return this->ShadowMaps;
}
// ----------------------------------------------------------------------------
vtkShadowMapBakerPassLightCameras *vtkShadowMapBakerPass::GetLightCameras()
{
return this->LightCameras;
}
// ----------------------------------------------------------------------------
bool vtkShadowMapBakerPass::GetNeedUpdate()
{
return this->NeedUpdate;
}
// ----------------------------------------------------------------------------
void vtkShadowMapBakerPass::SetUpToDate()
{
this->NeedUpdate=false;
}
// ----------------------------------------------------------------------------
// Description:
// Perform rendering according to a render state \p s.
// \pre s_exists: s!=0
void vtkShadowMapBakerPass::Render(const vtkRenderState *s)
{
assert("pre: s_exists" && s!=0);
vtkOpenGLClearErrorMacro();
this->NumberOfRenderedProps=0;
this->HasShadows=false;
vtkOpenGLRenderer *r=static_cast<vtkOpenGLRenderer *>(s->GetRenderer());
vtkOpenGLRenderWindow *context=static_cast<vtkOpenGLRenderWindow *>(
r->GetRenderWindow());
#ifdef VTK_SHADOW_MAP_BAKER_PASS_DEBUG
vtkOpenGLState *state=new vtkOpenGLState(context);
#endif
if(this->OpaquePass!=0)
{
// Disable the scissor test during the shadow map pass.
GLboolean saved_scissor_test;
glGetBooleanv(GL_SCISSOR_TEST, &saved_scissor_test);
glDisable(GL_SCISSOR_TEST);
// Test for Hardware support. If not supported, just render the delegate.
bool supported=vtkFrameBufferObject::IsSupported(r->GetRenderWindow());
if(!supported)
{
vtkErrorMacro("FBOs are not supported by the context. Cannot use shadow mapping.");
}
if(supported)
{
supported=vtkTextureObject::IsSupported(r->GetRenderWindow());
if(!supported)
{
vtkErrorMacro("Texture Objects are not supported by the context. Cannot use shadow mapping.");
}
}
if(supported)
{
supported=
vtkShaderProgram2::IsSupported(static_cast<vtkOpenGLRenderWindow *>(
r->GetRenderWindow()));
if(!supported)
{
vtkErrorMacro("GLSL is not supported by the context. Cannot use shadow mapping.");
}
}
if(!supported)
{
// Nothing to bake.
return;
}
// Shadow mapping requires:
// 1. at least one spotlight, not front light
// 2. at least one receiver, in the list of visible props after culling
// 3. at least one occluder, in the list of visible props before culling
vtkLightCollection *lights=r->GetLights();
lights->InitTraversal();
vtkLight *l=lights->GetNextItem();
bool hasLight=false;
bool hasReceiver=false;
bool hasOccluder=false;
while(!hasLight && l!=0)
{
hasLight=l->GetSwitch() && this->LightCreatesShadow(l);
l=lights->GetNextItem();
}
int propArrayCount=0;
vtkProp **propArray=0;
vtkMTimeType latestPropTime=0;
vtkInformation *requiredKeys=0;
if(hasLight)
{
// at least one receiver?
requiredKeys=vtkInformation::New();
requiredKeys->Set(vtkShadowMapBakerPass::RECEIVER(),0); // dummy val.
int i=0;
int count=s->GetPropArrayCount();
while(!hasReceiver && i<count)
{
hasReceiver=s->GetPropArray()[i]->HasKeys(requiredKeys);
++i;
}
if(hasReceiver)
{
requiredKeys->Remove(vtkShadowMapBakerPass::RECEIVER());
requiredKeys->Set(vtkShadowMapBakerPass::OCCLUDER(),0); // dummy val.
// at least one occluder?
vtkCollectionSimpleIterator pit;
vtkPropCollection *props=r->GetViewProps();
props->InitTraversal(pit);
vtkProp *p=props->GetNextProp(pit);
propArray=new vtkProp*[props->GetNumberOfItems()];
while(p!=0)
{
vtkMTimeType mTime=p->GetMTime();
if(latestPropTime<mTime)
{
latestPropTime=mTime;
}
if(p->GetVisibility())
{
propArray[propArrayCount]=p;
++propArrayCount;
hasOccluder|=p->HasKeys(requiredKeys);
}
p=props->GetNextProp(pit);
}
}
}
this->HasShadows=hasOccluder;
if(!hasOccluder)
{
// No shadows.
if(requiredKeys!=0)
{
requiredKeys->Delete();
}
#ifdef VTK_SHADOW_MAP_BAKER_PASS_DEBUG
if(!hasLight)
{
cout << "no spotlight" << endl;
}
else
{
if(!hasReceiver)
{
cout << "no receiver" << endl;
}
else
{
cout << "no occluder" << endl;
}
}
#endif
delete[] propArray;
// Nothing to bake.
return;
}
// Shadow mapping starts here.
// 1. Create a shadow map for each spotlight.
// Do we need to recreate shadow maps?
this->NeedUpdate=this->LastRenderTime<lights->GetMTime();
if(!this->NeedUpdate)
{
lights->InitTraversal();
l=lights->GetNextItem();
while(!this->NeedUpdate && l!=0)
{
// comparison should be this->LastRenderTime<l->GetMTime() but
// we modify the lights during rendering (enable/disable state)
// so cannot rely on this time, we use the list time instead.
this->NeedUpdate=this->LastRenderTime<l->GetMTime();
l=lights->GetNextItem();
}
}
if(!this->NeedUpdate)
{
this->NeedUpdate=this->LastRenderTime<r->GetViewProps()->GetMTime()
|| this->LastRenderTime<latestPropTime;
}
if(!this->NeedUpdate)
{
int i=0;
while(i<propArrayCount)
{
this->NeedUpdate=this->LastRenderTime<propArray[i]->GetMTime();
++i;
}
}
size_t lightIndex=0;
bool autoLight=r->GetAutomaticLightCreation()==1;
vtkCamera *realCamera=r->GetActiveCamera();
vtkRenderState s2(r);
if(this->NeedUpdate) // create or re-create the shadow maps.
{
#ifdef VTK_SHADOW_MAP_BAKER_PASS_DEBUG
cout << "update the shadow maps" << endl;
#endif
GLint savedDrawBuffer;
glGetIntegerv(GL_DRAW_BUFFER,&savedDrawBuffer);
realCamera->Register(this);
// 1. Create a new render state with an FBO.
// We need all the visible props, including the one culled out by the
// camera, because they can cast shadows too (ie being visible from the
// light cameras)
s2.SetPropArrayAndCount(propArray,propArrayCount);
if(this->FrameBufferObject==0)
{
this->FrameBufferObject=vtkFrameBufferObject::New();
this->FrameBufferObject->SetContext(context);
}
s2.SetFrameBuffer(this->FrameBufferObject);
requiredKeys->Remove(vtkShadowMapBakerPass::RECEIVER());
requiredKeys->Set(vtkShadowMapBakerPass::OCCLUDER(),0);
s2.SetRequiredKeys(requiredKeys);
lights->InitTraversal();
l=lights->GetNextItem();
size_t numberOfShadowLights=0;
while(l!=0)
{
if(l->GetSwitch() && this->LightCreatesShadow(l))
{
++numberOfShadowLights;
}
l=lights->GetNextItem();
}
if(this->ShadowMaps!=0 &&
this->ShadowMaps->Vector.size()!=numberOfShadowLights)
{
delete this->ShadowMaps;
this->ShadowMaps=0;
}
if(this->ShadowMaps==0)
{
this->ShadowMaps=new vtkShadowMapBakerPassTextures;
this->ShadowMaps->Vector.resize(numberOfShadowLights);
}
if(this->LightCameras!=0 &&
this->LightCameras->Vector.size()!=numberOfShadowLights)
{
delete this->LightCameras;
this->LightCameras=0;
}
if(this->LightCameras==0)
{
this->LightCameras=new vtkShadowMapBakerPassLightCameras;
this->LightCameras->Vector.resize(numberOfShadowLights);
}
r->SetAutomaticLightCreation(false);
r->UpdateLightsGeometryToFollowCamera();
double bb[6];
vtkMath::UninitializeBounds(bb);
vtkPropCollection* props = r->GetViewProps();
vtkCollectionSimpleIterator cookie;
props->InitTraversal(cookie);
vtkProp* prop;
bool first = true;
while((prop = props->GetNextProp(cookie)) != NULL)
{
double* bounds = prop->GetBounds();
if(first)
{
bb[0] = bounds[0];
bb[1] = bounds[1];
bb[2] = bounds[2];
bb[3] = bounds[3];
bb[4] = bounds[4];
bb[5] = bounds[5];
}
else
{
bb[0] = (bb[0] < bounds[0] ? bb[0] : bounds[0]);
bb[1] = (bb[1] > bounds[1] ? bb[1] : bounds[1]);
bb[2] = (bb[2] < bounds[2] ? bb[2] : bounds[2]);
bb[3] = (bb[3] > bounds[3] ? bb[3] : bounds[3]);
bb[4] = (bb[4] < bounds[4] ? bb[4] : bounds[4]);
bb[5] = (bb[5] > bounds[5] ? bb[5] : bounds[5]);
}
first = false;
}
lights->InitTraversal();
l=lights->GetNextItem();
lightIndex=0;
while(l!=0)
{
if(l->GetSwitch() && this->LightCreatesShadow(l))
{
vtkTextureObject *map=this->ShadowMaps->Vector[lightIndex];
if(map==0)
{
map=vtkTextureObject::New();
this->ShadowMaps->Vector[lightIndex]=map;
map->Delete();
}
map->SetContext(context);
map->SetMinificationFilter(vtkTextureObject::Nearest);
map->SetLinearMagnification(false);
map->SetWrapS(vtkTextureObject::ClampToEdge);
map->SetWrapT(vtkTextureObject::ClampToEdge);
map->SetWrapR(vtkTextureObject::ClampToEdge);
if(map->GetWidth()!=this->Resolution ||
map->GetHeight()!=this->Resolution)
{
map->Create2D(this->Resolution,this->Resolution,
1,VTK_VOID,false);
}
this->FrameBufferObject->SetDepthBufferNeeded(true);
this->FrameBufferObject->SetDepthBuffer(map);
this->FrameBufferObject->StartNonOrtho(
static_cast<int>(this->Resolution),
static_cast<int>(this->Resolution),false);
vtkCamera *lightCamera=this->LightCameras->Vector[lightIndex];
if(lightCamera==0)
{
lightCamera=vtkCamera::New();
this->LightCameras->Vector[lightIndex]=lightCamera;
lightCamera->Delete();
}
// Build light camera
r->SetActiveCamera(realCamera);
this->BuildCameraLight(l,bb, lightCamera);
r->SetActiveCamera(lightCamera);
glShadeModel(GL_FLAT);
glDisable(GL_LIGHTING);
glDisable(GL_COLOR_MATERIAL);
glDisable(GL_NORMALIZE);
glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(this->PolygonOffsetFactor,this->PolygonOffsetUnits);
glEnable(GL_DEPTH_TEST);
this->OpaquePass->Render(&s2);
this->NumberOfRenderedProps+=
this->OpaquePass->GetNumberOfRenderedProps();
if(this->CompositeZPass!=0)
{
this->CompositeZPass->Render(&s2);
}
r->SetActiveCamera(realCamera); //reset the camera
#ifdef VTK_SHADOW_MAP_BAKER_PASS_DEBUG
cout << "finish1 lightIndex=" <<lightIndex << endl;
glFinish();
state->Update();
vtkIndent indent;
std::ostringstream ost00;
ost00.setf(ios::fixed,ios::floatfield);
ost00.precision(5);
ost00 << "OpenGLState_" << pthread_self() << "_"
<< vtkTimerLog::GetUniversalTime() << "_.txt";
ofstream outfile(ost00.str().c_str());
state->PrintSelf(outfile,indent);
outfile.close();
#endif
#ifdef VTK_SHADOW_MAP_BAKER_PASS_DEBUG
state->Update();
std::ostringstream ost01;
ost01.setf(ios::fixed,ios::floatfield);
ost01.precision(5);
ost01 << "OpenGLState_" << pthread_self() << "_"
<< vtkTimerLog::GetUniversalTime() << "_after_compositez.txt";
ofstream outfile1(ost01.str().c_str());
state->PrintSelf(outfile1,indent);
outfile1.close();
#endif
++lightIndex;
}
l=lights->GetNextItem();
}
this->LastRenderTime.Modified(); // was a BUG
glDisable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0.0f,0.0f);
// back to the original frame buffer.
this->FrameBufferObject->UnBind();
glDrawBuffer(static_cast<GLenum>(savedDrawBuffer));
// Restore real camera.
r->SetActiveCamera(realCamera);
realCamera->UnRegister(this);
glShadeModel(GL_SMOOTH);
glEnable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_NORMALIZE);
glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
r->SetAutomaticLightCreation(autoLight);
} // end of the shadow map creations.
requiredKeys->Delete();
delete[] propArray;
if (saved_scissor_test)
{
glEnable(GL_SCISSOR_TEST);
}
}
else
{
vtkWarningMacro(<<" no delegate.");
}
vtkOpenGLCheckErrorMacro("failed after Render");
}
// ----------------------------------------------------------------------------
// Description:
// Build a camera from spot light parameters.
// \pre light_exists: light!=0
// \pre light_is_spotlight: light->LightTypeIsSceneLight() && light->GetPositional() && light->GetConeAngle()<180.0
// \pre camera_exists: camera!=0
void vtkShadowMapBakerPass::BuildCameraLight(vtkLight *light,
double *bb,
vtkCamera* lcamera)
{
assert("pre: light_exists" && light!=0);
assert("pre: camera_exists" && lcamera!=0);
lcamera->SetPosition(light->GetTransformedPosition());
lcamera->SetFocalPoint(light->GetTransformedFocalPoint());
double dir[3];
dir[0] = lcamera->GetFocalPoint()[0]-lcamera->GetPosition()[0];
dir[1] = lcamera->GetFocalPoint()[1]-lcamera->GetPosition()[1];
dir[2] = lcamera->GetFocalPoint()[2]-lcamera->GetPosition()[2];
vtkMath::Normalize(dir);
double vx[3], vup[3];
vtkMath::Perpendiculars(dir, vx, vup, 0);
double mNear, mFar;
BoxNearFar(bb, lcamera->GetPosition(), dir, mNear, mFar);
lcamera->SetViewUp(vup);
if(light->GetPositional())
{
assert("pre: cone_angle_is_inf_180" && light->GetConeAngle()<180.0);
lcamera->SetParallelProjection(0);
// view angle is an aperture, but cone (or light) angle is between
// the axis of the cone and a ray along the edge of the cone.
lcamera->SetViewAngle(light->GetConeAngle()*2.0);
// initial clip=(0.1,1000). mNear>0, mFar>mNear);
double mNearmin = (mFar - mNear) / 100.0;
if(mNear < mNearmin)
mNear = mNearmin;
if(mFar < mNearmin)
mFar = 2.0*mNearmin;
lcamera->SetClippingRange(mNear,mFar);
}
else
{
lcamera->SetParallelProjection(1);
double minx, maxx, miny, maxy, minz, maxz;
double orig[3] = {0, 0, 0};
this->BoxNearFar(bb, orig, vx, minx, maxx);
this->BoxNearFar(bb, orig, vup, miny, maxy);
this->BoxNearFar(bb, orig, dir, minz, maxz);
double sizex, sizey;
sizex = maxx-minx;
sizey = maxy-miny;
double realPos[3];
realPos[0] = dir[0] * (minz - 1.0) + (minx+maxx) / 2.0 * vx[0] + (miny+maxy) / 2.0 * vup[0];
realPos[1] = dir[1] * (minz - 1.0) + (minx+maxx) / 2.0 * vx[1] + (miny+maxy) / 2.0 * vup[1];
realPos[2] = dir[2] * (minz - 1.0) + (minx+maxx) / 2.0 * vx[2] + (miny+maxy) / 2.0 * vup[2];
lcamera->SetPosition(realPos);
lcamera->SetFocalPoint(realPos[0] + dir[0], realPos[1] + dir[1], realPos[2] + dir[2]);
double scale = (sizex > sizey ? sizex: sizey);
lcamera->SetParallelScale(scale);
lcamera->SetClippingRange(1.0, 1.0 + maxz - minz);
}
}
// ----------------------------------------------------------------------------
// Description:
// Release graphics resources and ask components to release their own
// resources.
// \pre w_exists: w!=0
void vtkShadowMapBakerPass::ReleaseGraphicsResources(vtkWindow *w)
{
assert("pre: w_exists" && w!=0);
if(this->OpaquePass!=0)
{
this->OpaquePass->ReleaseGraphicsResources(w);
}
if(this->CompositeZPass!=0)
{
this->CompositeZPass->ReleaseGraphicsResources(w);
}
if(this->FrameBufferObject!=0)
{
this->FrameBufferObject->Delete();
this->FrameBufferObject=0;
}
delete this->ShadowMaps;
this->ShadowMaps=0;
delete this->LightCameras;
this->LightCameras=0;
}