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
75 BaseSeparableFilterKernel::~BaseSeparableFilterKernel()
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
198 GaussianBlurFilter::~GaussianBlurFilter()
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
303 BilateralBlurFilter::~BilateralBlurFilter()
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
641 PoissonBlurFilter::~PoissonBlurFilter()
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 }
ACG::ShaderCache::getInstance
static ShaderCache * getInstance()
Return instance of the ShaderCache singleton.
Definition: ShaderCache.cc:84
ACG
Namespace providing different geometric functions concerning angles.
Definition: BaseObjectData.hh:68
ACG::ShaderCache::getProgram
GLSL::Program * getProgram(const ShaderGenDesc *_desc, const std::vector< unsigned int > &_mods)
Query a dynamically generated program from cache.
Definition: ShaderCache.cc:102
ACG::PoissonBlurFilter::~PoissonBlurFilter
virtual ~PoissonBlurFilter()
Class destructor.
Definition: FilterKernels.cc:641
ACG::PoissonBlurFilter::plotSamples
void plotSamples(QImage *_image)
plot samples on qt image
Definition: FilterKernels.cc:686
ACG::Vec2i
VectorT< signed int, 2 > Vec2i
Definition: VectorT.hh:98
ACG::BaseSeparableFilterKernel
Definition: FilterKernels.hh:71
ACG::GaussianBlurFilter::~GaussianBlurFilter
virtual ~GaussianBlurFilter()
Class destructor.
Definition: FilterKernels.cc:198
ACG::GLFormatInfo
Definition: GLFormatInfo.hh:70
GLSL::Program
GLSL program class.
Definition: GLSLShader.hh:211
ACG::FBO::bind
bool bind()
bind the fbo and sets it as rendertarget
Definition: FBO.cc:458
ACG::Vec2f
VectorT< float, 2 > Vec2f
Definition: VectorT.hh:102
ACG::BaseSeparableFilterKernel::~BaseSeparableFilterKernel
virtual ~BaseSeparableFilterKernel()
Class destructor.
Definition: FilterKernels.cc:75
ACG::FBO::unbind
void unbind()
unbind fbo, go to normal rendering mode
Definition: FBO.cc:481
ACG::FBO
Definition: FBO.hh:71
ACG::ScreenQuad::draw
static void draw(GLSL::Program *_prog=0)
Draw the screen quad.
Definition: ScreenQuad.cc:138
OpenMesh::VectorT
Definition: Vector11T.hh:83
ACG::FBO::samples_
GLsizei samples_
sample count if multisampling
Definition: FBO.hh:210
ACG::PoissonBlurFilter::dumpSamples
void dumpSamples(const char *_filename)
dump samples as point cloud in obj format
Definition: FilterKernels.cc:647
ACG::FBO::getAttachment
GLuint getAttachment(GLenum _attachment)
return attached texture id
Definition: FBO.cc:536
GLSL::Program::setUniform
void setUniform(const char *_name, GLint _value)
Set int uniform to specified value.
Definition: GLSLShader.cc:385
ACG::BilateralBlurFilter::~BilateralBlurFilter
virtual ~BilateralBlurFilter()
Class destructor.
Definition: FilterKernels.cc:303
GLSL::Program::use
void use()
Enables the program object for using.
Definition: GLSLShader.cc:345