From f1628c0cd5782a1ab2b6d14aabe156c7b9d11375 Mon Sep 17 00:00:00 2001
From: Emmanuel Benoit <tseeker@nocternity.net>
Date: Sat, 30 Sep 2017 17:58:48 +0200
Subject: [PATCH] Partial bloom (missing highpass filter)

---
 bloom-combine.glsl   |  20 ++++++++
 blur-pass.glsl       |  26 ++++++++++
 copy.glsl            |  11 ++++-
 main.cc              | 110 +++++++++++++++++++++++++++++++++++++++++--
 map.glsl             |   2 +-
 raymarch-header.glsl |   3 +-
 raymarcher.glsl      |   3 ++
 rendertarget.cc      |   4 ++
 rendertarget.hh      |   3 ++
 texture.cc           |  71 +++++++++++++++++++++++++---
 texture.hh           |  24 ++++++++--
 11 files changed, 258 insertions(+), 19 deletions(-)
 create mode 100644 bloom-combine.glsl
 create mode 100644 blur-pass.glsl

diff --git a/bloom-combine.glsl b/bloom-combine.glsl
new file mode 100644
index 0000000..f1997c6
--- /dev/null
+++ b/bloom-combine.glsl
@@ -0,0 +1,20 @@
+#version 450 core
+
+layout( location = 0 ) uniform sampler2D u_MainInput;
+layout( location = 1 ) uniform sampler2D u_BlurInput;
+layout( location = 2 ) uniform vec2 u_OutputSize;
+
+layout( location = 0 ) out vec3 color;
+
+void main( void )
+{
+	vec2 tmp = gl_FragCoord.xy / u_OutputSize;
+	float f = .8;
+	color = textureLod( u_MainInput , tmp , 0 ).rgb;
+	for ( int i = 0 ; i < 5 ; i ++ ) {
+		color += f * textureLod( u_BlurInput , tmp , i ).rgb;
+		f *= .90;
+	}
+	color = color / ( color + 1. );
+	color = pow( color , vec3( 2.2 ) );
+}
diff --git a/blur-pass.glsl b/blur-pass.glsl
new file mode 100644
index 0000000..5d1ff0e
--- /dev/null
+++ b/blur-pass.glsl
@@ -0,0 +1,26 @@
+#version 450 core
+
+layout( location = 0 ) uniform sampler2D u_InputTexture;
+layout( location = 1 ) uniform vec2 u_OutputSize;
+layout( location = 2 ) uniform vec2 u_InputSize;
+layout( location = 3 ) uniform int u_SourceLOD;
+layout( location = 4 ) uniform vec2 u_Direction;
+layout( location = 5 ) uniform vec3 u_Weights;
+
+layout( location = 0 ) out vec3 color;
+
+void main( void )
+{
+	vec2 tmp = gl_FragCoord.xy / u_OutputSize;
+	vec2 displace1 = vec2( 2.5 ) * u_Direction / u_InputSize;
+	vec2 displace2 = vec2( 5. ) * u_Direction / u_InputSize;
+	float wt = u_Weights.x + u_Weights.y * 2 + u_Weights.z * 2;
+	color = u_Weights.x * textureLod( u_InputTexture , tmp , u_SourceLOD ).xyz
+		+ u_Weights.y * (
+			textureLod( u_InputTexture , tmp + displace1 , u_SourceLOD ).xyz
+			+ textureLod( u_InputTexture , tmp - displace1 , u_SourceLOD ).xyz )
+		+ u_Weights.z * (
+			textureLod( u_InputTexture , tmp + displace2 , u_SourceLOD ).xyz
+			+ textureLod( u_InputTexture , tmp - displace2 , u_SourceLOD ).xyz );
+	color /= wt;
+}
diff --git a/copy.glsl b/copy.glsl
index dcd0402..4b65478 100644
--- a/copy.glsl
+++ b/copy.glsl
@@ -1,13 +1,20 @@
 #version 450 core
 
 layout( location = 0 ) uniform sampler2D u_InputTexture;
+layout( location = 1 ) uniform int u_LOD;
 
 layout( location = 0 ) out vec4 color;
 
 void main( void )
 {
+#if 1
+	vec2 ts = textureSize( u_InputTexture , u_LOD );
 	vec2 tmp = gl_FragCoord.xy / vec2( 1280. , 720. );
-	color = textureLod( u_InputTexture , tmp , 0 );
-	//color.xy *= .75 + tmp * .25;
+	ivec2 pos = ivec2( ts * tmp );
+	color = texelFetch( u_InputTexture , pos , u_LOD );
+#else
+	vec2 tmp = gl_FragCoord.xy / vec2( 1280. , 720. );
+	color = textureLod( u_InputTexture , tmp , float( u_LOD ) );
+#endif
 }
 
diff --git a/main.cc b/main.cc
index 961c2a2..b516c30 100644
--- a/main.cc
+++ b/main.cc
@@ -30,10 +30,15 @@ struct T_Main
 	T_FilesWatcher watcher;
 	std::unique_ptr< T_ShaderProgram > spRaymarch;
 	std::unique_ptr< T_ShaderProgram > spCopy;
+	std::unique_ptr< T_ShaderProgram > spBlur;
+	std::unique_ptr< T_ShaderProgram > spBloomCombine;
 
 	std::unique_ptr< T_Texture > txRaymarchOutput;
 	std::unique_ptr< T_Rendertarget > rtRaymarchOutput;
 
+	std::unique_ptr< T_Texture > txBlur0 , txBlur1;
+	std::vector< std::unique_ptr< T_Rendertarget > > rtBlur0 , rtBlur1;
+
 	void startIteration( );
 	void handleCapture( );
 	void makeUI( );
@@ -65,10 +70,22 @@ T_Main::T_Main( )
 	ImGui_ImplSdl_Init( window );
 
 	initProgram( );
+
 	txRaymarchOutput = std::make_unique< T_Texture >(
-			1280 , 720 , E_TexType::RGBA8 );
+			1280 , 720 , E_TexType::RGB16F );
 	rtRaymarchOutput = std::make_unique < T_Rendertarget >(
 			T_RendertargetSetup( ).add( *txRaymarchOutput ).create( ) );
+
+	txBlur0 = std::make_unique< T_Texture >( 1280 , 720 , E_TexType::RGB16F , 5 );
+	txBlur0->wrap( E_TexWrap::CLAMP_EDGE ).samplingMode( E_TexSampling::LINEAR );
+	txBlur1 = std::make_unique< T_Texture >( 1280 , 720 , E_TexType::RGB16F , 5 );
+	txBlur1->wrap( E_TexWrap::CLAMP_EDGE ).samplingMode( E_TexSampling::LINEAR );
+	for ( int i = 0 ; i < 5 ; i ++ ) {
+		rtBlur0.push_back( std::make_unique< T_Rendertarget >(
+					T_RendertargetSetup( ).add( *txBlur0 , i ).create( ) ) );
+		rtBlur1.push_back( std::make_unique< T_Rendertarget >(
+					T_RendertargetSetup( ).add( *txBlur1 , i ).create( ) ) );
+	}
 }
 
 void T_Main::mainLoop( )
@@ -165,6 +182,8 @@ void T_Main::makeUI( )
 
 void T_Main::render( )
 {
+	auto const& dspSize( ImGui::GetIO( ).DisplaySize );
+
 	if ( spRaymarch->activate( ) && rtRaymarchOutput->activate( ) ) {
 		glClearColor( 0 , 1 , 1 , 1 );
 		glClear( GL_COLOR_BUFFER_BIT );
@@ -179,7 +198,6 @@ void T_Main::render( )
 			U_RENDER		= 7 ,
 		};
 
-		auto const& dspSize( ImGui::GetIO( ).DisplaySize );
 		glUniform1f( U_TIME , 0 );
 		glUniform2f( U_RESOLUTION , dspSize.x , dspSize.y );
 
@@ -194,16 +212,91 @@ void T_Main::render( )
 		glRectf( -1, -1 , 1 , 1 );
 	}
 
+	if ( spBlur->activate( ) ) {
+		enum {
+			U_TEXTURE		= 0 ,
+			U_OUTPUT_SIZE		= 1 ,
+			U_INPUT_SIZE		= 2 ,
+			U_SOURCE_LOD		= 3 ,
+			U_DIRECTION		= 4 ,
+			U_WEIGHTS		= 5 ,
+		};
+		glUniform3f( U_WEIGHTS , 0.5 , 0.25 , 0.125 );
+		for ( int i = 0 ; i < 5 ; i ++ ) {
+			// IB		RMO	B0	B1	B0	B1	B0
+			// OB		B0/0	B1/0	B0/1	B1/1	B0/2	B1/2
+			// SLOD:	0	0	0	1 	1	2
+			// IW:		W	W	W	W/2	W/2	W/4
+			// OW:		W	W	W/2	W/2	W/4	W/4
+			T_TextureBinding tb( 0 );
+			uint32_t w , h;
+			if ( i == 0 ) {
+				tb.set( U_TEXTURE , *txRaymarchOutput );
+				w = txRaymarchOutput->width( );
+				h = txRaymarchOutput->height( );
+			} else {
+				tb.set( U_TEXTURE , *txBlur1 );
+				w = txBlur1->width( ) >> ( i - 1 );
+				h = txBlur1->height( ) >> ( i - 1 );
+			}
+
+			const uint32_t slod = i > 0 ? ( i - 1 ) : 0;
+			rtBlur0[ i ]->activate( );
+#ifdef INTRUSIVE_TRACES
+			printf( "BLUR %d/H IT %p SLOD %d IS %dx%d OS %dx%d\n" ,
+					i , &(*( i == 0 ? txRaymarchOutput : txBlur1 ) ) ,
+					slod , w , h , rtBlur0[ i ]->width( ) , rtBlur0[ i ]->height( ) );
+#endif
+			glUniform2f( U_OUTPUT_SIZE , rtBlur0[ i ]->width( ) , rtBlur0[ i ]->height( ) );
+			glUniform1i( U_SOURCE_LOD , slod );
+			glUniform2f( U_INPUT_SIZE , w , h );
+
+			glUniform2f( U_DIRECTION , 1 , 0 );
+			tb.bind( );
+			glRectf( -1 , -1 , 1 , 1 );
+
+			rtBlur1[ i ]->activate( );
+			tb.set( U_TEXTURE , *txBlur0 );
+			tb.bind( );
+			glUniform2f( U_DIRECTION , 0 , 1 );
+			glUniform1i( U_SOURCE_LOD , i );
+			glUniform2f( U_INPUT_SIZE ,
+					rtBlur0[ i ]->width( ) ,
+					rtBlur0[ i ]->height( ) );
+			glRectf( -1 , -1 , 1 , 1 );
+#ifdef INTRUSIVE_TRACES
+			printf( "BLUR %d/V IT %p SLOD %d IS %dx%d OS %dx%d\n" ,
+					i , &(*( txBlur0 ) ) ,
+					i , rtBlur0[ i ]->width( ) , rtBlur0[ i ]->height( ) ,
+					rtBlur0[ i ]->width( ) , rtBlur0[ i ]->height( ) );
+#endif
+		}
+	}
+
 	T_Rendertarget::MainOutput( );
 	glClearColor( 1 , 0 , 1 , 1 );
 	glClear( GL_COLOR_BUFFER_BIT );
+#if 0
 	if ( spCopy->activate( ) ) {
 		T_TextureBinding tb( 0 );
-		tb.set( 0 , *txRaymarchOutput );
+		tb.set( 0 , *txBlur1 );
 		tb.bind( );
+		glUniform1i( 1 , 3 );
 		glRectf( -1, -1 , 1 , 1 );
 		glBindTexture( GL_TEXTURE_2D , 0 );
 	}
+#else
+	if ( spBloomCombine->activate( ) ) {
+		T_TextureBinding main( 0 ) , blur( 1 );
+		main.set( 0 , *txRaymarchOutput );
+		main.bind( );
+		blur.set( 1 , *txBlur1 );
+		blur.bind( );
+		glUniform2f( 2 , dspSize.x , dspSize.y );
+		glRectf( -1, -1 , 1 , 1 );
+		glBindTexture( GL_TEXTURE_2D , 0 );
+	}
+#endif
 
 	glUseProgram( 0 );
 	ImGui::Render( );
@@ -220,10 +313,17 @@ void T_Main::initProgram( )
 	spRaymarch->addFile( "raymarcher.glsl" );
 	spRaymarch->load( );
 
-	spCopy = std::make_unique< T_ShaderProgram >( GL_FRAGMENT_SHADER ,
-			watcher );
+	spCopy = std::make_unique< T_ShaderProgram >( GL_FRAGMENT_SHADER , watcher );
 	spCopy->addFile( "copy.glsl" );
 	spCopy->load( );
+
+	spBlur = std::make_unique< T_ShaderProgram >( GL_FRAGMENT_SHADER , watcher );
+	spBlur->addFile( "blur-pass.glsl" );
+	spBlur->load( );
+
+	spBloomCombine = std::make_unique< T_ShaderProgram >( GL_FRAGMENT_SHADER , watcher );
+	spBloomCombine->addFile( "bloom-combine.glsl" );
+	spBloomCombine->load( );
 }
 
 
diff --git a/map.glsl b/map.glsl
index 3aec80b..3681bbe 100644
--- a/map.glsl
+++ b/map.glsl
@@ -2,5 +2,5 @@ vec2 map( vec3 pos )
 {
 	vec3 q = pos;
 	q.xy = mod( q.xy + 2. , 4. ) - 2.;
-	return vec2( length( q ) - 2.1 , 0. );
+	return vec2( length( q ) - 1.8 , step( 0. , 1.9 - length( pos.xy ) ) );
 }
diff --git a/raymarch-header.glsl b/raymarch-header.glsl
index 107b97c..2821952 100644
--- a/raymarch-header.glsl
+++ b/raymarch-header.glsl
@@ -12,7 +12,8 @@ layout( location = 7 ) uniform vec4 u_Render;
 vec3 camPos , lookAt , camUp;
 float nearPlane;
 
-out vec4 color;
+layout( location = 0 ) out vec4 color;
+
 void setCamFromUniforms( ) {
 	camPos = u_CamPos;
 	lookAt = u_LookAt;
diff --git a/raymarcher.glsl b/raymarcher.glsl
index 9ef9cad..afc7e7a 100644
--- a/raymarcher.glsl
+++ b/raymarcher.glsl
@@ -70,6 +70,9 @@ void main( )
 		float si = pow( clamp( ndoth , 0 , 1 ) , 4 ) ,
 		      di = .6 + .3 * clamp( ndotl , 0 , 1 );
 		bc = mix( bc * di , vec3( 1. ) , si );
+		if ( r.y >= 1. ) {
+			bc += vec3( 5. , .1 , 4. ) * 4;
+		}
 	} else {
 		bc = vec3( 0. );
 	}
diff --git a/rendertarget.cc b/rendertarget.cc
index 5d944cb..49c1863 100644
--- a/rendertarget.cc
+++ b/rendertarget.cc
@@ -55,6 +55,10 @@ T_Rendertarget T_RendertargetSetup::create( )
 	glGenFramebuffers( 1 , &id );
 	glBindFramebuffer( GL_FRAMEBUFFER , id );
 	for ( auto i = 0u ; i < nca ; i ++ ) {
+		printf( "init fb %p att %d tx %p level %d\n" ,
+				this , i ,
+				colorAttachments_[ i ].texture ,
+				colorAttachments_[ i ].level );
 		glFramebufferTexture( GL_FRAMEBUFFER ,
 				GL_COLOR_ATTACHMENT0 + i ,
 				colorAttachments_[ i ].texture->id( ) ,
diff --git a/rendertarget.hh b/rendertarget.hh
index e83ca2f..32b5bd7 100644
--- a/rendertarget.hh
+++ b/rendertarget.hh
@@ -60,6 +60,9 @@ struct T_Rendertarget
 
 	bool activate( );
 
+	uint32_t width( ) const { return width_; }
+	uint32_t height( ) const { return height_; }
+
 	static void MainOutput( );
 
     private:
diff --git a/texture.cc b/texture.cc
index 3047615..1ea5fde 100644
--- a/texture.cc
+++ b/texture.cc
@@ -7,7 +7,7 @@ T_Texture::T_Texture(
 		__rd__ const uint32_t height ,
 		__rd__ const E_TexType type ,
 		__rd__ const uint32_t levels )
-	: width_( width ) , height_( height )
+	: levels_( levels ) , width_( width ) , height_( height )
 {
 	assert( levels > 0 );
 
@@ -28,6 +28,18 @@ T_Texture::T_Texture(
 			dt = GL_FLOAT;
 			break;
 
+		case E_TexType::RGB8:
+			ifmt = GL_RGB8;
+			fmt = GL_RGB;
+			dt = GL_UNSIGNED_BYTE;
+			break;
+
+		case E_TexType::RGB16F:
+			ifmt = GL_RGB16F;
+			fmt = GL_RGB;
+			dt = GL_FLOAT;
+			break;
+
 		case E_TexType::R8:
 			ifmt = GL_R8;
 			fmt = GL_RED;
@@ -41,11 +53,16 @@ T_Texture::T_Texture(
 			break;
 	}
 
-	glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MIN_LOD , 0 );
-	glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MAX_LOD , levels - 1 );
+	glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_BASE_LEVEL , 0 );
+	glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , levels - 1 );
+	glTexParameterf( GL_TEXTURE_2D , GL_TEXTURE_MIN_LOD , 0 );
+	glTexParameterf( GL_TEXTURE_2D , GL_TEXTURE_MAX_LOD , levels - 1 );
+	glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_NEAREST );
+	glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_NEAREST );
 
 	uint32_t w = width , h = height;
 	for ( auto i = 0u ; i < levels ; i ++ ) {
+		printf( "init tx %p l %d w %d h %d\n" , this , i , w , h );
 		glTexImage2D( GL_TEXTURE_2D , i , ifmt , w , h , 0 , fmt , dt , nullptr );
 		w >>= 1;
 		h >>= 1;
@@ -53,8 +70,6 @@ T_Texture::T_Texture(
 	}
 
 	assert( glGetError( ) == GL_NO_ERROR );
-
-	glBindTexture( GL_TEXTURE_2D , 0 );
 }
 
 T_Texture::~T_Texture( )
@@ -63,6 +78,48 @@ T_Texture::~T_Texture( )
 }
 
 
+T_Texture& T_Texture::samplingMode(
+		__rd__ const E_TexSampling mode )
+{
+	glBindTexture( GL_TEXTURE_2D , id_ );
+
+	GLenum min , max;
+	switch ( mode ) {
+		case E_TexSampling::NEAREST:
+			min = levels_ > 1 ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST;
+			max = GL_NEAREST;
+			break;
+		case E_TexSampling::LINEAR:
+			min = levels_ > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR;
+			max = GL_LINEAR;
+			break;
+
+	}
+	glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , min );
+	glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , max );
+	return *this;
+}
+
+T_Texture& T_Texture::wrap(
+		__rd__ const E_TexWrap mode )
+{
+	GLenum gm;
+	switch ( mode ) {
+		case E_TexWrap::REPEAT:
+			gm = GL_REPEAT;
+			break;
+		case E_TexWrap::CLAMP_EDGE:
+			gm = GL_CLAMP_TO_EDGE;
+			break;
+		case E_TexWrap::CLAMP_BORDER:
+			gm = GL_CLAMP_TO_BORDER;
+			break;
+	}
+	glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , gm );
+	glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , gm );
+	return *this;
+}
+
 /*============================================================================*/
 
 
@@ -87,8 +144,8 @@ void T_TextureBinding::set(
 
 void T_TextureBinding::bind( ) const
 {
-	glActiveTexture( GL_TEXTURE0 + binding_ );
-	glBindTexture( GL_TEXTURE_2D , texture_ ? texture_->id( ) : 0 );
+	glBindTextureUnit( binding_ ,
+			texture_ ? texture_->id( ) : 0 );
 	glUniform1i( uniform_ , binding_ );
 	assert( glGetError( ) == GL_NO_ERROR );
 }
diff --git a/texture.hh b/texture.hh
index 3fcf4a3..03c4e0f 100644
--- a/texture.hh
+++ b/texture.hh
@@ -4,14 +4,26 @@
 #endif
 
 
-enum class E_TexType
-{
+enum class E_TexType {
 	RGBA8 ,
 	RGBA16F ,
+	RGB8 ,
+	RGB16F ,
 	R8 ,
 	R16F ,
 };
 
+enum class E_TexSampling {
+	NEAREST ,
+	LINEAR ,
+};
+
+enum class E_TexWrap {
+	REPEAT ,
+	CLAMP_EDGE ,
+	CLAMP_BORDER
+};
+
 struct T_Texture
 {
 	T_Texture( ) = delete;
@@ -25,13 +37,19 @@ struct T_Texture
 			__rd__ const uint32_t levels = 1 );
 	~T_Texture( );
 
+	T_Texture& samplingMode(
+			__rd__ const E_TexSampling mode );
+	T_Texture& wrap(
+			__rd__ const E_TexWrap mode );
+
 	GLuint id( ) const noexcept { return id_; }
+	uint32_t levels( ) const noexcept { return levels_; }
 	uint32_t width( ) const noexcept { return width_; }
 	uint32_t height( ) const noexcept { return height_; }
 
     private:
 	GLuint id_;
-	uint32_t width_ , height_;
+	uint32_t levels_ , width_ , height_;
 };
 
 struct T_TextureBinding