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 = QString("#define BLUR_SAMPLES %1").arg(samples_);
242  macros_.clear();
243  macros_.push_back(blurSamplesMacro);
244 }
245 
246 
247 
248 void GaussianBlurFilter::setKernel( int _blurRadius, float _blurSigma )
249 {
250  radius_ = _blurRadius;
251  sigma_ = _blurSigma;
252  updateKernel();
253 }
254 
255 GLSL::Program* GaussianBlurFilter::setupPass(int _pass, GLuint _srcTex)
256 {
257  GLSL::Program* blurShader = ACG::ShaderCache::getInstance()->getProgram("ScreenQuad/screenquad.glsl", "Blur/kernel.glsl", &macros_);
258 
259  if (_pass == 0)
260  {
261  // 1. pass horizontal blur
262 
263  blurShader->use();
264 
265  blurShader->setUniform("g_InputTex", 0);
266  blurShader->setUniform("g_SampleOffsets", &offsetsX_[0], samples_);
267  blurShader->setUniform("g_SampleWeights", &weights_[0], samples_);
268 
269  glActiveTexture(GL_TEXTURE0);
270  glBindTexture(GL_TEXTURE_2D, _srcTex);
271  }
272  else
273  {
274  // 2. pass vertical blur
275 
276  blurShader->setUniform("g_SampleOffsets", &offsetsY_[0], samples_);
277 
278  glBindTexture(GL_TEXTURE_2D, _srcTex);
279  }
280 
281  return blurShader;
282 }
283 
284 
285 // ----------------------------------------------------------------------------
286 
287 
288 BilateralBlurFilter::BilateralBlurFilter(int _texWidth, int _texHeight,
289  int _blurRadius,
290  float _blurSigmaS,
291  float _blurSigmaR,
292  GLenum _internalfmt)
293  : BaseSeparableFilterKernel(_texWidth, _texHeight, _internalfmt),
294  radius_(_blurRadius),
295  samples_(2 * _blurRadius + 1),
296  sigma_(_blurSigmaS, _blurSigmaR),
297  depthTex_(0)
298 {
299  updateKernel();
300 }
301 
303 {
304 
305 }
306 
307 
308 void BilateralBlurFilter::updateKernel()
309 {
310  samples_ = radius_ * 2 + 1;
311 
312  sigma2Rcp_ = ACG::Vec2f(-1.0f / (2.0f * sigma_[0] * sigma_[0]),
313  -1.0f / (2.0f * sigma_[1] * sigma_[1]));
314 
315 
316  // compute filter kernel
317 
318  offsetsX_.resize(samples_);
319  offsetsY_.resize(samples_);
320  spatialKernel_.resize(samples_);
321 
322 
323  // center sample
324  offsetsX_[radius_] = ACG::Vec2f(0.0f, 0.0f);
325  offsetsY_[radius_] = ACG::Vec2f(0.0f, 0.0f);
326  spatialKernel_[radius_] = 0.0f;
327 
328  // left and right taps
329  for (int i = 0; i < radius_; ++i)
330  {
331  // offset
332  ACG::Vec2f v = texelSize() * float(i + 1);
333  offsetsX_[i] = ACG::Vec2f(-v[0], 0.0f);
334  offsetsX_[radius_ + i + 1] = ACG::Vec2f(v[0], 0.0f);
335 
336  offsetsY_[i] = ACG::Vec2f(0.0f, -v[1]);
337  offsetsY_[radius_ + i + 1] = ACG::Vec2f(0.0f, v[1]);
338 
339  // spatial kernel
340  float r2 = float((i+1)*(i+1));
341  spatialKernel_[radius_ + i + 1] = spatialKernel_[i] = r2 * sigma2Rcp_[0];
342  }
343 
344 
345  macros_.clear();
346 
347  QString radiusMacro = QString("#define BLUR_SAMPLES %1").arg(samples_);
348 
349  int numChannels = GLFormatInfo(internalFormat()).channelCount();
350  numChannels = std::max(std::min(numChannels, 4), 1);
351 
352  const char* blurChannels[4] =
353  {
354  "BLUR_R",
355  "BLUR_RG",
356  "BLUR_RGB",
357  "BLUR_RGBA"
358  };
359 
360  // %1 printed as %s
361  QString channelMacro = QString("#define BLUR_CHANNELS %1").arg(blurChannels[numChannels - 1]);
362 
363  macros_.push_back(radiusMacro);
364  macros_.push_back(channelMacro);
365 }
366 
367 void BilateralBlurFilter::setKernel( int _blurRadius, float _blurSigmaS, float _blurSigmaR )
368 {
369  radius_ = _blurRadius;
370  sigma_ = ACG::Vec2f(_blurSigmaS, _blurSigmaR);
371  updateKernel();
372 }
373 
374 GLSL::Program* BilateralBlurFilter::setupPass(int _pass, GLuint _srcTex)
375 {
376  GLSL::Program* blurShader = ACG::ShaderCache::getInstance()->getProgram("ScreenQuad/screenquad.glsl", "Blur/bilateral.glsl", &macros_);
377 
378  if (_pass == 0)
379  {
380  // 1. pass horizontal blur
381 
382  blurShader->use();
383 
384  blurShader->setUniform("g_InputTex", 0);
385  blurShader->setUniform("g_DepthTex", 1);
386  blurShader->setUniform("g_P", proj_);
387 
388  blurShader->setUniform("g_BlurSigmaRcp2", sigma2Rcp_[1]);
389  blurShader->setUniform("g_SampleOffsets", &offsetsX_[0], samples_);
390  blurShader->setUniform("g_SpatialKernel", &spatialKernel_[0], samples_);
391 
392  glActiveTexture(GL_TEXTURE1);
393  glBindTexture(GL_TEXTURE_2D, depthTex_);
394 
395  glActiveTexture(GL_TEXTURE0);
396  glBindTexture(GL_TEXTURE_2D, _srcTex);
397  }
398  else
399  {
400  // 2. pass vertical blur
401 
402  blurShader->setUniform("g_SampleOffsets", &offsetsY_[0], samples_);
403 
404  glBindTexture(GL_TEXTURE_2D, _srcTex);
405  }
406 
407  return blurShader;
408 }
409 
410 
411 // ----------------------------------------------------------------------------
412 
413 
414 RadialBlurFilter::RadialBlurFilter( int _numSamples )
415  : samples_(0)
416 {
417  setKernel(_numSamples);
418 }
419 
420 bool RadialBlurFilter::execute( GLuint _srcTexture, float _blurRadius, float _blurIntensity, const ACG::Vec2f& _blurCenter )
421 {
422  GLSL::Program* blurShader = ACG::ShaderCache::getInstance()->getProgram("ScreenQuad/screenquad.glsl", "Blur/radial.glsl", &macros_);
423 
424  if (blurShader)
425  {
426  glActiveTexture(GL_TEXTURE0);
427  glBindTexture(GL_TEXTURE_2D, _srcTexture);
428 
429 
430  blurShader->use();
431 
432  blurShader->setUniform("g_InputTex", 0);
433  blurShader->setUniform("g_BlurCenter", _blurCenter);
434  blurShader->setUniform("g_BlurRadiusRcp2", 1.0f / (_blurRadius * _blurRadius));
435  blurShader->setUniform("g_BlurIntensity", _blurIntensity);
436 
437  ACG::ScreenQuad::draw(blurShader);
438 
439  return true;
440  }
441 
442  return false;
443 }
444 
445 void RadialBlurFilter::setKernel( int _numSamples )
446 {
447  samples_ = _numSamples;
448 
449  macros_.clear();
450 
451  QString sampleMacro = QString("#define BLUR_SAMPLES %1").arg(_numSamples);
452  macros_.push_back(sampleMacro);
453 }
454 
455 
456 // ----------------------------------------------------------------------------
457 
458 PoissonBlurFilter::PoissonBlurFilter(float _radius, float _sampleDistance, int _numTris, bool _disk, bool _tilingCheck)
459  : radius_(_radius), sampleDistance_(_sampleDistance), numTries_(_numTris), disk_(_disk)
460 {
461  // "Fast Poisson Disk Sampling in Arbitrary Dimensions"
462  // http://people.cs.ubc.ca/~rbridson/docs/bridson-siggraph07-poissondisk.pdf
463 
464  // rejection test for disk domain
465 
466  // domain during generation is [0, 2 * radius]
467  // this is mapped to [-radius, radius] afterwards
468 
469  // step 0.
470 
471  // r = sampleDistance, n = 2
472  float cellSize = _sampleDistance / sqrtf(2.0f);
473 
474  int gridSize = int(2.0f * _radius / cellSize) + 1; // account for partially sized cell at the end
475 
476  std::vector<int> grid(gridSize * gridSize, -1);
477 
478 
479 
480  // step 1.
481 
482  ACG::Vec2f x0(0.0f, 0.0f);
483 
484  // initial uniform sample in disk
485  do
486  {
487  x0 = ACG::Vec2f(float(rand()) / float(RAND_MAX), float(rand()) / float(RAND_MAX));
488  } while ((x0 * 2.0f - ACG::Vec2f(1.0f, 1.0f)).sqrnorm() > _radius*_radius);
489 
490  x0 = x0 * 2.0f * _radius;
491 
492  std::list<int> activeList;
493 
494  samples_.reserve(32);
495  samples_.push_back(x0);
496  activeList.push_back(0);
497  int numSamples = 1;
498 
499  ACG::Vec2i gridPos0(int(x0[0] / cellSize), int(x0[1] / cellSize));
500 
501  grid[gridPos0[1] * gridSize + gridPos0[0]] = 0;
502 
503 
504  // step 2.
505 
506  float sampleDistance2 = _sampleDistance * _sampleDistance;
507 
508  while (!activeList.empty())
509  {
510  int listSize = activeList.size();
511  // random index i
512  int i = int((float(rand()) / float(RAND_MAX)) * float(listSize) + 0.5f);
513 
514  i = std::min(i, listSize - 1);
515 
516 // int sample_i = activeList[i];
517  int sample_i = -1;
518  std::list<int>::iterator it_i = activeList.begin();
519  for (int counter = 0; it_i != activeList.end(); ++it_i, ++counter)
520  {
521  if (counter == i)
522  {
523  sample_i = *it_i;
524  break;
525  }
526  }
527 
528  ACG::Vec2f x_i = samples_[sample_i];
529 
530  ACG::Vec2i gridPos_i(int(x_i[0] / cellSize), int(x_i[1] / cellSize));
531 
532  bool foundNextSample_i = false;
533 
534  // k uniform point samples in circle around x_i
535  for (int s = 0; s < _numTris; ++s)
536  {
537  float u = float(rand()) / float(RAND_MAX);
538  float v = float(rand()) / float(RAND_MAX);
539 
540  float alpha = float(M_PI) * 2.0f * u;
541  ACG::Vec2f x_s(cosf(alpha), sinf(alpha));
542 
543  // between r and 2r, where r = sample distance
544  x_s *= _sampleDistance + 2.0f * _sampleDistance * v;
545 
546  // centered around x_i
547  x_s += x_i;
548 
549  ACG::Vec2i gridPos_s(int(x_s[0] / cellSize), int(x_s[1] / cellSize));
550 
551  // oob check
552  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)
553  continue;
554 
555  // disk check
556  if (_disk && (x_s - ACG::Vec2f(_radius, _radius)).sqrnorm() > _radius*_radius)
557  continue;
558  // box check
559  else if (!_disk && (x_s[0] > _radius * 2.0f || x_s[1] > _radius * 2.0f))
560  continue;
561 
562  // neighborhood check
563 
564  bool tooClose = false;
565 
566  for (int x = -1; x <= 1 && !tooClose; ++x)
567  {
568  for (int y = -1; y <= 1 && !tooClose; ++y)
569  {
570  ACG::Vec2i gridPos_t = gridPos_s + ACG::Vec2i(x, y);
571 
572  // position correction for sample in neighboring kernel (repeated Poisson kernel)
573  ACG::Vec2f wrapShift(0.0f, 0.0f);
574 
575  // oob check
576  if (gridPos_t[0] < 0 || gridPos_t[0] >= gridSize || gridPos_t[1] < 0 || gridPos_t[1] >= gridSize)
577  {
578  if (_tilingCheck)
579  {
580  // check in repeated neighbor kernel
581 
582  wrapShift[0] = _radius * 2.0f * float(x);
583  wrapShift[1] = _radius * 2.0f * float(y);
584 
585  for (int k = 0; k < 2; ++k)
586  gridPos_t[k] = (gridPos_t[k] + gridSize) % gridSize;
587  }
588  else
589  continue; // skip: don't check across kernel border
590  }
591 
592  int gridValue = grid[gridPos_t[1] * gridSize + gridPos_t[0]];
593 
594  if (gridValue >= 0)
595  {
596  ACG::Vec2f delta_t = samples_[gridValue] + wrapShift - x_s;
597  float d2 = delta_t | delta_t;
598 
599  if (d2 < sampleDistance2)
600  tooClose = true;
601  }
602  }
603  }
604 
605  if (!tooClose)
606  {
607  // new sample found
608  foundNextSample_i = true;
609 
610  grid[gridPos_s[1] * gridSize + gridPos_s[0]] = numSamples;
611  samples_.push_back(x_s);
612 
613  activeList.push_back(numSamples);
614 
615  ++numSamples;
616  }
617  }
618 
619  if (!foundNextSample_i)
620  {
621  // remove from list
622  activeList.erase(it_i);
623  }
624 
625  }
626 
627  // map to [-radius, radius]
628  for (int i = 0; i < numSamples; ++i)
629  samples_[i] = samples_[i] - ACG::Vec2f(_radius, _radius);
630 
631  samplesScaled_ = samples_;
632 
633 
634  QString samplesMacro= QString("#define BLUR_SAMPLES %1").arg(numSamples);
635  macros_.push_back(samplesMacro);
636 }
637 
639 {
640 
641 }
642 
643 
644 void PoissonBlurFilter::dumpSamples( const char* _filename )
645 {
646  std::fstream f(_filename, std::ios_base::out);
647 
648  if (f.is_open())
649  {
650  for (size_t i = 0; i < samples_.size(); ++i)
651  f << "v " << samples_[i][0] << " " << samples_[i][1] << " 0" << std::endl;
652 
653  f.close();
654  }
655 }
656 
657 bool PoissonBlurFilter::execute( GLuint _srcTex, float _kernelScale )
658 {
659  // scale kernel
660  int n = numSamples();
661  for (int i = 0; i < n; ++i)
662  samplesScaled_[i] = samples_[i] * _kernelScale;
663 
664  GLSL::Program* blurShader = ACG::ShaderCache::getInstance()->getProgram("ScreenQuad/screenquad.glsl", "Blur/poisson.glsl", &macros_);
665 
666  if (blurShader)
667  {
668  blurShader->use();
669  blurShader->setUniform("g_InputTex", 0);
670  blurShader->setUniform("g_SampleOffsets", &samplesScaled_[0], n);
671 
672  glActiveTexture(GL_TEXTURE0);
673  glBindTexture(GL_TEXTURE_2D, _srcTex);
674 
675  ACG::ScreenQuad::draw(blurShader);
676 
677  return true;
678  }
679 
680  return false;
681 }
682 
683 void PoissonBlurFilter::plotSamples( QImage* _image )
684 {
685  // cross radius of samples in image
686  const int crossRadius = 2;
687 
688  if (_image)
689  {
690  int w = _image->width(),
691  h = _image->height();
692 
693  _image->fill(qRgb(255,255,255));
694 
695  if (disk_)
696  {
697  // draw outer circle
698  QPainter plotter;
699  plotter.begin(_image);
700  plotter.setPen(QPen(qRgb(0, 0, 0)));
701  plotter.drawEllipse(0, 0, _image->width() - 1, _image->height() - 1);
702  plotter.end();
703  }
704 
705  // draw samples
706  for (int i = 0; i < numSamples(); ++i)
707  {
708  // map sample pos to [0,1]
709  Vec2f s = samples_[i];
710  s /= radius_;
711  s = s * 0.5f + Vec2f(0.5f, 0.5f);
712 
713  // map to [0, imageSize]
714  s *= Vec2f(w-1,h-1);
715 
716  // draw cross for samples
717  Vec2i pc(s[0] + 0.5f, s[1] + 0.5f); // pixel center
718 
719  for (int k = -crossRadius; k <= crossRadius; ++k)
720  {
721  for (int mirror = 0; mirror < 2; ++mirror)
722  {
723  Vec2i p = pc + Vec2i(k * (mirror ? -1 : 1),k);
724 
725  // clamp to image size
726  p[0] = std::min(std::max(p[0], 0), w-1);
727  p[1] = std::min(std::max(p[1], 0), h-1);
728 
729  _image->setPixel(p[0], p[1], qRgb(255, 0, 0));
730  }
731  }
732  }
733  }
734 }
735 
736 
737 }
virtual ~BilateralBlurFilter()
Class destructor.
VectorT< signed int, 2 > Vec2i
Definition: VectorT.hh:98
Definition: FBO.hh:71
static void draw(GLSL::Program *_prog=0)
Draw the screen quad.
Definition: ScreenQuad.cc:138
void use()
Enables the program object for using.
Definition: GLSLShader.cc:345
VectorT< float, 2 > Vec2f
Definition: VectorT.hh:102
Namespace providing different geometric functions concerning angles.
void unbind()
unbind fbo, go to normal rendering mode
Definition: FBO.cc:481
void plotSamples(QImage *_image)
plot samples on qt image
virtual ~BaseSeparableFilterKernel()
Class destructor.
virtual ~PoissonBlurFilter()
Class destructor.
GLSL program class.
Definition: GLSLShader.hh:211
static ShaderCache * getInstance()
Return instance of the ShaderCache singleton.
Definition: ShaderCache.cc:84
void dumpSamples(const char *_filename)
dump samples as point cloud in obj format
GLSL::Program * getProgram(const ShaderGenDesc *_desc, const std::vector< unsigned int > &_mods)
Query a dynamically generated program from cache.
Definition: ShaderCache.cc:102
void setUniform(const char *_name, GLint _value)
Set int uniform to specified value.
Definition: GLSLShader.cc:385
bool bind()
bind the fbo and sets it as rendertarget
Definition: FBO.cc:458
virtual ~GaussianBlurFilter()
Class destructor.
GLsizei samples_
sample count if multisampling
Definition: FBO.hh:210
GLuint getAttachment(GLenum _attachment)
return attached texture id
Definition: FBO.cc:536