Commit 1c573012 authored by Ken Martin's avatar Ken Martin

Add basic support for shadows

First cut at adding shadows back into VTK. It does
not use spot intensity maps and currently you have to
build the render passes yourself. But I will soon add
a Renderer method to turn shadows on that handles all
the details for you.
parent f7e845a7
......@@ -62,6 +62,8 @@ set(Module_SRCS
vtkSequencePass.cxx
vtkShader.cxx
vtkShaderProgram.cxx
vtkShadowMapBakerPass.cxx
vtkShadowMapPass.cxx
vtkSobelGradientMagnitudePass.cxx
vtkTextureObject.cxx
vtkTextureUnitManager.cxx
......
......@@ -5,6 +5,8 @@ vtk_add_test_cxx(${vtk-module}CxxTests tests
TestVBOPointsLines.cxx
TestGaussianBlurPass.cxx
TestBlurAndSobelPasses.cxx
TestShadowMapBakerPass.cxx
TestShadowMapPass.cxx
TestSobelGradientMagnitudePass.cxx
)
......
/*=========================================================================
Program: Visualization Toolkit
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.
=========================================================================*/
// test baking shadow maps
//
// The command line arguments are:
// -I => run in interactive mode; unless this is used, the program will
// not allow interaction and exit
#include "vtkCamera.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkActor.h"
#include "vtkCellArray.h"
#include "vtkPointData.h"
#include "vtkPolyDataMapper.h"
#include "vtkPLYReader.h"
#include "vtkNew.h"
#include "vtkProperty.h"
#include "vtkLightKit.h"
#include "vtkPolyDataNormals.h"
#include "vtkTimerLog.h"
#include "vtkOpenGLRenderer.h"
#include "vtkPlaneSource.h"
#include "vtkOpenGLTexture.h"
#include "vtkCameraPass.h"
#include "vtkLightsPass.h"
#include "vtkSequencePass.h"
#include "vtkOpaquePass.h"
#include "vtkRenderPassCollection.h"
#include "vtkShadowMapBakerPass.h"
#include "vtkShadowMapPassInternal.h"
#include "vtkRenderStepsPass.h"
#include "vtkRegressionTestImage.h"
#include "vtkTestUtilities.h"
#include "vtkRenderWindowInteractor.h"
//----------------------------------------------------------------------------
int TestShadowMapBakerPass(int argc, char *argv[])
{
vtkNew<vtkActor> actor;
vtkNew<vtkRenderer> renderer;
vtkNew<vtkPolyDataMapper> mapper;
renderer->SetBackground(0.3, 0.4, 0.6);
vtkNew<vtkRenderWindow> renderWindow;
renderWindow->SetSize(600, 600);
renderWindow->AddRenderer(renderer.Get());
renderer->AddActor(actor.Get());
vtkNew<vtkRenderWindowInteractor> iren;
iren->SetRenderWindow(renderWindow.Get());
vtkNew<vtkLightKit> lightKit;
lightKit->AddLightsToRenderer(renderer.Get());
const char* fileName =
vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/dragon.ply");
vtkNew<vtkPLYReader> reader;
reader->SetFileName(fileName);
reader->Update();
// vtkNew<vtkPolyDataNormals> norms;
// norms->SetInputConnection(reader->GetOutputPort());
// norms->Update();
mapper->SetInputConnection(reader->GetOutputPort());
//mapper->SetInputConnection(norms->GetOutputPort());
actor->SetMapper(mapper.Get());
actor->GetProperty()->SetAmbientColor(0.2, 0.2, 1.0);
actor->GetProperty()->SetDiffuseColor(1.0, 0.65, 0.7);
actor->GetProperty()->SetSpecularColor(1.0, 1.0, 1.0);
actor->GetProperty()->SetSpecular(0.5);
actor->GetProperty()->SetDiffuse(0.7);
actor->GetProperty()->SetAmbient(0.5);
actor->GetProperty()->SetSpecularPower(20.0);
actor->GetProperty()->SetOpacity(1.0);
//actor->GetProperty()->SetRepresentationToWireframe();
renderWindow->SetMultiSamples(0);
// create the basic VTK render steps
vtkNew<vtkRenderStepsPass> basicPasses;
vtkNew<vtkOpaquePass> opaque;
vtkNew<vtkLightsPass> lights;
vtkNew<vtkSequencePass> opaqueSequence;
vtkNew<vtkRenderPassCollection> passes2;
passes2->AddItem(lights.Get());
passes2->AddItem(opaque.Get());
opaqueSequence->SetPasses(passes2.Get());
vtkNew<vtkCameraPass> opaqueCameraPass;
opaqueCameraPass->SetDelegatePass(opaqueSequence.Get());
vtkNew<vtkShadowMapBakerPass> bakerPass;
// bakerPass->SetOpaquePass(basicPasses->GetOpaquePass());
bakerPass->SetOpaquePass(opaqueCameraPass.Get());
bakerPass->SetResolution(1024);
// To cancel self-shadowing.
bakerPass->SetPolygonOffsetFactor(3.1f);
bakerPass->SetPolygonOffsetUnits(10.0f);
basicPasses->SetOpaquePass(bakerPass.Get());
// tell the renderer to use our render pass pipeline
vtkOpenGLRenderer *glrenderer =
vtkOpenGLRenderer::SafeDownCast(renderer.GetPointer());
glrenderer->SetPass(basicPasses.Get());
vtkNew<vtkTimerLog> timer;
timer->StartTimer();
renderWindow->Render();
timer->StopTimer();
double firstRender = timer->GetElapsedTime();
cerr << "baking time: " << firstRender << endl;
// get a shadow map
vtkTextureObject *to = bakerPass->GetShadowMaps()->Vector[2];
// by default the textures have depth comparison on
// but for simple display we need to turn it off
to->SetDepthTextureCompare(false);
// now render this texture so we can see the depth map
vtkNew<vtkActor> actor2;
vtkNew<vtkPolyDataMapper> mapper2;
vtkNew<vtkOpenGLTexture> texture;
texture->SetTextureObject(to);
actor2->SetTexture(texture.Get());
actor2->SetMapper(mapper2.Get());
vtkNew<vtkPlaneSource> plane;
mapper2->SetInputConnection(plane->GetOutputPort());
renderer->RemoveActor(actor.Get());
renderer->AddActor(actor2.Get());
glrenderer->SetPass(NULL);
renderer->GetActiveCamera()->SetPosition(0,0,1);
renderer->GetActiveCamera()->SetFocalPoint(0,0,0);
renderer->GetActiveCamera()->SetViewUp(0,1,0);
renderer->ResetCamera();
renderer->GetActiveCamera()->Zoom(2.0);
renderWindow->Render();
int retVal = vtkRegressionTestImage( renderWindow.Get() );
if ( retVal == vtkRegressionTester::DO_INTERACTOR)
{
iren->Start();
}
bakerPass->ReleaseGraphicsResources(renderWindow.Get());
return EXIT_SUCCESS;
}
/*=========================================================================
Program: Visualization Toolkit
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.
=========================================================================*/
// test baking shadow maps
//
// The command line arguments are:
// -I => run in interactive mode; unless this is used, the program will
// not allow interaction and exit
#include "vtkActor.h"
#include "vtkCamera.h"
#include "vtkCameraPass.h"
#include "vtkCellArray.h"
#include "vtkLight.h"
#include "vtkLightsPass.h"
#include "vtkNew.h"
#include "vtkOpaquePass.h"
#include "vtkOpenGLRenderer.h"
#include "vtkOpenGLTexture.h"
#include "vtkPLYReader.h"
#include "vtkPlaneSource.h"
#include "vtkPolyDataMapper.h"
#include "vtkProperty.h"
#include "vtkRegressionTestImage.h"
#include "vtkRenderPassCollection.h"
#include "vtkRenderStepsPass.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkSequencePass.h"
#include "vtkShadowMapBakerPass.h"
#include "vtkShadowMapPass.h"
#include "vtkShadowMapPassInternal.h"
#include "vtkTestUtilities.h"
#include "vtkTimerLog.h"
//----------------------------------------------------------------------------
int TestShadowMapPass(int argc, char *argv[])
{
vtkNew<vtkRenderer> renderer;
renderer->SetBackground(0.3, 0.4, 0.6);
vtkNew<vtkRenderWindow> renderWindow;
renderWindow->SetSize(600, 600);
renderWindow->AddRenderer(renderer.Get());
vtkNew<vtkRenderWindowInteractor> iren;
iren->SetRenderWindow(renderWindow.Get());
vtkNew<vtkLight> light1;
light1->SetFocalPoint(0,0,0);
light1->SetPosition(0,1,0.2);
light1->SetColor(0.95,0.97,1.0);
light1->SetIntensity(0.8);
renderer->AddLight(light1.Get());
vtkNew<vtkLight> light2;
light2->SetFocalPoint(0,0,0);
light2->SetPosition(1.0,1.0,1.0);
light2->SetColor(1.0,0.8,0.7);
light2->SetIntensity(0.3);
renderer->AddLight(light2.Get());
const char* fileName =
vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/dragon.ply");
vtkNew<vtkPLYReader> reader;
reader->SetFileName(fileName);
reader->Update();
vtkNew<vtkPolyDataMapper> mapper;
mapper->SetInputConnection(reader->GetOutputPort());
vtkNew<vtkActor> actor;
actor->SetMapper(mapper.Get());
actor->GetProperty()->SetAmbientColor(0.135, 0.2225, 0.3);
actor->GetProperty()->SetDiffuseColor(0.54, 0.89, 0.63);
actor->GetProperty()->SetSpecularColor(1.0, 1.0, 1.0);
actor->GetProperty()->SetSpecular(0.51);
actor->GetProperty()->SetDiffuse(0.7);
actor->GetProperty()->SetAmbient(0.7);
actor->GetProperty()->SetSpecularPower(30.0);
actor->GetProperty()->SetOpacity(1.0);
renderer->AddActor(actor.Get());
//actor->GetProperty()->SetRepresentationToWireframe();
// add a plane
vtkNew<vtkPlaneSource> plane;
double *plybounds = mapper->GetBounds();
plane->SetOrigin(-0.2, plybounds[2], -0.2);
plane->SetPoint1( 0.2, plybounds[2], -0.2);
plane->SetPoint2(-0.2, plybounds[2], 0.2);
vtkNew<vtkPolyDataMapper> planeMapper;
planeMapper->SetInputConnection(plane->GetOutputPort());
vtkNew<vtkActor> planeActor;
planeActor->SetMapper(planeMapper.Get());
renderer->AddActor(planeActor.Get());
renderWindow->SetMultiSamples(0);
vtkNew<vtkOpaquePass> opaque;
vtkNew<vtkLightsPass> lights;
vtkNew<vtkSequencePass> opaqueSequence;
vtkNew<vtkRenderPassCollection> passes2;
passes2->AddItem(lights.Get());
passes2->AddItem(opaque.Get());
opaqueSequence->SetPasses(passes2.Get());
vtkNew<vtkCameraPass> opaqueCameraPass;
opaqueCameraPass->SetDelegatePass(opaqueSequence.Get());
vtkNew<vtkShadowMapBakerPass> bakerPass;
// bakerPass->SetOpaquePass(basicPasses->GetOpaquePass());
bakerPass->SetOpaquePass(opaqueCameraPass.Get());
bakerPass->SetResolution(1024);
// To cancel self-shadowing.
bakerPass->SetPolygonOffsetFactor(3.1f);
bakerPass->SetPolygonOffsetUnits(10.0f);
// tell the renderer to use our render pass pipeline
vtkOpenGLRenderer *glrenderer =
vtkOpenGLRenderer::SafeDownCast(renderer.GetPointer());
vtkNew<vtkShadowMapPass> shadows;
shadows->SetShadowMapBakerPass(bakerPass.Get());
shadows->SetOpaquePass(opaqueSequence.Get());
vtkNew<vtkSequencePass> seq;
vtkNew<vtkRenderPassCollection> passes;
passes->AddItem(bakerPass.Get());
passes->AddItem(shadows.Get());
passes->AddItem(lights.Get());
seq->SetPasses(passes.Get());
vtkNew<vtkCameraPass> cameraP;
cameraP->SetDelegatePass(seq.Get());
glrenderer->SetPass(cameraP.Get());
vtkNew<vtkTimerLog> timer;
timer->StartTimer();
renderWindow->Render();
timer->StopTimer();
double firstRender = timer->GetElapsedTime();
cerr << "first render time: " << firstRender << endl;
timer->StartTimer();
int numRenders = 8;
for (int i = 0; i < numRenders; ++i)
{
renderer->GetActiveCamera()->Azimuth(80.0/numRenders);
renderer->GetActiveCamera()->Elevation(80.0/numRenders);
renderWindow->Render();
}
timer->StopTimer();
double elapsed = timer->GetElapsedTime();
cerr << "interactive render time: " << elapsed / numRenders << endl;
unsigned int numTris = reader->GetOutput()->GetPolys()->GetNumberOfCells();
cerr << "number of triangles: " << numTris << endl;
cerr << "triangles per second: " << numTris*(numRenders/elapsed) << endl;
renderer->GetActiveCamera()->SetPosition(-0.2,0.2,1);
renderer->GetActiveCamera()->SetFocalPoint(0,0,0);
renderer->GetActiveCamera()->SetViewUp(0,1,0);
renderer->GetActiveCamera()->OrthogonalizeViewUp();
renderer->ResetCamera();
renderer->GetActiveCamera()->Zoom(2.5);
renderWindow->Render();
int retVal = vtkRegressionTestImage( renderWindow.Get() );
if ( retVal == vtkRegressionTester::DO_INTERACTOR)
{
iren->Start();
}
bakerPass->ReleaseGraphicsResources(renderWindow.Get());
return EXIT_SUCCESS;
}
......@@ -27,6 +27,9 @@ attribute vec4 vertexMC;
// optional normal declaration
//VTK::Normal::Dec
// extra lighting parameters
//VTK::Light::Dec
// Texture coordinates
//VTK::TCoord::Dec
......@@ -55,4 +58,6 @@ void main()
//VTK::PrimID::Impl
//VTK::PositionVC::Impl
//VTK::Light::Impl
}
......@@ -50,13 +50,15 @@
#include "vtkTransform.h"
#include "vtkUnsignedIntArray.h"
#include "vtkShadowMapPass.h"
// Bring in our fragment lit shader symbols.
#include "vtkPolyDataVS.h"
#include "vtkPolyDataFS.h"
#include "vtkPolyDataWideLineGS.h"
#include <algorithm>
#include <sstream>
//-----------------------------------------------------------------------------
......@@ -466,10 +468,93 @@ void vtkOpenGLPolyDataMapper::ReplaceShaderColor(
void vtkOpenGLPolyDataMapper::ReplaceShaderLight(
std::map<vtkShader::Type, vtkShader *> shaders,
vtkRenderer *, vtkActor *)
vtkRenderer *, vtkActor *actor)
{
std::string FSSource = shaders[vtkShader::Fragment]->GetSource();
// check for shadow maps
vtkInformation *info = actor->GetPropertyKeys();
std::string shadowFactor = "";
if (info && info->Has(vtkShadowMapPass::ShadowMapTextures()))
{
int *shadowMapTextures = 0;
int numLights = 0;
std::string VSSource = shaders[vtkShader::Vertex]->GetSource();
shadowMapTextures = info->Get(vtkShadowMapPass::ShadowMapTextures());
numLights = info->Length(vtkShadowMapPass::ShadowMapTextures());
// how many lights have shadow maps
int numSMT = 0;
for (int i = 0; i < numLights; i++)
{
if (shadowMapTextures[i] >= 0)
{
numSMT++;
}
}
std::ostringstream toString;
toString << numSMT;
vtkShaderProgram::Substitute(FSSource,"//VTK::Light::Dec",
"//VTK::Light::Dec\n"
"#define VTK_NUM_SHADOW_MAPS " + toString.str() + "\n"
"uniform sampler2DShadow shadowMaps[VTK_NUM_SHADOW_MAPS];\n"
"varying vec4 shadowCoordsVSOutput[VTK_NUM_SHADOW_MAPS];\n"
, false);
vtkShaderProgram::Substitute(VSSource,"//VTK::Light::Dec",
"//VTK::Light::Dec\n"
"#define VTK_NUM_SHADOW_MAPS " + toString.str() + "\n"
"uniform mat4 shadowTransforms[VTK_NUM_SHADOW_MAPS];\n"
"varying vec4 shadowCoordsVSOutput[VTK_NUM_SHADOW_MAPS];\n"
, false);
// build the code for the lighting factors
std::string lfc =
"float factors[6];\n";
for (int i = 0, numSMT = 0; i < 6; i++)
{
toString.str("");
toString.clear();
toString << i;
lfc += " factors[" + toString.str() + "] = 1.0;\n";
if (shadowMapTextures[i] >= 0 && i < numLights)
{
std::ostringstream toString2;
toString2 << numSMT;
lfc +=
" if(shadowCoordsVSOutput[" + toString2.str() + "].w > 0.0)\n"
" {\n"
" vec2 projected = shadowCoordsVSOutput[" + toString2.str() + "].xy/shadowCoordsVSOutput[" + toString2.str() + "].w;\n"
" if(projected.x >= 0.0 && projected.x <= 1.0\n"
" && projected.y >= 0.0 && projected.y <= 1.0)\n"
" {\n"
" factors[" + toString.str() + "] = textureProj(shadowMaps["
+ toString2.str() + "],shadowCoordsVSOutput[" + toString2.str() + "]);\n"
" }\n"
// " factors[" + toString.str() + "] = 0.0;\n"
" }\n";
numSMT++;
}
}
lfc += "//VTK::Light::Impl";
vtkShaderProgram::Substitute(FSSource,"//VTK::Light::Impl",
lfc.c_str(), false);
vtkShaderProgram::Substitute(VSSource,"//VTK::Light::Impl",
"//VTK::Light::Impl\n"
" for (int i = 0; i < VTK_NUM_SHADOW_MAPS; i++)\n"
" {\n"
" shadowCoordsVSOutput[i] = shadowTransforms[i]*vertexVCVSOutput;\n"
" }\n"
, false);
shaders[vtkShader::Vertex]->SetSource(VSSource);
shadowFactor = "*factors[lightNum]";
}
switch (this->LastLightComplexity)
{
case 0: // no lighting
......@@ -503,11 +588,11 @@ void vtkOpenGLPolyDataMapper::ReplaceShaderLight(
" for (int lightNum = 0; lightNum < numberOfLights; lightNum++)\n"
" {\n"
" float df = max(0.0, dot(normalVCVSOutput, -lightDirectionVC[lightNum]));\n"
" diffuse += (df * lightColor[lightNum]);\n"
" diffuse += ((df" + shadowFactor + ") * lightColor[lightNum]);\n"
" if (dot(normalVCVSOutput, lightDirectionVC[lightNum]) < 0.0)\n"
" {\n"
" float sf = pow( max(0.0, dot(lightHalfAngleVC[lightNum],normalVCVSOutput)), specularPower);\n"
" specular += (sf * lightColor[lightNum]);\n"
" specular += ((sf" + shadowFactor + ") * lightColor[lightNum]);\n"
" }\n"
" }\n"
" diffuse = diffuse * diffuseColor;\n"
......@@ -566,11 +651,11 @@ void vtkOpenGLPolyDataMapper::ReplaceShaderLight(
" }\n"
" }\n"
" float df = max(0.0, attenuation*dot(normalVCVSOutput, -vertLightDirectionVC));\n"
" diffuse += (df * lightColor[lightNum]);\n"
" diffuse += ((df" + shadowFactor + ") * lightColor[lightNum]);\n"
" if (dot(normalVCVSOutput, vertLightDirectionVC) < 0.0)\n"
" {\n"
" float sf = attenuation*pow( max(0.0, dot(lightHalfAngleVC[lightNum],normalVCVSOutput)), specularPower);\n"
" specular += (sf * lightColor[lightNum]);\n"
" specular += ((sf" + shadowFactor + ") * lightColor[lightNum]);\n"
" }\n"
" }\n"
" diffuse = diffuse * diffuseColor;\n"
......@@ -1393,8 +1478,10 @@ void vtkOpenGLPolyDataMapper::SetMapperShaderParameters(vtkOpenGLHelper &cellBO,
}
//-----------------------------------------------------------------------------
void vtkOpenGLPolyDataMapper::SetLightingShaderParameters(vtkOpenGLHelper &cellBO,
vtkRenderer* ren, vtkActor *vtkNotUsed(actor))
void vtkOpenGLPolyDataMapper::SetLightingShaderParameters(
vtkOpenGLHelper &cellBO,
vtkRenderer* ren,
vtkActor *actor)
{
// for unlit and headlight there are no lighting parameters
if (this->LastLightComplexity < 2 || this->DrawingEdges)
......@@ -1404,6 +1491,35 @@ void vtkOpenGLPolyDataMapper::SetLightingShaderParameters(vtkOpenGLHelper &cellB
vtkShaderProgram *program = cellBO.Program;
// check for shadow maps
vtkInformation *info = actor->GetPropertyKeys();
std::string shadowFactor = "";
if (info && info->Has(vtkShadowMapPass::ShadowMapTextures()))
{
int *shadowMapTextures = info->Get(vtkShadowMapPass::ShadowMapTextures());
double *shadowTransforms = info->Get(vtkShadowMapPass::ShadowMapTransforms());
int numLights = info->Length(vtkShadowMapPass::ShadowMapTextures());
// how many lights have shadow maps
int numSMT = 0;
int tunits[6];
float transforms[6*16];
for (int i = 0; i < numLights; i++)
{
if (shadowMapTextures[i] >= 0)
{
tunits[numSMT] = shadowMapTextures[i];
for (int i = 0; i < 16; i++)
{
transforms[numSMT*16+i] = shadowTransforms[numSMT*16+i];
}
numSMT++;
}
}
program->SetUniform1iv("shadowMaps", numSMT, tunits);
program->SetUniformMatrix4x4v("shadowTransforms", numSMT, transforms);
}
// for lightkit case there are some parameters to set
vtkCamera *cam = ren->GetActiveCamera();
vtkTransform* viewTF = cam->GetModelViewTransformObject();
......
......@@ -547,6 +547,14 @@ bool vtkShaderProgram::SetUniformMatrix3x3(const char *name,
bool vtkShaderProgram::SetUniformMatrix4x4(const char *name,
float *matrix)
{
return this->SetUniformMatrix4x4v(name,1,matrix);
}
bool vtkShaderProgram::SetUniformMatrix4x4v(
const char *name,
const int count,
float *matrix)
{
GLint location = static_cast<GLint>(this->FindUniform(name));
if (location == -1)
......@@ -554,7 +562,7 @@ bool vtkShaderProgram::SetUniformMatrix4x4(const char *name,
this->Error = "Could not set uniform " + std::string(name) + ". No such uniform.";
return false;