Developer Documentation
FilterKernels.cc
1 /*===========================================================================*\
2 * *
3 * OpenFlipper *
4  * Copyright (c) 2001-2015, RWTH-Aachen University *
5  * Department of Computer Graphics and Multimedia *
6  * All rights reserved. *
7  * www.openflipper.org *
8  * *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenFlipper. *
11  *---------------------------------------------------------------------------*
12  * *
13  * Redistribution and use in source and binary forms, with or without *
14  * modification, are permitted provided that the following conditions *
15  * are met: *
16  * *
17  * 1. Redistributions of source code must retain the above copyright notice, *
18  * this list of conditions and the following disclaimer. *
19  * *
20  * 2. Redistributions in binary form must reproduce the above copyright *
21  * notice, this list of conditions and the following disclaimer in the *
22  * documentation and/or other materials provided with the distribution. *
23  * *
24  * 3. Neither the name of the copyright holder nor the names of its *
25  * contributors may be used to endorse or promote products derived from *
26  * this software without specific prior written permission. *
27  * *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39 * *
40 \*===========================================================================*/
41 
42 
43 
44 
45 #include <ACG/GL/GLFormatInfo.hh>
46 #include <ACG/GL/ShaderCache.hh>
47 #include <ACG/GL/ScreenQuad.hh>
48 #include <ACG/ShaderUtils/GLSLShader.hh>
49 
50 #include <ACG/GL/FBO.hh>
51 
52 #include "FilterKernels.hh"
53 
54 #include <QPainter>
55 
56 #include <fstream>
57 
58 
59 namespace ACG
60 {
61 
62 
63 BaseSeparableFilterKernel::BaseSeparableFilterKernel( int _texWidth, int _texHeight, GLenum _internalfmt )
64  : texWidth_(_texWidth),
65  texHeight_(_texHeight),
66  internalfmt_(_internalfmt),
67  externalfmt_(_internalfmt),
68  tempRT_(new FBO())
69 {
70  externalfmt_ = ACG::GLFormatInfo(internalfmt_).format();
71  texelSize_ = ACG::Vec2f(1.0f / float(_texWidth), 1.0f / float(_texHeight));
72 }
73 
74 
76 {
77  delete tempRT_;
78 }
79 
80 
81 bool BaseSeparableFilterKernel::execute( GLuint _srcTexture, ACG::FBO* _dstFBO, GLuint _dstColorAttachment, GLuint _tempColorAttachment )
82 {
83  bool success = true;
84 
85  GLuint tempTexture = 0;
86  if (!_dstFBO || !_tempColorAttachment)
87  {
88  tempTexture = tempRT_->getAttachment(GL_COLOR_ATTACHMENT0);
89 
90  if (!tempTexture)
91  {
92  tempRT_->attachTexture2D(GL_COLOR_ATTACHMENT0, texWidth_, texHeight_, internalfmt_, externalfmt_, GL_CLAMP, GL_LINEAR, GL_LINEAR);
93 
94  tempTexture = tempRT_->getAttachment(GL_COLOR_ATTACHMENT0);
95  }
96  }
97  else
98  {
99  tempTexture = _dstFBO->getAttachment(tempTexture);
100 
101  if (!tempTexture)
102  return false;
103  }
104 
105  GLint vp[4];
106  glGetIntegerv(GL_VIEWPORT, vp);
107  glViewport(0, 0, texWidth_, texHeight_);
108 
109  // temp target
110 
111  ACG::FBO* passFBO = 0;
112  if (_tempColorAttachment && _dstFBO)
113  {
114  _dstFBO->bind();
115  glDrawBuffer(_tempColorAttachment);
116  passFBO = _dstFBO;
117  }
118  else
119  {
120  tempRT_->bind();
121  glDrawBuffer(GL_COLOR_ATTACHMENT0);
122  passFBO = tempRT_;
123  }
124 
125 
126  // 1. pass
127 
128  GLSL::Program* passShader = setupPass(0, _srcTexture);
129 
130  if (passShader)
131  ACG::ScreenQuad::draw(passShader);
132  else
133  success = false;
134 
135  passFBO->unbind();
136 
137  // 2. pass
138 
139  if (_dstFBO && _dstColorAttachment)
140  {
141  _dstFBO->bind();
142  glDrawBuffer(_dstColorAttachment);
143  passFBO = _dstFBO;
144  }
145  else
146  {
147  // render to previously bound fbo
148  passFBO = 0;
149  }
150 
151 
152  passShader = setupPass(1, tempTexture);
153 
154  if (passShader)
155  ACG::ScreenQuad::draw(passShader);
156  else
157  success = false;
158 
159  // restore input fbo
160  if (passFBO)
161  passFBO->unbind();
162  glViewport(vp[0], vp[1], vp[2], vp[3]);
163 
164  return success;
165 }
166 
167 void BaseSeparableFilterKernel::resizeInput( int _texWidth, int _texHeight )
168 {
169  if ( (texWidth_ != _texWidth || texHeight_ != _texHeight) )
170  {
171  texWidth_ = _texWidth;
172  texHeight_ = _texHeight;
173  texelSize_ = ACG::Vec2f(1.0f / float(_texWidth), 1.0f / float(_texHeight));
174 
175  if (tempRT_->width())
176  tempRT_->resize(_texWidth, _texHeight);
177 
178  updateKernel();
179  }
180 }
181 
182 // ----------------------------------------------------------------------------
183 
184 
185 GaussianBlurFilter::GaussianBlurFilter( int _texWidth,
186  int _texHeight,
187  int _blurRadius,
188  float _blurSigma,
189  GLenum _internalfmt )
190  : BaseSeparableFilterKernel(_texWidth, _texHeight, _internalfmt),
191  radius_(_blurRadius),
192  samples_(2 * _blurRadius + 1),
193  sigma_(_blurSigma)
194 {
195  updateKernel();
196 }
197 
199 {
200 
201 }
202 
203 
204 void GaussianBlurFilter::updateKernel()
205 {
206  samples_ = radius_ * 2 + 1;
207 
208  offsetsX_.resize(samples_);
209  offsetsY_.resize(samples_);
210  weights_.resize(samples_);
211 
212  // center sample
213  offsetsX_[radius_] = ACG::Vec2f(0.0f, 0.0f);
214  offsetsY_[radius_] = ACG::Vec2f(0.0f, 0.0f);
215  weights_[radius_] = 1.0f;
216 
217  float weightSum = 1.0f; // 1.0 is the weight of the center sample
218 
219  // left and right taps
220  for (int i = 0; i < radius_; ++i)
221  {
222  // offset
223  ACG::Vec2f v = texelSize() * float(i + 1);
224  offsetsX_[i] = ACG::Vec2f(-v[0], 0.0f);
225  offsetsX_[radius_ + i + 1] = ACG::Vec2f(v[0], 0.0f);
226 
227  offsetsY_[i] = ACG::Vec2f(0.0f, -v[1]);
228  offsetsY_[radius_ + i + 1] = ACG::Vec2f(0.0f, v[1]);
229 
230  // weight
231  float w = expf(-float((i+1)*(i+1)) / (sigma_ * sigma_));
232  weights_[radius_ + i + 1] = weights_[i] = w;
233  weightSum += 2.0f * w;
234  }
235 
236 
237  // normalize
238  for (int i = 0; i < samples_; ++i)
239  weights_[i] /= weightSum;
240 
241  QString blurSamplesMacro;
242  blurSamplesMacro.sprintf("#define BLUR_SAMPLES %i", samples_);
243  macros_.clear();
244  macros_.push_back(blurSamplesMacro);
245 }
246 
247 
248 
249 void GaussianBlurFilter::setKernel( int _blurRadius, float _blurSigma )
250 {
251  radius_ = _blurRadius;
252  sigma_ = _blurSigma;
253  updateKernel();
254 }
255 
256 GLSL::Program* GaussianBlurFilter::setupPass(int _pass, GLuint _srcTex)
257 {
258  GLSL::Program* blurShader = ACG::ShaderCache::getInstance()->getProgram("ScreenQuad/screenquad.glsl", "Blur/kernel.glsl", &macros_);
259 
260  if (_pass == 0)
261  {
262  // 1. pass horizontal blur
263 
264  blurShader->use();
265 
266  blurShader->setUniform("g_InputTex", 0);
267  blurShader->setUniform("g_SampleOffsets", &offsetsX_[0], samples_);
268  blurShader->setUniform("g_SampleWeights", &weights_[0], samples_);
269 
270  glActiveTexture(GL_TEXTURE0);
271  glBindTexture(GL_TEXTURE_2D, _srcTex);
272  }
273  else
274  {
275  // 2. pass vertical blur
276 
277  blurShader->setUniform("g_SampleOffsets", &offsetsY_[0], samples_);
278 
279  glBindTexture(GL_TEXTURE_2D, _srcTex);
280  }
281 
282  return blurShader;
283 }
284 
285 
286 // ----------------------------------------------------------------------------
287 
288 
289 BilateralBlurFilter::BilateralBlurFilter(int _texWidth, int _texHeight,
290  int _blurRadius,
291  float _blurSigmaS,
292  float _blurSigmaR,
293  GLenum _internalfmt)
294  : BaseSeparableFilterKernel(_texWidth, _texHeight, _internalfmt),
295  radius_(_blurRadius),
296  samples_(2 * _blurRadius + 1),
297  sigma_(_blurSigmaS, _blurSigmaR),
298  depthTex_(0)
299 {
300  updateKernel();
301 }
302 
304 {
305 
306 }
307 
308 
309 void BilateralBlurFilter::updateKernel()
310 {
311  samples_ = radius_ * 2 + 1;
312 
313  sigma2Rcp_ = ACG::Vec2f(-1.0f / (2.0f * sigma_[0] * sigma_[0]),
314  -1.0f / (2.0f * sigma_[1] * sigma_[1]));
315 
316 
317  // compute filter kernel
318 
319  offsetsX_.resize(samples_);
320  offsetsY_.resize(samples_);
321  spatialKernel_.resize(samples_);
322 
323 
324  // center sample
325  offsetsX_[radius_] = ACG::Vec2f(0.0f, 0.0f);
326  offsetsY_[radius_] = ACG::Vec2f(0.0f, 0.0f);
327  spatialKernel_[radius_] = 0.0f;
328 
329  // left and right taps
330  for (int i = 0; i < radius_; ++i)
331  {
332  // offset
333  ACG::Vec2f v = texelSize() * float(i + 1);
334  offsetsX_[i] = ACG::Vec2f(-v[0], 0.0f);
335  offsetsX_[radius_ + i + 1] = ACG::Vec2f(v[0], 0.0f);
336 
337  offsetsY_[i] = ACG::Vec2f(0.0f, -v[1]);
338  offsetsY_[radius_ + i + 1] = ACG::Vec2f(0.0f, v[1]);
339 
340  // spatial kernel
341  float r2 = float((i+1)*(i+1));
342  spatialKernel_[radius_ + i + 1] = spatialKernel_[i] = r2 * sigma2Rcp_[0];
343  }
344 
345 
346  macros_.clear();
347 
348  QString radiusMacro;
349  radiusMacro.sprintf("#define BLUR_SAMPLES %i", samples_);
350 
351  int numChannels = GLFormatInfo(internalFormat()).channelCount();
352  numChannels = std::max(std::min(numChannels, 4), 1);
353 
354  const char* blurChannels[4] =
355  {
356  "BLUR_R",
357  "BLUR_RG",
358  "BLUR_RGB",
359  "BLUR_RGBA"
360  };
361 
362  QString channelMacro;
363  channelMacro.sprintf("#define BLUR_CHANNELS %s", blurChannels[numChannels - 1]);
364 
365  macros_.push_back(radiusMacro);
366  macros_.push_back(channelMacro);
367 }
368 
369 void BilateralBlurFilter::setKernel( int _blurRadius, float _blurSigmaS, float _blurSigmaR )
370 {
371  radius_ = _blurRadius;
372  sigma_ = ACG::Vec2f(_blurSigmaS, _blurSigmaR);
373  updateKernel();
374 }
375 
376 GLSL::Program* BilateralBlurFilter::setupPass(int _pass, GLuint _srcTex)
377 {
378  GLSL::Program* blurShader = ACG::ShaderCache::getInstance()->getProgram("ScreenQuad/screenquad.glsl", "Blur/bilateral.glsl", &macros_);
379 
380  if (_pass == 0)
381  {
382  // 1. pass horizontal blur
383 
384  blurShader->use();
385 
386  blurShader->setUniform("g_InputTex", 0);
387  blurShader->setUniform("g_DepthTex", 1);
388  blurShader->setUniform("g_P", proj_);
389 
390  blurShader->setUniform("g_BlurSigmaRcp2", sigma2Rcp_[1]);
391  blurShader->setUniform("g_SampleOffsets", &offsetsX_[0], samples_);
392  blurShader->setUniform("g_SpatialKernel", &spatialKernel_[0], samples_);
393 
394  glActiveTexture(GL_TEXTURE1);
395  glBindTexture(GL_TEXTURE_2D, depthTex_);
396 
397  glActiveTexture(GL_TEXTURE0);
398  glBindTexture(GL_TEXTURE_2D, _srcTex);
399  }
400  else
401  {
402  // 2. pass vertical blur
403 
404  blurShader->setUniform("g_SampleOffsets", &offsetsY_[0], samples_);
405 
406  glBindTexture(GL_TEXTURE_2D, _srcTex);
407  }
408 
409  return blurShader;
410 }
411 
412 
413 // ----------------------------------------------------------------------------
414 
415 
416 RadialBlurFilter::RadialBlurFilter( int _numSamples )
417  : samples_(0)
418 {
419  setKernel(_numSamples);
420 }
421 
422 bool RadialBlurFilter::execute( GLuint _srcTexture, float _blurRadius, float _blurIntensity, const ACG::Vec2f& _blurCenter )
423 {
424  GLSL::Program* blurShader = ACG::ShaderCache::getInstance()->getProgram("ScreenQuad/screenquad.glsl", "Blur/radial.glsl", &macros_);
425 
426  if (blurShader)
427  {
428  glActiveTexture(GL_TEXTURE0);
429  glBindTexture(GL_TEXTURE_2D, _srcTexture);
430 
431 
432  blurShader->use();
433 
434  blurShader->setUniform("g_InputTex", 0);
435  blurShader->setUniform("g_BlurCenter", _blurCenter);
436  blurShader->setUniform("g_BlurRadiusRcp2", 1.0f / (_blurRadius * _blurRadius));
437  blurShader->setUniform("g_BlurIntensity", _blurIntensity);
438 
439  ACG::ScreenQuad::draw(blurShader);
440 
441  return true;
442  }
443 
444  return false;
445 }
446 
447 void RadialBlurFilter::setKernel( int _numSamples )
448 {
449  samples_ = _numSamples;
450 
451  macros_.clear();
452  QString sampleMacro;
453  sampleMacro.sprintf("#define BLUR_SAMPLES %i", _numSamples);
454  macros_.push_back(sampleMacro);
455 }
456 
457 
458 // ----------------------------------------------------------------------------
459 
460 PoissonBlurFilter::PoissonBlurFilter(float _radius, float _sampleDistance, int _numTris, bool _disk, bool _tilingCheck)
461  : radius_(_radius), sampleDistance_(_sampleDistance), numTries_(_numTris), disk_(_disk)
462 {
463  // "Fast Poisson Disk Sampling in Arbitrary Dimensions"
464  // http://people.cs.ubc.ca/~rbridson/docs/bridson-siggraph07-poissondisk.pdf
465 
466  // rejection test for disk domain
467 
468  // domain during generation is [0, 2 * radius]
469  // this is mapped to [-radius, radius] afterwards
470 
471  // step 0.
472 
473  // r = sampleDistance, n = 2
474  float cellSize = _sampleDistance / sqrtf(2.0f);
475 
476  int gridSize = int(2.0f * _radius / cellSize) + 1; // account for partially sized cell at the end
477 
478  std::vector<int> grid(gridSize * gridSize, -1);
479 
480 
481 
482  // step 1.
483 
484  ACG::Vec2f x0(0.0f, 0.0f);
485 
486  // initial uniform sample in disk
487  do
488  {
489  x0 = ACG::Vec2f(float(rand()) / float(RAND_MAX), float(rand()) / float(RAND_MAX));
490  } while ((x0 * 2.0f - ACG::Vec2f(1.0f, 1.0f)).sqrnorm() > _radius*_radius);
491 
492  x0 = x0 * 2.0f * _radius;
493 
494  std::list<int> activeList;
495 
496  samples_.reserve(32);
497  samples_.push_back(x0);
498  activeList.push_back(0);
499  int numSamples = 1;
500 
501  ACG::Vec2i gridPos0(int(x0[0] / cellSize), int(x0[1] / cellSize));
502 
503  grid[gridPos0[1] * gridSize + gridPos0[0]] = 0;
504 
505 
506  // step 2.
507 
508  float sampleDistance2 = _sampleDistance * _sampleDistance;
509 
510  while (!activeList.empty())
511  {
512  int listSize = activeList.size();
513  // random index i
514  int i = int((float(rand()) / float(RAND_MAX)) * float(listSize) + 0.5f);
515 
516  i = std::min(i, listSize - 1);
517 
518 // int sample_i = activeList[i];
519  int sample_i = -1;
520  std::list<int>::iterator it_i = activeList.begin();
521  for (int counter = 0; it_i != activeList.end(); ++it_i, ++counter)
522  {
523  if (counter == i)
524  {
525  sample_i = *it_i;
526  break;
527  }
528  }
529 
530  ACG::Vec2f x_i = samples_[sample_i];
531 
532  ACG::Vec2i gridPos_i(int(x_i[0] / cellSize), int(x_i[1] / cellSize));
533 
534  bool foundNextSample_i = false;
535 
536  // k uniform point samples in circle around x_i
537  for (int s = 0; s < _numTris; ++s)
538  {
539  float u = float(rand()) / float(RAND_MAX);
540  float v = float(rand()) / float(RAND_MAX);
541 
542  float alpha = float(M_PI) * 2.0f * u;
543  ACG::Vec2f x_s(cosf(alpha), sinf(alpha));
544 
545  // between r and 2r, where r = sample distance
546  x_s *= _sampleDistance + 2.0f * _sampleDistance * v;
547 
548  // centered around x_i
549  x_s += x_i;
550 
551  ACG::Vec2i gridPos_s(int(x_s[0] / cellSize), int(x_s[1] / cellSize));
552 
553  // oob check
554  if (x_s[0] < 0.0f || x_s[1] < 0.0f || gridPos_s[0] < 0 || gridPos_s[0] >= gridSize || gridPos_s[1] < 0 || gridPos_s[1] >= gridSize)
555  continue;
556 
557  // disk check
558  if (_disk && (x_s - ACG::Vec2f(_radius, _radius)).sqrnorm() > _radius*_radius)
559  continue;
560  // box check
561  else if (!_disk && (x_s[0] > _radius * 2.0f || x_s[1] > _radius * 2.0f))
562  continue;
563 
564  // neighborhood check
565 
566  bool tooClose = false;
567 
568  for (int x = -1; x <= 1 && !tooClose; ++x)
569  {
570  for (int y = -1; y <= 1 && !tooClose; ++y)
571  {
572  ACG::Vec2i gridPos_t = gridPos_s + ACG::Vec2i(x, y);
573 
574  // position correction for sample in neighboring kernel (repeated Poisson kernel)
575  ACG::Vec2f wrapShift(0.0f, 0.0f);
576 
577  // oob check
578  if (gridPos_t[0] < 0 || gridPos_t[0] >= gridSize || gridPos_t[1] < 0 || gridPos_t[1] >= gridSize)
579  {
580  if (_tilingCheck)
581  {
582  // check in repeated neighbor kernel
583 
584  wrapShift[0] = _radius * 2.0f * float(x);
585  wrapShift[1] = _radius * 2.0f * float(y);
586 
587  for (int k = 0; k < 2; ++k)
588  gridPos_t[k] = (gridPos_t[k] + gridSize) % gridSize;
589  }
590  else
591  continue; // skip: don't check across kernel border
592  }
593 
594  int gridValue = grid[gridPos_t[1] * gridSize + gridPos_t[0]];
595 
596  if (gridValue >= 0)
597  {
598  ACG::Vec2f delta_t = samples_[gridValue] + wrapShift - x_s;
599  float d2 = delta_t | delta_t;
600 
601  if (d2 < sampleDistance2)
602  tooClose = true;
603  }
604  }
605  }
606 
607  if (!tooClose)
608  {
609  // new sample found
610  foundNextSample_i = true;
611 
612  grid[gridPos_s[1] * gridSize + gridPos_s[0]] = numSamples;
613  samples_.push_back(x_s);
614 
615  activeList.push_back(numSamples);
616 
617  ++numSamples;
618  }
619  }
620 
621  if (!foundNextSample_i)
622  {
623  // remove from list
624  activeList.erase(it_i);
625  }
626 
627  }
628 
629  // map to [-radius, radius]
630  for (int i = 0; i < numSamples; ++i)
631  samples_[i] = samples_[i] - ACG::Vec2f(_radius, _radius);
632 
633  samplesScaled_ = samples_;
634 
635 
636  QString samplesMacro;
637  samplesMacro.sprintf("#define BLUR_SAMPLES %i", numSamples);
638  macros_.push_back(samplesMacro);
639 }
640 
642 {
643 
644 }
645 
646 
647 void PoissonBlurFilter::dumpSamples( const char* _filename )
648 {
649  std::fstream f(_filename, std::ios_base::out);
650 
651  if (f.is_open())
652  {
653  for (size_t i = 0; i < samples_.size(); ++i)
654  f << "v " << samples_[i][0] << " " << samples_[i][1] << " 0" << std::endl;
655 
656  f.close();
657  }
658 }
659 
660 bool PoissonBlurFilter::execute( GLuint _srcTex, float _kernelScale )
661 {
662  // scale kernel
663  int n = numSamples();
664  for (int i = 0; i < n; ++i)
665  samplesScaled_[i] = samples_[i] * _kernelScale;
666 
667  GLSL::Program* blurShader = ACG::ShaderCache::getInstance()->getProgram("ScreenQuad/screenquad.glsl", "Blur/poisson.glsl", &macros_);
668 
669  if (blurShader)
670  {
671  blurShader->use();
672  blurShader->setUniform("g_InputTex", 0);
673  blurShader->setUniform("g_SampleOffsets", &samplesScaled_[0], n);
674 
675  glActiveTexture(GL_TEXTURE0);
676  glBindTexture(GL_TEXTURE_2D, _srcTex);
677 
678  ACG::ScreenQuad::draw(blurShader);
679 
680  return true;
681  }
682 
683  return false;
684 }
685 
686 void PoissonBlurFilter::plotSamples( QImage* _image )
687 {
688  // cross radius of samples in image
689  const int crossRadius = 2;
690 
691  if (_image)
692  {
693  int w = _image->width(),
694  h = _image->height();
695 
696  _image->fill(qRgb(255,255,255));
697 
698  if (disk_)
699  {
700  // draw outer circle
701  QPainter plotter;
702  plotter.begin(_image);
703  plotter.setPen(QPen(qRgb(0, 0, 0)));
704  plotter.drawEllipse(0, 0, _image->width() - 1, _image->height() - 1);
705  plotter.end();
706  }
707 
708  // draw samples
709  for (int i = 0; i < numSamples(); ++i)
710  {
711  // map sample pos to [0,1]
712  Vec2f s = samples_[i];
713  s /= radius_;
714  s = s * 0.5f + Vec2f(0.5f, 0.5f);
715 
716  // map to [0, imageSize]
717  s *= Vec2f(w-1,h-1);
718 
719  // draw cross for samples
720  Vec2i pc(s[0] + 0.5f, s[1] + 0.5f); // pixel center
721 
722  for (int k = -crossRadius; k <= crossRadius; ++k)
723  {
724  for (int mirror = 0; mirror < 2; ++mirror)
725  {
726  Vec2i p = pc + Vec2i(k * (mirror ? -1 : 1),k);
727 
728  // clamp to image size
729  p[0] = std::min(std::max(p[0], 0), w-1);
730  p[1] = std::min(std::max(p[1], 0), h-1);
731 
732  _image->setPixel(p[0], p[1], qRgb(255, 0, 0));
733  }
734  }
735  }
736  }
737 }
738 
739 
740 }
static ShaderCache * getInstance()
Return instance of the ShaderCache singleton.
Definition: ShaderCache.cc:84
Namespace providing different geometric functions concerning angles.
GLSL::Program * getProgram(const ShaderGenDesc *_desc, const std::vector< unsigned int > &_mods)
Query a dynamically generated program from cache.
Definition: ShaderCache.cc:102
virtual ~PoissonBlurFilter()
Class destructor.
void plotSamples(QImage *_image)
plot samples on qt image
VectorT< signed int, 2 > Vec2i
Definition: VectorT.hh:98
virtual ~GaussianBlurFilter()
Class destructor.
GLSL program class.
Definition: GLSLShader.hh:211
bool bind()
bind the fbo and sets it as rendertarget
Definition: FBO.cc:458
VectorT< float, 2 > Vec2f
Definition: VectorT.hh:102
virtual ~BaseSeparableFilterKernel()
Class destructor.
void unbind()
unbind fbo, go to normal rendering mode
Definition: FBO.cc:481
Definition: FBO.hh:71
static void draw(GLSL::Program *_prog=0)
Draw the screen quad.
Definition: ScreenQuad.cc:138
GLsizei samples_
sample count if multisampling
Definition: FBO.hh:210
void dumpSamples(const char *_filename)
dump samples as point cloud in obj format
GLuint getAttachment(GLenum _attachment)
return attached texture id
Definition: FBO.cc:536
void setUniform(const char *_name, GLint _value)
Set int uniform to specified value.
Definition: GLSLShader.cc:385
virtual ~BilateralBlurFilter()
Class destructor.
void use()
Enables the program object for using.
Definition: GLSLShader.cc:345