Commit 9ac65078 authored by Christopher Tenter's avatar Christopher Tenter
Browse files

- update stereo rendering

- change interface for postprocessing to allow multiple inputs

git-svn-id: http://www.openflipper.org/svnrepo/OpenFlipper/branches/Free@19687 383ad7c9-94d9-4d36-a494-682f7c89f535
parent 2ebf1ee6
......@@ -55,11 +55,11 @@
struct PostProcessorInput
{
PostProcessorInput(GLuint colTex = 0,
GLuint depthTex = 0,
int width = 0,
int height = 0)
: colorTex_(colTex), depthTex_(depthTex), width(width), height(height) {}
PostProcessorInput(GLuint _colTex = 0,
GLuint _depthTex = 0,
int _width = 0,
int _height = 0)
: colorTex_(_colTex), depthTex_(_depthTex), width(_width), height(_height) {}
GLuint colorTex_;
GLuint depthTex_;
......@@ -68,6 +68,25 @@ struct PostProcessorInput
};
struct PostProcessorOutput
{
PostProcessorOutput(GLuint _fbo = 0,
GLuint _drawBuffer = 0,
int _width = 0,
int _height = 0)
: fbo_(_fbo), drawBuffer_(_drawBuffer), width(_width), height(_height) {}
// opengl fbo id
GLuint fbo_;
// draw target of fbo: GL_BACK, GL_FRONT, GL_COLOR_ATTACHMENT0..
GLuint drawBuffer_;
int width, height;
};
/** \brief Interface to add global image post processor functions from within plugins.
*
* \ref postProcessorInterfacePage "Detailed description"
......@@ -88,7 +107,7 @@ class PostProcessorInterface {
/** \brief post processor function
*
*/
virtual void postProcess(ACG::GLState* _glState, const PostProcessorInput& _input, GLuint _targetFBO = 0) {};
virtual void postProcess(ACG::GLState* _glState, const std::vector<const PostProcessorInput*>& _input, const PostProcessorOutput& _output) = 0;
/** \brief announce name for the postProcessor function
*
......@@ -96,6 +115,15 @@ class PostProcessorInterface {
*/
virtual QString postProcessorName() = 0;
/** \brief does post processor resolve stereo buffer
*
* The post processor gets the left and right image as input and composes a combined stereo output.
*
* @return true if implementation resolves stereo buffer, false otherwise
*/
virtual bool isStereoProcessor() {return false;}
/** \brief Return options menu
*
* If you want an options Menu or menu entry, you can return your action here.
......
......@@ -52,6 +52,8 @@
//== INCLUDES =================================================================
#include <ACG/GL/acg_glew.hh>
#include "QtBaseViewer.hh"
#include "QtGLViewerLayout.hh"
#include "CursorPainter.hh"
......@@ -653,6 +655,16 @@ void glViewer::drawScene()
glDisable(GL_SCISSOR_TEST);
const int numPostProcessors = postProcessorManager().numActive(properties_.viewerId());
bool stereoOpenGL = false;
bool stereoAnaglyph = false;
if (properties_.stereo()) {
stereoOpenGL = OpenFlipper::Options::stereoMode () == OpenFlipper::Options::OpenGL && OpenFlipper::Options::glStereo ();
stereoAnaglyph = !stereoOpenGL;
}
// Check if we use build in default renderer
if ( renderManager().activeId( properties_.viewerId() ) == 0 ) {
drawScene_mono();
......@@ -665,7 +677,53 @@ void glViewer::drawScene()
if (shaderRenderPlugin)
shaderRenderPlugin->setViewerID( properties_.viewerId() );
renderPlugin->render(glstate_,properties_);
if (properties_.stereo()) {
// save current fbo
GLuint backbufferFbo = 0;
GLuint backbufferTarget = 0;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint*)&backbufferFbo);
glGetIntegerv(GL_DRAW_BUFFER, (GLint*)&backbufferTarget);
// setup stereo rendering
ACG::GLMatrixd projSave = glstate_->projection();
ACG::GLMatrixd projLR[2];
computeProjStereo(glstate_->viewport_width(), glstate_->viewport_height(), properties_, projLR, projLR+1);
updateStereoFBOs(glstate_->viewport_width(), glstate_->viewport_height());
// left eye: fbo 0
// right eye: fbo 1
for (int eye = 0; eye < 2; ++eye) {
glstate_->set_projection(projLR[eye]);
if (stereoOpenGL && !numPostProcessors) {
// render directly into back_left
glDrawBuffer(eye ? GL_BACK_RIGHT : GL_BACK_LEFT);
}
else {
stereoFBO_[eye].bind();
glDrawBuffer(GL_COLOR_ATTACHMENT0);
}
glEnable(GL_SCISSOR_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
renderPlugin->render(glstate_,properties_);
drawCursor();
}
// restore projection
glstate_->set_projection(projSave);
// restore backbuffer
glBindFramebuffer(GL_FRAMEBUFFER, backbufferFbo);
glDrawBuffer(backbufferTarget);
}
else {
renderPlugin->render(glstate_,properties_);
drawCursor();
}
}
checkGLError();
......@@ -673,55 +731,135 @@ void glViewer::drawScene()
// =================================================================================
// Post-Processing pipeline
const int numPostProcessors = postProcessorManager().numActive(properties_.viewerId());
if (numPostProcessors)
{
if (numPostProcessors || stereoAnaglyph) {
GLuint backbufferFbo = 0;
GLuint backbufferTarget = 0;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint*)&backbufferFbo);
glGetIntegerv(GL_DRAW_BUFFER, (GLint*)&backbufferTarget);
updatePostProcessingBufs(glstate_->viewport_width(),glstate_->viewport_height());
readBackBuffer(glstate_);
// 1st post processing source: active fbo
int postProcSrc = 1;
PostProcessorInput postProcInput;
postProcInput.colorTex_ = readBackFbo_.getAttachment(GL_COLOR_ATTACHMENT0);
postProcInput.depthTex_ = readBackFbo_.getAttachment(GL_DEPTH_ATTACHMENT);
postProcInput.width = readBackFbo_.width();
postProcInput.height = readBackFbo_.height();
std::vector<const PostProcessorInput*> postProcInputVec;
postProcInputVec.push_back(&postProcInput);
// # executions of postproc chain
int numChainExecs = properties_.stereo() ? 2 : 1;
// stereo anaglyph specifics
// input textures to resolve stereo buffers to anaglyph
PostProcessorInput resolveStereoAnaglyph[2];
// execute
for (int chainId = 0; chainId < numChainExecs; ++chainId) {
if (!properties_.stereo()) {
readBackBuffer(glstate_);
postProcInput.colorTex_ = readBackFbo_.getAttachment(GL_COLOR_ATTACHMENT0);
postProcInput.depthTex_ = readBackFbo_.getAttachment(GL_DEPTH_ATTACHMENT);
postProcInput.width = readBackFbo_.width();
postProcInput.height = readBackFbo_.height();
}
else {
postProcInput.colorTex_ = stereoFBO_[chainId].getAttachment(GL_COLOR_ATTACHMENT0);
postProcInput.depthTex_ = stereoFBO_[chainId].getAttachment(GL_DEPTH_ATTACHMENT);
postProcInput.width = stereoFBO_[chainId].width();
postProcInput.height = stereoFBO_[chainId].height();
}
resolveStereoAnaglyph[chainId].colorTex_ = stereoFBO_[chainId].getAttachment(GL_COLOR_ATTACHMENT0);
resolveStereoAnaglyph[chainId].depthTex_ = stereoFBO_[chainId].getAttachment(GL_DEPTH_ATTACHMENT);
resolveStereoAnaglyph[chainId].width = stereoFBO_[chainId].width();
resolveStereoAnaglyph[chainId].height = stereoFBO_[chainId].height();
// execute post processing chain with 2 FBOs
for (int i = 0; i < numPostProcessors; ++i) {
int postProcTarget = 1 - postProcSrc;
PostProcessorOutput targetFBO(postProcessFBO_[postProcTarget].getFboID(),
GL_COLOR_ATTACHMENT0,
postProcessFBO_[postProcTarget].width(), postProcessFBO_[postProcTarget].height());
// write to back buffer in last step
if (i + 1 == numPostProcessors) {
if (stereoOpenGL) {
targetFBO.fbo_ = backbufferFbo;
targetFBO.drawBuffer_ = chainId == 0 ? GL_BACK_LEFT : GL_BACK_RIGHT;
}
else if (stereoAnaglyph) {
// only write to stereo buffer image if its not the input of the last postproc
if (i > 1) {
targetFBO.fbo_ = stereoFBO_[chainId].getFboID();
targetFBO.drawBuffer_ = GL_COLOR_ATTACHMENT0;
}
else {
resolveStereoAnaglyph[chainId].colorTex_ = postProcessFBO_[postProcTarget].getAttachment(GL_COLOR_ATTACHMENT0);
resolveStereoAnaglyph[chainId].depthTex_ = postProcessFBO_[postProcTarget].getAttachment(GL_DEPTH_ATTACHMENT);
resolveStereoAnaglyph[chainId].width = postProcessFBO_[postProcTarget].width();
resolveStereoAnaglyph[chainId].height = postProcessFBO_[postProcTarget].height();
}
}
else {
targetFBO.fbo_ = backbufferFbo;
targetFBO.drawBuffer_ = backbufferTarget;
}
}
// apply post processor
PostProcessorInfo* proc = postProcessorManager().active( properties_.viewerId(), i );
if (proc && proc->plugin)
proc->plugin->postProcess(glstate_, postProcInputVec, targetFBO);
// execute post processing chain with 2 FBOs
for (int i = 0; i < numPostProcessors; ++i) {
int postProcTarget = 1 - postProcSrc;
// swap target/source fbo
postProcSrc = postProcTarget;
GLuint targetFBO = postProcessFBO_[postProcTarget].getFboID();
postProcInput.colorTex_ = postProcessFBO_[postProcSrc].getAttachment(GL_COLOR_ATTACHMENT0);
}
}
// write to back buffer in last step
if (i + 1 == numPostProcessors)
targetFBO = backbufferFbo;
// apply post processor
PostProcessorInfo* proc = postProcessorManager().active( properties_.viewerId(), i );
if (proc && proc->plugin)
proc->plugin->postProcess(glstate_, postProcInput, targetFBO);
glBindFramebuffer(GL_FRAMEBUFFER, backbufferFbo);
glDrawBuffer(backbufferTarget);
// resolve stereo anaglyph
if (stereoAnaglyph) {
PostProcessorInfo* procAnaglyph = postProcessorManager().getPostProcessor("Anaglyph Stereo Postprocessor Plugin");
// swap target/source fbo
postProcSrc = postProcTarget;
if (procAnaglyph) {
std::vector<const PostProcessorInput*> anaglyphInput(2);
anaglyphInput[0] = resolveStereoAnaglyph;
anaglyphInput[1] = resolveStereoAnaglyph + 1;
PostProcessorOutput anaglyphOutput(backbufferFbo,
backbufferTarget,
glstate_->viewport_width(),glstate_->viewport_height());
procAnaglyph->plugin->postProcess(glstate_, anaglyphInput, anaglyphOutput);
}
else
std::cerr << "error: stereo anaglyph plugin missing!" << std::endl;
postProcInput.colorTex_ = postProcessFBO_[postProcSrc].getAttachment(GL_COLOR_ATTACHMENT0);
}
}
// =================================================================================
glDrawBuffer(GL_BACK);
// unbind vbo for qt log window
ACG::GLState::bindBuffer(GL_ARRAY_BUFFER, 0);
......@@ -869,6 +1007,13 @@ void glViewer::drawScene_mono()
}
drawCursor();
}
void glViewer::drawCursor()
{
if (properties_.cursorPainter() && properties_.cursorPainter()->enabled () && properties_.cursorPositionValid() )
{
glstate_->push_modelview_matrix ();
......@@ -880,9 +1025,9 @@ void glViewer::drawScene_mono()
properties_.cursorPainter()->paintCursor (glstate_);
glstate_->pop_modelview_matrix ();
}
}
void glViewer::setHome()
{
home_modelview_ = glstate_->modelview();
......@@ -2522,6 +2667,60 @@ void glViewer::updatePostProcessingBufs(int _width, int _height)
}
}
void glViewer::computeProjStereo( int _viewportWidth, int _viewportHeight, Viewer::ViewerProperties& _properties, ACG::GLMatrixd* _outLeft, ACG::GLMatrixd* _outRight )
{
double l, r, t, b, w, h, a, radians, wd2, ndfl, zerop, xrange;
w = double(_viewportWidth);
h = double(_viewportHeight);
a = w / h;
double fovy = OpenFlipperSettings().value("Core/Projection/FOVY", 45.0).toDouble();
radians = fovy * 0.5 / 180.0 * M_PI;
wd2 = _properties.nearPlane() * tan(radians);
zerop = _properties.nearPlane() + ((_properties.farPlane() - _properties.nearPlane()) * OpenFlipperSettings().value("Core/Stereo/FocalDistance", 0.5).toDouble() );
ndfl = _properties.nearPlane() / zerop ;
xrange = a * wd2 * 2 * zerop / _properties.nearPlane();
l = -a*wd2;
r = a*wd2;
t = wd2;
b = -wd2;
double offset = 0.5 * OpenFlipperSettings().value("Core/Stereo/EyeDistance", 0.07).toDouble() * xrange;
double offset2 = offset * ndfl;
if (_outLeft)
{
_outLeft->identity();
_outLeft->frustum(l+offset2, r+offset2, b, t, _properties.nearPlane(), _properties.farPlane());
_outLeft->translate(offset, 0.0, 0.0);
}
if (_outRight)
{
_outRight->identity();
_outRight->frustum(l-offset2, r-offset2, b, t, _properties.nearPlane(), _properties.farPlane());
_outRight->translate(-offset, 0.0, 0.0);
}
}
void glViewer::updateStereoFBOs( int _width, int _height )
{
for (int i = 0; i < 2; ++i)
{
if (!stereoFBO_[i].getFboID())
{
stereoFBO_[i].init();
stereoFBO_[i].attachTexture2D(GL_COLOR_ATTACHMENT0, _width, _height, GL_RGBA, GL_RGBA, GL_CLAMP, GL_NEAREST, GL_NEAREST);
stereoFBO_[i].attachTexture2DDepth(_width, _height, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL);
}
else
stereoFBO_[i].resize(_width, _height);
}
}
//=============================================================================
//=============================================================================
......@@ -539,7 +539,8 @@ private:
void drawScene_mono();
/// helper function for setting the projection mode of the coordinate system node
void setCoordSysProjection(glViewer::ProjectionMode _mode);
/// draw the cursor
void drawCursor();
......@@ -1078,6 +1079,37 @@ private:
//===========================================================================
/** @name Stereo rendering
* @{ */
//===========================================================================
private:
ACG::FBO stereoFBO_[2];
/** \brief Compute left and right eye projection matrix for stereo rendering
*
* @param _width viewport width
* @param _height viewport height
* @param _properties viewer props
* @param _outLeft [out] projection matrix for left eye
* @param _outRight [out] projection matrix for right eye
*/
void computeProjStereo(int _width, int _height,
Viewer::ViewerProperties& _properties,
ACG::GLMatrixd* _outLeft, ACG::GLMatrixd* _outRight);
/** \brief Update target fbos for stereo rendering
*
* @param _width viewport width
* @param _height viewport height
*/
void updateStereoFBOs(int _width, int _height);
/** @} */
//===========================================================================
/** @name Post Processing
......
include (plugin)
openflipper_plugin ( INSTALLDATA Shaders )
/*===========================================================================*\
* *
* OpenFlipper *
* Copyright (C) 2001-2014 by Computer Graphics Group, RWTH Aachen *
* www.openflipper.org *
* *
*--------------------------------------------------------------------------- *
* This file is part of OpenFlipper. *
* *
* OpenFlipper is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of *
* the License, or (at your option) any later version with the *
* following exceptions: *
* *
* If other files instantiate templates or use macros *
* or inline functions from this file, or you compile this file and *
* link it with other files to produce an executable, this file does *
* not by itself cause the resulting executable to be covered by the *
* GNU Lesser General Public License. This exception does not however *
* invalidate any other reasons why the executable file might be *
* covered by the GNU Lesser General Public License. *
* *
* OpenFlipper is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU LesserGeneral Public *
* License along with OpenFlipper. If not, *
* see <http://www.gnu.org/licenses/>. *
* *
\*===========================================================================*/
/*===========================================================================*\
* *
* $Revision: 19248 $ *
* $LastChangedBy: moebius $ *
* $Date: 2014-07-21 14:27:08 +0200 (Mon, 21 Jul 2014) $ *
* *
\*===========================================================================*/
#include <ACG/GL/acg_glew.hh>
#include "PostProcessorAnaglyphPlugin.hh"
#include <iostream>
#include <ACG/GL/GLState.hh>
#include <ACG/GL/gl.hh>
#include <ACG/GL/ScreenQuad.hh>
#include <ACG/GL/ShaderCache.hh>
#include <OpenFlipper/BasePlugin/PluginFunctions.hh>
#include <OpenFlipper/common/GlobalOptions.hh>
PostProcessorAnaglyphPlugin::PostProcessorAnaglyphPlugin()
{
}
PostProcessorAnaglyphPlugin::~PostProcessorAnaglyphPlugin()
{
}
QString PostProcessorAnaglyphPlugin::checkOpenGL() {
if ( ! ACG::openGLVersion(3, 0) )
return QString("Insufficient OpenGL Version! OpenGL 3.0 or higher required");
return QString("");
}
QString PostProcessorAnaglyphPlugin::postProcessorName() {
return QString("AnaglyphStereo");
}
void PostProcessorAnaglyphPlugin::postProcess(ACG::GLState* _glstate, const std::vector<const PostProcessorInput*>& _input, const PostProcessorOutput& _output) {
if (_input.size() != 2) {
std::cerr << "PostProcessorAnaglyphPlugin: two input images required!" << std::endl;
return;
}
// ======================================================================================================
// Fetch shader from cache
// ======================================================================================================
QStringList macros;
if (OpenFlipper::Options::stereoMode () == OpenFlipper::Options::AnaglyphCustom)
macros.push_back("#define ANAGLYPH_CUSTOM");
GLSL::Program* shader = ACG::ShaderCache::getInstance()->getProgram("ScreenQuad/screenquad.glsl", "AnaglyphStereo/anaglyph.glsl", &macros);
// ======================================================================================================
// Bind input texture
// ======================================================================================================
glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, _input[1]->colorTex_);
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, _input[0]->colorTex_);
// ======================================================================================================
// Bind output FBO
// ======================================================================================================
glBindFramebuffer(GL_FRAMEBUFFER, _output.fbo_);
glDrawBuffer(_output.drawBuffer_);
// ======================================================================================================
// Setup render states
// ======================================================================================================
glDepthMask(1);
glColorMask(1,1,1,1);
glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
// ======================================================================================================
// Setup shader
// ======================================================================================================
shader->use();
shader->setUniform("SceneLeft", 0);
shader->setUniform("SceneRight", 1);
if (OpenFlipper::Options::stereoMode () == OpenFlipper::Options::AnaglyphCustom)
{
// column major
std::vector<float> le = OpenFlipper::Options::anaglyphLeftEyeColorMatrix();
std::vector<float> re = OpenFlipper::Options::anaglyphRightEyeColorMatrix();
ACG::GLMatrixf ml, mr;
for (int r = 0; r < 3; ++r)
{
for (int c = 0; c < 3; ++c)
{
ml(r,c) = le[c*3 + r];