vtkShadowMapPass.cxx 13.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkShadowMapPass.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 "vtkShadowMapPass.h"
#include "vtkObjectFactory.h"

Ken Martin's avatar
Ken Martin committed
19 20
#include "vtkAbstractTransform.h" // for helper classes stack and concatenation
#include "vtkCamera.h"
21
#include "vtkOpenGLFramebufferObject.h"
Ken Martin's avatar
Ken Martin committed
22 23 24
#include "vtkImageData.h"
#include "vtkImplicitSum.h"
#include "vtkInformation.h"
25
#include "vtkInformationObjectBaseKey.h"
26
#include "vtkLight.h"
Ken Martin's avatar
Ken Martin committed
27 28 29 30 31 32 33 34 35 36
#include "vtkLightCollection.h"
#include "vtkLightsPass.h"
#include "vtkMath.h"
#include "vtkMatrixToLinearTransform.h"
#include "vtkNew.h"
#include "vtkOpaquePass.h"
#include "vtkOpenGLCamera.h"
#include "vtkOpenGLError.h"
#include "vtkOpenGLRenderWindow.h"
#include "vtkOpenGLRenderer.h"
37
#include "vtkPerspectiveTransform.h"
Ken Martin's avatar
Ken Martin committed
38 39 40
#include "vtkRenderPassCollection.h"
#include "vtkRenderState.h"
#include "vtkSequencePass.h"
41
#include "vtkShaderProgram.h"
Ken Martin's avatar
Ken Martin committed
42 43 44 45 46
#include "vtkShadowMapBakerPass.h"
#include "vtkTextureObject.h"
#include "vtkTextureUnitManager.h"
#include "vtkTransform.h"
#include "vtk_glew.h"
47 48 49 50

// debugging
#include "vtkTimerLog.h"

Ken Martin's avatar
Ken Martin committed
51
#include <cassert>
52
#include <sstream>
Ken Martin's avatar
Ken Martin committed
53 54 55 56 57 58 59 60 61
#include "vtkStdString.h"


// to be able to dump intermediate passes into png files for debugging.
// only for vtkShadowMapPass developers.
//#define VTK_SHADOW_MAP_PASS_DEBUG
//#define DONT_DUPLICATE_LIGHTS


62 63 64
vtkStandardNewMacro(vtkShadowMapPass);
vtkCxxSetObjectMacro(vtkShadowMapPass,ShadowMapBakerPass,
                     vtkShadowMapBakerPass);
Ken Martin's avatar
Ken Martin committed
65
vtkCxxSetObjectMacro(vtkShadowMapPass,OpaqueSequence,
66 67
                     vtkRenderPass);

68
vtkInformationKeyMacro(vtkShadowMapPass,ShadowMapPass,ObjectBase);
69 70 71 72 73 74


// ----------------------------------------------------------------------------
vtkShadowMapPass::vtkShadowMapPass()
{
  this->ShadowMapBakerPass=0;
Ken Martin's avatar
Ken Martin committed
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89

  vtkNew<vtkSequencePass> seqP;
  vtkNew<vtkLightsPass> lightP;
  vtkNew<vtkOpaquePass> opaqueP;
  vtkNew<vtkRenderPassCollection> rpc;
  rpc->AddItem(lightP.Get());
  rpc->AddItem(opaqueP.Get());
  seqP->SetPasses(rpc.Get());

  this->OpaqueSequence=0;
  this->SetOpaqueSequence(seqP.Get());

  vtkNew<vtkShadowMapBakerPass> bp;
  this->ShadowMapBakerPass = 0;
  this->SetShadowMapBakerPass(bp.Get());
90 91 92 93 94 95
}

// ----------------------------------------------------------------------------
vtkShadowMapPass::~vtkShadowMapPass()
{
  if(this->ShadowMapBakerPass!=0)
96
  {
97
    this->ShadowMapBakerPass->Delete();
98
  }
Ken Martin's avatar
Ken Martin committed
99
  if(this->OpaqueSequence!=0)
100
  {
Ken Martin's avatar
Ken Martin committed
101
    this->OpaqueSequence->Delete();
102
  }
103 104 105 106 107 108 109 110 111
}

// ----------------------------------------------------------------------------
void vtkShadowMapPass::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);

  os << indent << "ShadowMapBackerPass: ";
  if(this->ShadowMapBakerPass!=0)
112
  {
113
    this->ShadowMapBakerPass->PrintSelf(os,indent);
114
  }
115
  else
116
  {
117
    os << "(none)" <<endl;
118
  }
Ken Martin's avatar
Ken Martin committed
119 120
  os << indent << "OpaqueSequence: ";
  if(this->OpaqueSequence!=0)
121
  {
Ken Martin's avatar
Ken Martin committed
122
    this->OpaqueSequence->PrintSelf(os,indent);
123
  }
124
  else
125
  {
126
    os << "(none)" <<endl;
127
  }
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
}

// ----------------------------------------------------------------------------
// Description:
// Perform rendering according to a render state \p s.
// \pre s_exists: s!=0
void vtkShadowMapPass::Render(const vtkRenderState *s)
{
  assert("pre: s_exists" && s!=0);

  vtkOpenGLClearErrorMacro();

  this->NumberOfRenderedProps=0;

  vtkOpenGLRenderer *r = static_cast<vtkOpenGLRenderer *>(s->GetRenderer());
  vtkOpenGLCamera *cam = static_cast<vtkOpenGLCamera *>(r->GetActiveCamera());
  vtkOpenGLRenderWindow *context = static_cast<vtkOpenGLRenderWindow *>(
    r->GetRenderWindow());

  if(this->ShadowMapBakerPass != 0 &&
Ken Martin's avatar
Ken Martin committed
148
     this->OpaqueSequence != 0)
149
  {
150
     // Test for Hardware support. If not supported, just render the delegate.
151
    bool supported=vtkOpenGLFramebufferObject::IsSupported(context);
152 153

    if(!supported)
154
    {
155
      vtkErrorMacro("FBOs are not supported by the context. Cannot use shadow mapping.");
Ken Martin's avatar
Ken Martin committed
156
      this->OpaqueSequence->Render(s);
157
      this->NumberOfRenderedProps+=
Ken Martin's avatar
Ken Martin committed
158
        this->OpaqueSequence->GetNumberOfRenderedProps();
159
      return;
160
    }
161 162

    if(!this->ShadowMapBakerPass->GetHasShadows())
163
    {
Ken Martin's avatar
Ken Martin committed
164
      this->OpaqueSequence->Render(s);
165
      this->NumberOfRenderedProps+=
Ken Martin's avatar
Ken Martin committed
166
        this->OpaqueSequence->GetNumberOfRenderedProps();
167
      return;
168
    }
169 170

    vtkLightCollection *lights=r->GetLights();
171 172
    this->ShadowTextureUnits.clear();
    this->ShadowTextureUnits.resize(lights->GetNumberOfItems());
173 174
    this->ShadowAttenuation.clear();
    this->ShadowAttenuation.resize(lights->GetNumberOfItems());
175 176 177 178 179 180 181

    // get the shadow maps and activate them
    int shadowingLightIndex = 0;
    int lightIndex = 0;
    vtkLight *light = 0;
    for (lights->InitTraversal(), light = lights->GetNextItem();
          light != 0; light = lights->GetNextItem(), lightIndex++)
182
    {
183
      this->ShadowTextureUnits[lightIndex] = -1;
184 185
      if(light->GetSwitch() &&
         this->ShadowMapBakerPass->LightCreatesShadow(light) )
186
      {
187
        vtkTextureObject *map=
Ken Martin's avatar
Ken Martin committed
188
          (*this->ShadowMapBakerPass->GetShadowMaps())[
189 190 191
            static_cast<size_t>(shadowingLightIndex)];
        // activate the texture map
        map->Activate();
192
        this->ShadowTextureUnits[lightIndex] = map->GetTextureUnit();
193
        this->ShadowAttenuation[lightIndex] = light->GetShadowAttenuation();
194 195
        shadowingLightIndex++;
      }
196
    }
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220

    vtkMatrix4x4 *tmp = vtkMatrix4x4::New();
    vtkMatrix4x4 *mat = vtkMatrix4x4::New();
    vtkPerspectiveTransform *transform = vtkPerspectiveTransform::New();

    vtkMatrix4x4 *wcdc;
    vtkMatrix4x4 *wcvc;
    vtkMatrix3x3 *norms;
    vtkMatrix4x4 *vcdc;
    cam->GetKeyMatrices(r,wcvc,norms,vcdc,wcdc);

    mat->DeepCopy(wcvc);
    mat->Transpose();
    mat->Invert();

    vtkMatrixToLinearTransform *viewCamera_Inv
      = vtkMatrixToLinearTransform::New();
    viewCamera_Inv->SetInput(mat);
    mat->Delete();

    // identity. pre-multiply mode
    transform->Translate(0.5,0.5,0.5); // bias
    transform->Scale(0.5,0.5,0.5); // scale

221
    this->ShadowTransforms.clear();
222 223 224
    shadowingLightIndex = 0;
    for (lights->InitTraversal(), light = lights->GetNextItem(), lightIndex = 0;
          light != 0; light = lights->GetNextItem(), lightIndex++)
225
    {
226
      if (this->ShadowTextureUnits[lightIndex] >= 0)
227
      {
228
        vtkCamera *lightCamera=
Ken Martin's avatar
Ken Martin committed
229
          (*this->ShadowMapBakerPass->GetLightCameras())[
230 231 232 233 234 235 236 237 238
          static_cast<size_t>(shadowingLightIndex)];
        transform->Push();
        transform->Concatenate(
          lightCamera->GetProjectionTransformObject(1,-1,1));
        transform->Concatenate(lightCamera->GetViewTransformObject());
        transform->Concatenate(viewCamera_Inv);
        transform->GetMatrix(tmp);
        transform->Pop();
        tmp->Transpose();
239
        for (int i = 0; i < 4; i++)
240
        {
241
          for (int j = 0; j < 4; j++)
242
          {
243
            this->ShadowTransforms.push_back(tmp->Element[i][j]);
244 245
          }
        }
246
        ++shadowingLightIndex;
247
      }
248
    }
249

250 251 252
    // build the shader code
    this->BuildShaderCode();

253 254 255
    // set the prop keys
    int c = s->GetPropArrayCount();
    for (int i = 0; i < c; i++)
256
    {
257 258 259
      vtkProp *p=s->GetPropArray()[i];
      vtkInformation *info = p->GetPropertyKeys();
      if (!info)
260
      {
261 262 263 264
        info = vtkInformation::New();
        p->SetPropertyKeys(info);
        info->Delete();
      }
265 266
      info->Set(vtkShadowMapPass::ShadowMapPass(), this);
    }
267 268 269 270 271 272 273

    viewCamera_Inv->Delete();
    transform->Delete();
    tmp->Delete();

    // render with shadows
    // note this time we use the list of props after culling.
Ken Martin's avatar
Ken Martin committed
274
    this->OpaqueSequence->Render(s);
275
    this->NumberOfRenderedProps+=
Ken Martin's avatar
Ken Martin committed
276
      this->OpaqueSequence->GetNumberOfRenderedProps();
277 278 279 280 281

    // now deactivate the shadow maps
    shadowingLightIndex = 0;
    for (lights->InitTraversal(), light = lights->GetNextItem(), lightIndex = 0;
          light != 0; light = lights->GetNextItem(), lightIndex++)
282
    {
283 284
      if(light->GetSwitch() &&
         this->ShadowMapBakerPass->LightCreatesShadow(light) )
285
      {
286
        vtkTextureObject *map=
Ken Martin's avatar
Ken Martin committed
287
          (*this->ShadowMapBakerPass->GetShadowMaps())[
288 289 290 291 292 293
            static_cast<size_t>(shadowingLightIndex)];
        // activate the texture map
        map->Deactivate();
        shadowingLightIndex++;
      }
    }
294 295

  }
296
  else
297
  {
Ken Martin's avatar
Ken Martin committed
298
    vtkWarningMacro(<<" no ShadowMapBakerPass or no OpaqueSequence on the ShadowMapBakerPass.");
299
  }
300 301 302 303

  vtkOpenGLCheckErrorMacro("failed after Render");
}

304 305 306 307 308 309 310 311
void vtkShadowMapPass::SetUniforms(vtkShaderProgram *program)
{
  size_t numLights = this->ShadowTextureUnits.size();

  // how many lights have shadow maps
  int numSMT = 0;
  float transform[16];
  std::ostringstream toString;
312

313
  for (size_t i = 0; i < numLights; i++)
314
  {
315
    if (this->ShadowTextureUnits[i] >= 0)
316
    {
317
      for (int j = 0; j < 16; j++)
318
      {
319
        transform[j] = this->ShadowTransforms[numSMT*16 + j];
320
      }
321 322 323
      toString.str("");
      toString.clear();
      toString << numSMT;
324 325 326
      program->SetUniformf(
        std::string("shadowAttenuation"+toString.str()).c_str(),
        this->ShadowAttenuation[i]);
327 328 329 330 331 332 333 334
      program->SetUniformi(
        std::string("shadowMap"+toString.str()).c_str(),
        this->ShadowTextureUnits[i]);
      program->SetUniformMatrix4x4(
        std::string("shadowTransform"+toString.str()).c_str(),
        transform);
      numSMT++;
    }
335
  }
336 337 338 339 340 341 342 343 344
}

void vtkShadowMapPass::BuildShaderCode()
{
  size_t numLights = this->ShadowTextureUnits.size();

  // count how many lights have shadow maps
  int numSMT = 0;
  for (size_t i = 0; i < numLights; i++)
345
  {
346
    if (this->ShadowTextureUnits[i] >= 0)
347
    {
348 349
      numSMT++;
    }
350
  }
351 352 353 354 355 356 357

  std::ostringstream toString;
  toString.str("");
  toString.clear();
  toString << this->ShadowMapBakerPass->GetResolution();

  std::string fdec = "//VTK::Light::Dec\n"
358 359 360 361
    "float calcShadow(in vec4 vert,\n"
    "                  in sampler2D shadowMap,\n"
    "                  in mat4 shadowTransform,\n"
    "                  in float attenuation)\n"
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
    "{\n"
    "  vec4 shadowCoord = shadowTransform*vert;\n"
    "  float result = 1.0;\n"
    "  if(shadowCoord.w > 0.0)\n"
    "    {\n"
    "    vec2 projected = shadowCoord.xy/shadowCoord.w;\n"
    "    if(projected.x >= 0.0 && projected.x <= 1.0\n"
    "       && projected.y >= 0.0 && projected.y <= 1.0)\n"
    "      {\n"
    "      result = 0.0;\n"
    "      float zval = shadowCoord.z - 0.005;\n"
    "      vec2 projT = projected*" + toString.str() + ";\n"
    "      projT = fract(projT);\n"
    "      if (texture2D(shadowMap,projected + (vec2(-1.0,-1.0)/" + toString.str() + ")).r - zval > 0.0) { result = result + (1.0-projT.x)*(1.0-projT.y); }\n"
    "      if (texture2D(shadowMap,projected + (vec2(0.0,-1.0)/" + toString.str() + ")).r - zval > 0.0) { result = result + (1.0-projT.y); }\n"
    "      if (texture2D(shadowMap,projected + (vec2(1.0,-1.0)/" + toString.str() + ")).r - zval > 0.0) { result = result + projT.x*(1.0-projT.y); }\n"
    "      if (texture2D(shadowMap,projected + (vec2(1.0,0.0)/" + toString.str() + ")).r - zval > 0.0) { result = result + projT.x; }\n"
    "      if (texture2D(shadowMap,projected + (vec2(0.0,0.0)/" + toString.str() + ")).r - zval > 0.0) { result = result + 1.0; }\n"
    "      if (texture2D(shadowMap,projected + (vec2(-1.0,0.0)/" + toString.str() + ")).r - zval > 0.0) { result = result + (1.0-projT.x); }\n"
    "      if (texture2D(shadowMap,projected + (vec2(0.0,1.0)/" + toString.str() + ")).r - zval > 0.0) { result = result + projT.y; }\n"
    "      if (texture2D(shadowMap,projected + (vec2(-1.0,1.0)/" + toString.str() + ")).r - zval > 0.0) { result = result + (1.0-projT.x)*projT.y; }\n"
    "      if (texture2D(shadowMap,projected + (vec2(1.0,1.0)/" + toString.str() + ")).r - zval > 0.0) { result = result + projT.x*projT.y; }\n"
    "      result /= 4.0;\n"
    "      }\n"
    "    }\n"
387 388
    "  return (1.0 - attenuation + attenuation*result);\n"
//    "  return result;\n"
389 390 391
    "}\n";

  for (int i = 0; i < numSMT; i++)
392
  {
393 394 395 396
    toString.str("");
    toString.clear();
    toString << i;
    fdec +=
397
    "uniform float shadowAttenuation" + toString.str() + ";\n"
398 399
    "uniform sampler2D shadowMap" + toString.str() + ";\n"
    "uniform mat4 shadowTransform" + toString.str() + ";\n";
400
  }
401 402 403 404

  // build the code for the lighting factors
  std::string fimpl = "float factors[6];\n";
  numSMT = 0;
Ken Martin's avatar
Ken Martin committed
405
  for (size_t i = 0; i < 6; i++)
406
  {
407 408 409 410 411
    toString.str("");
    toString.clear();
    toString << i;
    fimpl += "  factors[" + toString.str() + "] = ";
    if (i < numLights && this->ShadowTextureUnits[i] >= 0)
412
    {
413 414
      std::ostringstream toString2;
      toString2 << numSMT;
415 416 417
      fimpl += "calcShadow(vertexVC, shadowMap" +toString2.str() +
        ", shadowTransform" + toString2.str() +
        ", shadowAttenuation" + toString2.str() +");\n";
418
      numSMT++;
419
    }
420
    else
421
    {
422 423
      fimpl += "1.0;\n";
    }
424
  }
425 426 427 428 429 430 431

  // compute the factors then do the normal lighting
  fimpl += "//VTK::Light::Impl\n";
  this->FragmentDeclaration = fdec;
  this->FragmentImplementation = fimpl;
}

432 433 434 435 436 437 438 439 440
// ----------------------------------------------------------------------------
// Description:
// Release graphics resources and ask components to release their own
// resources.
// \pre w_exists: w!=0
void vtkShadowMapPass::ReleaseGraphicsResources(vtkWindow *w)
{
  assert("pre: w_exists" && w!=0);
  if(this->ShadowMapBakerPass!=0)
441
  {
442
    this->ShadowMapBakerPass->ReleaseGraphicsResources(w);
443
  }
444
}