Commit 17b634c4 authored by Sebastian Eichelbaum's avatar Sebastian Eichelbaum
Browse files

[ADD] - added GLSL code injector and MRSSAO and ...

parent c9df513a
......@@ -28,20 +28,21 @@
#include "WGEPostprocessorCelShading.h"
#include "WGEPostprocessorGauss.h"
#include "WGEPostprocessorSSAO.h"
#include "WGEPostprocessorMRSSAO.h"
#include "WGEPostprocessorLineAO.h"
#include "WGEPostprocessor.h"
WGEPostprocessor::WGEPostprocessor( std::string name, std::string description ):
WPrototyped(),
m_resultTexture(),
m_resultTextures(),
m_depthTexture(),
m_properties( boost::shared_ptr< WProperties >( new WProperties( "Settings for " + name, "Post-processing properties" ) ) ),
m_name( name ),
m_description( description )
{
// there is always one property:
m_effectOnly = m_properties->addProperty( "Effect only", "If active, the plain effect will be shown instead a combination of effect "
m_effectOnly = m_properties->addProperty( "Effect Only", "If active, the plain effect will be shown instead a combination of effect "
"and color. This settings does not affect all postprocessors.", false );
// for convenience, also create a preprocessor for this property
......@@ -59,9 +60,14 @@ WPropGroup WGEPostprocessor::getProperties() const
return m_properties;
}
osg::ref_ptr< osg::Texture2D > WGEPostprocessor::getOutput() const
osg::ref_ptr< osg::Texture2D > WGEPostprocessor::getOutput( size_t idx ) const
{
return m_resultTexture;
return m_resultTextures[ idx ];
}
const std::vector< osg::ref_ptr< osg::Texture2D > >& WGEPostprocessor::getOutputList() const
{
return m_resultTextures;
}
osg::ref_ptr< osg::Texture2D > WGEPostprocessor::getDepth() const
......@@ -69,6 +75,47 @@ osg::ref_ptr< osg::Texture2D > WGEPostprocessor::getDepth() const
return m_depthTexture;
}
WGEPostprocessor::PostprocessorInput::PostprocessorInput()
{
// leave them uni-initialized
}
WGEPostprocessor::PostprocessorInput::PostprocessorInput( std::vector< osg::ref_ptr< osg::Texture2D > > from )
{
if( from.size() > 0 )
{
m_colorTexture = from[0];
}
if( from.size() > 1 )
{
m_normalTexture = from[1];
}
if( from.size() > 2 )
{
m_parameterTexture = from[2];
}
if( from.size() > 3 )
{
m_tangentTexture = from[3];
}
if( from.size() > 4 )
{
m_depthTexture = from[4];
}
}
WGEPostprocessor::PostprocessorInput::PostprocessorInput( osg::ref_ptr< osg::Texture2D > color,
osg::ref_ptr< osg::Texture2D > normal,
osg::ref_ptr< osg::Texture2D > parameter,
osg::ref_ptr< osg::Texture2D > tangent,
osg::ref_ptr< osg::Texture2D > depth ):
m_colorTexture( color ),
m_normalTexture( normal ),
m_parameterTexture( parameter ),
m_tangentTexture( tangent ),
m_depthTexture( depth )
{
}
WGEPostprocessor::PostprocessorInput WGEPostprocessor::PostprocessorInput::attach( osg::ref_ptr< WGEOffscreenRenderPass > from )
{
PostprocessorInput buf;
......@@ -101,6 +148,7 @@ WGEPostprocessor::ProcessorList WGEPostprocessor::getPostprocessors()
postprocs.push_back( WGEPostprocessor::SPtr( new WGEPostprocessorCelShading() ) );
postprocs.push_back( WGEPostprocessor::SPtr( new WGEPostprocessorGauss() ) );
postprocs.push_back( WGEPostprocessor::SPtr( new WGEPostprocessorSSAO() ) );
postprocs.push_back( WGEPostprocessor::SPtr( new WGEPostprocessorMRSSAO() ) );
postprocs.push_back( WGEPostprocessor::SPtr( new WGEPostprocessorLineAO() ) );
return postprocs;
}
......
......@@ -58,6 +58,34 @@ public:
class PostprocessorInput
{
public:
/**
* Constructs an instance from a given list of textures. The order in the list define color, normal, parameter, tangent, depth. There are
* no restrictions to the input list. If textures are missing, the corresponding textures in the GBuffer are missing.
*
* \param from source list
*/
explicit PostprocessorInput( std::vector< osg::ref_ptr< osg::Texture2D > > from );
/**
* Construct GBuffer with an explicit list of textures.
*
* \param color color texture
* \param normal normal texture
* \param parameter parameter texture
* \param tangent tangent texture
* \param depth depth texture
*/
PostprocessorInput( osg::ref_ptr< osg::Texture2D > color,
osg::ref_ptr< osg::Texture2D > normal,
osg::ref_ptr< osg::Texture2D > parameter,
osg::ref_ptr< osg::Texture2D > tangent,
osg::ref_ptr< osg::Texture2D > depth );
/**
* Constructor creates empty GBuffer. All textures are un-initialized.
*/
PostprocessorInput();
/**
* Attaches the needed textures to the specified render pass and returns the G-Buffer
*
......@@ -156,9 +184,18 @@ public:
/**
* Returns the result texture. Use this to continue processing.
*
* \param idx which output. Each postprocessor returns at least one texture in index 0, which also is the default value
*
* \return the result texture
*/
virtual osg::ref_ptr< osg::Texture2D > getOutput() const;
virtual osg::ref_ptr< osg::Texture2D > getOutput( size_t idx = 0 ) const;
/**
* This processor can produce multiple outputs. Grab them here. This vector always contains at least the first filtered texture in unit 0.
*
* \return the vector as copy.
*/
const std::vector< osg::ref_ptr< osg::Texture2D > >& getOutputList() const;
/**
* Returns the new depth texture. Allows you to modify the depth values. By default, this is NULL. Check this!
......@@ -182,9 +219,9 @@ public:
virtual const std::string getDescription() const;
protected:
/**
* The texture contains the result
* The textures contain the result. Add at least one result texture
*/
osg::ref_ptr< osg::Texture2D > m_resultTexture;
std::vector< osg::ref_ptr< osg::Texture2D > > m_resultTextures;
/**
* The texture contains the new depth
......
......@@ -61,7 +61,7 @@ WGEPostprocessorCelShading::WGEPostprocessorCelShading( osg::ref_ptr< WGEOffscre
pass->getOrCreateStateSet()->addUniform( new WGEPropertyUniform< WPropInt >( "u_celShadingBins", bins ) );
// attach color0 output
m_resultTexture = pass->attach( osg::Camera::COLOR_BUFFER0, GL_RGBA );
m_resultTextures.push_back( pass->attach( osg::Camera::COLOR_BUFFER0, GL_RGBA ) );
// provide the Gbuffer input
gbuffer.bind( pass );
......
......@@ -65,7 +65,7 @@ WGEPostprocessorEdgeEnhance::WGEPostprocessorEdgeEnhance( osg::ref_ptr< WGEOffsc
pass->getOrCreateStateSet()->addUniform( new WGEPropertyUniform< WPropDouble >( "u_edgeEdgeThresholdLower", edgeThresholdL ) );
// attach color0 output
m_resultTexture = pass->attach( osg::Camera::COLOR_BUFFER0, GL_RGB );
m_resultTextures.push_back( pass->attach( osg::Camera::COLOR_BUFFER0, GL_RGB ) );
// provide the Gbuffer input
gbuffer.bind( pass );
......
......@@ -60,58 +60,57 @@ WGEPostprocessorGauss::WGEPostprocessorGauss( osg::ref_ptr< WGEOffscreenRenderNo
// for each of the textures do:
// attach color0 output and bind tex0
m_resultTexture = pass->attach( osg::Camera::COLOR_BUFFER0, GL_RGBA );
m_resultTextures.push_back( m_resultTexture );
pass->bind( tex0 );
m_resultTextures.push_back( pass->attach( osg::Camera::COLOR_BUFFER0, GL_RGBA ) );
pass->bind( tex0, 0 );
s->setDefine( "WGE_POSTPROCESSOR_GAUSS_UNIT0" );
// attach color1 output and bind tex1
if( tex1 )
{
m_resultTextures.push_back( pass->attach( osg::Camera::COLOR_BUFFER1, GL_RGBA ) );
pass->bind( tex1 );
pass->bind( tex1, 1 );
s->setDefine( "WGE_POSTPROCESSOR_GAUSS_UNIT1" );
}
// attach color2 output and bind tex2
if( tex2 )
{
m_resultTextures.push_back( pass->attach( osg::Camera::COLOR_BUFFER2, GL_RGBA ) );
pass->bind( tex2 );
pass->bind( tex2, 2 );
s->setDefine( "WGE_POSTPROCESSOR_GAUSS_UNIT2" );
}
// attach color3 output and bind tex3
if( tex3 )
{
m_resultTextures.push_back( pass->attach( osg::Camera::COLOR_BUFFER3, GL_RGBA ) );
pass->bind( tex3 );
pass->bind( tex3, 3 );
s->setDefine( "WGE_POSTPROCESSOR_GAUSS_UNIT3" );
}
// attach color4 output and bind tex4
if( tex4 )
{
m_resultTextures.push_back( pass->attach( osg::Camera::COLOR_BUFFER4, GL_RGBA ) );
pass->bind( tex4 );
pass->bind( tex4, 4 );
s->setDefine( "WGE_POSTPROCESSOR_GAUSS_UNIT4" );
}
// attach color5 output and bind tex5
if( tex5 )
{
m_resultTextures.push_back( pass->attach( osg::Camera::COLOR_BUFFER5, GL_RGBA ) );
pass->bind( tex5 );
pass->bind( tex5, 5 );
s->setDefine( "WGE_POSTPROCESSOR_GAUSS_UNIT5" );
}
// attach color6 output and bind tex6
if( tex6 )
{
m_resultTextures.push_back( pass->attach( osg::Camera::COLOR_BUFFER6, GL_RGBA ) );
pass->bind( tex6 );
pass->bind( tex6, 6 );
s->setDefine( "WGE_POSTPROCESSOR_GAUSS_UNIT6" );
}
// attach color7 output and bind tex7
if( tex7 )
{
m_resultTextures.push_back( pass->attach( osg::Camera::COLOR_BUFFER7, GL_RGBA ) );
pass->bind( tex7 );
pass->bind( tex7, 7 );
s->setDefine( "WGE_POSTPROCESSOR_GAUSS_UNIT7" );
}
}
......@@ -127,8 +126,3 @@ WGEPostprocessor::SPtr WGEPostprocessorGauss::create( osg::ref_ptr< WGEOffscreen
return WGEPostprocessor::SPtr( new WGEPostprocessorGauss( offscreen, gbuffer.m_colorTexture ) );
}
const std::vector< osg::ref_ptr< osg::Texture2D > >& WGEPostprocessorGauss::getResultTextures() const
{
return m_resultTextures;
}
......@@ -87,20 +87,10 @@ public:
* \param offscreen use this offscreen node to add your texture pass'
* \param gbuffer the input textures you should use
*/
virtual WGEPostprocessor::SPtr create( osg::ref_ptr< WGEOffscreenRenderNode > offscreen, const PostprocessorInput& gbuffer ) const;
/**
* This processor can produce multiple outputs. Grab them here. This vector always contains at least the first filtered texture in unit 0
*
* \return the vector
*/
const std::vector< osg::ref_ptr< osg::Texture2D > >& getResultTextures() const;
virtual WGEPostprocessor::SPtr create( osg::ref_ptr< WGEOffscreenRenderNode > offscreen,
const PostprocessorInput& gbuffer ) const;
protected:
private:
/**
* Contains all output textures we produce in the order of binding.
*/
std::vector< osg::ref_ptr< osg::Texture2D > > m_resultTextures;
};
#endif // WGEPOSTPROCESSORGAUSS_H
......
......@@ -89,7 +89,7 @@ WGEPostprocessorLineAO::WGEPostprocessorLineAO( osg::ref_ptr< WGEOffscreenRender
pass->getOrCreateStateSet()->addUniform( new WGEPropertyUniform< WPropDouble >( "u_lineaoRadiusSS", lineaoRadiusSS ) );
// attach color0 output
m_resultTexture = pass->attach( osg::Camera::COLOR_BUFFER0, GL_RGB );
m_resultTextures.push_back( pass->attach( osg::Camera::COLOR_BUFFER0, GL_RGB ) );
// provide the Gbuffer input
size_t gBufUnitOffset = gbuffer.bind( pass );
......
//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
// For more information see http://www.openwalnut.org/copying
//
// This file is part of OpenWalnut.
//
// OpenWalnut 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.
//
// OpenWalnut 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 Lesser General Public License
// along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
//
//---------------------------------------------------------------------------
#include <osg/Camera>
#include "../../common/WLogger.h"
#include "../WGETextureUtils.h"
#include "../shaders/WGEPropertyUniform.h"
#include "../shaders/WGEShaderPropertyDefine.h"
#include "WGEPostprocessorGauss.h"
#include "WGEPostprocessorSSAO.h"
#include "WGEPostprocessorMergeOp.h"
#include "WGEPostprocessorMRSSAO.h"
WGEPostprocessorMRSSAO::WGEPostprocessorMRSSAO():
WGEPostprocessor( "MRSSAO",
"MRSSAO is a special ambient occlusion technique which evaluates SSAO at different resolution levels." )
{
}
WGEPostprocessorMRSSAO::WGEPostprocessorMRSSAO( osg::ref_ptr< WGEOffscreenRenderNode > offscreen,
const WGEPostprocessor::PostprocessorInput& gbuffer ):
WGEPostprocessor( "MRSSAO",
"MRSSAO is a special ambient occlusion technique which evaluates SSAO at different resolution levels." )
{
// abbreviate processor input as GBuffer
typedef WGEPostprocessor::PostprocessorInput GBuffer;
// also create some prototypes of the needed postprocessors
WGEPostprocessorSSAO::SPtr ssaoProto = WGEPostprocessorSSAO::SPtr( new WGEPostprocessorSSAO() );
// Construct the pipeline:
// first step create a gaussed version of the GBuffer, ... we only need gaussed normals and depths
WGEPostprocessor::SPtr g1( new WGEPostprocessorGauss( offscreen, gbuffer.m_normalTexture, gbuffer.m_depthTexture ) );
// SSAO the result of gauss 1: (note: we use the input GBuffer but change depth and normal texture)
WGEPostprocessor::SPtr ssaoOfG1 = ssaoProto->create( offscreen, GBuffer( gbuffer.m_colorTexture,
g1->getOutput( 0 ),
gbuffer.m_parameterTexture,
gbuffer.m_tangentTexture,
g1->getOutput( 1 ) ) );
ssaoOfG1->getProperties()->getProperty( "Effect Only" )->toPropBool()->set( true );
ssaoOfG1->getProperties()->getProperty( "Radius" )->toPropDouble()->set( 20 );
// second step: gauss the gauss 1 result again
WGEPostprocessor::SPtr g2( new WGEPostprocessorGauss( offscreen, g1->getOutput( 0 ), g1->getOutput( 1 ) ) );
// SSAO the result of gauss 2: (note: we use the input GBuffer but change depth and normal texture)
WGEPostprocessor::SPtr ssaoOfG2 = ssaoProto->create( offscreen, GBuffer( gbuffer.m_colorTexture,
g2->getOutput( 0 ),
gbuffer.m_parameterTexture,
gbuffer.m_tangentTexture,
g2->getOutput( 1 ) ) );
ssaoOfG2->getProperties()->getProperty( "Effect Only" )->toPropBool()->set( true );
ssaoOfG2->getProperties()->getProperty( "Radius" )->toPropDouble()->set( 40 );
// we also need a SSAO of the original buffer:
WGEPostprocessor::SPtr ssaoOfOriginal = ssaoProto->create( offscreen, gbuffer );
ssaoOfOriginal->getProperties()->getProperty( "Effect Only" )->toPropBool()->set( true );
ssaoOfOriginal->getProperties()->getProperty( "Radius" )->toPropDouble()->set( 10 );
WGEPostprocessor::SPtr gOfssaoOfG1( new WGEPostprocessorGauss( offscreen, ssaoOfG1->getOutput() ) );
WGEPostprocessor::SPtr gOfssaoOfG2( new WGEPostprocessorGauss( offscreen, ssaoOfG2->getOutput() ) );
WGEPostprocessor::SPtr gOfssaoOfGOrg( new WGEPostprocessorGauss( offscreen, ssaoOfOriginal->getOutput() ) );
// merge the 3 SSAO images together
WGEPostprocessorMergeOp::SPtr merge( new WGEPostprocessorMergeOp( offscreen, gbuffer.m_colorTexture,
ssaoOfOriginal->getOutput(),
gOfssaoOfG1->getOutput(),
gOfssaoOfG2->getOutput()
) );
// set some custom merge code:
std::string mergecode=
"float ssaoO = texture2D( u_texture1Sampler, pixelCoord ).r;\n"
"float ssao0 = texture2D( u_texture2Sampler, pixelCoord ).r;\n"
"float ssao1 = texture2D( u_texture3Sampler, pixelCoord ).r;\n"
"float combinedSSAO = min( min( ssaoO, ssao0 ), ssao1 );\n"
"#ifdef WGE_POSTPROCESSOR_OUTPUT_COMBINE\n"
"color = vec4( ( texture2D( u_texture0Sampler, pixelCoord ) * combinedSSAO ).rgb, 1.0 );\n"
"#else\n"
"color = vec4( vec3( combinedSSAO ), 1.0 );\n"
"#endif\n";
merge->setGLSLMergeCode( mergecode );
// thats it. output result
m_resultTextures.push_back( merge->getOutput() );
}
WGEPostprocessorMRSSAO::~WGEPostprocessorMRSSAO()
{
// cleanup
}
WGEPostprocessor::SPtr WGEPostprocessorMRSSAO::create( osg::ref_ptr< WGEOffscreenRenderNode > offscreen,
const WGEPostprocessor::PostprocessorInput& gbuffer ) const
{
return WGEPostprocessor::SPtr( new WGEPostprocessorMRSSAO( offscreen, gbuffer ) );
}
//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
// For more information see http://www.openwalnut.org/copying
//
// This file is part of OpenWalnut.
//
// OpenWalnut 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.
//
// OpenWalnut 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 Lesser General Public License
// along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
//
//---------------------------------------------------------------------------
#ifndef WGEPOSTPROCESSORMRSSAO_H
#define WGEPOSTPROCESSORMRSSAO_H
#include <boost/shared_ptr.hpp>
#include "WGEPostprocessor.h"
/**
* Naive MRSSAO implementation. It re-implements the technique "Multi-resolution screen-space ambient occlusion" in CGI 2011 by Hoang Thai Duong
* et al.
*/
class WGEPostprocessorMRSSAO: public WGEPostprocessor
{
public:
/**
* Convenience typedef for a boost::shared_ptr< WGEPostprocessorMRSSAO >.
*/
typedef boost::shared_ptr< WGEPostprocessorMRSSAO > SPtr;
/**
* Convenience typedef for a boost::shared_ptr< const WGEPostprocessorMRSSAO >.
*/
typedef boost::shared_ptr< const WGEPostprocessorMRSSAO > ConstSPtr;
/**
* Default constructor.
*/
WGEPostprocessorMRSSAO();
/**
* Destructor.
*/
virtual ~WGEPostprocessorMRSSAO();
/**
* Create instance. Uses the protected constructor. Implement it if you derive from this class!
*
* \param offscreen use this offscreen node to add your texture pass'
* \param gbuffer the input textures you should use
*/
virtual WGEPostprocessor::SPtr create( osg::ref_ptr< WGEOffscreenRenderNode > offscreen, const PostprocessorInput& gbuffer ) const;
protected:
/**
* Constructor. Implement this constructor and build your processing pipeline in here
*
* \param offscreen use this offscreen node to add your texture pass'
* \param gbuffer the input textures you should use
*/
WGEPostprocessorMRSSAO( osg::ref_ptr< WGEOffscreenRenderNode > offscreen, const PostprocessorInput& gbuffer );
private:
};
#endif // WGEPOSTPROCESSORMRSSAO_H
//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
// For more information see http://www.openwalnut.org/copying
//
// This file is part of OpenWalnut.
//
// OpenWalnut 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.
//
// OpenWalnut 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 Lesser General Public License
// along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
//
//---------------------------------------------------------------------------
#include <osg/Camera>
#include "../shaders/WGEPropertyUniform.h"
#include "../shaders/WGEShaderPropertyDefineOptions.h"
#include "WGEPostprocessorMergeOp.h"
WGEPostprocessorMergeOp::WGEPostprocessorMergeOp():
WGEPostprocessor( "MergeOp",
"MergeOp - combines all input textures in a user defined way." )
{
}
WGEPostprocessorMergeOp::WGEPostprocessorMergeOp( osg::ref_ptr< WGEOffscreenRenderNode > offscreen,
osg::ref_ptr< osg::Texture2D > tex0,
osg::ref_ptr< osg::Texture2D > tex1,
osg::ref_ptr< osg::Texture2D > tex2,
osg::ref_ptr< osg::Texture2D > tex3,
osg::ref_ptr< osg::Texture2D > tex4,
osg::ref_ptr< osg::Texture2D > tex5,
osg::ref_ptr< osg::Texture2D > tex6,
osg::ref_ptr< osg::Texture2D > tex7 ):
WGEPostprocessor( "MergeOp",
"MergeOp - combines all input textures in a user defined way." ),
m_codeInjector( new WGEShaderCodeInjector( "WGE_POSTPROCESSOR_MERGEOP_CODE" ) )
{
// Use the standard postprocessor uber-shader
m_mergeOpShader = new WGEShader( "WGEPostprocessor" );
m_mergeOpShader->setDefine( "WGE_POSTPROCESSOR_MERGEOP" );
m_mergeOpShader->addPreprocessor( m_codeInjector );
// also add the m_effectOnly property as shader preprocessor
m_mergeOpShader->addPreprocessor( m_effectOnlyPreprocessor );
// create the rendering pass
osg::ref_ptr< WGEOffscreenTexturePass > pass = offscreen->addTextureProcessingPass( m_mergeOpShader, "MergeOp" );
// for each of the textures do:
// attach color0 output and bind tex0
m_resultTextures.push_back( pass->attach( osg::Camera::COLOR_BUFFER0, GL_RGBA ) );
pass->bind( tex0, 0 );
m_mergeOpShader->setDefine( "WGE_POSTPROCESSOR_MERGEOP_UNIT0" );
// attach color1 output and bind tex1
if( tex1 )
{
pass->bind( tex1, 1 );
m_mergeOpShader->setDefine( "WGE_POSTPROCESSOR_MERGEOP_UNIT1" );
}
// attach color2 output and bind tex2
if( tex2 )
{
pass->bind( tex2, 2 );
m_mergeOpShader->setDefine( "WGE_POSTPROCESSOR_MERGEOP_UNIT2" );
}
// attach color3 output and bind tex3
if( tex3 )
{
pass->bind( tex3, 3 );
m_mergeOpShader->setDefine( "WGE_POSTPROCESSOR_MERGEOP_UNIT3" );
}