diff --git a/Documentation/release/dev/fix-shadowmap-light.md b/Documentation/release/dev/fix-shadowmap-light.md new file mode 100644 index 0000000000000000000000000000000000000000..e22d1d3d77878b465290c7f58cce2060e42fa43d --- /dev/null +++ b/Documentation/release/dev/fix-shadowmap-light.md @@ -0,0 +1,9 @@ +## Fix light transforms when computing shadow maps + +The `vtkShadowMapBakerPass` computes shadow maps for each light by simulating a `vtkCamera` for each +light and compositing the rendered images. However, an unintentional side-effect of this was that +the original light's transforms were being affected by this. In other words, the lights would be +corrupt after the shadow map computation. To circumvent this, we cache the light +transforms prior to the shadow map computation and then reset them after. + + diff --git a/Rendering/OpenGL2/Testing/Cxx/TestShadowMapBakerPass.cxx b/Rendering/OpenGL2/Testing/Cxx/TestShadowMapBakerPass.cxx index 27c1f1a8b849287c0458ca049cdf697b5e6935ec..8c5dc323ab10c80f8780bab4c8cb2b4ebfe9af28 100644 --- a/Rendering/OpenGL2/Testing/Cxx/TestShadowMapBakerPass.cxx +++ b/Rendering/OpenGL2/Testing/Cxx/TestShadowMapBakerPass.cxx @@ -51,7 +51,6 @@ int TestShadowMapBakerPass(int argc, char* argv[]) delete[] fileName; mapper->SetInputConnection(reader->GetOutputPort()); - // mapper->SetInputConnection(norms->GetOutputPort()); actor->SetMapper(mapper); actor->GetProperty()->SetAmbientColor(0.2, 0.2, 1.0); actor->GetProperty()->SetDiffuseColor(1.0, 0.65, 0.7); diff --git a/Rendering/OpenGL2/Testing/Cxx/TestShadowMapPass.cxx b/Rendering/OpenGL2/Testing/Cxx/TestShadowMapPass.cxx index 4c6d8d620fe475c553b43eb145d92babd26ee207..2b82e9a9c8d409d3c9f84c45306a0cf1274c454d 100644 --- a/Rendering/OpenGL2/Testing/Cxx/TestShadowMapPass.cxx +++ b/Rendering/OpenGL2/Testing/Cxx/TestShadowMapPass.cxx @@ -41,20 +41,29 @@ int TestShadowMapPass(int argc, char* argv[]) vtkNew<vtkRenderWindowInteractor> iren; iren->SetRenderWindow(renderWindow); + // Add a couple of additional lights vtkNew<vtkLight> light1; - light1->SetFocalPoint(0, 0, 0); + light1->SetFocalPoint(1, 0, 1); light1->SetPosition(0, 1, 0.2); light1->SetColor(0.95, 0.97, 1.0); light1->SetIntensity(0.8); renderer->AddLight(light1); vtkNew<vtkLight> light2; - light2->SetFocalPoint(0, 0, 0); - light2->SetPosition(1.0, 1.0, 1.0); + light2->SetFocalPoint(0, 0, 1); + light2->SetPosition(0.2, 0.5, 0.5); light2->SetColor(1.0, 0.8, 0.7); - light2->SetIntensity(0.3); + light2->SetIntensity(0.5); renderer->AddLight(light2); + vtkNew<vtkLight> light3; + light3->SetFocalPoint(-0.1, -0.5, -0.5); + light3->SetPosition(0.2, 0.5, 0.5); + light3->SetColor(1.0, 0.8, 0.7); + light3->SetPositional(true); + light3->SetIntensity(0.3); + renderer->AddLight(light3); + const char* fileName = vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/dragon.ply"); vtkNew<vtkPLYReader> reader; reader->SetFileName(fileName); diff --git a/Rendering/OpenGL2/Testing/Data/Baseline/TestShadowMapBakerPass.png.sha512 b/Rendering/OpenGL2/Testing/Data/Baseline/TestShadowMapBakerPass.png.sha512 index 0767af628c04d84c37dd774f1bac3026c9bf22b7..bb16a9c29dff48e34f8f8051c310bece5cd06b9b 100644 --- a/Rendering/OpenGL2/Testing/Data/Baseline/TestShadowMapBakerPass.png.sha512 +++ b/Rendering/OpenGL2/Testing/Data/Baseline/TestShadowMapBakerPass.png.sha512 @@ -1 +1 @@ -8e1cdfe8edc9fbe88c027fca7521e8f6c2dadb16d9b3c99baef2b093bfb42fc3aa56e251f3d5b02ee66a67f5c86cf4e92a9b0901cfb4cbd9e58f04b7f5e1a333 +a33be4c89e9eb6a282ac4ed7b14d3cd9fa8b90f3e493937c1c022faddfa678a5efb466b48f0951a1f880325f0234cfbd99c0c1c64d4a410eb4e64c8888a9f29c diff --git a/Rendering/OpenGL2/Testing/Data/Baseline/TestShadowMapPass.png.sha512 b/Rendering/OpenGL2/Testing/Data/Baseline/TestShadowMapPass.png.sha512 index 19128806d8104609b9d73cec0932b48e882f8e40..f83f09d5228a056f684cdfb26530e468a7ce70c4 100644 --- a/Rendering/OpenGL2/Testing/Data/Baseline/TestShadowMapPass.png.sha512 +++ b/Rendering/OpenGL2/Testing/Data/Baseline/TestShadowMapPass.png.sha512 @@ -1 +1 @@ -e9402f528869f5f3e6367b479e062aa1cfa63a781697f45fc89efd23cf6feb8a5e12a0b7ff1ebcd1b751515eca38945c710afc30ba225b847b3d7f851d270f06 +9412ed54080c7a972d786a269538a3dbd56a5aa070f2b35ba448dfd3cca211e7e4b4d196e07c8ff2d56ff4bb7b0855e5584758c09457d3d6f3c760ee47a4dded diff --git a/Rendering/OpenGL2/vtkShadowMapBakerPass.cxx b/Rendering/OpenGL2/vtkShadowMapBakerPass.cxx index 9ca97a4bd619c467fd427e86e8bc0c86f2b65eea..dfc4ec742e18ab2955e1d6a3b5a8a4229ad58676 100644 --- a/Rendering/OpenGL2/vtkShadowMapBakerPass.cxx +++ b/Rendering/OpenGL2/vtkShadowMapBakerPass.cxx @@ -10,6 +10,7 @@ #include "vtkLightCollection.h" #include "vtkLightsPass.h" #include "vtkMath.h" +#include "vtkMatrix4x4.h" #include "vtkNew.h" #include "vtkObjectFactory.h" #include "vtkOpaquePass.h" @@ -453,6 +454,29 @@ void vtkShadowMapBakerPass::Render(const vtkRenderState* s) first = false; } + // Rendering a map for each light requires creating a camera from that + // light's perspective and doing an opaque rendering pass. That opaque + // rendering pass, in turn, updates the light transforms relative to that + // particular light camera. When it comes time to create a camera for + // a subsequent light, we need to restore it to its original light + // transform. We cache them here so we can restore them later. + std::map<vtkLight*, vtkSmartPointer<vtkMatrix4x4>> cachedLightTransforms; + lights->InitTraversal(); + l = lights->GetNextItem(); + while (l != nullptr) + { + if (!l->GetSwitch() || !this->LightCreatesShadow(l) || l->GetTransformMatrix() == nullptr) + { + cachedLightTransforms[l] = nullptr; + } + else + { + cachedLightTransforms[l] = vtkNew<vtkMatrix4x4>(); + cachedLightTransforms[l]->DeepCopy(l->GetTransformMatrix()); + } + l = lights->GetNextItem(); + } + lights->InitTraversal(); l = lights->GetNextItem(); this->CurrentLightIndex = 0; @@ -464,6 +488,19 @@ void vtkShadowMapBakerPass::Render(const vtkRenderState* s) { if (l->GetSwitch() && this->LightCreatesShadow(l)) { + // Restore the light's original matrix. + vtkMatrix4x4* cachedTransform = cachedLightTransforms.at(l); + if (cachedTransform != nullptr) + { + // Restore values without tweaking modified time. + vtkMatrix4x4::DeepCopy(*l->GetTransformMatrix()->Element, *cachedTransform->Element); + } + else + { + // This may be redundant; it is unlikely if the light didn't + // originally have a transform that it would pick one up. + l->SetTransformMatrix(nullptr); + } vtkTextureObject* map = (*this->ShadowMaps)[this->CurrentLightIndex]; if (map == nullptr) {