vtkGaussianBlurPass.cxx 13.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkGaussianBlurPass.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 "vtkGaussianBlurPass.h"
#include "vtkObjectFactory.h"
#include <cassert>
#include "vtkRenderState.h"
#include "vtkRenderer.h"
#include "vtkFrameBufferObject.h"
#include "vtkTextureObject.h"
#include "vtkOpenGLRenderWindow.h"
#include "vtkOpenGLError.h"
#include "vtkShaderProgram.h"
#include "vtkOpenGLShaderCache.h"
#include "vtkOpenGLRenderWindow.h"
Ken Martin's avatar
Ken Martin committed
28
#include "vtkOpenGLVertexArrayObject.h"
29

Ken Martin's avatar
Ken Martin committed
30
#include "vtkOpenGLHelper.h"
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62

// to be able to dump intermediate passes into png files for debugging.
// only for vtkGaussianBlurPass developers.
//#define VTK_GAUSSIAN_BLUR_PASS_DEBUG

#ifdef VTK_GAUSSIAN_BLUR_PASS_DEBUG
#include "vtkPNGWriter.h"
#include "vtkImageImport.h"
#include "vtkPixelBufferObject.h"
#include "vtkImageExtractComponents.h"
#endif

#include "vtkGaussianBlurPassFS.h"
#include "vtkGaussianBlurPassVS.h"

vtkStandardNewMacro(vtkGaussianBlurPass);

// ----------------------------------------------------------------------------
vtkGaussianBlurPass::vtkGaussianBlurPass()
{
  this->FrameBufferObject=0;
  this->Pass1=0;
  this->Pass2=0;
  this->Supported=false;
  this->SupportProbed=false;
  this->BlurProgram = NULL;
}

// ----------------------------------------------------------------------------
vtkGaussianBlurPass::~vtkGaussianBlurPass()
{
  if(this->FrameBufferObject!=0)
63
  {
64
    vtkErrorMacro(<<"FrameBufferObject should have been deleted in ReleaseGraphicsResources().");
65
  }
66
   if(this->Pass1!=0)
67
   {
68
    vtkErrorMacro(<<"Pass1 should have been deleted in ReleaseGraphicsResources().");
69
   }
70
   if(this->Pass2!=0)
71
   {
72
    vtkErrorMacro(<<"Pass2 should have been deleted in ReleaseGraphicsResources().");
73
   }
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
}

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

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

  vtkOpenGLClearErrorMacro();

  this->NumberOfRenderedProps=0;

  vtkRenderer *r=s->GetRenderer();
  vtkOpenGLRenderWindow *renWin = static_cast<vtkOpenGLRenderWindow *>(r->GetRenderWindow());

  if(this->DelegatePass!=0)
98
  {
99
    if(!this->SupportProbed)
100
    {
101 102 103 104 105
      this->SupportProbed=true;
      // Test for Hardware support. If not supported, just render the delegate.
      bool supported=vtkFrameBufferObject::IsSupported(renWin);

      if(!supported)
106
      {
107
        vtkErrorMacro("FBOs are not supported by the context. Cannot blur the image.");
108
      }
109
      if(supported)
110
      {
111 112
        supported=vtkTextureObject::IsSupported(renWin);
        if(!supported)
113
        {
114 115
          vtkErrorMacro("Texture Objects are not supported by the context. Cannot blur the image.");
        }
116
      }
117 118

      if(supported)
119
      {
120 121
        // FBO extension is supported. Is the specific FBO format supported?
        if(this->FrameBufferObject==0)
122
        {
123 124
          this->FrameBufferObject=vtkFrameBufferObject::New();
          this->FrameBufferObject->SetContext(renWin);
125
        }
126
        if(this->Pass1==0)
127
        {
128 129
          this->Pass1=vtkTextureObject::New();
          this->Pass1->SetContext(renWin);
130
        }
131 132 133 134 135 136
        this->Pass1->Create2D(64,64,4,VTK_UNSIGNED_CHAR,false);
        this->FrameBufferObject->SetColorBuffer(0,this->Pass1);
        this->FrameBufferObject->SetNumberOfRenderTargets(1);
        this->FrameBufferObject->SetActiveBuffer(0);
        this->FrameBufferObject->SetDepthBufferNeeded(true);

137
#if GL_ES_VERSION_2_0 != 1
138 139
        GLint savedCurrentDrawBuffer;
        glGetIntegerv(GL_DRAW_BUFFER,&savedCurrentDrawBuffer);
140
#endif
141 142
        supported=this->FrameBufferObject->StartNonOrtho(64,64,false);
        if(!supported)
143
        {
144
          vtkErrorMacro("The requested FBO format is not supported by the context. Cannot blur the image.");
145
        }
146
        else
147
        {
148
          this->FrameBufferObject->UnBind();
149
#if GL_ES_VERSION_2_0 != 1
150
          glDrawBuffer(static_cast<GLenum>(savedCurrentDrawBuffer));
151
#endif
152
        }
153
      }
154 155

      this->Supported=supported;
156
    }
157 158

    if(!this->Supported)
159
    {
160 161 162 163
      this->DelegatePass->Render(s);
      this->NumberOfRenderedProps+=
        this->DelegatePass->GetNumberOfRenderedProps();
      return;
164
    }
165

166
#if GL_ES_VERSION_2_0 != 1
167 168
    GLint savedDrawBuffer;
    glGetIntegerv(GL_DRAW_BUFFER,&savedDrawBuffer);
169
#endif
170 171 172 173 174 175 176 177 178 179

    // 1. Create a new render state with an FBO.

    int width;
    int height;
    int size[2];
    s->GetWindowSize(size);
    width=size[0];
    height=size[1];

180 181 182

    // I suggest set this to 100 for debugging, makes some errors
    // much easier to find
183 184 185 186 187 188
    const int extraPixels=2; // two on each side, as the kernel is 5x5

    int w=width+extraPixels*2;
    int h=height+extraPixels*2;

    if(this->Pass1==0)
189
    {
190 191
      this->Pass1=vtkTextureObject::New();
      this->Pass1->SetContext(renWin);
192
    }
193 194

    if(this->FrameBufferObject==0)
195
    {
196 197
      this->FrameBufferObject=vtkFrameBufferObject::New();
      this->FrameBufferObject->SetContext(renWin);
198
    }
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245

    this->RenderDelegate(s,width,height,w,h,this->FrameBufferObject,
                         this->Pass1);

#ifdef VTK_GAUSSIAN_BLUR_PASS_DEBUG
    // Save first pass in file for debugging.
    vtkPixelBufferObject *pbo=this->Pass1->Download();

    unsigned char *openglRawData=new unsigned char[4*w*h];
    unsigned int dims[2];
    dims[0]=w;
    dims[1]=h;
    vtkIdType incs[2];
    incs[0]=0;
    incs[1]=0;
    bool status=pbo->Download2D(VTK_UNSIGNED_CHAR,openglRawData,dims,4,incs);
    assert("check" && status);
    pbo->Delete();

    // no pbo
    this->Pass1->Bind();
    glGetTexImage(GL_TEXTURE_2D,0,GL_RGBA,GL_UNSIGNED_BYTE,openglRawData);
    this->Pass1->UnBind();

    vtkImageImport *importer=vtkImageImport::New();
    importer->CopyImportVoidPointer(openglRawData,4*w*h*sizeof(unsigned char));
    importer->SetDataScalarTypeToUnsignedChar();
    importer->SetNumberOfScalarComponents(4);
    importer->SetWholeExtent(0,w-1,0,h-1,0,0);
    importer->SetDataExtentToWholeExtent();
    delete[] openglRawData;

    vtkImageExtractComponents *rgbaToRgb=vtkImageExtractComponents::New();
    rgbaToRgb->SetInputConnection(importer->GetOutputPort());
    rgbaToRgb->SetComponents(0,1,2);

    vtkPNGWriter *writer=vtkPNGWriter::New();
    writer->SetFileName("BlurPass1.png");
    writer->SetInputConnection(rgbaToRgb->GetOutputPort());
    importer->Delete();
    rgbaToRgb->Delete();
    writer->Write();
    writer->Delete();
#endif

    // 3. Same FBO, but new color attachment (new TO).
    if(this->Pass2==0)
246
    {
247 248
      this->Pass2=vtkTextureObject::New();
      this->Pass2->SetContext(this->FrameBufferObject->GetContext());
249
    }
250 251 252

    if(this->Pass2->GetWidth()!=static_cast<unsigned int>(w) ||
       this->Pass2->GetHeight()!=static_cast<unsigned int>(h))
253
    {
254 255 256
      this->Pass2->Create2D(static_cast<unsigned int>(w),
                            static_cast<unsigned int>(h),4,
                            VTK_UNSIGNED_CHAR,false);
257
    }
258 259 260 261 262 263 264 265 266 267 268 269 270 271

    this->FrameBufferObject->SetColorBuffer(0,this->Pass2);
    this->FrameBufferObject->Start(w,h,false);

#ifdef VTK_GAUSSIAN_BLUR_PASS_DEBUG
    cout << "gauss finish2" << endl;
    glFinish();
#endif

    // Use a blur shader, do it horizontally. this->Pass1 is the source
    // (this->Pass2 is the fbo render target)

    // has something changed that would require us to recreate the shader?
    if (!this->BlurProgram)
272
    {
Ken Martin's avatar
Ken Martin committed
273
      this->BlurProgram = new vtkOpenGLHelper;
274 275 276 277 278 279 280
      // build the shader source code
      std::string VSSource = vtkGaussianBlurPassVS;
      std::string FSSource = vtkGaussianBlurPassFS;
      std::string GSSource;

      // compile and bind it if needed
      vtkShaderProgram *newShader =
281 282 283 284
        renWin->GetShaderCache()->ReadyShaderProgram(
          VSSource.c_str(),
          FSSource.c_str(),
          GSSource.c_str());
285 286 287

      // if the shader changed reinitialize the VAO
      if (newShader != this->BlurProgram->Program)
288
      {
289
        this->BlurProgram->Program = newShader;
Ken Martin's avatar
Ken Martin committed
290
        this->BlurProgram->VAO->ShaderProgramChanged(); // reset the VAO as the shader has changed
291
      }
292 293

      this->BlurProgram->ShaderSourceTime.Modified();
294
    }
295
    else
296
    {
297
      renWin->GetShaderCache()->ReadyShaderProgram(this->BlurProgram->Program);
298
    }
299 300

    if(this->BlurProgram->Program->GetCompiled() != true)
301
    {
302 303 304 305
      vtkErrorMacro("Couldn't build the shader program. At this point , it can be an error in a shader or a driver bug.");

      // restore some state.
      this->FrameBufferObject->UnBind();
306
#if GL_ES_VERSION_2_0 != 1
307
      glDrawBuffer(static_cast<GLenum>(savedDrawBuffer));
308
#endif
309
      return;
310
    }
311 312 313 314 315 316 317 318 319 320 321 322 323

    this->Pass1->Activate();
    int sourceId = this->Pass1->GetTextureUnit();
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    this->BlurProgram->Program->SetUniformi("source",sourceId);
    float fvalues[3];

    static float kernel[3]={5.0f,6.0f,5.0f};

    int i=0;
    float sum=0.0f;
    while(i<3)
324
    {
325 326
      sum+=kernel[i];
      ++i;
327
    }
328 329 330 331

    // kernel
    i=0;
    while(i<3)
332
    {
333 334
      fvalues[i]=kernel[i]/sum;
      ++i;
335
    }
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
    this->BlurProgram->Program->SetUniform1fv("coef", 3, fvalues);

    // horizontal offset
    fvalues[0]=static_cast<float>(1.2/w);
    this->BlurProgram->Program->SetUniformf("offsetx",fvalues[0]);
    fvalues[0]=0.0f;
    this->BlurProgram->Program->SetUniformf("offsety",fvalues[0]);

#ifdef VTK_GAUSSIAN_BLUR_PASS_DEBUG
    cout << "gauss finish3-" << endl;
    glFinish();
#endif

    glDisable(GL_BLEND);
    glDisable(GL_DEPTH_TEST);

    this->FrameBufferObject->RenderQuad(0,w-1,0,h-1,
Ken Martin's avatar
Ken Martin committed
353
      this->BlurProgram->Program, this->BlurProgram->VAO);
354 355 356 357 358 359 360 361 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 387 388 389 390 391 392 393 394 395 396 397 398 399 400

#ifdef VTK_GAUSSIAN_BLUR_PASS_DEBUG
    cout << "gauss finish3" << endl;
    glFinish();
#endif

    this->Pass1->Deactivate();

#ifdef VTK_GAUSSIAN_BLUR_PASS_DEBUG

    // Save second pass in file for debugging.
    pbo=this->Pass2->Download();
    openglRawData=new unsigned char[4*w*h];
    status=pbo->Download2D(VTK_UNSIGNED_CHAR,openglRawData,dims,4,incs);
    assert("check2" && status);
    pbo->Delete();

    // no pbo
    this->Pass2->Bind();
    glGetTexImage(GL_TEXTURE_2D,0,GL_RGBA,GL_UNSIGNED_BYTE,openglRawData);
    this->Pass2->UnBind();

    importer=vtkImageImport::New();
    importer->CopyImportVoidPointer(openglRawData,4*w*h*sizeof(unsigned char));
    importer->SetDataScalarTypeToUnsignedChar();
    importer->SetNumberOfScalarComponents(4);
    importer->SetWholeExtent(0,w-1,0,h-1,0,0);
    importer->SetDataExtentToWholeExtent();
    delete[] openglRawData;

    rgbaToRgb=vtkImageExtractComponents::New();
    rgbaToRgb->SetInputConnection(importer->GetOutputPort());
    rgbaToRgb->SetComponents(0,1,2);

    writer=vtkPNGWriter::New();
    writer->SetFileName("BlurPass2.png");
    writer->SetInputConnection(rgbaToRgb->GetOutputPort());
    importer->Delete();
    rgbaToRgb->Delete();
    writer->Write();
    writer->Delete();
#endif

    // 4. Render in original FB (from renderstate in arg)

    this->FrameBufferObject->UnBind();

401
#if GL_ES_VERSION_2_0 != 1
402
    glDrawBuffer(static_cast<GLenum>(savedDrawBuffer));
403
#endif
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421

    // to2 is the source
    this->Pass2->Activate();
    sourceId = this->Pass2->GetTextureUnit();
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    this->BlurProgram->Program->SetUniformi("source",sourceId);

    // Use the same blur shader, do it vertically.

    // vertical offset.
    fvalues[0]=0.0f;
    this->BlurProgram->Program->SetUniformf("offsetx",fvalues[0]);
    fvalues[0]=static_cast<float>(1.2/h);
    this->BlurProgram->Program->SetUniformf("offsety",fvalues[0]);

    this->Pass2->CopyToFrameBuffer(extraPixels, extraPixels,
                                  w-1-extraPixels,h-1-extraPixels,
422
                                  0,0, width, height,
Ken Martin's avatar
Ken Martin committed
423 424
                                  this->BlurProgram->Program,
                                  this->BlurProgram->VAO);
425 426 427 428 429 430 431

    this->Pass2->Deactivate();

#ifdef VTK_GAUSSIAN_BLUR_PASS_DEBUG
    cout << "gauss finish4" << endl;
    glFinish();
#endif
432
  }
433
  else
434
  {
435
    vtkWarningMacro(<<" no delegate.");
436
  }
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452

  vtkOpenGLCheckErrorMacro("failed after Render");
}

// ----------------------------------------------------------------------------
// Description:
// Release graphics resources and ask components to release their own
// resources.
// \pre w_exists: w!=0
void vtkGaussianBlurPass::ReleaseGraphicsResources(vtkWindow *w)
{
  assert("pre: w_exists" && w!=0);

  this->Superclass::ReleaseGraphicsResources(w);

  if (this->BlurProgram !=0)
453
  {
454 455 456
    this->BlurProgram->ReleaseGraphicsResources(w);
    delete this->BlurProgram;
    this->BlurProgram = 0;
457
  }
458
  if(this->FrameBufferObject!=0)
459
  {
460 461
    this->FrameBufferObject->Delete();
    this->FrameBufferObject=0;
462
  }
463
   if(this->Pass1!=0)
464
   {
465 466
    this->Pass1->Delete();
    this->Pass1=0;
467
   }
468
   if(this->Pass2!=0)
469
   {
470 471
    this->Pass2->Delete();
    this->Pass2=0;
472
   }
473
}