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

  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"
21
#include "vtkOpenGLFramebufferObject.h"
22 23 24 25 26 27
#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

// 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->BlurProgram = NULL;
}

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

// ----------------------------------------------------------------------------
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)
96
  {
97 98 99 100 101 102 103 104 105
    // 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];

106 107 108

    // I suggest set this to 100 for debugging, makes some errors
    // much easier to find
109 110 111 112 113 114
    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)
115
    {
116 117
      this->Pass1=vtkTextureObject::New();
      this->Pass1->SetContext(renWin);
118
    }
119 120

    if(this->FrameBufferObject==0)
121
    {
122
      this->FrameBufferObject=vtkOpenGLFramebufferObject::New();
123
      this->FrameBufferObject->SetContext(renWin);
124
    }
125

126
    this->FrameBufferObject->SaveCurrentBindingsAndBuffers();
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
    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)
173
    {
174 175
      this->Pass2=vtkTextureObject::New();
      this->Pass2->SetContext(this->FrameBufferObject->GetContext());
176
    }
177 178 179

    if(this->Pass2->GetWidth()!=static_cast<unsigned int>(w) ||
       this->Pass2->GetHeight()!=static_cast<unsigned int>(h))
180
    {
181 182 183
      this->Pass2->Create2D(static_cast<unsigned int>(w),
                            static_cast<unsigned int>(h),4,
                            VTK_UNSIGNED_CHAR,false);
184
    }
185

186 187 188
    this->FrameBufferObject->AddColorAttachment(
      this->FrameBufferObject->GetBothMode(), 0,this->Pass2);
    this->FrameBufferObject->Start(w, h);
189 190 191 192 193 194 195 196 197 198 199

#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)
200
    {
Ken Martin's avatar
Ken Martin committed
201
      this->BlurProgram = new vtkOpenGLHelper;
202 203 204 205 206 207 208
      // build the shader source code
      std::string VSSource = vtkGaussianBlurPassVS;
      std::string FSSource = vtkGaussianBlurPassFS;
      std::string GSSource;

      // compile and bind it if needed
      vtkShaderProgram *newShader =
209 210 211 212
        renWin->GetShaderCache()->ReadyShaderProgram(
          VSSource.c_str(),
          FSSource.c_str(),
          GSSource.c_str());
213 214 215

      // if the shader changed reinitialize the VAO
      if (newShader != this->BlurProgram->Program)
216
      {
217
        this->BlurProgram->Program = newShader;
Ken Martin's avatar
Ken Martin committed
218
        this->BlurProgram->VAO->ShaderProgramChanged(); // reset the VAO as the shader has changed
219
      }
220 221

      this->BlurProgram->ShaderSourceTime.Modified();
222
    }
223
    else
224
    {
225
      renWin->GetShaderCache()->ReadyShaderProgram(this->BlurProgram->Program);
226
    }
227 228

    if(this->BlurProgram->Program->GetCompiled() != true)
229
    {
230 231 232 233
      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();
234
      this->FrameBufferObject->RestorePreviousBindingsAndBuffers();
235
      return;
236
    }
237 238 239 240 241 242 243 244 245 246 247 248 249

    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)
250
    {
251 252
      sum+=kernel[i];
      ++i;
253
    }
254 255 256 257

    // kernel
    i=0;
    while(i<3)
258
    {
259 260
      fvalues[i]=kernel[i]/sum;
      ++i;
261
    }
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
    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
279
      this->BlurProgram->Program, this->BlurProgram->VAO);
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325

#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();
326
    this->FrameBufferObject->RestorePreviousBindingsAndBuffers();
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344

    // 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,
345
                                  0,0, width, height,
Ken Martin's avatar
Ken Martin committed
346 347
                                  this->BlurProgram->Program,
                                  this->BlurProgram->VAO);
348 349 350 351 352 353 354

    this->Pass2->Deactivate();

#ifdef VTK_GAUSSIAN_BLUR_PASS_DEBUG
    cout << "gauss finish4" << endl;
    glFinish();
#endif
355
  }
356
  else
357
  {
358
    vtkWarningMacro(<<" no delegate.");
359
  }
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375

  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)
376
  {
377 378 379
    this->BlurProgram->ReleaseGraphicsResources(w);
    delete this->BlurProgram;
    this->BlurProgram = 0;
380
  }
381
  if(this->FrameBufferObject!=0)
382
  {
383 384
    this->FrameBufferObject->Delete();
    this->FrameBufferObject=0;
385
  }
386
   if(this->Pass1!=0)
387
   {
388 389
    this->Pass1->Delete();
    this->Pass1=0;
390
   }
391
   if(this->Pass2!=0)
392
   {
393 394
    this->Pass2->Delete();
    this->Pass2=0;
395
   }
396
}