Commit 7e5d0d74 authored by Sebastian Eichelbaum's avatar Sebastian Eichelbaum
Browse files

[ADD] - added SSAO and LineAO skeleton to new posprocessor scheme

parent 2bb01c26
......@@ -96,12 +96,6 @@ WGEPostprocessingNode::WGEPostprocessingNode( osg::ref_ptr< osg::Camera > refere
// let the props control some stuff
addUpdateCallback( new WGESwitchCallback< WPropSelection >( m_activePostprocessor ) );
// some of the post-processors need some white noise, like the ssao
/* const size_t size = 64;
osg::ref_ptr< WGETexture2D > randTex = wge::genWhiteNoiseTexture( size, size, 3 );
m_postprocess->bind( randTex, 4 );
*/
}
WGEPostprocessingNode::~WGEPostprocessingNode()
......
......@@ -24,6 +24,8 @@
#include "WGEPostprocessorEdgeEnhance.h"
#include "WGEPostprocessorCelShading.h"
#include "WGEPostprocessorSSAO.h"
#include "WGEPostprocessorLineAO.h"
#include "WGEPostprocessor.h"
......@@ -86,13 +88,15 @@ WGEPostprocessor::PostprocessorInput WGEPostprocessor::PostprocessorInput::attac
return buf;
}
void WGEPostprocessor::PostprocessorInput::bind( osg::ref_ptr< WGEOffscreenRenderPass > to ) const
size_t WGEPostprocessor::PostprocessorInput::bind( osg::ref_ptr< WGEOffscreenRenderPass > to ) const
{
to->bind( m_colorTexture, 0 );
to->bind( m_normalTexture, 1 );
to->bind( m_parameterTexture, 2 );
to->bind( m_depthTexture, 3 );
to->bind( m_tangentTexture, 4 );
return 5;
}
WGEPostprocessor::ProcessorList WGEPostprocessor::getPostprocessors()
......@@ -102,7 +106,8 @@ WGEPostprocessor::ProcessorList WGEPostprocessor::getPostprocessors()
// create prototypes of the postprocessors OW knows about
postprocs.push_back( WGEPostprocessor::SPtr( new WGEPostprocessorEdgeEnhance() ) );
postprocs.push_back( WGEPostprocessor::SPtr( new WGEPostprocessorCelShading() ) );
postprocs.push_back( WGEPostprocessor::SPtr( new WGEPostprocessorSSAO() ) );
postprocs.push_back( WGEPostprocessor::SPtr( new WGEPostprocessorLineAO() ) );
return postprocs;
}
......
......@@ -71,8 +71,10 @@ public:
* Attaches these textures to the specified renderpass
*
* \param to attach to this
*
* \return the ID of the NEXT free texture unit you can use
*/
void bind( osg::ref_ptr< WGEOffscreenRenderPass > to ) const;
size_t bind( osg::ref_ptr< WGEOffscreenRenderPass > to ) const;
/**
* Color in RGBA
......
//---------------------------------------------------------------------------
//
// 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 "WGEPostprocessorLineAO.h"
WGEPostprocessorLineAO::WGEPostprocessorLineAO():
WGEPostprocessor( "LineAO",
"LineAO is a special ambient occlusion technique optimized for dense line and tube rendering." )
{
}
WGEPostprocessorLineAO::WGEPostprocessorLineAO( osg::ref_ptr< WGEOffscreenRenderNode > offscreen,
const WGEPostprocessor::PostprocessorInput& gbuffer ):
WGEPostprocessor( offscreen, gbuffer,
"LineAO",
"LineAO is a special ambient occlusion technique optimized for dense line and tube rendering." )
{
// we also provide a property
WPropBool whiteEdge = m_properties->addProperty( "White Edge", "If set, the edge is drawn in white instead of black.", false );
WPropDouble edgeThresholdL = m_properties->addProperty( "Edge Lower Threshold", "Define the edge threshold. Filters way \"weak\" edges.", 0.0 );
WPropDouble edgeThresholdU = m_properties->addProperty( "Edge Upper Threshold", "Define the edge threshold. Filters way \"weak\" edges.", 1.0 );
edgeThresholdL->setMin( 0.0 );
edgeThresholdL->setMax( 1.0 );
edgeThresholdU->setMin( 0.0 );
edgeThresholdU->setMax( 1.0 );
// Use the standard postprocessor uber-shader
WGEShader::RefPtr s = new WGEShader( "WGEPostprocessor" );
s->setDefine( "WGE_POSTPROCESSOR_EDGE" );
// also add the m_effectOnly property as shader preprocessor
s->addPreprocessor( m_effectOnlyPreprocessor );
s->addPreprocessor( WGEShaderPreprocessor::SPtr(
new WGEShaderPropertyDefineOptions< WPropBool >( whiteEdge, "WGE_POSTPROCESSOR_EDGE_BLACKEDGE", "WGE_POSTPROCESSOR_EDGE_WHITEEDGE" ) )
);
// create the rendering pass
osg::ref_ptr< WGEOffscreenTexturePass > pass = offscreen->addTextureProcessingPass( s, "Edge Detection" );
pass->getOrCreateStateSet()->addUniform( new WGEPropertyUniform< WPropDouble >( "u_edgeEdgeThresholdUpper", edgeThresholdU ) );
pass->getOrCreateStateSet()->addUniform( new WGEPropertyUniform< WPropDouble >( "u_edgeEdgeThresholdLower", edgeThresholdL ) );
// attach color0 output
m_resultTexture = pass->attach( osg::Camera::COLOR_BUFFER0, GL_RGB );
// provide the Gbuffer input
gbuffer.bind( pass );
}
WGEPostprocessorLineAO::~WGEPostprocessorLineAO()
{
// cleanup
}
WGEPostprocessor::SPtr WGEPostprocessorLineAO::create( osg::ref_ptr< WGEOffscreenRenderNode > offscreen,
const WGEPostprocessor::PostprocessorInput& gbuffer ) const
{
return WGEPostprocessor::SPtr( new WGEPostprocessorLineAO( 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 WGEPOSTPROCESSORLINEAO_H
#define WGEPOSTPROCESSORLINEAO_H
#include <boost/shared_ptr.hpp>
#include "WGEPostprocessor.h"
/**
* LineAO implementation.
*/
class WGEPostprocessorLineAO: public WGEPostprocessor
{
public:
/**
* Convenience typedef for a boost::shared_ptr< WGEPostprocessorLineAO >.
*/
typedef boost::shared_ptr< WGEPostprocessorLineAO > SPtr;
/**
* Convenience typedef for a boost::shared_ptr< const WGEPostprocessorLineAO >.
*/
typedef boost::shared_ptr< const WGEPostprocessorLineAO > ConstSPtr;
/**
* Default constructor.
*/
WGEPostprocessorLineAO();
/**
* Destructor.
*/
virtual ~WGEPostprocessorLineAO();
/**
* 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
*/
WGEPostprocessorLineAO( osg::ref_ptr< WGEOffscreenRenderNode > offscreen, const PostprocessorInput& gbuffer );
private:
};
#endif // WGEPOSTPROCESSORLINEAO_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 "../WGETextureUtils.h"
#include "../shaders/WGEPropertyUniform.h"
#include "../shaders/WGEShaderPropertyDefine.h"
#include "WGEPostprocessorSSAO.h"
WGEPostprocessorSSAO::WGEPostprocessorSSAO():
WGEPostprocessor( "SSAO",
"SSAO is a special ambient occlusion technique." )
{
}
WGEPostprocessorSSAO::WGEPostprocessorSSAO( osg::ref_ptr< WGEOffscreenRenderNode > offscreen,
const WGEPostprocessor::PostprocessorInput& gbuffer ):
WGEPostprocessor( offscreen, gbuffer,
"SSAO",
"SSAO is a special ambient occlusion technique." )
{
// the SSAO algorithm has some parameters. Provide these parameters to the user
WPropInt ssaoSamples = m_properties->addProperty( "Samples", "The number of samples to take in screen-space. Higher values produce better "
"quality but can reduce FPS dramatically.", 16 );
ssaoSamples->setMin( 1 );
ssaoSamples->setMax( 128 );
WPropDouble ssaoRadius = m_properties->addProperty( "Radius", "The radius around the pixel to sample for occluders in pixels.", 10.0 );
ssaoRadius->setMin( 0.0 );
ssaoRadius->setMax( 100.0 );
WPropDouble ssaoTotalStrength = m_properties->addProperty( "Total Strength", "The strength of the effect. Higher values emphasize the effect.",
2.0 );
ssaoTotalStrength->setMin( 0.0 );
ssaoTotalStrength->setMax( 100.0 );
WPropDouble ssaoStrength = m_properties->addProperty( "Strength", "This defines the influence of one occluder to the overall AO effect.", 1.0 );
ssaoStrength->setMin( 0.0 );
ssaoStrength->setMax( 1.0 );
WPropDouble ssaoFalloff = m_properties->addProperty( "Falloff", "Define the edge at which a depth difference between two pixels is assumed to "
"be non-zero.", 0.0 );
ssaoFalloff->setMin( 0.0 );
ssaoFalloff->setMax( 1.0 );
// Use the standard postprocessor uber-shader
WGEShader::RefPtr s = new WGEShader( "WGEPostprocessor" );
s->setDefine( "WGE_POSTPROCESSOR_SSAO" );
// also add the m_effectOnly property as shader preprocessor
s->addPreprocessor( m_effectOnlyPreprocessor );
s->addPreprocessor( WGEShaderPreprocessor::SPtr(
new WGEShaderPropertyDefine< WPropInt >( "WGE_POSTPROCESSOR_SSAO_SAMPLES", ssaoSamples ) )
);
// create the rendering pass
osg::ref_ptr< WGEOffscreenTexturePass > pass = offscreen->addTextureProcessingPass( s, "SSAO" );
pass->getOrCreateStateSet()->addUniform( new WGEPropertyUniform< WPropDouble >( "u_ssaoTotalStrength", ssaoTotalStrength ) );
pass->getOrCreateStateSet()->addUniform( new WGEPropertyUniform< WPropDouble >( "u_ssaoStrength", ssaoStrength ) );
pass->getOrCreateStateSet()->addUniform( new WGEPropertyUniform< WPropDouble >( "u_ssaoRadius", ssaoRadius ) );
pass->getOrCreateStateSet()->addUniform( new WGEPropertyUniform< WPropDouble >( "u_ssaoFalloff", ssaoFalloff ) );
// attach color0 output
m_resultTexture = pass->attach( osg::Camera::COLOR_BUFFER0, GL_RGB );
// provide the Gbuffer input
size_t gBufUnitOffset = gbuffer.bind( pass );
// this effect needs some additional noise texture:
// some of the post-processors need some white noise, like the ssao
const size_t size = 64;
osg::ref_ptr< WGETexture2D > randTex = wge::genWhiteNoiseTexture( size, size, 3 );
pass->bind( randTex, gBufUnitOffset );
}
WGEPostprocessorSSAO::~WGEPostprocessorSSAO()
{
// cleanup
}
WGEPostprocessor::SPtr WGEPostprocessorSSAO::create( osg::ref_ptr< WGEOffscreenRenderNode > offscreen,
const WGEPostprocessor::PostprocessorInput& gbuffer ) const
{
return WGEPostprocessor::SPtr( new WGEPostprocessorSSAO( 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 WGEPOSTPROCESSORSSAO_H
#define WGEPOSTPROCESSORSSAO_H
#include <boost/shared_ptr.hpp>
#include "WGEPostprocessor.h"
/**
* Naive SSAO implementation.
*/
class WGEPostprocessorSSAO: public WGEPostprocessor
{
public:
/**
* Convenience typedef for a boost::shared_ptr< WGEPostprocessorSSAO >.
*/
typedef boost::shared_ptr< WGEPostprocessorSSAO > SPtr;
/**
* Convenience typedef for a boost::shared_ptr< const WGEPostprocessorSSAO >.
*/
typedef boost::shared_ptr< const WGEPostprocessorSSAO > ConstSPtr;
/**
* Default constructor.
*/
WGEPostprocessorSSAO();
/**
* Destructor.
*/
virtual ~WGEPostprocessorSSAO();
/**
* 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
*/
WGEPostprocessorSSAO( osg::ref_ptr< WGEOffscreenRenderNode > offscreen, const PostprocessorInput& gbuffer );
private:
};
#endif // WGEPOSTPROCESSORSSAO_H
......@@ -32,21 +32,10 @@
#include "WGEPostprocessorUtils-fragment.glsl"
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Uniforms
// Global Uniforms
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// These are specific to the postprocessor
/**
* The number of bins to use for cel shading
*/
uniform int u_celShadingBins = 2;
/**
* The threshold used to clip away "unwanted" borders. Basically removes noise
*/
uniform float u_edgeEdgeThresholdLower = 0.25;
uniform float u_edgeEdgeThresholdUpper = 0.75;
// These are NOT specific to any postprocessor
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Varying
......@@ -64,6 +53,14 @@ uniform float u_edgeEdgeThresholdUpper = 0.75;
// Postprocessors
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef WGE_POSTPROCESSOR_EDGE
/**
* The threshold used to clip away "unwanted" borders. Basically removes noise
*/
uniform float u_edgeEdgeThresholdLower = 0.25;
uniform float u_edgeEdgeThresholdUpper = 0.75;
/**
* Apply laplace-filter to depth buffer. An edge is > 0.0.
*
......@@ -105,6 +102,15 @@ float getEdge()
return smoothstep( u_edgeEdgeThresholdLower, u_edgeEdgeThresholdUpper, edge );
}
#endif
#ifdef WGE_POSTPROCESSOR_CEL
/**
* The number of bins to use for cel shading
*/
uniform int u_celShadingBins = 2;
/**
* Calculate the cel-shading of a specified color. The number of colors is defined by the u_celShadingSamples uniform.
*
......@@ -131,6 +137,111 @@ vec4 getCelShading()
return getCelShading( getColor() );
}
#endif
#ifdef WGE_POSTPROCESSOR_SSAO
#ifndef WGE_POSTPROCESSOR_SSAO_SAMPLES
#define WGE_POSTPROCESSOR_SSAO_SAMPLES 16
#endif
/**
* Overall effect strength. In (0,100]
*/
uniform float u_ssaoTotalStrength = 2.0;
/**
* Strength of occlusion caused by one occluder. In (0,1]
*/
uniform float u_ssaoStrength = 1.0;
/**
* Radius in screen-space. This value influences whether local or global effects influence the occlusion. In (0, 100];
*/
uniform float u_ssaoRadius = 10.0;
/**
* Defines a falloff at which the depthDifference is assumed to be 0 (occluder is on same height)
*/
uniform float u_ssaoFalloff = 0.0;
/**
* Calculate SSAO effect for the specified pixel. Requires a noise texture in u_noiseSampler. This implementation is taken from
* http://www.gamerendering.com/2009/01/14/ssao/ and slightly modified. We added a small hack to provide scaling-invariance to the effect.
*
* \param where coordinate in screenspace
* \param radius additional radius scaler.
*
* \return AO
*/
float getSSAO( vec2 where, float radius )
{
float totStrength = u_ssaoTotalStrength;
float strength = u_ssaoStrength;
float falloff = u_ssaoFalloff;
float rad = radius * u_ssaoRadius;
#define SAMPLES WGE_POSTPROCESSOR_SSAO_SAMPLES
const float invSamples = 1.0/ float( SAMPLES );
// grab a normal for reflecting the sample rays later on
vec3 fres = normalize( ( texture2D( u_noiseSampler, where * u_noiseSizeX ).xyz * 2.0 ) - vec3( 1.0 ) );
vec4 currentPixelSample = getNormal( where );
float currentPixelDepth = getDepth( where );
float radiusSS = ( getZoom() * u_ssaoRadius / float( u_texture0SizeX ) ) / ( 1.0 - currentPixelDepth );
// current fragment coords in screen space
vec3 ep = vec3( where.xy, currentPixelDepth );
// get the normal of current fragment
vec3 norm = currentPixelSample.xyz;
float bl = 0.0;
// adjust for the depth ( not shure if this is good..)
float radD = rad/currentPixelDepth;
vec3 ray, se;
float occluderDepth, depthDifference, normDiff;
for( int i = 0; i < SAMPLES; ++i )
{
// get a vector (randomized inside of a sphere with radius 1.0) from a texture and reflect it
// grab a rand normal from the noise texture
vec3 randSphereNormal = getNoiseAsVector( vec2( float( i ) / float( SAMPLES ), fres.y ) );
ray = radiusSS * reflect( randSphereNormal, fres );
// if the ray is outside the hemisphere then change direction
se = ep + sign( dot( ray, norm ) ) * ray;
// get the depth of the occluder fragment
vec4 occluderFragment = getNormal( se.xy );
occluderFragment.a = getDepth( se.xy );
// if depthDifference is negative = occluder is behind current fragment
depthDifference = currentPixelDepth - occluderFragment.a;
// calculate the difference between the normals as a weight
normDiff = ( 1.0 - dot( occluderFragment.xyz, norm ) );
// the falloff equation, starts at falloff and is kind of 1/x^2 falling
bl += step( falloff, depthDifference ) * normDiff * ( 1.0 - smoothstep( falloff, strength, depthDifference ) );
}
// output the result
return 1.0 - totStrength * bl * invSamples;
}
/**
* Calculate the screen-space ambient occlusion from normal and depth map for the current pixel.
*
* \return the SSAO factor
*/
float getSSAO()
{
return getSSAO( pixelCoord, 1.0 );
}
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Main
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
......@@ -143,6 +254,7 @@ void main()
// don't do this stuff for background pixel
float depth = getDepth();
gl_FragDepth = depth;
gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
if( depth > 0.99 )
{
discard;
......@@ -178,6 +290,15 @@ void main()
// output the depth and final color.
gl_FragColor = getCelShading();
#endif