Commit b457301c authored by Ken Martin's avatar Ken Martin

Improve rendering shadows and DOF

Add an option in vtkLight to control how much a shadow
will attenuate the light.

Added a mode in the DOF pass where the focal depth is driven
by the center pixel.

Added a test of using SSAA with Shadows and with DOF all
at once.
parent 8509becf
vtk_add_test_cxx(${vtk-module}CxxTests tests
TestPDBBallAndStickShadows.cxx
TestPDBBallAndStickShadowsDOFSSAA.cxx
)
vtk_test_cxx_executable(${vtk-module}CxxTests tests
RENDERING_FACTORY
......
......@@ -38,6 +38,10 @@
#include "vtkOpenGLRenderer.h"
#include "vtkCameraPass.h"
#include "vtkRenderPassCollection.h"
#include "vtkSSAAPass.h"
#include "vtkLookupTable.h"
#include "vtkPeriodicTable.h"
#include "vtkTimerLog.h"
#include "vtkCamera.h"
......@@ -56,29 +60,39 @@ int TestPDBBallAndStickShadows(int argc, char *argv[])
vtkNew<vtkOpenGLMoleculeMapper> molmapper;
molmapper->SetInputConnection(reader->GetOutputPort(1));
cerr << "Class: " << molmapper->GetClassName() << endl;
cerr << "Atoms: " << molmapper->GetInput()->GetNumberOfAtoms() << endl;
cerr << "Bonds: " << molmapper->GetInput()->GetNumberOfBonds() << endl;
// molmapper->UseBallAndStickSettings();
molmapper->SetRenderBonds(false);
molmapper->SetAtomicRadiusType( vtkMoleculeMapper::VDWRadius );
molmapper->SetAtomicRadiusScaleFactor( 1.0 );
molmapper->SetAtomicRadiusScaleFactor( 0.9 );
molmapper->SetScalarMaterialModeToAmbientAndDiffuse();
// get the default lookup table and desaturate it to be
// more pleasing
vtkNew<vtkPeriodicTable> pt;
vtkNew<vtkLookupTable> lut;
pt->GetDefaultLUT(lut.Get());
const unsigned short numColors = lut->GetNumberOfColors();
double rgb[4];
for (vtkIdType i = 0; static_cast<unsigned int>(i) < numColors; ++i)
{
lut->GetTableValue(i, rgb);
lut->SetTableValue(i, 0.45 + rgb[0]*0.55,
0.45 + rgb[1]*0.55, 0.45 + rgb[2]*0.55);
}
molmapper->GetFastAtomMapper()->SetLookupTable(lut.Get());
vtkNew<vtkActor> actor;
actor->SetMapper(molmapper.GetPointer());
actor->GetProperty()->SetDiffuse(0.7);
actor->GetProperty()->SetSpecular(0.3);
actor->GetProperty()->SetSpecularPower(40);
molmapper->SetScalarMaterialModeToAmbientAndDiffuse();
// we override the default shader very slightly so that
// the ambient color component is scaled off the diffuse
molmapper->GetFastAtomMapper()->AddShaderReplacement(
vtkShader::Fragment, // in the fragment shader
"//VTK::Color::Impl",
true, // before the standard replacements
"//VTK::Color::Impl\n" // we still want the default
" ambientColor *= 0.3;\n", //but we add this
" ambientColor = diffuseColor*0.2;\n", //but we add this
false // only do it once
);
......@@ -91,10 +105,11 @@ int TestPDBBallAndStickShadows(int argc, char *argv[])
ren->AddActor(actor.GetPointer());
ren->ResetCamera();
ren->GetActiveCamera()->Zoom(1.7);
ren->SetBackground2(0.4, 0.5, 0.6);
ren->SetBackground(0.3, 0.3, 0.3);
ren->GetActiveCamera()->SetFocalDisk(ren->GetActiveCamera()->GetDistance()*0.05);
ren->SetBackground2(0.2, 0.2, 0.3);
ren->SetBackground(0.1, 0.1, 0.15);
ren->GradientBackgroundOn();
win->SetSize(450, 450);
win->SetSize(600, 600);
// add a plane
vtkNew<vtkPlaneSource> plane;
......@@ -110,16 +125,16 @@ int TestPDBBallAndStickShadows(int argc, char *argv[])
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.7);
light1->SetPosition(0, 0.9, 0.3);
light1->SetIntensity(0.5);
light1->SetShadowAttenuation(0.6);
ren->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.4);
light2->SetPosition(0.0,0.9,-0.3);
light2->SetIntensity(0.5);
light2->SetShadowAttenuation(0.6);
ren->AddLight(light2.Get());
vtkNew<vtkShadowMapPass> shadows;
......@@ -141,10 +156,20 @@ int TestPDBBallAndStickShadows(int argc, char *argv[])
// finally add the DOF passs
vtkNew<vtkDepthOfFieldPass> dofp;
dofp->AutomaticFocalDistanceOff();
dofp->SetDelegatePass(cameraP.Get());
// finally blur the resulting image
// The blur delegates rendering the unblured image
// to the basicPasses
vtkNew<vtkSSAAPass> ssaa;
ssaa->SetDelegatePass(dofp.Get());
//ssaa->SetDelegatePass(cameraP.Get());
// tell the renderer to use our render pass pipeline
// glrenderer->SetPass(ssaa.Get());
glrenderer->SetPass(dofp.Get());
// glrenderer->SetPass(cameraP.Get());
// tell the renderer to use our render pass pipeline
vtkNew<vtkTimerLog> timer;
......@@ -154,8 +179,7 @@ int TestPDBBallAndStickShadows(int argc, char *argv[])
double firstRender = timer->GetElapsedTime();
cerr << "first render time: " << firstRender << endl;
/*
int numRenders = 500;
int numRenders = 5;
timer->StartTimer();
for (int i = 0; i < numRenders; ++i)
{
......@@ -166,7 +190,6 @@ int TestPDBBallAndStickShadows(int argc, char *argv[])
timer->StopTimer();
double elapsed = timer->GetElapsedTime();
cerr << "interactive render time: " << elapsed / numRenders << endl;
*/
ren->GetActiveCamera()->SetPosition(0,0,1);
ren->GetActiveCamera()->SetFocalPoint(0,0,0);
......
/*=========================================================================
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.
=========================================================================*/
#include "vtkTestUtilities.h"
#include "vtkRegressionTestImage.h"
#include "vtkActor.h"
#include "vtkCamera.h"
#include "vtkMolecule.h"
#include "vtkLight.h"
#include "vtkOpenGLMoleculeMapper.h"
#include "vtkOpenGLSphereMapper.h"
#include "vtkNew.h"
#include "vtkProperty.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderWindow.h"
#include "vtkRenderer.h"
#include "vtkPDBReader.h"
#include "vtkPlaneSource.h"
#include "vtkPolyDataMapper.h"
#include "vtkDepthOfFieldPass.h"
#include "vtkRenderStepsPass.h"
#include "vtkSequencePass.h"
#include "vtkShadowMapBakerPass.h"
#include "vtkShadowMapPass.h"
#include "vtkOpenGLRenderer.h"
#include "vtkCameraPass.h"
#include "vtkRenderPassCollection.h"
#include "vtkSSAAPass.h"
#include "vtkLookupTable.h"
#include "vtkPeriodicTable.h"
#include "vtkTimerLog.h"
#include "vtkCamera.h"
int TestPDBBallAndStickShadowsDOFSSAA(int argc, char *argv[])
{
char* fileName =
vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/2LYZ.pdb");
// read protein from pdb
vtkNew<vtkPDBReader> reader;
reader->SetFileName(fileName);
reader->Update();
delete [] fileName;
vtkNew<vtkOpenGLMoleculeMapper> molmapper;
molmapper->SetInputConnection(reader->GetOutputPort(1));
molmapper->SetRenderBonds(false);
molmapper->SetAtomicRadiusType( vtkMoleculeMapper::VDWRadius );
molmapper->SetAtomicRadiusScaleFactor( 0.9 );
molmapper->SetScalarMaterialModeToAmbientAndDiffuse();
// get the default lookup table and desaturate it to be
// more pleasing
vtkNew<vtkPeriodicTable> pt;
vtkNew<vtkLookupTable> lut;
pt->GetDefaultLUT(lut.Get());
const unsigned short numColors = lut->GetNumberOfColors();
double rgb[4];
for (vtkIdType i = 0; static_cast<unsigned int>(i) < numColors; ++i)
{
lut->GetTableValue(i, rgb);
lut->SetTableValue(i, 0.45 + rgb[0]*0.55,
0.45 + rgb[1]*0.55, 0.45 + rgb[2]*0.55);
}
molmapper->GetFastAtomMapper()->SetLookupTable(lut.Get());
vtkNew<vtkActor> actor;
actor->SetMapper(molmapper.GetPointer());
actor->GetProperty()->SetDiffuse(0.7);
// we override the default shader very slightly so that
// the ambient color component is scaled off the diffuse
molmapper->GetFastAtomMapper()->AddShaderReplacement(
vtkShader::Fragment, // in the fragment shader
"//VTK::Color::Impl",
true, // before the standard replacements
"//VTK::Color::Impl\n" // we still want the default
" ambientColor = diffuseColor*0.2;\n", //but we add this
false // only do it once
);
vtkNew<vtkRenderer> ren;
vtkNew<vtkRenderWindow> win;
win->AddRenderer(ren.GetPointer());
vtkNew<vtkRenderWindowInteractor> iren;
iren->SetRenderWindow(win.GetPointer());
ren->AddActor(actor.GetPointer());
ren->ResetCamera();
ren->GetActiveCamera()->Zoom(1.7);
ren->GetActiveCamera()->SetFocalDisk(ren->GetActiveCamera()->GetDistance()*0.05);
ren->SetBackground2(0.2, 0.2, 0.3);
ren->SetBackground(0.1, 0.1, 0.15);
ren->GradientBackgroundOn();
win->SetSize(600, 600);
// add a plane
vtkNew<vtkPlaneSource> plane;
double *bounds = molmapper->GetBounds();
plane->SetOrigin(bounds[0], bounds[2], bounds[4]);
plane->SetPoint1(bounds[1], bounds[2], bounds[4]);
plane->SetPoint2(bounds[0], bounds[2], bounds[5]);
vtkNew<vtkPolyDataMapper> planeMapper;
planeMapper->SetInputConnection(plane->GetOutputPort());
vtkNew<vtkActor> planeActor;
planeActor->SetMapper(planeMapper.Get());
ren->AddActor(planeActor.Get());
vtkNew<vtkLight> light1;
light1->SetFocalPoint(0,0,0);
light1->SetPosition(-0.3, 0.9, 0.3);
light1->SetIntensity(0.5);
light1->SetShadowAttenuation(0.6);
ren->AddLight(light1.Get());
vtkNew<vtkLight> light2;
light2->SetFocalPoint(0,0,0);
light2->SetPosition(0.3,0.9,0.3);
light2->SetIntensity(0.5);
light2->SetShadowAttenuation(0.6);
ren->AddLight(light2.Get());
vtkNew<vtkShadowMapPass> shadows;
vtkNew<vtkSequencePass> seq;
vtkNew<vtkRenderPassCollection> passes;
passes->AddItem(shadows->GetShadowMapBakerPass());
passes->AddItem(shadows.Get());
seq->SetPasses(passes.Get());
vtkNew<vtkCameraPass> cameraP;
cameraP->SetDelegatePass(seq.Get());
// create the basic VTK render steps
vtkNew<vtkRenderStepsPass> basicPasses;
vtkOpenGLRenderer *glrenderer =
vtkOpenGLRenderer::SafeDownCast(ren.GetPointer());
// finally add the DOF passs
vtkNew<vtkDepthOfFieldPass> dofp;
//dofp->AutomaticFocalDistanceOff();
dofp->SetDelegatePass(cameraP.Get());
// finally blur the resulting image
// The blur delegates rendering the unblured image
// to the basicPasses
vtkNew<vtkSSAAPass> ssaa;
ssaa->SetDelegatePass(dofp.Get());
// tell the renderer to use our render pass pipeline
glrenderer->SetPass(ssaa.Get());
// glrenderer->SetPass(dofp.Get());
// glrenderer->SetPass(cameraP.Get());
// tell the renderer to use our render pass pipeline
vtkNew<vtkTimerLog> timer;
timer->StartTimer();
win->Render();
timer->StopTimer();
double firstRender = timer->GetElapsedTime();
cerr << "first render time: " << firstRender << endl;
// this example will suck the life out of your fragment shaders
// until we provide some optimizations. The DOF pass is a brute force
// approach which takes 81 tlookups per pixel. Combine that with
// 5x SSAA and you have around 400 texture lookups per final pixel
// we ust have everything on here to make sure it all works together.
// We will likely want to provide a second quality setting for the DOF
// pass that is designed to work with SSAA where we know we can tolerate more
// DOF noise as the SSAA will be averaging it anyhow.
int numRenders = 5;
timer->StartTimer();
for (int i = 0; i < numRenders; ++i)
{
ren->GetActiveCamera()->Azimuth(85.0/numRenders);
ren->GetActiveCamera()->Elevation(85.0/numRenders);
win->Render();
}
timer->StopTimer();
double elapsed = timer->GetElapsedTime();
cerr << "interactive render time: " << elapsed / numRenders << endl;
ren->GetActiveCamera()->SetPosition(0,0,1);
ren->GetActiveCamera()->SetFocalPoint(0,0,0);
ren->GetActiveCamera()->SetViewUp(0,1,0);
ren->ResetCamera();
ren->GetActiveCamera()->Elevation(40.0);
ren->GetActiveCamera()->Zoom(2.0);
win->Render();
// Finally render the scene and compare the image to a reference image
win->SetMultiSamples(0);
win->GetInteractor()->Initialize();
int retVal = vtkRegressionTestImage( win.Get() );
if ( retVal == vtkRegressionTester::DO_INTERACTOR)
{
iren->Start();
}
return !retVal;
}
......@@ -63,6 +63,8 @@ vtkLight::vtkLight()
this->LightType = VTK_LIGHT_TYPE_SCENE_LIGHT;
this->TransformMatrix = NULL;
this->ShadowAttenuation = 1.0;
}
vtkLight::~vtkLight()
......@@ -291,6 +293,7 @@ void vtkLight::PrintSelf(ostream& os, vtkIndent indent)
{
os << "(none)\n";
}
os << indent << "ShadowAttenuation: " << this->ShadowAttenuation << "\n";
}
void vtkLight::WriteSelf(ostream& os)
......@@ -313,6 +316,7 @@ void vtkLight::WriteSelf(ostream& os)
os << this->ConeAngle << " ";
os << this->AttenuationValues[0] << " " << this->AttenuationValues[1] << " "
<< this->AttenuationValues[2] << " ";
os << this->ShadowAttenuation << " ";
// XXX - LightType, TransformMatrix ???
}
......@@ -330,6 +334,7 @@ void vtkLight::ReadSelf(istream& is)
is >> this->ConeAngle;
is >> this->AttenuationValues[0] >> this->AttenuationValues[1]
>> this->AttenuationValues[2];
is >> this->ShadowAttenuation;
// XXX - LightType, TransformMatrix ???
}
......
......@@ -220,6 +220,15 @@ public:
void ReadSelf(istream& is);
void WriteSelf(ostream& os);
// Description:
// Set/Get the shadow intensity
// By default a light will be completely blocked when in shadow
// by setting this value to less than 1.0 you can control how much
// light is attenuated when in shadow
vtkSetMacro(ShadowAttenuation,float);
vtkGetMacro(ShadowAttenuation,float);
protected:
vtkLight();
~vtkLight();
......@@ -239,6 +248,7 @@ protected:
double TransformedFocalPointReturn[3];
double TransformedPositionReturn[3];
int LightType;
float ShadowAttenuation;
private:
vtkLight(const vtkLight&); // Not implemented.
......
......@@ -106,6 +106,7 @@ int TestDepthOfFieldPass(int argc, char* argv[])
// finally add the DOF passs
vtkNew<vtkDepthOfFieldPass> dofp;
dofp->SetDelegatePass(basicPasses.Get());
dofp->AutomaticFocalDistanceOff();
// tell the renderer to use our render pass pipeline
glrenderer->SetPass(dofp.Get());
......
......@@ -23,9 +23,10 @@ uniform sampler2D depth;
uniform vec2 worldToTCoord;
uniform vec2 pixelToTCoord;
uniform float focalDistVC;
uniform float CoCScale;
uniform float CoCBias;
uniform float nearC;
uniform float farC;
uniform float focalDisk;
uniform float focalDistance;
// the output of this shader
//VTK::Output::Dec
......@@ -48,6 +49,16 @@ void main(void)
vec4 fcolor = texture2D(source,tcoordVC);
float fsum = 1.0;
float fdist = focalDistance;
// use automatic focalDistance? when focalDistance = 0
if (fdist == 0.0)
{
fdist = -farC * nearC / (texture2D(depth,vec2(0.5,0.5)).r * (farC - nearC) - farC);
}
float CoCScale = focalDisk*fdist*(farC - nearC)/(farC*nearC);
float CoCBias = focalDisk*(nearC - fdist)/nearC;
float cdepth = texture2D(depth,tcoordVC).r;
float CoC = CoCScale*cdepth + CoCBias;
......
......@@ -45,6 +45,7 @@ vtkDepthOfFieldPass::vtkDepthOfFieldPass()
this->Supported=false;
this->SupportProbed=false;
this->BlurProgram = NULL;
this->AutomaticFocalDistance = true;
}
// ----------------------------------------------------------------------------
......@@ -254,17 +255,11 @@ void vtkDepthOfFieldPass::Render(const vtkRenderState *s)
this->Pass1Depth->Activate();
this->BlurProgram->Program->SetUniformi("depth",this->Pass1Depth->GetTextureUnit());
// CoCScale = (aperture * focallength * planeinfocus * (zfar - znear)) /
// ((planeinfocus - focallength) * znear * zfar)
// CoCBias = (aperture * focallength * (znear - planeinfocus)) /
// ((planeinfocus - focallength) * znear)
vtkCamera *cam = r->GetActiveCamera();
double *frange = cam->GetClippingRange();
float fdist = cam->GetDistance();
// fdist = fdist - 0.1*(frange[1] - frange[0]);
float focalDisk = cam->GetFocalDisk();
float CoCScale = focalDisk*fdist*(frange[1] - frange[0])/(frange[1]*frange[0]);
float CoCBias = focalDisk*(frange[0] - fdist)/frange[0];
float vAngle = cam->GetViewAngle();
double *aspect = r->GetAspect();
......@@ -288,10 +283,18 @@ void vtkDepthOfFieldPass::Render(const vtkRenderState *s)
offset[0] = 1.0/w;
offset[1] = 1.0/h;
this->BlurProgram->Program->SetUniform2f("pixelToTCoord", offset);
// this is a factor of the lense size
this->BlurProgram->Program->SetUniformf("focalDistVC", fdist);
this->BlurProgram->Program->SetUniformf("CoCScale", CoCScale);
this->BlurProgram->Program->SetUniformf("CoCBias", CoCBias);
this->BlurProgram->Program->SetUniformf("nearC",frange[0]);
this->BlurProgram->Program->SetUniformf("farC",frange[1]);
this->BlurProgram->Program->SetUniformf("focalDisk",focalDisk);
if (this->AutomaticFocalDistance)
{
this->BlurProgram->Program->SetUniformf("focalDistance",0.0);
}
else
{
this->BlurProgram->Program->SetUniformf("focalDistance",fdist);
}
this->Pass1->CopyToFrameBuffer(extraPixels, extraPixels,
w-1-extraPixels,h-1-extraPixels,
......
......@@ -50,6 +50,14 @@ public:
vtkTypeMacro(vtkDepthOfFieldPass,vtkDepthImageProcessingPass);
void PrintSelf(ostream& os, vtkIndent indent);
// Description:
// Use automatic focal distance calculation, this is on by default
// When on the center of the viewport will always be in focus
// regardless of where the focal point is.
vtkSetMacro(AutomaticFocalDistance,bool);
vtkGetMacro(AutomaticFocalDistance,bool);
vtkBooleanMacro(AutomaticFocalDistance,bool);
//BTX
// Description:
// Perform rendering according to a render state \p s.
......@@ -84,6 +92,8 @@ public:
bool Supported;
bool SupportProbed;
bool AutomaticFocalDistance;
private:
vtkDepthOfFieldPass(const vtkDepthOfFieldPass&); // Not implemented.
void operator=(const vtkDepthOfFieldPass&); // Not implemented.
......
......@@ -199,6 +199,8 @@ void vtkShadowMapPass::Render(const vtkRenderState *s)
vtkLightCollection *lights=r->GetLights();
this->ShadowTextureUnits.clear();
this->ShadowTextureUnits.resize(lights->GetNumberOfItems());
this->ShadowAttenuation.clear();
this->ShadowAttenuation.resize(lights->GetNumberOfItems());
// get the shadow maps and activate them
int shadowingLightIndex = 0;
......@@ -217,6 +219,7 @@ void vtkShadowMapPass::Render(const vtkRenderState *s)
// activate the texture map
map->Activate();
this->ShadowTextureUnits[lightIndex] = map->GetTextureUnit();
this->ShadowAttenuation[lightIndex] = light->GetShadowAttenuation();
shadowingLightIndex++;
}
}
......@@ -335,6 +338,7 @@ void vtkShadowMapPass::SetUniforms(vtkShaderProgram *program)
int numSMT = 0;
float transform[16];
std::ostringstream toString;
for (size_t i = 0; i < numLights; i++)
{
if (this->ShadowTextureUnits[i] >= 0)
......@@ -346,6 +350,9 @@ void vtkShadowMapPass::SetUniforms(vtkShaderProgram *program)
toString.str("");
toString.clear();
toString << numSMT;
program->SetUniformf(
std::string("shadowAttenuation"+toString.str()).c_str(),
this->ShadowAttenuation[i]);
program->SetUniformi(
std::string("shadowMap"+toString.str()).c_str(),
this->ShadowTextureUnits[i]);
......@@ -377,7 +384,10 @@ void vtkShadowMapPass::BuildShaderCode()
toString << this->ShadowMapBakerPass->GetResolution();
std::string fdec = "//VTK::Light::Dec\n"
"float calcShadow(in vec4 vert, in sampler2D shadowMap, in mat4 shadowTransform)\n"
"float calcShadow(in vec4 vert,\n"
" in sampler2D shadowMap,\n"
" in mat4 shadowTransform,\n"
" in float attenuation)\n"
"{\n"
" vec4 shadowCoord = shadowTransform*vert;\n"
" float result = 1.0;\n"
......@@ -403,7 +413,8 @@ void vtkShadowMapPass::BuildShaderCode()
" result /= 4.0;\n"
" }\n"
" }\n"
" return result;\n"
" return (1.0 - attenuation + attenuation*result);\n"
// " return result;\n"
"}\n";
for (int i = 0; i < numSMT; i++)
......@@ -412,6 +423,7 @@ void vtkShadowMapPass::BuildShaderCode()
toString.clear();
toString << i;
fdec +=
"uniform float shadowAttenuation" + toString.str() + ";\n"
"uniform sampler2D shadowMap" + toString.str() + ";\n"
"uniform mat4 shadowTransform" + toString.str() + ";\n";
}
......@@ -429,7 +441,9 @@ void vtkShadowMapPass::BuildShaderCode()
{
std::ostringstream toString2;
toString2 << numSMT;
fimpl += "calcShadow(vertexVC, shadowMap" +toString2.str() + ", shadowTransform" + toString2.str() + ");\n";
fimpl += "calcShadow(vertexVC, shadowMap" +toString2.str() +
", shadowTransform" + toString2.str() +
", shadowAttenuation" + toString2.str() +");\n";
numSMT++;
}
else
......
......@@ -167,6 +167,7 @@ public:
std::string FragmentImplementation;
std::vector<int> ShadowTextureUnits;
std::vector<double> ShadowTransforms;
std::vector<float> ShadowAttenuation;
private:
vtkShadowMapPass(const vtkShadowMapPass&); // Not implemented.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment