Developer Documentation
PostProcessing.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/acg_glew.hh>
46 #include <ACG/GL/GLError.hh>
47 #include <ACG/GL/AntiAliasing.hh>
48 
49 #include <OpenFlipper/common/RendererInfo.hh>
50 
51 
52 #include "PostProcessing.hh"
53 
54 
55 
56 
57 PostProcessing::PostProcessing()
58  : debugLevel_(10), backbufferFBO_(0), backbufferTarget_(GL_BACK), stereoMode_(false)
59 {
60  backbufferViewport_[0] = backbufferViewport_[1] = backbufferViewport_[2] = backbufferViewport_[3] = 0;
61 }
62 
63 
64 PostProcessing::~PostProcessing()
65 {
66 
67 }
68 
69 
70 int PostProcessing::setupScene( int _viewerID, int _width, int _height, int _samples /*= 0*/, int _stereoEye /*= -1*/ )
71 {
72  /*
73  try to keep memory usage minimal
74  -> prefer unorm over fp format
75  */
76 
77  const int numProcs = postProcessorManager().numActive(_viewerID);
78 
79  if (!numProcs && _stereoEye < 0)
80  return -1; // nothing to do
81 
82  /* create enough textures for the postprocessing chain:
83 
84  - Scene Rendering pass: 1 FBO, maybe multisampled
85  - Resolve MSAA: 1 FBO, non msaa target, post processor input, can be skipped if multisampling is disabled
86  - 1st PostProc:
87  if no more following PostProcs, write to hw fbo
88  otherwise, write to temp fbo
89 
90  - 2nd PostProc:
91  write to
92 
93 
94  At least 2 FBO's required, 3 for multiple PostProcs.
95 
96  */
97 
98 
99  // All render targets have the same format for now.
100  // If one PostProc requires floating point, then all PostProcs work on fp data.
101 
102  // Search for a post proc that requires floating pt.
103  bool requiresFP = false;
104 
105  for (int i = 0; i < numProcs && !requiresFP; ++i)
106  {
107  PostProcessorInfo* pi = postProcessorManager().active(_viewerID, i);
108 
109  if (pi && pi->plugin)
110  {
112  fmt.outputFormat_ = PostProcessorFormatDesc::PostProcessorFormat_DONTCARE;
113  pi->plugin->getFormatDesc(&fmt);
114 
115  if (!fmt.inputFormats_.empty())
116  {
117  size_t numInputs = fmt.inputFormats_.size();
118 
119  for (size_t k = 0; k < numInputs; ++k)
120  {
121  if (fmt.inputFormats_[k] == PostProcessorFormatDesc::PostProcessorFormat_FLOAT)
122  {
123  requiresFP = true;
124  break;
125  }
126  }
127  }
128 
129  if (fmt.outputFormat_ == PostProcessorFormatDesc::PostProcessorFormat_FLOAT)
130  requiresFP = true;
131  }
132  }
133 
134  // avoid dealing with multisampling texture type for only one sample
135  if (_samples < 2)
136  _samples = 0;
137 
138  // number of eyes from which to scene is rendered
139  const int numEyes = _stereoEye < 0 ? 1 : 2;
140 
141  GLuint texInternalFmt = requiresFP ? GL_RGBA32F : GL_RGBA;
142 
143  // allocate enough FBOs for the pipeline
144  if (_stereoEye < 1)
145  {
146  // save output target FBO
147  glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint*)&backbufferFBO_);
148  glGetIntegerv(GL_DRAW_BUFFER, (GLint*)&backbufferTarget_);
149  glGetIntegerv(GL_VIEWPORT, backbufferViewport_);
150 
151  // in stereo mode, allocation for both eyes is done while setting up the left eye
152 
153  // alloc/resize scene targets
154  for (int i = 0; i < numEyes; ++i)
155  setupFBO(sceneFBO_ + i, _width, _height, texInternalFmt, _samples);
156 
157  // alloc/resize processing double buffer
158 
159  int numTempBuffers = std::min(numProcs - 1, 2);
160 
161  if (_samples > 0) // resolve msaa into temp buffer, requires additional step
162  numTempBuffers = std::min(numProcs, 2);
163 
164  for (int i = 0; i < numTempBuffers; ++i)
165  setupFBO(procFBO_ + i, _width, _height, texInternalFmt, 0);
166 
167  // alloc/resize stereo final targets
168  for (int i = 0; i < (_stereoEye >= 0 ? 2 : 0); ++i)
169  setupFBO(stereoFBO_ + i, _width, _height, texInternalFmt, 0);
170  }
171 
172 
173  // bind scene render target FBO
174  int targetFboId = std::max(std::min(_stereoEye, 1), 0); // clamp(eye, 0, 1)
175  sceneFBO_[targetFboId].bind();
176  glDrawBuffer(GL_COLOR_ATTACHMENT0);
177  glViewport(0, 0, _width, _height);
178 
179  // clear fbo
180  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
181 
182  // save stereo setting
183  stereoMode_ = _stereoEye >= 0;
184 
185  // enable multisample FBO
186  if (_samples > 0)
187  glEnable(GL_MULTISAMPLE);
188 
189  return 1;
190 }
191 
192 
193 void PostProcessing::setupFBO( ACG::FBO* _dst, int _width, int _height, GLuint _intfmt , int _samples )
194 {
195  // render to multisampled texture -> no texfilter!
196  GLuint filterMode = _samples ? GL_NEAREST : GL_LINEAR;
197 
198  if (!_dst->getFboID())
199  {
200  // alloc
201  _dst->setMultisampling(_samples);
202  _dst->attachTexture2D(GL_COLOR_ATTACHMENT0, _width, _height, _intfmt, GL_RGBA, GL_CLAMP, filterMode, filterMode);
203 
204  // reading from GL_DEPTH24_STENCIL8 is buggy on ati
205  _dst->attachTexture2DDepth(_width, _height, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL);
206 
207  // GL_DEPTH_COMPONENT32: has weird effect on color buffer - colors are darker!
208  // reading from depth works, but no stencil support
209 // _dst->attachTexture2DDepth(_width, _height, GL_DEPTH_COMPONENT32, GL_DEPTH_COMPONENT);
210 
211  // GL_DEPTH_COMPONENT: same as GL_DEPTH_COMPONENT32
212 // _dst->attachTexture2DDepth(_width, _height, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT);
213 
214  // colors are darker only in classical renderer!!
215  }
216  else
217  {
218  // resize
219  _dst->setMultisampling(_samples);
220  _dst->resize(_width, _height);
221 
222  // reformat
223  if (_dst->getInternalFormat(GL_COLOR_ATTACHMENT0) != _intfmt)
224  _dst->attachTexture2D(GL_COLOR_ATTACHMENT0, _width, _height, _intfmt, GL_RGBA, GL_CLAMP, filterMode, filterMode);
225  }
226 }
227 
228 
229 void PostProcessing::postProcess( int _viewerID, ACG::GLState* _glstate, const ACG::GLMatrixd& _modelview, const ACG::GLMatrixd& _proj1, const ACG::GLMatrixd& _proj2, bool _hwOpenGLStereo )
230 {
231  // disable multisample FBO
232  if (sceneFBO_[0].getMultisamplingCount())
233  glDisable(GL_MULTISAMPLE);
234 
235  // 1st post processing source: active fbo
236  int postProcSrc = 1;
237  PostProcessorInput postProcInput;
238 
239  std::vector<const PostProcessorInput*> postProcInputVec;
240  postProcInputVec.push_back(&postProcInput);
241 
242  // # post processors in chain
243  const int numProcs = postProcessorManager().numActive(_viewerID);
244 
245  // # executions of postproc chain, one for each eye
246  int numChainExecs = stereoMode_ ? 2 : 1;
247 
248  // use GL_LEQUAL for depth testing, restore current depth func afterwards
249  GLint saveDepthFunc = GL_LESS;
250  glGetIntegerv(GL_DEPTH_FUNC, &saveDepthFunc);
251 
252  glDepthMask(1);
253  glColorMask(1,1,1,1);
254 
255 
256  // execute
257 
258  for (int chainId = 0; chainId < numChainExecs; ++chainId) {
259 
260  // 1. input: scene render target
261 
262  postProcInput.width = sceneFBO_[chainId].width();
263  postProcInput.height = sceneFBO_[chainId].height();
264 
265  postProcInput.depthRange_[0] = 0.0f;
266  postProcInput.depthRange_[1] = 1.0f;
267 
268  postProcInput.view_ = _modelview;
269  postProcInput.proj_ = chainId ? _proj2 : _proj1;
270 
271  // resolve multisampling before post processing
272  if (sceneFBO_[chainId].getMultisamplingCount())
273  {
274  // resolve into first temp target or into stereo output (no postprocs, but stereo)
275  ACG::FBO* resolveTarget = numProcs ? &procFBO_[0] : &stereoFBO_[chainId];
276 
277  if (numProcs || (stereoMode_ && !_hwOpenGLStereo))
278  resolveMultisampling(resolveTarget, &sceneFBO_[chainId]);
279 
280  // use resolved texture as input for processing
281  postProcInput.colorTex_ = resolveTarget->getAttachment(GL_COLOR_ATTACHMENT0);
282  postProcInput.depthTex_ = resolveTarget->getAttachment(GL_DEPTH_ATTACHMENT);
283 
284  postProcInput.texfmt_ = resolveTarget->getInternalFormat(GL_COLOR_ATTACHMENT0);
285 
286  // read from procFBO_[0], write to procFBO_[1]
287  postProcSrc = 0;
288  }
289  else
290  {
291  // read directly from scene fbo
292  postProcInput.colorTex_ = sceneFBO_[chainId].getAttachment(GL_COLOR_ATTACHMENT0);
293  postProcInput.depthTex_ = sceneFBO_[chainId].getAttachment(GL_DEPTH_ATTACHMENT);
294 
295  postProcInput.texfmt_ = sceneFBO_[chainId].getInternalFormat(GL_COLOR_ATTACHMENT0);
296 
297  // write to procFBO_[0]
298  postProcSrc = 1;
299  }
300 
301 
302  // execute post processing chain with 2 FBOs
303  for (int i = 0; i < numProcs; ++i) {
304 
305  int postProcTarget = 1 - postProcSrc;
306 
307  // specify output target of the next post processor
308  GLuint outputFBO = procFBO_[postProcTarget].getFboID();
309  GLuint outputDrawbuffer = GL_COLOR_ATTACHMENT0;
310  GLint outputViewport[4] = {0, 0, procFBO_[postProcTarget].width(), procFBO_[postProcTarget].height()};
311 
312  const GLint* outputViewportPtr = outputViewport;
313 
314  // write to stereo or back buffer in last step
315  if (i + 1 == numProcs)
316  {
317  if (stereoMode_)
318  {
319  if (_hwOpenGLStereo)
320  {
321  outputFBO = backbufferFBO_;
322  outputDrawbuffer = chainId ? GL_BACK_RIGHT : GL_BACK_LEFT;
323 
324  outputViewportPtr = backbufferViewport_;
325  }
326  else
327  {
328  outputFBO = stereoFBO_[chainId].getFboID();
329  outputDrawbuffer = GL_COLOR_ATTACHMENT0;
330 
331  outputViewport[0] = outputViewport[1] = 0;
332  outputViewport[2] = stereoFBO_[chainId].width();
333  outputViewport[3] = stereoFBO_[chainId].height();
334  }
335  }
336  else
337  {
338  outputFBO = backbufferFBO_;
339  outputDrawbuffer = backbufferTarget_;
340  outputViewportPtr = backbufferViewport_;
341  }
342  }
343 
344  PostProcessorOutput postProcOutput(outputFBO, outputDrawbuffer,
345  procFBO_[postProcTarget].width(), procFBO_[postProcTarget].height(), outputViewportPtr);
346 
347  // apply post processor
348  PostProcessorInfo* proc = postProcessorManager().active( _viewerID, i );
349  if (proc && proc->plugin)
350  {
351  // set default opengl states
352  glDepthMask(1);
353  glColorMask(1,1,1,1);
354 
355  glEnable(GL_CULL_FACE);
356  glCullFace(GL_BACK);
357  glFrontFace(GL_CCW);
358  glDisable(GL_BLEND);
359  glDepthFunc(GL_LEQUAL);
360 
361  proc->plugin->postProcess(_glstate, postProcInputVec, postProcOutput);
362 
363  // make sure that post processor writes to specified output
364  if (debugLevel_ > 0)
365  {
366  GLint testFBO = 0, testDrawbuffer = 0;
367  GLint testVp[4];
368 
369  glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &testFBO);
370  glGetIntegerv(GL_DRAW_BUFFER, &testDrawbuffer);
371  glGetIntegerv(GL_VIEWPORT, testVp);
372 
373  if (GLuint(testFBO) != postProcOutput.fbo_)
374  std::cerr << "Error: PostProcessor does not write to specified FBO: " << proc->plugin->postProcessorName().toStdString() << std::endl;
375 
376  if (GLuint(testDrawbuffer) != postProcOutput.drawBuffer_)
377  std::cerr << "Error: PostProcessor does not write to specified draw-buffer: " << proc->plugin->postProcessorName().toStdString() << std::endl;
378 
379  if (testVp[0] != postProcOutput.viewport_[0] || testVp[1] != postProcOutput.viewport_[1] ||
380  testVp[2] != postProcOutput.viewport_[2] || testVp[3] != postProcOutput.viewport_[3])
381  std::cerr << "Error: PostProcessor does not write to specified viewport: " << proc->plugin->postProcessorName().toStdString()
382  << " actual: (" << testVp[0] << " " << testVp[1] << " " << testVp[2] << " " << testVp[3] << ") expected: ("
383  << postProcOutput.viewport_[0] << " " << postProcOutput.viewport_[1] << " " << postProcOutput.viewport_[2] << " " << postProcOutput.viewport_[3] << ")" << std::endl;
384  }
385  }
386 
387  // swap target/source fbo
388  postProcSrc = postProcTarget;
389 
390  postProcInput.colorTex_ = procFBO_[postProcSrc].getAttachment(GL_COLOR_ATTACHMENT0);
391  }
392 
393  }
394 
395 
396  // restore backbuffer
397  glBindFramebuffer(GL_FRAMEBUFFER, backbufferFBO_);
398  glDrawBuffer(backbufferTarget_);
399  glViewport(backbufferViewport_[0], backbufferViewport_[1], backbufferViewport_[2], backbufferViewport_[3]);
400 
401  // restore depth func
402  glDepthFunc(saveDepthFunc);
403 }
404 
405 void PostProcessing::resolveMultisampling( ACG::FBO* _dst, ACG::FBO* _src )
406 {
407  const int resolveMethod = 0;
408 
409  GLint curFbo = 0;
410  glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &curFbo);
411 
412  if (resolveMethod == 0)
413  {
414  // resolve by blitting
415 
416  glBindFramebuffer(GL_READ_FRAMEBUFFER, _src->getFboID());
417  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _dst->getFboID());
418 
419  if (debugLevel_ > 0)
420  {
421  if (_src->size() != _dst->size())
422  std::cerr << "PostProcessing::resolveMultisampling - FBO size mismatch : src ("
423  << _src->width() << " " << _src->height() << ") dst ("
424  << _dst->width() << " " << _dst->height() << ")" << std::endl;
425  }
426 
427  glBlitFramebuffer(0, 0, _src->width(), _src->height(),
428  0, 0, _dst->width(), _dst->height(),
429  GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);
430 
431  }
432  else
433  {
434  // resolve manually
435  _dst->bind();
436  glDrawBuffer(GL_COLOR_ATTACHMENT0);
437  GLuint multisampledColorTex = _src->getAttachment(GL_COLOR_ATTACHMENT0);
438  GLuint multisampledDepthTex = _src->getAttachment(GL_DEPTH_ATTACHMENT);
439  glDepthFunc(GL_ALWAYS);
440  ACG::MSTextureSampler::filterMSAATexture_Nearest(multisampledColorTex, multisampledDepthTex, _src->getMultisamplingCount());
441  glDepthFunc(GL_LEQUAL);
442  }
443 
444  checkGLError();
445 
446  glBindFramebuffer(GL_FRAMEBUFFER, curFbo);
447 
448 }
449 
450 
452 {
453  PostProcessorInfo* procAnaglyph = postProcessorManager().getPostProcessor("Anaglyph Stereo Postprocessor Plugin");
454 
455  if (procAnaglyph)
456  {
457  const int numProcs = postProcessorManager().numActive(_viewerID);
458 
459  PostProcessorInput inputs[2];
460 
461  for (int i = 0; i < 2; ++i)
462  {
463  // if the scene is multisampled or at least one post processor is active, read from stereoFBO_
464  // otherwise, read from the non-multisampled and unprocessed sceneFBO_
465  ACG::FBO* eyeSrcFBO = &stereoFBO_[i];
466 
467  if (!numProcs && !sceneFBO_[i].getMultisamplingCount())
468  eyeSrcFBO = &sceneFBO_[i];
469 
470  inputs[i].colorTex_ = eyeSrcFBO->getAttachment(GL_COLOR_ATTACHMENT0);
471  inputs[i].depthTex_ = eyeSrcFBO->getAttachment(GL_DEPTH_ATTACHMENT);
472  inputs[i].width = eyeSrcFBO->width();
473  inputs[i].height = eyeSrcFBO->height();
474 
475  inputs[i].texfmt_ = eyeSrcFBO->getInternalFormat(GL_COLOR_ATTACHMENT0);
476  }
477 
478 
479  std::vector<const PostProcessorInput*> anaglyphInputVec(2);
480  anaglyphInputVec[0] = inputs;
481  anaglyphInputVec[1] = inputs + 1;
482 
483  PostProcessorOutput anaglyphOutput(backbufferFBO_,
484  backbufferTarget_,
485  inputs[0].width, inputs[0].height, backbufferViewport_);
486 
487  procAnaglyph->plugin->postProcess(0, anaglyphInputVec, anaglyphOutput);
488  }
489  else
490  std::cerr << "error: stereo anaglyph plugin missing!" << std::endl;
491 }
492 
void attachTexture2DDepth(GLsizei _width, GLsizei _height, GLuint _internalFmt=GL_DEPTH_COMPONENT32, GLenum _format=GL_DEPTH_COMPONENT)
function to attach a depth-buffer texture to fbo (using GL_DEPTH_ATTACHMENT)
Definition: FBO.cc:334
GLuint getFboID()
return opengl id
Definition: FBO.cc:604
virtual void postProcess(ACG::GLState *_glState, const std::vector< const PostProcessorInput *> &_input, const PostProcessorOutput &_output)=0
post processor function
GLsizei getMultisamplingCount() const
get number of samples
Definition: FBO.hh:93
GLsizei setMultisampling(GLsizei _samples, GLboolean _fixedsamplelocations=GL_TRUE)
Definition: FBO.cc:609
void postProcess(int _viewerID, ACG::GLState *_glstate, const ACG::GLMatrixd &_modelview, const ACG::GLMatrixd &_proj1, const ACG::GLMatrixd &_proj2, bool _hwOpenGLStereo=false)
Perform all post processing.
PostProcessorInfo * getPostProcessor(QString _name)
get post processor with the given name
bool bind()
bind the fbo and sets it as rendertarget
Definition: FBO.cc:458
PostProcessorInfo * active(int _id, int _chainIdx=0)
Get the current active post processor for viewer at chain index.
void resolveStereoAnyglyph(int _viewerID)
Resolve stereo buffers as anaglyph.
void resize(GLsizei _width, GLsizei _height, bool _forceResize=false)
resize function (if textures created by this class)
Definition: FBO.cc:550
void attachTexture2D(GLenum _attachment, GLsizei _width, GLsizei _height, GLuint _internalFmt, GLenum _format, GLint _wrapMode=GL_CLAMP_TO_EDGE, GLint _minFilter=GL_NEAREST, GLint _magFilter=GL_NEAREST)
function to attach a texture to fbo
Definition: FBO.cc:207
int setupScene(int _viewerID, int _width, int _height, int _samples=0, int _stereoEye=-1)
Bind fbo for scene rendering.
Definition: FBO.hh:71
int numActive(int _id)
Get the number of active post processors for viewer.
GLsizei height() const
get height of fbo texture
Definition: FBO.hh:152
PostProcessorInterface * plugin
Pointer to the loaded plugin (Already casted when loading it)
GLuint getInternalFormat(GLenum _attachment)
return internal texture format of attachment
Definition: FBO.cc:543
GLsizei width() const
get width of fbo texture
Definition: FBO.hh:149
ACG::Vec2i size() const
get width and height of fbo texture
Definition: FBO.hh:155
GLuint getAttachment(GLenum _attachment)
return attached texture id
Definition: FBO.cc:536
virtual QString postProcessorName()=0
announce name for the postProcessor function