aboutsummaryrefslogtreecommitdiff
path: root/apps/openmb/renderer/SSAORenderer.cpp
diff options
context:
space:
mode:
authorEthan Morgan <ethan@gweithio.com>2026-02-14 16:44:06 +0000
committerEthan Morgan <ethan@gweithio.com>2026-02-14 16:44:06 +0000
commit54409423f767d8b1cf30cb7d0efca6b4ca138823 (patch)
treed915ac7828703ce4b963efdd9728a1777ba18c1e /apps/openmb/renderer/SSAORenderer.cpp
move to own git serverHEADmaster
Diffstat (limited to 'apps/openmb/renderer/SSAORenderer.cpp')
-rw-r--r--apps/openmb/renderer/SSAORenderer.cpp319
1 files changed, 319 insertions, 0 deletions
diff --git a/apps/openmb/renderer/SSAORenderer.cpp b/apps/openmb/renderer/SSAORenderer.cpp
new file mode 100644
index 0000000..8493529
--- /dev/null
+++ b/apps/openmb/renderer/SSAORenderer.cpp
@@ -0,0 +1,319 @@
+#include "SSAORenderer.hpp"
+
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+#ifdef __APPLE__
+#include <OpenGL/gl3.h>
+#endif
+#include <glm/gtc/type_ptr.hpp>
+#include <iostream>
+#include <random>
+
+namespace renderer {
+
+SSAORenderer::SSAORenderer ()
+ : mWidth( 0 ), mHeight( 0 ), mHalfRes( true ), mKernelSize( 32 ), mGBufferFBO( 0 ), mGNormal( 0 ), mGDepth( 0 ),
+ mSSAOFBO( 0 ), mSSAOTexture( 0 ), mSSAOBlurFBO( 0 ), mSSAOBlurTexture( 0 ), mNoiseTexture( 0 ), mQuadVAO( 0 ),
+ mQuadVBO( 0 ) {
+}
+
+SSAORenderer::~SSAORenderer () {
+ if( mGNormal )
+ glDeleteTextures( 1, &mGNormal );
+ if( mGDepth )
+ glDeleteTextures( 1, &mGDepth );
+ if( mGBufferFBO )
+ glDeleteFramebuffers( 1, &mGBufferFBO );
+ if( mSSAOTexture )
+ glDeleteTextures( 1, &mSSAOTexture );
+ if( mSSAOBlurTexture )
+ glDeleteTextures( 1, &mSSAOBlurTexture );
+ if( mSSAOFBO )
+ glDeleteFramebuffers( 1, &mSSAOFBO );
+ if( mSSAOBlurFBO )
+ glDeleteFramebuffers( 1, &mSSAOBlurFBO );
+ if( mNoiseTexture )
+ glDeleteTextures( 1, &mNoiseTexture );
+ if( mQuadVBO )
+ glDeleteBuffers( 1, &mQuadVBO );
+ if( mQuadVAO )
+ glDeleteVertexArrays( 1, &mQuadVAO );
+}
+
+bool SSAORenderer::init ( int width, int height, bool halfRes ) {
+ mWidth = width;
+ mHeight = height;
+ mHalfRes = halfRes;
+
+ if( !mGBufferShader.fromFiles( "apps/openmb/resources/shaders/gbuffer.vert",
+ "apps/openmb/resources/shaders/gbuffer.frag" ) ) {
+ std::cerr << "Failed to load gbuffer shader" << std::endl;
+ return false;
+ }
+ if( !mSSAOShader.fromFiles( "apps/openmb/resources/shaders/ssao.vert",
+ "apps/openmb/resources/shaders/ssao.frag" ) ) {
+ std::cerr << "Failed to load ssao shader" << std::endl;
+ return false;
+ }
+ if( !mBlurShader.fromFiles( "apps/openmb/resources/shaders/ssao.vert",
+ "apps/openmb/resources/shaders/ssao_blur.frag" ) ) {
+ std::cerr << "Failed to load ssao blur shader" << std::endl;
+ return false;
+ }
+
+ initBuffers();
+ initKernelAndNoise();
+
+ float quadVertices[] = {
+
+ -1.0f,
+ -1.0f,
+ 0.0f,
+ 0.0f,
+ 1.0f,
+ -1.0f,
+ 1.0f,
+ 0.0f,
+ -1.0f,
+ 1.0f,
+ 0.0f,
+ 1.0f,
+ -1.0f,
+ 1.0f,
+ 0.0f,
+ 1.0f,
+ 1.0f,
+ -1.0f,
+ 1.0f,
+ 0.0f,
+ 1.0f,
+ 1.0f,
+ 1.0f,
+ 1.0f,
+ };
+
+ glGenVertexArrays( 1, &mQuadVAO );
+ glGenBuffers( 1, &mQuadVBO );
+ glBindVertexArray( mQuadVAO );
+ glBindBuffer( GL_ARRAY_BUFFER, mQuadVBO );
+ glBufferData( GL_ARRAY_BUFFER, sizeof( quadVertices ), quadVertices, GL_STATIC_DRAW );
+ glEnableVertexAttribArray( 0 );
+ glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof( float ), (void*)0 );
+ glEnableVertexAttribArray( 1 );
+ glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof( float ), (void*)( 2 * sizeof( float ) ) );
+ glBindVertexArray( 0 );
+
+ return true;
+}
+
+void SSAORenderer::initBuffers () {
+
+ if( mGBufferFBO == 0 )
+ glGenFramebuffers( 1, &mGBufferFBO );
+ glBindFramebuffer( GL_FRAMEBUFFER, mGBufferFBO );
+
+ if( mGNormal )
+ glDeleteTextures( 1, &mGNormal );
+ glGenTextures( 1, &mGNormal );
+ glBindTexture( GL_TEXTURE_2D, mGNormal );
+ glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB16F, mWidth, mHeight, 0, GL_RGB, GL_FLOAT, nullptr );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+ glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mGNormal, 0 );
+
+ if( mGDepth )
+ glDeleteTextures( 1, &mGDepth );
+ glGenTextures( 1, &mGDepth );
+ glBindTexture( GL_TEXTURE_2D, mGDepth );
+ glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, mWidth, mHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE );
+ glFramebufferTexture2D( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, mGDepth, 0 );
+
+ GLenum attachments[1] = { GL_COLOR_ATTACHMENT0 };
+ glDrawBuffers( 1, attachments );
+
+ if( glCheckFramebufferStatus( GL_FRAMEBUFFER ) != GL_FRAMEBUFFER_COMPLETE )
+ std::cerr << "GBuffer not complete" << std::endl;
+ glBindFramebuffer( GL_FRAMEBUFFER, 0 );
+
+ if( mSSAOFBO == 0 )
+ glGenFramebuffers( 1, &mSSAOFBO );
+ glBindFramebuffer( GL_FRAMEBUFFER, mSSAOFBO );
+ if( mSSAOTexture )
+ glDeleteTextures( 1, &mSSAOTexture );
+ glGenTextures( 1, &mSSAOTexture );
+ glBindTexture( GL_TEXTURE_2D, mSSAOTexture );
+ int w = mHalfRes ? std::max( 1, mWidth / 2 ) : mWidth;
+ int h = mHalfRes ? std::max( 1, mHeight / 2 ) : mHeight;
+ glTexImage2D( GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+ glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mSSAOTexture, 0 );
+ if( glCheckFramebufferStatus( GL_FRAMEBUFFER ) != GL_FRAMEBUFFER_COMPLETE )
+ std::cerr << "SSAO FBO not complete" << std::endl;
+ glBindFramebuffer( GL_FRAMEBUFFER, 0 );
+
+ if( mSSAOBlurFBO == 0 )
+ glGenFramebuffers( 1, &mSSAOBlurFBO );
+ glBindFramebuffer( GL_FRAMEBUFFER, mSSAOBlurFBO );
+ if( mSSAOBlurTexture )
+ glDeleteTextures( 1, &mSSAOBlurTexture );
+ glGenTextures( 1, &mSSAOBlurTexture );
+ glBindTexture( GL_TEXTURE_2D, mSSAOBlurTexture );
+ glTexImage2D( GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+ glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mSSAOBlurTexture, 0 );
+ if( glCheckFramebufferStatus( GL_FRAMEBUFFER ) != GL_FRAMEBUFFER_COMPLETE )
+ std::cerr << "SSAO Blur FBO not complete" << std::endl;
+ glBindFramebuffer( GL_FRAMEBUFFER, 0 );
+}
+
+void SSAORenderer::initKernelAndNoise () {
+ mKernel.clear();
+ std::uniform_real_distribution<float> randomFloats( 0.0f, 1.0f );
+ std::mt19937 gen;
+
+ for( int i = 0; i < mKernelSize; ++i ) {
+ glm::vec3 sample( randomFloats( gen ) * 2.0f - 1.0f, randomFloats( gen ) * 2.0f - 1.0f,
+ randomFloats( gen ) );
+ sample = glm::normalize( sample );
+ sample *= randomFloats( gen );
+ float scale = float( i ) / float( mKernelSize );
+ scale = glm::mix( 0.1f, 1.0f, scale * scale );
+ sample *= scale;
+ mKernel.push_back( sample );
+ }
+
+ mSSAOShader.use();
+ for( int i = 0; i < mKernelSize; ++i ) {
+ mSSAOShader.setVec3( std::string( "samples[" ) + std::to_string( i ) + std::string( "]" ), mKernel[i] );
+ }
+ mSSAOShader.setInt( "kernelSize", mKernelSize );
+
+ std::vector<float> noise;
+ for( int i = 0; i < 16; ++i ) {
+ float x = randomFloats( gen ) * 2.0f - 1.0f;
+ float y = randomFloats( gen ) * 2.0f - 1.0f;
+ noise.push_back( x );
+ noise.push_back( y );
+ noise.push_back( 0.0f );
+ }
+ if( mNoiseTexture )
+ glDeleteTextures( 1, &mNoiseTexture );
+ glGenTextures( 1, &mNoiseTexture );
+ glBindTexture( GL_TEXTURE_2D, mNoiseTexture );
+ glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB32F, 4, 4, 0, GL_RGB, GL_FLOAT, noise.data() );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
+ glBindTexture( GL_TEXTURE_2D, 0 );
+}
+
+void SSAORenderer::resize ( int width, int height ) {
+ if( width == mWidth && height == mHeight )
+ return;
+ mWidth = width;
+ mHeight = height;
+ initBuffers();
+}
+
+void SSAORenderer::bindGBuffer () {
+ glBindFramebuffer( GL_FRAMEBUFFER, mGBufferFBO );
+ glViewport( 0, 0, mWidth, mHeight );
+ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+}
+
+void SSAORenderer::unbind () {
+ glBindFramebuffer( GL_FRAMEBUFFER, 0 );
+}
+
+void SSAORenderer::computeSSAO ( const glm::mat4& proj, const glm::mat4& invProj, float radius, float bias,
+ float power ) {
+ int w = mHalfRes ? std::max( 1, mWidth / 2 ) : mWidth;
+ int h = mHalfRes ? std::max( 1, mHeight / 2 ) : mHeight;
+
+ glBindFramebuffer( GL_FRAMEBUFFER, mSSAOFBO );
+ glViewport( 0, 0, w, h );
+ glClear( GL_COLOR_BUFFER_BIT );
+
+ mSSAOShader.use();
+ mSSAOShader.setMat4( "proj", proj );
+ mSSAOShader.setMat4( "invProj", invProj );
+ mSSAOShader.setFloat( "radius", radius );
+ mSSAOShader.setFloat( "bias", bias );
+ mSSAOShader.setFloat( "power", power );
+
+ glActiveTexture( GL_TEXTURE0 );
+ glBindTexture( GL_TEXTURE_2D, mGDepth );
+ mSSAOShader.setInt( "gDepth", 0 );
+
+ glActiveTexture( GL_TEXTURE1 );
+ glBindTexture( GL_TEXTURE_2D, mGNormal );
+ mSSAOShader.setInt( "gNormal", 1 );
+
+ glActiveTexture( GL_TEXTURE2 );
+ glBindTexture( GL_TEXTURE_2D, mNoiseTexture );
+ mSSAOShader.setInt( "texNoise", 2 );
+
+ float noiseScaleVal = float( w ) / 4.0f;
+ mSSAOShader.setInt( "gDepth", 0 );
+ mSSAOShader.setInt( "gNormal", 1 );
+ mSSAOShader.setInt( "texNoise", 2 );
+ mSSAOShader.setFloat( "noiseScale", noiseScaleVal );
+ glBindVertexArray( mQuadVAO );
+ glDrawArrays( GL_TRIANGLES, 0, 6 );
+ glBindVertexArray( 0 );
+
+ glBindFramebuffer( GL_FRAMEBUFFER, 0 );
+}
+
+void SSAORenderer::blurSSAO () {
+ int w = mHalfRes ? std::max( 1, mWidth / 2 ) : mWidth;
+ int h = mHalfRes ? std::max( 1, mHeight / 2 ) : mHeight;
+
+ glBindFramebuffer( GL_FRAMEBUFFER, mSSAOBlurFBO );
+ glViewport( 0, 0, w, h );
+ glClear( GL_COLOR_BUFFER_BIT );
+
+ mBlurShader.use();
+ glActiveTexture( GL_TEXTURE0 );
+ glBindTexture( GL_TEXTURE_2D, mSSAOTexture );
+ mBlurShader.setInt( "ssaoInput", 0 );
+ mBlurShader.setInt( "horizontal", 1 );
+ mBlurShader.setVec2( "texelSize", glm::vec2( 1.0f / float( w ), 1.0f / float( h ) ) );
+ glBindVertexArray( mQuadVAO );
+ glDrawArrays( GL_TRIANGLES, 0, 6 );
+ glBindVertexArray( 0 );
+
+ glBindFramebuffer( GL_FRAMEBUFFER, mSSAOFBO );
+ glViewport( 0, 0, w, h );
+ glClear( GL_COLOR_BUFFER_BIT );
+
+ mBlurShader.use();
+ glActiveTexture( GL_TEXTURE0 );
+ glBindTexture( GL_TEXTURE_2D, mSSAOBlurTexture );
+ mBlurShader.setInt( "ssaoInput", 0 );
+ mBlurShader.setInt( "horizontal", 0 );
+ mBlurShader.setVec2( "texelSize", glm::vec2( 1.0f / float( w ), 1.0f / float( h ) ) );
+ glBindVertexArray( mQuadVAO );
+ glDrawArrays( GL_TRIANGLES, 0, 6 );
+ glBindVertexArray( 0 );
+
+ glBindFramebuffer( GL_FRAMEBUFFER, 0 );
+}
+
+unsigned int SSAORenderer::getSSAOTextureID () const {
+ return mSSAOTexture;
+}
+
+void SSAORenderer::bindSSAOTextureToUnit ( int unit, renderer::Shader& shader, const std::string& uniformName ) const {
+ glActiveTexture( GL_TEXTURE0 + unit );
+ glBindTexture( GL_TEXTURE_2D, getSSAOTextureID() );
+ shader.setInt( uniformName, unit );
+}
+
+} // namespace renderer