Skip to content

Commit

Permalink
[dataflow][rendering] add ssao node
Browse files Browse the repository at this point in the history
  • Loading branch information
MathiasPaulin committed Nov 8, 2022
1 parent 4eaec55 commit c1d4d3f
Show file tree
Hide file tree
Showing 6 changed files with 402 additions and 0 deletions.
234 changes: 234 additions & 0 deletions src/Dataflow/Rendering/Nodes/RenderNodes/SsaoRenderNode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
#include <Dataflow/Rendering/Nodes/RenderNodes/SsaoRenderNode.hpp>

#include <Core/Geometry/MeshPrimitives.hpp>
#include <Engine/Data/Mesh.hpp>
#include <Engine/Data/RenderParameters.hpp>
#include <Engine/Data/ShaderConfiguration.hpp>
#include <Engine/Data/ShaderProgramManager.hpp>
#include <Engine/RadiumEngine.hpp>

namespace Ra {
namespace Dataflow {
namespace Rendering {
namespace Nodes {

using namespace Ra::Engine::Data;

SsaoNode::SsaoNode( const std::string& name ) : RenderingNode( name, getTypename() ) {
addInput( m_inWorldPos );
m_inWorldPos->mustBeLinked();

addInput( m_inWorldNormal );
m_inWorldNormal->mustBeLinked();

addInput( m_inCamera );
m_inCamera->mustBeLinked();

addInput( m_aoRadius );
addInput( m_aoSamples );

Ra::Engine::Data::TextureParameters texParams;
texParams.target = gl::GL_TEXTURE_2D;
texParams.minFilter = gl::GL_LINEAR;
texParams.magFilter = gl::GL_LINEAR;
texParams.internalFormat = gl::GL_RGBA32F;
texParams.format = gl::GL_RGBA;
texParams.type = gl::GL_FLOAT;
texParams.name = "ssao";
m_AO = new Ra::Engine::Data::Texture( texParams );

addOutput( m_ssao, m_AO );

auto editableRadius = new EditableParameter( "radius", m_editableAORadius );
editableRadius->addAdditionalData( 0. );
editableRadius->addAdditionalData( 100. );
addEditableParameter( editableRadius );

auto editableSamples = new EditableParameter( "samples", m_editableSamples );
editableSamples->addAdditionalData( 0 );
editableSamples->addAdditionalData( 4096 );
addEditableParameter( editableSamples );
}

void SsaoNode::init() {
Ra::Engine::Data::TextureParameters texParams;
texParams.target = gl::GL_TEXTURE_2D;
texParams.minFilter = gl::GL_LINEAR;
texParams.magFilter = gl::GL_LINEAR;
texParams.internalFormat = gl::GL_RGBA32F;
texParams.format = gl::GL_RGBA;
texParams.type = gl::GL_FLOAT;
texParams.name = "Raw Ambient Occlusion";
m_rawAO = new Ra::Engine::Data::Texture( texParams );

Ra::Core::Geometry::TriangleMesh mesh =
Ra::Core::Geometry::makeZNormalQuad( Ra::Core::Vector2( -1.f, 1.f ) );
auto qm = std::make_unique<Ra::Engine::Data::Mesh>( "caller" );
qm->loadGeometry( std::move( mesh ) );
m_quadMesh = std::move( qm );
m_quadMesh->updateGL();

// TODO make the sampling method an editable parameter
m_sphereSampler =
std::make_unique<SphereSampler>( SphereSampler::SamplingMethod::HAMMERSLEY, 64 );

m_blurFramebuffer = new globjects::Framebuffer();
m_framebuffer = new globjects::Framebuffer();
}

void SsaoNode::destroy() {
delete m_rawAO;
delete m_AO;
delete m_blurFramebuffer;
delete m_framebuffer;
}

void SsaoNode::resize( uint32_t width, uint32_t height ) {
m_rawAO->resize( width, height );
m_AO->resize( width, height );

m_framebuffer->bind();
m_framebuffer->attachTexture( gl::GL_COLOR_ATTACHMENT0, m_rawAO->texture() );

m_blurFramebuffer->bind();
m_blurFramebuffer->attachTexture( gl::GL_COLOR_ATTACHMENT0, m_AO->texture() );

globjects::Framebuffer::unbind();
}

bool SsaoNode::execute() {
auto aabb = Ra::Engine::RadiumEngine::getInstance()->computeSceneAabb();
if ( aabb.isEmpty() ) { m_sceneDiag = 1_ra; }
else {
m_sceneDiag = aabb.diagonal().norm();
}

Ra::Engine::Data::RenderParameters inPassParams;
// Positions
auto posTexture = &m_inWorldPos->getData();
// Normals
auto normalTexture = &m_inWorldNormal->getData();

// AO Radius
auto aoRadius = m_editableAORadius;
if ( m_aoRadius->isLinked() ) { aoRadius = m_aoRadius->getData(); }

// AO Samples
unsigned int samples = m_editableSamples;
if ( m_aoSamples->isLinked() ) { samples = m_aoSamples->getData(); }
if ( m_currentSamples != samples ) {
m_currentSamples = samples;
m_sphereSampler = std::make_unique<SphereSampler>(
SphereSampler::SamplingMethod::HAMMERSLEY, m_currentSamples );
}

// Cameras
auto& camera = m_inCamera->getData();

m_framebuffer->bind();
const gl::GLenum buffers[] = { gl::GL_COLOR_ATTACHMENT0 };
gl::glDrawBuffers( 1, buffers );
float clearWhite[4] = { 1.0f, 1.0f, 1.0f, 0.0f };
gl::glClearBufferfv( gl::GL_COLOR, 0, clearWhite );
gl::glDisable( gl::GL_DEPTH_TEST );
gl::glDepthMask( gl::GL_FALSE );

m_shader->bind();
Ra::Core::Matrix4 viewProj = camera.projMatrix * camera.viewMatrix;

m_shader->setUniform( "transform.mvp", viewProj );
m_shader->setUniform( "transform.proj", camera.projMatrix );
m_shader->setUniform( "transform.view", camera.viewMatrix );

m_shader->setUniform( "normal_sampler", normalTexture, 0 );
m_shader->setUniform( "position_sampler", posTexture, 1 );
m_shader->setUniform( "dir_sampler", m_sphereSampler->asTexture(), 2 );
m_shader->setUniform( "ssdoRadius", aoRadius / 100_ra * m_sceneDiag );

m_quadMesh->render( m_shader );
gl::glEnable( gl::GL_DEPTH_TEST );
gl::glDepthMask( gl::GL_TRUE );
m_framebuffer->unbind();

m_blurFramebuffer->bind();
m_blurShader->bind();
gl::glDrawBuffers( 1, buffers );
gl::glClearBufferfv( gl::GL_COLOR, 0, clearWhite );
gl::glDisable( gl::GL_DEPTH_TEST );
gl::glDepthMask( gl::GL_FALSE );

m_shader->setUniform( "transform.mvp", viewProj );
m_shader->setUniform( "transform.proj", camera.projMatrix );
m_shader->setUniform( "transform.view", camera.viewMatrix );
m_shader->setUniform( "ao_sampler", m_rawAO, 0 );

m_quadMesh->render( m_shader );
gl::glEnable( gl::GL_DEPTH_TEST );
gl::glDepthMask( gl::GL_TRUE );
m_blurFramebuffer->unbind();

return true;
}

void SsaoNode::toJsonInternal( nlohmann::json& data ) const {
if ( m_currentSamples != AO_DefaultSamples ) {
// do not write default value
data["samples"] = m_currentSamples;
}
if ( m_editableAORadius != AO_DefaultRadius ) {
// do not write default value
data["radius"] = m_editableAORadius;
}
}

bool SsaoNode::fromJsonInternal( const nlohmann::json& data ) {
if ( data.contains( "radius" ) ) { m_editableAORadius = data["radius"]; }
if ( data.contains( "samples" ) ) {
m_currentSamples = data["samples"];
m_editableSamples = Scalar( m_currentSamples );
}
return true;
}

bool SsaoNode::initInternalShaders() {
if ( !m_hasShaders ) {
const std::string vertexShaderSource { "layout (location = 0) in vec3 in_position;\n"
"out vec2 varTexcoord;\n"
"void main(void) {\n"
" gl_Position = vec4(in_position.xyz, 1.0);\n"
" varTexcoord = (in_position.xy + 1.0) / 2.0;\n"
"}" };
std::string resourcesRootDir = m_resourceDir + "Shaders/SsaoNode/";

Ra::Engine::Data::ShaderConfiguration ssdoConfig { "SSDO" };
ssdoConfig.addShaderSource( Ra::Engine::Data::ShaderType::ShaderType_VERTEX,
vertexShaderSource );
ssdoConfig.addShader( Ra::Engine::Data::ShaderType::ShaderType_FRAGMENT,
resourcesRootDir + "ssao.frag.glsl" );
auto added = m_shaderMngr->addShaderProgram( ssdoConfig );
if ( added ) { m_shader = added.value(); }
else {
std::cout << "AO: Could not add shader." << std::endl;
return false;
}

Ra::Engine::Data::ShaderConfiguration blurssdoConfig { "blurSSDO" };
blurssdoConfig.addShaderSource( Ra::Engine::Data::ShaderType::ShaderType_VERTEX,
vertexShaderSource );
blurssdoConfig.addShader( Ra::Engine::Data::ShaderType::ShaderType_FRAGMENT,
resourcesRootDir + "blurao.frag.glsl" );
added = m_shaderMngr->addShaderProgram( blurssdoConfig );
if ( added ) { m_blurShader = added.value(); }
else {
std::cout << "AO: Could not add shader." << std::endl;
return false;
}
m_hasShaders = true;
}
return m_hasShaders;
}

} // namespace Nodes
} // namespace Rendering
} // namespace Dataflow
} // namespace Ra
79 changes: 79 additions & 0 deletions src/Dataflow/Rendering/Nodes/RenderNodes/SsaoRenderNode.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#pragma once
#include <Dataflow/RaDataflow.hpp>

#include <Dataflow/Rendering/Nodes/RenderingNode.hpp>

#include <globjects/Framebuffer.h>
#include <globjects/State.h>

#include <Engine/Data/SphereSampler.hpp>

namespace Ra {
namespace Dataflow {
namespace Rendering {
namespace Nodes {
using namespace Ra::Dataflow::Core;
using namespace Ra::Engine::Data;

/**
* \brief compute ssao in image space from world space geometry buffer.
*
*/
class RA_DATAFLOW_API SsaoNode : public RenderingNode
{
public:
explicit SsaoNode( const std::string& name );

void init() override;
bool execute() override;
void destroy() override;
void resize( uint32_t width, uint32_t height ) override;
bool initInternalShaders() override;

// SSao does not need rendertechnique
bool hasRenderTechnique() override { return false; }

static const std::string getTypename() { return "SSAO Node"; }

protected:
void toJsonInternal( nlohmann::json& data ) const override;
bool fromJsonInternal( const nlohmann::json& data ) override;

private:
bool m_hasShaders { false };

static constexpr Scalar AO_DefaultRadius { 5_ra };
static constexpr int AO_DefaultSamples { 64 };

TextureType* m_rawAO { nullptr };
TextureType* m_AO { nullptr };

Scalar m_editableAORadius { AO_DefaultRadius };
int m_editableSamples { AO_DefaultSamples };

int m_currentSamples { AO_DefaultSamples };
Scalar m_sceneDiag { 1.0 };

std::unique_ptr<Ra::Engine::Data::Displayable> m_quadMesh { nullptr };
std::unique_ptr<SphereSampler> m_sphereSampler { nullptr };

const Ra::Engine::Data::ShaderProgram* m_shader { nullptr };
const Ra::Engine::Data::ShaderProgram* m_blurShader { nullptr };

globjects::Framebuffer* m_blurFramebuffer { nullptr };
globjects::Framebuffer* m_framebuffer { nullptr };

PortIn<TextureType>* m_inWorldPos { new PortIn<TextureType>( "worldPosition", this ) };
PortIn<TextureType>* m_inWorldNormal { new PortIn<TextureType>( "worldNormal", this ) };
PortIn<CameraType>* m_inCamera { new PortIn<CameraType>( "camera", this ) };

PortIn<Scalar>* m_aoRadius { new PortIn<Scalar>( "radius", this ) };
PortIn<int>* m_aoSamples { new PortIn<int>( "samples", this ) };

PortOut<TextureType>* m_ssao { new PortOut<TextureType>( "ssao", this ) };
};

} // namespace Nodes
} // namespace Rendering
} // namespace Dataflow
} // namespace Ra
10 changes: 10 additions & 0 deletions src/Dataflow/Rendering/Nodes/RenderingBuiltInsNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ DATAFLOW_LIBRARY_INITIALIZER_DECL( RenderingNodes );
#include <Dataflow/Rendering/Nodes/RenderNodes/EmissivityRenderNode.hpp>
#include <Dataflow/Rendering/Nodes/RenderNodes/GeometryAovsNode.hpp>
#include <Dataflow/Rendering/Nodes/RenderNodes/SimpleRenderNode.hpp>
#include <Dataflow/Rendering/Nodes/RenderNodes/SsaoRenderNode.hpp>

#include <Dataflow/Rendering/RenderingGraph.hpp>

Expand Down Expand Up @@ -90,6 +91,15 @@ std::string registerRenderingNodesFactories() {
},
"Render" );

renderingFactory->registerNodeCreator<SsaoNode>(
[resourcesPath, renderingFactory]( const nlohmann::json& data ) {
auto node = new SsaoNode( "Ssao_" + std::to_string( renderingFactory->nextNodeId() ) );
node->fromJson( data );
node->setResourcesDir( resourcesPath );
return node;
},
"Render" );

/* --- Graphs --- */
renderingFactory->registerNodeCreator<RenderingGraph>( RenderingGraph::getTypename() + "_",
"Graph" );
Expand Down
20 changes: 20 additions & 0 deletions src/Dataflow/Rendering/Shaders/SsaoNode/blurao.frag.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
layout (location = 0) out vec4 out_ssao;

uniform sampler2D ao_sampler;
in vec2 varTexcoord;

const int half_width = 2;

void main() {
vec2 texelSize = 1.0 / vec2(textureSize(ao_sampler, 0));
float result = 0.0;
for (int x = -half_width; x < half_width; ++x)
{
for (int y = -half_width; y < half_width; ++y)
{
vec2 offset = vec2(float(x), float(y)) * texelSize;
result += texture(ao_sampler, varTexcoord + offset).r;
}
}
out_ssao = vec4(vec3(result / (4 * half_width * half_width)), 1);
}
Loading

0 comments on commit c1d4d3f

Please sign in to comment.