2
0
Эх сурвалжийг харах

Implemented ambient occlusion (SSAO and HBAO)

Added ambient occlusion rendering pass
Added SSAO pass and SSAO blur shaders
Added HBAO pass shaders
Added HBAO blur shaders that are split into horizontal and vertical passes, and takes into account fragments depth (so to not blur edges)
Added AmbientOcclusionData container that holds all the AO settings, in Containers
Added AODataSet container that holds the AO shader settings and gets sent to the GPU as Uniform block, in GraphicsDataSets, UnfirormData
Added ambient occlution data to RendererScene, so each map can load and have its own AO settings, that get sent to the renderer
Added a transpose inverse of view matrix calculation on the CPU and sending it as a uniform to shaders, in RendererFrontend, ShaderUniforms, ShaderUniformUpdater
Added settings for ambient occlusion, in EditorWindow
Added more texture data types and texture data formats, in EditorWindow, CommonDefinitions
Added more uniforms in ShaderUniforms, ShaderUniformUpdater
Added a way to set magnification and minification filtering for each texture separately, in CommandBuffer, RendererBackend, RendererFrontend
Added texture filtering type enum in CommonDefinitions
Added vertex tangent and bitangent calculation flags for Assimp, instead of calculating them manually while loading models, in ModelLoader
Added more Config variables
Added more PropertyIDs
Added more ErrorCodes
Incremented engine version to 0.1.3 and commit date to 2023-12-24
Fixed a bug of error window popup showing text color tags in plain text, in ErrorHandler
Paul A 2 жил өмнө
parent
commit
21533f51f5
44 өөрчлөгдсөн 1909 нэмэгдсэн , 281 устгасан
  1. 1 1
      Dependencies/include/ImGuiColorTextEdit/TextEditor.h
  2. 17 2
      Praxis3D/Data/Maps/componentTest.pmap
  3. 10 0
      Praxis3D/Data/Shaders/ambientOcclusionBlurHBAO.vert
  4. 58 0
      Praxis3D/Data/Shaders/ambientOcclusionBlurHBAOhorizontal.frag
  5. 64 0
      Praxis3D/Data/Shaders/ambientOcclusionBlurHBAOvertical.frag
  6. 46 0
      Praxis3D/Data/Shaders/ambientOcclusionBlurSSAO.frag
  7. 10 0
      Praxis3D/Data/Shaders/ambientOcclusionBlurSSAO.vert
  8. 156 0
      Praxis3D/Data/Shaders/ambientOcclusionPassHBAO.frag
  9. 10 0
      Praxis3D/Data/Shaders/ambientOcclusionPassHBAO.vert
  10. 126 0
      Praxis3D/Data/Shaders/ambientOcclusionPassSSAO.frag
  11. 10 0
      Praxis3D/Data/Shaders/ambientOcclusionPassSSAO.vert
  12. 22 4
      Praxis3D/Data/Shaders/geometryPass.frag
  13. 18 10
      Praxis3D/Data/Shaders/geometryPass.vert
  14. 14 8
      Praxis3D/Data/Shaders/lightPass.frag
  15. 2 2
      Praxis3D/Data/config.ini
  16. 1 0
      Praxis3D/Data/error-strings-eng.data
  17. 1 0
      Praxis3D/Praxis3D.vcxproj
  18. 3 0
      Praxis3D/Praxis3D.vcxproj.filters
  19. 415 0
      Praxis3D/Source/AmbientOcclusionPass.h
  20. 1 24
      Praxis3D/Source/BloomPass.h
  21. 2 0
      Praxis3D/Source/CommandBuffer.h
  22. 24 11
      Praxis3D/Source/CommonDefinitions.h
  23. 32 0
      Praxis3D/Source/Config.cpp
  24. 89 5
      Praxis3D/Source/Config.h
  25. 107 0
      Praxis3D/Source/Containers.h
  26. 106 3
      Praxis3D/Source/EditorWindow.cpp
  27. 82 69
      Praxis3D/Source/EditorWindow.h
  28. 1 0
      Praxis3D/Source/ErrorCodes.h
  29. 3 2
      Praxis3D/Source/ErrorHandler.cpp
  30. 3 23
      Praxis3D/Source/GeometryBuffer.h
  31. 35 0
      Praxis3D/Source/GraphicsDataSets.h
  32. 2 0
      Praxis3D/Source/ModelLoader.cpp
  33. 20 0
      Praxis3D/Source/RenderPassBase.h
  34. 25 16
      Praxis3D/Source/RendererBackend.h
  35. 9 76
      Praxis3D/Source/RendererFrontend.cpp
  36. 4 0
      Praxis3D/Source/RendererFrontend.h
  37. 85 1
      Praxis3D/Source/RendererScene.cpp
  38. 9 2
      Praxis3D/Source/RendererScene.h
  39. 15 0
      Praxis3D/Source/ShaderUniformUpdater.cpp
  40. 141 9
      Praxis3D/Source/ShaderUniforms.h
  41. 68 9
      Praxis3D/Source/TextureLoader.h
  42. 12 2
      Praxis3D/Source/UniformData.h
  43. 2 2
      Praxis3D/Source/Version.h
  44. 48 0
      Praxis3D/imgui.ini

+ 1 - 1
Dependencies/include/ImGuiColorTextEdit/TextEditor.h

@@ -43,7 +43,7 @@ public:
 	inline bool IsShowLineNumbersEnabled() const { return mShowLineNumbers; }
 	inline void SetShortTabsEnabled(bool aValue) { mShortTabs = aValue; }
 	inline bool IsShortTabsEnabled() const { return mShortTabs; }
-	inline int GetLineCount() const { return mLines.size(); }
+	inline int GetLineCount() const { return (int)mLines.size(); }
 	inline bool IsOverwriteEnabled() const { return mOverwrite; }
 	void SetPalette(PaletteId aValue);
 	PaletteId GetPalette() const { return mPaletteId; }

+ 17 - 2
Praxis3D/Data/Maps/componentTest.pmap

@@ -227,7 +227,7 @@
 					"CollisionShape": 
 					{
 						"Type": "Box",
-						"Size": "1.92f, 1.92f, 1.92f"
+						"Size": "1.64f, 1.64f, 1.64f"
 					}
 				}
 			},
@@ -652,7 +652,7 @@
 					"CollisionShape": 
 					{
 						"Type": "Box",
-						"Size": "1.92f, 1.92f, 1.92f"
+						"Size": "1.64f, 1.64f, 1.64f"
 					}
 				}
 			},
@@ -939,6 +939,18 @@
 				"AmbientIntensity": "0.2f",
 				"ZFar": "40000.0f",
 				"ZNear": "1.0f",
+				"AmbientOcclusion": 
+				{
+					"Type": "HBAO",
+					"Bias": "0.1f",
+					"Radius": "2.0f",
+					"Intensity": "2.0f",
+					"Directions": "8",
+					"Samples": "64",
+					"Steps": "4",
+					"BlurSamples": "6",
+					"BlurSharpness": "40.0f"
+				},
 				"ObjectPoolSize": 
 				{
 					"CameraComponent": "1",
@@ -951,6 +963,9 @@
 					{
 						"Type": "GeometryRenderPass"
 					},
+					{
+						"Type": "AmbientOcclusionRenderPass"
+					},
 					{
 						"Type": "AtmScatteringRenderPass"
 					},

+ 10 - 0
Praxis3D/Data/Shaders/ambientOcclusionBlurHBAO.vert

@@ -0,0 +1,10 @@
+#version 430 core
+
+void main(void) 
+{
+	// Determine texture coordinates
+	vec2 texCoord = vec2((gl_VertexID == 2) ?  2.0 :  0.0, (gl_VertexID == 1) ?  2.0 :  0.0);
+	
+	// Calculate the position, so that the triangle fills the whole screen
+	gl_Position = vec4(texCoord * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 0.5, 1.0);
+}

+ 58 - 0
Praxis3D/Data/Shaders/ambientOcclusionBlurHBAOhorizontal.frag

@@ -0,0 +1,58 @@
+#version 430 core
+
+out vec4 outputColor;
+
+uniform sampler2D inputColorMap;
+
+uniform ivec2 screenSize;
+uniform vec2 hbaoBlurHorizontalInvResDirection;
+uniform int hbaoBlurNumOfSamples;
+uniform float hbaoBlurSharpness;
+  
+vec2 calcTexCoord(void)
+{
+    return gl_FragCoord.xy / screenSize;
+}
+
+float blurFunction(vec2 p_texCoord, float p_offset, float p_depth, inout float p_totalWeight)
+{
+	vec2  occlusionAndDepth = texture2D(inputColorMap, p_texCoord).xy;
+	float occlusion = occlusionAndDepth.x;
+	float depth = occlusionAndDepth.y;
+
+	const float BlurSigma = float(hbaoBlurNumOfSamples) * 0.5;
+	const float BlurFalloff = 1.0 / (2.0 * BlurSigma * BlurSigma);
+
+	float depthDifference = (depth - p_depth) * hbaoBlurSharpness;
+	float weight = exp2(-p_offset * p_offset * BlurFalloff - depthDifference * depthDifference);
+	p_totalWeight += weight;
+
+	return occlusion * weight;
+}
+
+void main()
+{	
+	// Calculate screen-space texture coordinates, for buffer access
+	vec2 texCoord = calcTexCoord();
+	
+	vec2 occlusionAndDepth = texture(inputColorMap, texCoord).xy;
+	float occlusionAtCenter = occlusionAndDepth.x;
+	float depth = occlusionAndDepth.y;
+
+	float totalOcclusion = occlusionAtCenter;
+	float totalWeight = 1.0;
+
+	for(float r = 1; r <= hbaoBlurNumOfSamples; ++r)
+	{
+		vec2 offsetTexCoord = texCoord + hbaoBlurHorizontalInvResDirection * r;
+		totalOcclusion += blurFunction(offsetTexCoord, r, depth, totalWeight);  
+	}
+
+	for(float r = 1; r <= hbaoBlurNumOfSamples; ++r)
+	{
+		vec2 offsetTexCoord = texCoord - hbaoBlurHorizontalInvResDirection * r;
+		totalOcclusion += blurFunction(offsetTexCoord, r, depth, totalWeight);  
+	}
+	
+    outputColor = vec4(totalOcclusion / totalWeight, depth, 0.0, 0.0);
+}

+ 64 - 0
Praxis3D/Data/Shaders/ambientOcclusionBlurHBAOvertical.frag

@@ -0,0 +1,64 @@
+#version 430 core
+
+out vec4 outputColor;
+
+uniform sampler2D inputColorMap;
+uniform sampler2D matPropertiesMap;
+
+uniform ivec2 screenSize;
+uniform vec2 hbaoBlurVerticalInvResDirection;
+uniform int hbaoBlurNumOfSamples;
+uniform float hbaoBlurSharpness;
+  
+vec2 calcTexCoord(void)
+{
+    return gl_FragCoord.xy / screenSize;
+}
+
+float blurFunction(vec2 p_texCoord, float p_offset, float p_depth, inout float p_totalWeight)
+{
+	vec2  occlusionAndDepth = texture2D(inputColorMap, p_texCoord).xy;
+	float occlusion = occlusionAndDepth.x;
+	float depth = occlusionAndDepth.y;
+
+	const float BlurSigma = float(hbaoBlurNumOfSamples) * 0.5;
+	const float BlurFalloff = 1.0 / (2.0 * BlurSigma * BlurSigma);
+
+	float depthDifference = (depth - p_depth) * hbaoBlurSharpness;
+	float weight = exp2(-p_offset * p_offset * BlurFalloff - depthDifference * depthDifference);
+	p_totalWeight += weight;
+
+	return occlusion * weight;
+}
+
+void main()
+{	
+	// Calculate screen-space texture coordinates, for buffer access
+	vec2 texCoord = calcTexCoord();
+	
+	vec2 occlusionAndDepth = texture(inputColorMap, texCoord).xy;
+	float occlusionAtCenter = occlusionAndDepth.x;
+	float depth = occlusionAndDepth.y;
+
+	float totalOcclusion = occlusionAtCenter;
+	float totalWeight = 1.0;
+
+	for(float r = 1; r <= hbaoBlurNumOfSamples; ++r)
+	{
+		vec2 offsetTexCoord = texCoord + hbaoBlurVerticalInvResDirection * r;
+		totalOcclusion += blurFunction(offsetTexCoord, r, depth, totalWeight);  
+	}
+
+	for(float r = 1; r <= hbaoBlurNumOfSamples; ++r)
+	{
+		vec2 offsetTexCoord = texCoord - hbaoBlurVerticalInvResDirection * r;
+		totalOcclusion += blurFunction(offsetTexCoord, r, depth, totalWeight);  
+	}
+
+	// Multiply the existing AO value (from textures) by the calculated occlusion value
+	vec4 matProperties = texture(matPropertiesMap, texCoord);
+	matProperties.z = (matProperties.z + totalOcclusion / totalWeight) * 0.5;
+	//matProperties.z *= c_total / w_total;
+	
+    outputColor = matProperties;
+}

+ 46 - 0
Praxis3D/Data/Shaders/ambientOcclusionBlurSSAO.frag

@@ -0,0 +1,46 @@
+#version 430 core
+
+out vec4 outputColor;
+
+uniform sampler2D finalColorMap;
+
+uniform ivec2 screenSize;
+
+vec2 calcTexCoord(void)
+{
+    return gl_FragCoord.xy / screenSize;
+}
+
+void main() 
+{
+	// Calculate screen-space texture coordinates, for buffer access
+	vec2 texCoord = calcTexCoord();
+	
+	// Calculate offset scale for the blur sampling
+    vec2 texelSize = 1.0 / vec2(textureSize(finalColorMap, 0));
+	
+    float result = 0.0;
+	
+	// Go over each vertical sample pixel
+    for(int x = -2; x < 2; ++x) 
+    {
+		// Go over each horizontal sample pixel
+        for(int y = -2; y < 2; ++y) 
+        {
+			// Offset the sample position
+            vec2 offset = vec2(float(x), float(y)) * texelSize;
+			
+			// Accumulate the result
+            result += texture(finalColorMap, texCoord + offset).z;
+        }
+    }
+	
+	// Average the result
+    float occlusion = result / (4.0 * 4.0);
+	
+	// Get the material properties value and set the occlusion value
+	vec4 matProperties = texture(finalColorMap, texCoord);
+	matProperties.z = occlusion;
+	
+    outputColor = matProperties;
+}  

+ 10 - 0
Praxis3D/Data/Shaders/ambientOcclusionBlurSSAO.vert

@@ -0,0 +1,10 @@
+#version 430 core
+
+void main(void) 
+{	
+	// Determine texture coordinates
+	vec2 texCoord = vec2((gl_VertexID == 2) ?  2.0 :  0.0, (gl_VertexID == 1) ?  2.0 :  0.0);
+	
+	// Calculate the position, so that the triangle fills the whole screen
+	gl_Position = vec4(texCoord * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 0.5, 1.0);
+}

+ 156 - 0
Praxis3D/Data/Shaders/ambientOcclusionPassHBAO.frag

@@ -0,0 +1,156 @@
+#version 430 core
+
+#define PI 3.1415926535
+#define AO_RANDOMTEX_SIZE 4
+
+out vec4 outputColor;
+
+uniform sampler2D positionMap;
+uniform sampler2D normalMap;
+uniform sampler2D noiseTexture;
+
+uniform mat4 viewMat;
+uniform mat4 transposeInverseViewMat;
+uniform ivec2 screenSize;
+
+struct AODataSet
+{
+	float m_RadiusToScreen;	// radius
+	float m_radius;
+	float m_NegInvR2;     	// radius * radius
+	float m_NDotVBias;
+
+	vec2 m_InvFullResolution;
+	float m_AOMultiplier;
+	float m_PowExponent;
+
+	float m_bias;
+	int m_numOfDirections;
+	int m_numOfSamples;
+	int m_numOfSteps;
+};
+
+layout (std140) uniform AODataSetBuffer
+{
+	AODataSet m_aoDataSet;
+};
+  
+vec2 calcTexCoord(void)
+{
+    return gl_FragCoord.xy / screenSize;
+}
+
+// Converts the position from position g-buffer that is in world-space to position in view-space
+vec3 getPositionInCameraSpace(vec2 p_texCoord)
+{
+	vec4 fragPos = vec4(texture(positionMap, p_texCoord).xyz, 1.0);
+	fragPos = viewMat * fragPos;
+	return fragPos.xyz;
+}
+
+// Converts the normal from normal g-buffer that is in world-space to normal in view-space
+vec3 getNormalInCameraSpace(vec2 p_texCoord)
+{
+	vec4 normal = vec4(texture(normalMap, p_texCoord).xyz, 1.0);
+	normal = transposeInverseViewMat * normal;
+	return normalize(normal.xyz);
+}
+
+// Get the random (jitter) value for the current pixel
+vec4 getRandom(void)
+{
+	// Get the current jitter vector from the per-pass constant buffer
+	return texture(noiseTexture, (gl_FragCoord.xy / AO_RANDOMTEX_SIZE));
+}
+
+// Rotate the given direction by the cosine / sine
+vec2 rotateDirection(vec2 p_direction, vec2 p_cosSin)
+{
+	return vec2(p_direction.x * p_cosSin.x - p_direction.y * p_cosSin.y, 
+				p_direction.x * p_cosSin.y + p_direction.y * p_cosSin.x);
+}
+
+// Calculate falloff by the given distance
+float falloff(float p_distanceSquare)
+{
+	// 1 scalar mad instruction
+	return p_distanceSquare * m_aoDataSet.m_NegInvR2 + 1.0;
+}
+
+//----------------------------------------------------------------------------------
+// p_viewPosition = view-space position at the kernel center
+// p_viewNormal = view-space normal at the kernel center
+// p_sampleViewPosition = view-space position of the current sample
+//----------------------------------------------------------------------------------
+float computeAmbientOcclusion(vec3 p_viewPosition, vec3 p_viewNormal, vec3 p_sampleViewPosition)
+{
+	vec3 viewRay = p_sampleViewPosition - p_viewPosition;
+	float VdotV = dot(viewRay, viewRay);
+	float NdotV = dot(p_viewNormal, viewRay) * 1.0/sqrt(VdotV);
+
+	// Use saturate(x) instead of max(x,0.f) because that is faster on Kepler
+	return clamp(NdotV - m_aoDataSet.m_NDotVBias, 0.0, 1.0) * clamp(falloff(VdotV), 0.0, 1.0);
+}
+
+float computeCoarseAmbientOcclusion(vec2 p_texCoord, float p_radiusPixels, vec4 p_randomVec, vec3 p_viewPosition, vec3 p_viewNormal)
+{
+	// Divide by NUM_STEPS+1 so that the farthest samples are not fully attenuated
+	float stepSizePixels = p_radiusPixels / (m_aoDataSet.m_numOfSteps + 1);
+
+	const float alpha = 2.0 * PI / m_aoDataSet.m_numOfDirections;
+	float occlusion = 0;
+
+	// Go over each direction
+	for(float DirectionIndex = 0; DirectionIndex < m_aoDataSet.m_numOfDirections; ++DirectionIndex)
+	{
+		float angle = alpha * DirectionIndex;
+
+		// Compute normalized 2D direction
+		vec2 direction = rotateDirection(vec2(cos(angle), sin(angle)), p_randomVec.xy);
+
+		// Jitter starting sample within the first step
+		float rayPixels = (p_randomVec.z * stepSizePixels + 1.0);
+		
+		// Go over each step
+		for(float stepIndex = 0; stepIndex < m_aoDataSet.m_numOfSteps; ++stepIndex)
+		{
+			vec2 snappedTexCoord = round(rayPixels * direction) * m_aoDataSet.m_InvFullResolution + p_texCoord;
+			vec3 sampleViewPosition = getPositionInCameraSpace(snappedTexCoord);
+			
+			// March the ray further
+			rayPixels += stepSizePixels;
+			
+			// Accumulate the occlusion result
+			occlusion += computeAmbientOcclusion(p_viewPosition, p_viewNormal, sampleViewPosition);
+		}
+	}
+
+	// Average the occlusion result
+	occlusion *= m_aoDataSet.m_AOMultiplier / (m_aoDataSet.m_numOfDirections * m_aoDataSet.m_numOfSteps);
+	
+	// Make sure the occlusion is not clipping
+	return clamp(1.0 - occlusion * 2.0, 0.0, 1.0);
+}
+
+void main()
+{
+	// Calculate screen-space texture coordinates, for buffer access
+	vec2 texCoord = calcTexCoord();
+	
+	// Get pixel position in view space
+	vec3 viewPosition = getPositionInCameraSpace(texCoord);
+	
+	// Get pixel normal in view space
+	vec3 viewNormal = getNormalInCameraSpace(texCoord);
+
+	// Calculate the radius for the occlusion calculation
+	float radiusPixels = m_aoDataSet.m_RadiusToScreen / viewPosition.z;
+
+	// Get jitter (random) vector for the current pixel
+	vec4 randomVec = getRandom();
+
+	// Perform ambient occlusion
+	float occlusionValue = computeCoarseAmbientOcclusion(texCoord, radiusPixels, randomVec, viewPosition, viewNormal);
+	
+	outputColor = vec4(pow(occlusionValue, m_aoDataSet.m_PowExponent), viewPosition.z, 0.0, 0.0);
+}

+ 10 - 0
Praxis3D/Data/Shaders/ambientOcclusionPassHBAO.vert

@@ -0,0 +1,10 @@
+#version 430 core
+
+void main(void) 
+{
+	// Determine texture coordinates
+	vec2 texCoord = vec2((gl_VertexID == 2) ?  2.0 :  0.0, (gl_VertexID == 1) ?  2.0 :  0.0);
+	
+	// Calculate the position, so that the triangle fills the whole screen
+	gl_Position = vec4(texCoord * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 0.5, 1.0);
+}

+ 126 - 0
Praxis3D/Data/Shaders/ambientOcclusionPassSSAO.frag

@@ -0,0 +1,126 @@
+#version 430 core
+
+out vec4 outputColor;
+
+uniform sampler2D positionMap;
+uniform sampler2D normalMap;
+uniform sampler2D noiseTexture;
+uniform sampler2D matPropertiesMap;
+
+uniform mat4 viewMat;
+uniform mat4 transposeInverseViewMat;
+uniform mat4 projMat;
+uniform ivec2 screenSize;
+
+// parameters (you'd probably want to use them as uniforms to more easily tweak the effect)
+//int kernelSize = 64;
+//float radius = 10.5;
+//float bias = 0.025;
+
+struct AODataSet
+{
+	float m_RadiusToScreen;	// radius
+	float m_radius;
+	float m_NegInvR2;     	// radius * radius
+	float m_NDotVBias;
+
+	vec2 m_InvFullResolution;
+	float m_AOMultiplier;
+	float m_PowExponent;
+
+	float m_bias;
+	int m_numOfDirections;
+	int m_numOfSamples;
+	int m_numOfSteps;
+};
+
+layout (std140) uniform AODataSetBuffer
+{
+	AODataSet m_aoDataSet;
+};
+
+layout (std140) uniform SSAOSampleBuffer
+{
+	vec4 m_samples[64];
+};
+
+vec2 calcTexCoord(void)
+{
+    return gl_FragCoord.xy / screenSize;
+}
+
+// Converts the position from position g-buffer that is in world-space to position in view-space
+vec3 getPositionInCameraSpace(vec2 p_texCoord)
+{
+	vec4 fragPos = vec4(texture(positionMap, p_texCoord).xyz, 1.0);
+	fragPos = viewMat * fragPos;
+	return fragPos.xyz;
+}
+
+// Converts the normal from normal g-buffer that is in world-space to normal in view-space
+vec3 getNormalInCameraSpace(vec2 p_texCoord)
+{
+	vec4 normal = vec4(texture(normalMap, p_texCoord).xyz, 1.0);
+	normal = transposeInverseViewMat * normal;
+	return normalize(normal.xyz);
+}
+
+void main(void)
+{
+	// Calculate noise scale (based on screen size and noise texture size), so the noise texture will tile over the whole screen
+	vec2 noiseScale = vec2(screenSize.x / 4.0, screenSize.y / 4.0);
+
+	// Calculate screen-space texture coordinates, for buffer access
+	vec2 texCoord = calcTexCoord();
+	
+	// Get pixel position in view space
+	vec3 fragPos = getPositionInCameraSpace(texCoord);
+	
+	// Get pixel normal in view space
+    vec3 normal = getNormalInCameraSpace(texCoord);
+	
+	// Get random direction vector
+    vec3 randomVec = normalize(texture(noiseTexture, texCoord * noiseScale).xyz);
+	
+    // Create TBN change-of-basis matrix: from tangent-space to view-space
+    vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal));
+    vec3 bitangent = cross(normal, tangent);
+    mat3 TBN = mat3(tangent, bitangent, normal);
+	
+    float occlusion = 0.0;
+	
+    // Iterate over the sample kernel and calculate occlusion factor
+    for(int i = 0; i < m_aoDataSet.m_numOfSamples; ++i)
+    {
+        // Get sample position
+        vec3 samplePos = TBN * m_samples[i].xyz; // from tangent to view-space
+        samplePos = fragPos + samplePos * m_aoDataSet.m_radius; 
+        
+        // Project sample position (to sample texture) (to get position on screen/texture)
+        vec4 offset = vec4(samplePos, 1.0);
+        offset = projMat * offset; // from view to clip-space
+        offset.xyz /= offset.w; // perspective divide
+        offset.xyz = offset.xyz * 0.5 + 0.5; // transform to range 0.0 - 1.0
+        
+        // Get sample depth (distance from camera to the sample pixel)
+		float sampleDepth = getPositionInCameraSpace(offset.xy).z;
+        
+        // Range check
+        float rangeCheck = smoothstep(0.0, 1.0, m_aoDataSet.m_radius / abs(fragPos.z - sampleDepth));
+		
+		// Accumulate the occlusion result
+        occlusion += (sampleDepth >= samplePos.z + m_aoDataSet.m_bias ? 1.0 : 0.0) * rangeCheck;           
+    }
+	
+	// Average the occlusion result
+    occlusion = 1.0 - (occlusion / m_aoDataSet.m_numOfSamples);
+	
+	// Apply ambient intensity
+	occlusion = pow(occlusion, m_aoDataSet.m_PowExponent);
+	    
+	// Put the new occlusion value into the material properties texture
+	vec4 matProperties = texture(matPropertiesMap, texCoord);
+	matProperties.z *= occlusion;
+	
+	outputColor = matProperties;
+}

+ 10 - 0
Praxis3D/Data/Shaders/ambientOcclusionPassSSAO.vert

@@ -0,0 +1,10 @@
+#version 430 core
+
+void main(void) 
+{	
+	// Determine texture coordinates
+	vec2 texCoord = vec2((gl_VertexID == 2) ?  2.0 :  0.0, (gl_VertexID == 1) ?  2.0 :  0.0);
+	
+	// Calculate the position, so that the triangle fills the whole screen
+	gl_Position = vec4(texCoord * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 0.5, 1.0);
+}

+ 22 - 4
Praxis3D/Data/Shaders/geometryPass.frag

@@ -18,6 +18,7 @@ layout(location = 4) out vec4 matPropertiesBuffer;
 
 // Variables from vertex shader
 in mat3 TBN;
+in mat3 normalMatrix;
 in vec2 texCoord;
 in vec3 fragPos;
 in vec3 normal;
@@ -217,7 +218,7 @@ vec2 parallaxMappingNew(vec2 T, vec3 V)
 
 	int nCurrSample = 0;
 
-	while ( nCurrSample < nNumSamples )
+	while(nCurrSample < nNumSamples)
 	{
 		// Sample the heightmap at the current texcoord offset.  The heightmap 
 		// is stored in the alpha channel of the height/normal map.
@@ -316,6 +317,23 @@ vec2 simpleParallaxMapping(vec2 p_texCoords, vec3 p_viewDir)
     return p_texCoords - p;
 }
 
+vec3 getNormalFromMap(vec2 p_texCoord, vec3 p_worldPos)
+{
+    vec3 tangentNormal = texture(normalTexture, p_texCoord).xyz * 2.0 - 1.0;
+
+    vec3 Q1  = dFdx(p_worldPos);
+    vec3 Q2  = dFdy(p_worldPos);
+    vec2 st1 = dFdx(p_texCoord);
+    vec2 st2 = dFdy(p_texCoord);
+
+    vec3 N   = normalize(normal);
+    vec3 T  = normalize(Q1*st2.t - Q2*st1.t);
+    vec3 B  = -normalize(cross(N, T));
+    mat3 TBN = mat3(T, B, N);
+
+    return normalize(TBN * tangentNormal);
+}
+
 void main(void)
 {
 	float height = getHeight(texCoord);
@@ -343,7 +361,7 @@ void main(void)
 	if(diffuse.a < alphaThreshold)
 		discard;
 	
-	// Get roughness and metalness values with the new coordinates
+	// Get material properties values with the new coordinates
 	// R - roughness
 	// G - metalness
 	// B - height
@@ -372,6 +390,6 @@ void main(void)
 	// Write fragment's position in world space	to the position buffer
 	positionBuffer = fragPos;
 	
-	// Perform normal mapping and write the new normal to the normal buffer
-	normalBuffer = TBN * normalize(texture(normalTexture, newCoords).rgb * 2.0 - 1.0);
+	// Write fragment's normal direction in world space
+	normalBuffer = normalize(TBN * normalize(texture(normalTexture, newCoords).rgb * 2.0 - 1.0));
 }

+ 18 - 10
Praxis3D/Data/Shaders/geometryPass.vert

@@ -9,6 +9,7 @@ layout(location = 4) in vec3 vertexBitangent;
 
 // Variables passed to fragment shader
 out mat3 TBN;
+out mat3 normalMatrix;
 out vec2 texCoord;
 out vec3 fragPos;
 out vec3 normal;
@@ -30,9 +31,12 @@ uniform float parallaxMappingLOD;
 
 void main(void)
 {		
-	// Multiply position and normal by model matrix (to convert them into world space)
+	// Multiply position by model matrix (to convert it into world space)
     fragPos = vec3(modelMat * vec4(vertexPosition, 1.0));
-	normal = normalize(mat3(modelMat) * vertexNormal);
+	
+	// Calculate normal matrix and convert normal into world space
+    normalMatrix = transpose(inverse(mat3(modelMat)));
+    normal = normalMatrix * vertexNormal;
 	
 	// Square the parallax LOD distance, so there's no need to do that in the fragment shader
 	parallaxLOD = parallaxMappingLOD * parallaxMappingLOD;
@@ -42,16 +46,20 @@ void main(void)
 	
 	// Multiply texture coordinates by the tiling factor. The higher the factor, the denser the tiling
 	texCoord = textureCoord * textureTilingFactor;
-	
+		
+	// Compute TBN matrix components
+	vec3 T = normalize(normalMatrix * vertexTangent);
+    vec3 B = normalize(normalMatrix * vertexBitangent);
+    vec3 N = normalize(normalMatrix * vertexNormal);
+    //T = normalize(T - dot(T, N) * N);
+    //vec3 B = cross(N, T);
+    
 	// Compute TBN matrix
-    vec3 T = normalize(mat3(modelMat) * vertexTangent);
-    vec3 B = normalize(mat3(modelMat) * vertexBitangent);
-	
-	TBN = transpose(inverse(mat3(T, B, normal)));
-	mat3 TBN2 = transpose((mat3(T, B, normal)));
+    TBN = (mat3(T, B, N));
 	
-	tangentCameraPos = TBN2 * cameraPosVec;
-	tangentFragPos = TBN2 * fragPos;
+	// Calculate variables needed for parallax mapping
+    tangentCameraPos = TBN * cameraPosVec;
+    tangentFragPos  = TBN * fragPos;
 	
 	gl_Position = MVP * vec4(vertexPosition, 1.0);
 }

+ 14 - 8
Praxis3D/Data/Shaders/lightPass.frag

@@ -19,7 +19,6 @@ layout(std430, binding = 0) buffer HDRBuffer
 	float screenBrightness;
 };
 
-//layout(location = 0) out vec4 emissiveBuffer;
 layout(location = 0) out vec4 colorBuffer;
 
 in float avgBrightness;
@@ -134,7 +133,12 @@ float GeometrySchlickGGX(float p_NdotV, float p_roughness)
     float roughness = (p_roughness + 1.0);
     float k = (roughness * roughness) / 8.0;
 	
-    return p_NdotV / (p_NdotV * (1.0 - k) + k);
+    float nominator   = p_NdotV;
+    float denominator = p_NdotV * (1.0 - k) + k;
+	
+	return nominator / denominator;
+	
+    //return p_NdotV / (p_NdotV * (1.0 - k) + k);
 }
 
 // Calculates geometric attenuation (or visibility term - self shadowing of microfacets)
@@ -151,7 +155,8 @@ float GeometrySmith(vec3 p_normal, vec3 p_fragToEye, vec3 L, float p_roughness)
 // Calculates fresnel effect using Schlick's approximation
 vec3 fresnelSchlick(float p_cosTheta, vec3 p_F0)
 {
-    return p_F0 + (1.0 - p_F0) * pow(1.0 - p_cosTheta, 5.0);
+	return p_F0 + (1.0 - p_F0) * pow(clamp(1.0 - p_cosTheta, 0.0, 1.0), 5.0);
+    //return p_F0 + (1.0 - p_F0) * pow(1.0 - p_cosTheta, 5.0);
 } 
 
 vec3 calcLightColor(vec3 p_albedoColor, vec3 p_normal, vec3 p_fragToEye, vec3 p_lightColor, vec3 p_lightDirection, float p_lightDistance, vec3 p_F0, float p_roughness, float p_metalic, float p_ambientOcclusion)
@@ -175,7 +180,6 @@ vec3 calcLightColor(vec3 p_albedoColor, vec3 p_normal, vec3 p_fragToEye, vec3 p_
 	
 	vec3 kS = F;
 	vec3 kD = vec3(1.0) - kS;
-	//vec3 kDambient = kD * (1.0 - p_metalic * 0.5);
 	kD *= 1.0 - p_metalic;
 	
 	vec3 numerator = NDF * G * F;
@@ -185,8 +189,13 @@ vec3 calcLightColor(vec3 p_albedoColor, vec3 p_normal, vec3 p_fragToEye, vec3 p_
 	// Combine diffuse, specular, radiance with albedo color and return it
 	float NdotL = max(dot(p_normal, p_lightDirection), 0.0);
 	//return (kD * p_albedoColor / PI + specular) * radiance * NdotL;
+	
+	// Add light color
 	vec3 lightColor = (kD * p_albedoColor / PI + specular) * radiance * NdotL;
+	
+	// Add ambient light
 	lightColor += radiance * p_ambientOcclusion * ambientLightIntensity * (kD * p_albedoColor);
+	
 	return lightColor;
 }
 
@@ -216,7 +225,6 @@ void main(void)
 	float roughnessSqrt = matProperties.x;
 	float metalic = matProperties.y;
 	float ambientOcclusion = matProperties.z;
-	ambientOcclusion *= ambientOcclusion;
 
 	// Calculate F0, with minimum IOR as 0.04
 	vec3 f0 = mix(vec3(0.04), diffuseColor, metalic);
@@ -312,7 +320,5 @@ void main(void)
 	}
 	
 	colorBuffer = vec4(finalLightColor + emissiveColor, 1.0);
-	//colorBuffer = vec4(ambientOcclusion, ambientOcclusion, ambientOcclusion, 1.0);
-	//colorBuffer = vec4(matProperties.b, matProperties.b, matProperties.b, 1.0);
-	//colorBuffer = vec4(diffuseColor / PI, 1.0);
+	//colorBuffer = vec4(matProperties.z, matProperties.z, matProperties.z, 1.0);
 }

+ 2 - 2
Praxis3D/Data/config.ini

@@ -1,5 +1,5 @@
-window_position_x 10
-window_position_y 50
+window_position_x 1500
+window_position_y 100
 window_size_windowed_x 1920
 window_size_windowed_y 1080
 window_size_fullscreen_x 1920

+ 1 - 0
Praxis3D/Data/error-strings-eng.data

@@ -111,6 +111,7 @@
 		"Source_SkyObject"								: "Sky Object",
 		"Source_SkyPass"									: "Sky Rendering Pass",
 		"Source_SoundComponent"						: "Sound Component",
+		"Source_SSAOPass"									: "SSAO Pass",
 		"Source_SoundListenerComponent"		: "Sound Listener Component",
 		"Source_UniversalScene"						: "Universal Scene",
 		"Source_TextureLoader"						: "Texture Loader",

+ 1 - 0
Praxis3D/Praxis3D.vcxproj

@@ -360,6 +360,7 @@
     <ClInclude Include="Source\SpatialComponent.h" />
     <ClInclude Include="Source\SpatialDataManager.h" />
     <ClInclude Include="Source\SpinWait.h" />
+    <ClInclude Include="Source\AmbientOcclusionPass.h" />
     <ClInclude Include="Source\SunScript.h" />
     <ClInclude Include="Source\System.h" />
     <ClInclude Include="Source\TaskManager.h" />

+ 3 - 0
Praxis3D/Praxis3D.vcxproj.filters

@@ -950,6 +950,9 @@
     <ClInclude Include="..\Dependencies\include\imgui\backends\imgui_impl_sdl2.h">
       <Filter>3rd Party\imgui\Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="Source\AmbientOcclusionPass.h">
+      <Filter>Renderer\Render Passes\Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="Data\config.ini" />

+ 415 - 0
Praxis3D/Source/AmbientOcclusionPass.h

@@ -0,0 +1,415 @@
+#pragma once
+
+#include <glm/gtc/type_ptr.hpp>
+#include <random>
+
+#include "RenderPassBase.h"
+
+class AmbientOcclusionPass : public RenderPass
+{
+public:
+	AmbientOcclusionPass(RendererFrontend &p_renderer) :
+		RenderPass(p_renderer, RenderPassType::RenderPassType_AmbientOcclusion),
+		m_hbaoPassShader(nullptr),
+		m_ssaoPassShader(nullptr),
+		m_hbaoBlurHorizontalShader(nullptr),
+		m_hbaoBlurVerticalShader(nullptr),
+		m_ssaoBlurShader(nullptr),
+		m_ssaoSampleBuffer(BufferType_Uniform, BufferBindTarget_Uniform, BufferUsageHint_DynamicDraw),
+		m_aoDataSetShaderBuffer(BufferType_Uniform, BufferBindTarget_Uniform, BufferUsageHint_DynamicDraw),
+		m_ssaoNoiseTexture(Loaders::texture2D().create("SSAONoiseTexture", 4, 4, TextureFormat_RGB, TextureDataFormat_RGB32F, TextureDataType_Float)),
+		m_hbaoNoiseTexture(Loaders::texture2D().create("HBAONoiseTexture", 4, 4, TextureFormat_RGBA, TextureDataFormat_RGBA16F, TextureDataType_Float))
+	{
+
+	}
+
+	~AmbientOcclusionPass() { }
+
+	ErrorCode init()
+	{
+		ErrorCode returnError = ErrorCode::Success;
+
+		m_name = "SSAO Rendering Pass";
+
+		// Create the HBAO pass shader
+		{
+			// Create a property-set used to load the shader
+			PropertySet shaderProperties(Properties::Shaders);
+			shaderProperties.addProperty(Properties::FragmentShader, Config::rendererVar().hbao_pass_frag_shader);
+			shaderProperties.addProperty(Properties::VertexShader, Config::rendererVar().hbao_pass_vert_shader);
+
+			// Create the shader
+			m_hbaoPassShader = Loaders::shader().load(shaderProperties);
+
+			// Load the shader to memory
+			if(ErrorCode shaderError = m_hbaoPassShader->loadToMemory(); shaderError == ErrorCode::Success)
+			{
+				// Queue the shader to be loaded to GPU
+				m_renderer.queueForLoading(*m_hbaoPassShader);
+			}
+			else
+				returnError = shaderError;
+		}
+
+		// Create the SSAO pass shader
+		{
+			// Create a property-set used to load the shader
+			PropertySet shaderProperties(Properties::Shaders);
+			shaderProperties.addProperty(Properties::FragmentShader, Config::rendererVar().ssao_pass_frag_shader);
+			shaderProperties.addProperty(Properties::VertexShader, Config::rendererVar().ssao_pass_vert_shader);
+
+			// Create the shader
+			m_ssaoPassShader = Loaders::shader().load(shaderProperties);
+
+			// Load the shader to memory
+			if(ErrorCode shaderError = m_ssaoPassShader->loadToMemory(); shaderError == ErrorCode::Success)
+			{
+				// Queue the shader to be loaded to GPU
+				m_renderer.queueForLoading(*m_ssaoPassShader);
+			}
+			else
+				returnError = shaderError;
+		}
+
+		// Create the HBAO horizontal blur shader
+		{
+			// Create a property-set used to load the shader
+			PropertySet shaderProperties(Properties::Shaders);
+			shaderProperties.addProperty(Properties::FragmentShader, Config::rendererVar().hbao_blur_horizontal_frag_shader);
+			shaderProperties.addProperty(Properties::VertexShader, Config::rendererVar().hbao_blur_horizontal_vert_shader);
+
+			// Create the shader
+			m_hbaoBlurHorizontalShader = Loaders::shader().load(shaderProperties);
+
+			// Load the shader to memory
+			if(ErrorCode shaderError = m_hbaoBlurHorizontalShader->loadToMemory(); shaderError == ErrorCode::Success)
+			{
+				// Queue the shader to be loaded to GPU
+				m_renderer.queueForLoading(*m_hbaoBlurHorizontalShader);
+			}
+			else
+				returnError = shaderError;
+		}
+
+		// Create the HBAO vertical blur shader
+		{
+			// Create a property-set used to load the shader
+			PropertySet shaderProperties(Properties::Shaders);
+			shaderProperties.addProperty(Properties::FragmentShader, Config::rendererVar().hbao_blur_vertical_frag_shader);
+			shaderProperties.addProperty(Properties::VertexShader, Config::rendererVar().hbao_blur_vertical_vert_shader);
+
+			// Create the shader
+			m_hbaoBlurVerticalShader = Loaders::shader().load(shaderProperties);
+
+			// Load the shader to memory
+			if(ErrorCode shaderError = m_hbaoBlurVerticalShader->loadToMemory(); shaderError == ErrorCode::Success)
+			{
+				// Queue the shader to be loaded to GPU
+				m_renderer.queueForLoading(*m_hbaoBlurVerticalShader);
+			}
+			else
+				returnError = shaderError;
+		}
+
+		// Create the SSAO blur shader
+		{
+			// Create a property-set used to load the shader
+			PropertySet shaderProperties(Properties::Shaders);
+			shaderProperties.addProperty(Properties::FragmentShader, Config::rendererVar().ssao_blur_frag_shader);
+			shaderProperties.addProperty(Properties::VertexShader, Config::rendererVar().ssao_blur_vert_shader);
+
+			// Create the shader
+			m_ssaoBlurShader = Loaders::shader().load(shaderProperties);
+
+			// Load the shader to memory
+			if(ErrorCode shaderError = m_ssaoBlurShader->loadToMemory(); shaderError == ErrorCode::Success)
+			{
+				// Queue the shader to be loaded to GPU
+				m_renderer.queueForLoading(*m_ssaoBlurShader);
+			}
+			else
+				returnError = shaderError;
+		}
+
+		// Set AO data
+		updateAODataSet();
+
+		// Set data for AO buffer
+		m_aoDataSetShaderBuffer.m_bindingIndex = UniformBufferBinding::UniformBufferBinding_AODataSet;
+		m_aoDataSetShaderBuffer.m_size = sizeof(AODataSet);
+		m_aoDataSetShaderBuffer.m_data = (void *)&m_aoDataSet;
+
+		// Queue the HBAO buffer for loading
+		m_renderer.queueForLoading(m_aoDataSetShaderBuffer);
+
+		// Generate and load the SSAO sample buffer
+		generateSSAOsampleBuffer(m_ssaoSampleBuffer);
+		m_renderer.queueForLoading(m_ssaoSampleBuffer);
+
+		// Generate and load the SSAO noise texture
+		generateSSAOnoiseData(m_ssaoNoiseTexture);
+		m_renderer.queueForLoading(m_ssaoNoiseTexture);
+
+		// Generate and load the HBAO noise texture
+		generateHBAOnoiseData(m_hbaoNoiseTexture);
+		m_renderer.queueForLoading(m_hbaoNoiseTexture);
+
+		// Check for errors and log either a successful or a failed initialization
+		if(returnError == ErrorCode::Success)
+		{
+			ErrHandlerLoc::get().log(ErrorCode::Initialize_success, ErrorSource::Source_SSAOPass);
+			setInitialized(true);
+		}
+		else
+			ErrHandlerLoc::get().log(ErrorCode::Initialize_failure, ErrorSource::Source_SSAOPass);
+
+		return returnError;
+	}
+
+	void update(RenderPassData &p_renderPassData, const SceneObjects &p_sceneObjects, const float p_deltaTime)
+	{
+		glDisable(GL_DEPTH_TEST);
+
+		// If the ambient occlusion data has changed, update it
+		if(m_aoData != m_renderer.getFrameData().m_aoData)
+		{
+			m_aoData = m_renderer.getFrameData().m_aoData;
+			updateAODataSet();
+			m_aoDataSetShaderBuffer.m_updateSize = sizeof(AODataSet);
+			m_renderer.queueForUpdate(m_aoDataSetShaderBuffer);
+			m_renderer.passUpdateCommandsToBackend();
+		}
+
+		switch(m_aoData.m_aoType)
+		{
+			case AmbientOcclusionType_None:
+				return;
+				break;
+			case AmbientOcclusionType_SSAO:
+				{
+					//	 ____________________________
+					//	|							 |
+					//	|		   AO PASS           |
+					//	|____________________________|
+					//
+
+					// Bind the noise texture
+					m_renderer.m_backend.bindTextureForReadering(MaterialType::MaterialType_Noise, m_ssaoNoiseTexture);
+
+					glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+					glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+					m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(GBufferTextureType::GBufferPosition, GBufferTextureType::GBufferPosition);
+					m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(GBufferTextureType::GBufferNormal, GBufferTextureType::GBufferNormal);
+					m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(GBufferTextureType::GBufferMatProperties, GBufferTextureType::GBufferMatProperties);
+
+					m_renderer.m_backend.getGeometryBuffer()->bindBufferForWriting(GBufferTextureType::GBufferFinal);
+
+					// Queue and render a full screen quad using a final pass shader
+					m_renderer.queueForDrawing(m_ssaoPassShader->getShaderHandle(), m_ssaoPassShader->getUniformUpdater(), p_sceneObjects.m_cameraViewMatrix);
+					m_renderer.passScreenSpaceDrawCommandsToBackend();
+
+					//	 ____________________________
+					//	|							 |
+					//	|		  BLUR PASS          |
+					//	|____________________________|
+					//
+
+					m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(GBufferTextureType::GBufferFinal, GBufferTextureType::GBufferFinal);
+					m_renderer.m_backend.getGeometryBuffer()->bindBufferForWriting(GBufferTextureType::GBufferMatProperties);
+
+					// Queue and render a full screen quad using a final pass shader
+					m_renderer.queueForDrawing(m_ssaoBlurShader->getShaderHandle(), m_ssaoBlurShader->getUniformUpdater(), p_sceneObjects.m_cameraViewMatrix);
+					m_renderer.passScreenSpaceDrawCommandsToBackend();
+				}
+				break;
+			case AmbientOcclusionType_HBAO:
+				{
+					//	 ____________________________
+					//	|							 |
+					//	|		   AO PASS           |
+					//	|____________________________|
+					//
+
+					// Bind the noise texture
+					m_renderer.m_backend.bindTextureForReadering(MaterialType::MaterialType_Noise, m_hbaoNoiseTexture);
+
+					//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+					//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+					m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(GBufferTextureType::GBufferPosition, GBufferTextureType::GBufferPosition);
+					m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(GBufferTextureType::GBufferNormal, GBufferTextureType::GBufferNormal);
+
+					m_renderer.m_backend.getGeometryBuffer()->bindBufferForWriting(GBufferTextureType::GBufferFinal);
+
+					// Queue and render a full screen quad using a final pass shader
+					m_renderer.queueForDrawing(m_hbaoPassShader->getShaderHandle(), m_hbaoPassShader->getUniformUpdater(), p_sceneObjects.m_cameraViewMatrix);
+					m_renderer.passScreenSpaceDrawCommandsToBackend();
+
+					//	 ____________________________
+					//	|							 |
+					//	|		  BLUR PASS          |
+					//	|____________________________|
+					//
+					// Horizontal blur
+					{
+						m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(GBufferTextureType::GBufferFinal, GBufferTextureType::GBufferInputTexture);
+						m_renderer.m_backend.getGeometryBuffer()->bindBufferForWriting(GBufferTextureType::GBufferIntermediate);
+
+						// Queue and render a full screen quad using a final pass shader
+						m_renderer.queueForDrawing(m_hbaoBlurHorizontalShader->getShaderHandle(), m_hbaoBlurHorizontalShader->getUniformUpdater(), p_sceneObjects.m_cameraViewMatrix);
+						m_renderer.passScreenSpaceDrawCommandsToBackend();
+					}
+
+					// Vertical blur
+					{
+						m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(GBufferTextureType::GBufferIntermediate, GBufferTextureType::GBufferInputTexture);
+						m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(GBufferTextureType::GBufferMatProperties, GBufferTextureType::GBufferMatProperties);
+						m_renderer.m_backend.getGeometryBuffer()->bindBufferForWriting(GBufferTextureType::GBufferMatProperties);
+
+						// Queue and render a full screen quad using a final pass shader
+						m_renderer.queueForDrawing(m_hbaoBlurVerticalShader->getShaderHandle(), m_hbaoBlurVerticalShader->getUniformUpdater(), p_sceneObjects.m_cameraViewMatrix);
+						m_renderer.passScreenSpaceDrawCommandsToBackend();
+					}
+				}
+				break;
+		}
+	}
+
+private:
+	float ssaoLerp(float p_a, float p_b, float p_f)
+	{
+		return p_a + p_f * (p_b - p_a);
+	}
+
+	void generateHBAOnoiseData(TextureLoader2D::Texture2DHandle &p_noiseTexture)
+	{
+		// Create a Mersenne Twister pseudo-random generator
+		std::mt19937 rmt;
+
+		unsigned int numberOfRandomElements = m_aoData.m_aoNumOfSteps * m_aoData.m_aoNumOfSteps;
+
+		// Reserve enough space for all the pixels in advance
+		m_hbaoNoise.reserve(numberOfRandomElements); //HBAO_RANDOM_ELEMENTS
+
+		// Go over each pixel
+		for(unsigned int i = 0; i < numberOfRandomElements; i++) //HBAO_RANDOM_ELEMENTS
+		{
+			// Get random numbers
+			float randomNumber1 = static_cast<float>(rmt()) / 4294967296.0f;
+			float randomNumber2 = static_cast<float>(rmt()) / 4294967296.0f;
+
+			// Use random rotation angles in ( 0 <-> 2PI / NUM_DIRECTIONS )
+			float randomAngle = glm::two_pi<float>() * randomNumber1 / m_aoData.m_aoNumOfDirections;
+
+			// Set the pixel values
+			m_hbaoNoise.push_back(glm::vec4(cosf(randomAngle), sinf(randomAngle), randomNumber2, 0.0f));
+		}
+
+		// Set the pixel data of the texture
+		p_noiseTexture.setPixelData(&m_hbaoNoise[0]);
+
+		// Set texture parameters
+		p_noiseTexture.setMagnificationFilterType(TextureFilterType::TextureFilterType_Nearest);
+		p_noiseTexture.setMinificationFilterType(TextureFilterType::TextureFilterType_Nearest);
+	}
+	void generateSSAOnoiseData(TextureLoader2D::Texture2DHandle &p_noiseTexture)
+	{
+		// Create the number distribution of floats between 0.0 and 1.0
+		std::uniform_real_distribution<GLfloat> randomFloats(0.0, 1.0);
+		std::default_random_engine generator;
+
+		unsigned int numberOfRandomElements = m_aoData.m_aoNumOfSteps * m_aoData.m_aoNumOfSteps;
+
+		// Go over each pixel
+		for(unsigned int i = 0; i < numberOfRandomElements; i++)
+		{
+			// Rotate around z-axis (in tangent space)
+			glm::vec3 noise(randomFloats(generator) * 2.0 - 1.0, randomFloats(generator) * 2.0 - 1.0, 0.0f);
+
+			// Set the pixel values
+			m_ssaoNoise.push_back(noise);
+		}
+
+		// Set the data for the noise texture
+		p_noiseTexture.setPixelData((void *)&m_ssaoNoise[0]);
+
+		// Set texture parameters
+		p_noiseTexture.setMagnificationFilterType(TextureFilterType::TextureFilterType_Nearest);
+		p_noiseTexture.setMinificationFilterType(TextureFilterType::TextureFilterType_Nearest);
+	}
+	void generateSSAOsampleBuffer(RendererFrontend::ShaderBuffer &p_sampleBuffer)
+	{
+		// Create the number distribution of floats between 0.0 and 1.0
+		std::uniform_real_distribution<GLfloat> randomFloats(0.0, 1.0);
+		std::default_random_engine generator;
+
+		// Go over each sample
+		for(int i = 0; i < m_aoData.m_aoNumOfSamples; ++i)
+		{
+			// Generate sample
+			glm::vec3 sample(randomFloats(generator) * 2.0 - 1.0, randomFloats(generator) * 2.0 - 1.0, randomFloats(generator));
+			sample = glm::normalize(sample);
+			sample *= randomFloats(generator);
+
+			// Scale samples so they're more aligned to the center of the kernel
+			float scale = float(i) / (float)m_aoData.m_aoNumOfSamples;
+			scale = ssaoLerp(0.1f, 1.0f, scale * scale);
+			sample *= scale;
+
+			// Set the sample data
+			m_ssaoKernel.push_back(glm::vec4(sample, 0.0f));
+		}
+
+		// Set the data for the sample buffer
+		p_sampleBuffer.m_bindingIndex = UniformBufferBinding::UniformBufferBinding_SSAOSampleBuffer;
+		p_sampleBuffer.m_size = sizeof(glm::vec4) * m_aoData.m_aoNumOfSamples;
+		p_sampleBuffer.m_data = &m_ssaoKernel[0];
+	}
+
+	void updateAODataSet()
+	{
+		float projScale = float(m_renderer.getFrameData().m_screenSize.y) / (tanf(Config::graphicsVar().fov * 0.5f) * 2.0f);
+
+		m_aoDataSet.m_RadiusToScreen = m_aoData.m_aoRadius * 0.5f * projScale;
+		m_aoDataSet.m_radius = m_aoData.m_aoRadius;
+		m_aoDataSet.m_NegInvR2 = -1.0f / (m_aoDataSet.m_radius * m_aoDataSet.m_radius);
+		m_aoDataSet.m_NDotVBias = std::min(std::max(0.0f, m_aoData.m_aoBias), 1.0f);
+
+		// Resolution
+		m_aoDataSet.m_InvFullResolution = glm::vec2(1.0f / float(m_renderer.getFrameData().m_screenSize.x), 1.0f / float(m_renderer.getFrameData().m_screenSize.y));
+
+		m_aoDataSet.m_AOMultiplier = 1.0f / (1.0f - m_aoDataSet.m_NDotVBias);
+		m_aoDataSet.m_PowExponent = std::max(m_renderer.getFrameData().m_aoData.m_aoIntensity, 0.0f);
+
+		m_aoDataSet.m_bias = m_aoData.m_aoBias;
+		m_aoDataSet.m_numOfDirections = m_aoData.m_aoNumOfDirections;
+		m_aoDataSet.m_numOfSamples = m_aoData.m_aoNumOfSamples;
+		m_aoDataSet.m_numOfSteps = m_aoData.m_aoNumOfSteps;
+	}
+
+	AODataSet m_aoDataSet;
+	AmbientOcclusionData m_aoData;
+
+	// AO pass shaders
+	ShaderLoader::ShaderProgram *m_hbaoPassShader;
+	ShaderLoader::ShaderProgram *m_ssaoPassShader;
+
+	// Blur shaders
+	ShaderLoader::ShaderProgram *m_hbaoBlurHorizontalShader;
+	ShaderLoader::ShaderProgram *m_hbaoBlurVerticalShader;
+	ShaderLoader::ShaderProgram *m_ssaoBlurShader;
+
+	// AO buffers
+	RendererFrontend::ShaderBuffer m_ssaoSampleBuffer;
+	RendererFrontend::ShaderBuffer m_aoDataSetShaderBuffer;
+
+	// Noise textures
+	TextureLoader2D::Texture2DHandle m_ssaoNoiseTexture;
+	TextureLoader2D::Texture2DHandle m_hbaoNoiseTexture;
+
+	// AO generated data
+	std::vector<glm::vec3> m_ssaoNoise;
+	std::vector<glm::vec4> m_ssaoKernel;
+	std::vector<glm::vec4> m_hbaoNoise;
+};

+ 1 - 24
Praxis3D/Source/BloomPass.h

@@ -69,29 +69,6 @@ public:
 		return returnError;
 	}
 
-	// Calculates the maximum mipmap levels based on the image size and bloom mipmap, bloom downscale limits 
-	unsigned int calculateMipmapLevels(unsigned int p_width, unsigned int p_height)
-	{
-		unsigned int width = p_width / 2;
-		unsigned int height = p_height / 2;
-		unsigned int mipLevels = 1;
-
-		unsigned int mipmapLimit = (unsigned int)Config::graphicsVar().bloom_mipmap_limit;
-		unsigned int downscaleLimit = (unsigned int)Config::graphicsVar().bloom_downscale_limit;
-
-		for(unsigned int i = 0; i < mipmapLimit; i++)
-		{
-			width = width / 2;
-			height = height / 2;
-
-			if(width < downscaleLimit || height < downscaleLimit) break;
-
-			mipLevels++;
-		}
-
-		return mipLevels + 1;
-	}
-
 	void update(RenderPassData &p_renderPassData, const SceneObjects &p_sceneObjects, const float p_deltaTime)
 	{
 		// Assign the bloom threshold value so it can be sent to the shader
@@ -103,7 +80,7 @@ public:
 
 		// Calculate mipmap size and level
 		glm::uvec2 mipmapSize = glm::uvec2(imageWidth / 2, imageHeight / 2);
-		unsigned int mipmapLevels = calculateMipmapLevels(imageWidth, imageHeight);
+		unsigned int mipmapLevels = calculateMipmapLevels(imageWidth, imageHeight, (unsigned int)Config::graphicsVar().bloom_mipmap_limit, (unsigned int)Config::graphicsVar().bloom_downscale_limit);
 
 		m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(GBufferTextureType::GBufferFinal, GBufferTextureType::GBufferInputTexture);
 

+ 2 - 0
Praxis3D/Source/CommandBuffer.h

@@ -136,6 +136,8 @@ public:
 										 p_texture.getTextureFormat(),
 										 p_texture.getTextureDataFormat(),
 										 p_texture.getTextureDataType(),
+										 p_texture.getMagnificationFilterType(),
+										 p_texture.getMinificationFilterType(),
 										 p_texture.getEnableMipmap(),
 										 p_texture.getMipmapLevel(),
 										 p_texture.getTextureWidth(),

+ 24 - 11
Praxis3D/Source/CommonDefinitions.h

@@ -4,6 +4,7 @@
 #include <functional>
 
 #include "EnumFactory.h"
+#include "Utilities.h"
 
 typedef std::uint32_t EntityID;
 typedef unsigned int UpdateCount;
@@ -83,6 +84,7 @@ enum MemoryBarrierType : unsigned int
 	Code(RenderPassType_Luminance,) \
 	Code(RenderPassType_Final,) \
 	Code(RenderPassType_GUI,) \
+	Code(RenderPassType_AmbientOcclusion,) \
 	Code(RenderPassType_NumOfTypes,)
 DECLARE_ENUM(RenderPassType, RENDER_PASS_TYPE)
 typedef std::vector<RenderPassType> RenderingPasses;
@@ -144,6 +146,7 @@ enum MaterialType : unsigned int
 	MaterialType_Combined,
 	MaterialType_NumOfTypes,
 	MaterialType_Roughness = MaterialType_NumOfTypes,
+	MaterialType_Noise,
 	MaterialType_Metalness,
 	MaterialType_Height,
 	MaterialType_AmbientOcclusion,
@@ -209,6 +212,7 @@ enum TextureFormat : int
 };
 enum TextureDataType : int
 {
+	TextureDataType_Short			= GL_SHORT,
 	TextureDataType_Float			= GL_FLOAT,
 	TextureDataType_Int				= GL_INT,
 	TextureDataType_UnsignedByte	= GL_UNSIGNED_BYTE
@@ -229,6 +233,7 @@ enum TextureDataFormat : int
 	TextureDataFormat_RGB32F	= GL_RGB32F,
 	TextureDataFormat_RGBA8		= GL_RGBA8,
 	TextureDataFormat_RGBA16	= GL_RGBA16,
+	TextureDataFormat_RGBA16SN	= GL_RGBA16_SNORM,
 	TextureDataFormat_RGBA16F	= GL_RGBA16F,
 	TextureDataFormat_RGBA32F	= GL_RGBA32F,
 	TextureDataFormat_R16I		= GL_R16I,
@@ -236,6 +241,15 @@ enum TextureDataFormat : int
 	TextureDataFormat_R16UI		= GL_R16UI,
 	TextureDataFormat_R32UI		= GL_R32UI
 };
+enum TextureFilterType : int
+{
+	TextureFilterType_Linear				= GL_LINEAR,
+	TextureFilterType_LinearMipmapLinear	= GL_LINEAR_MIPMAP_LINEAR,
+	TextureFilterType_LinearMipmapNearest	= GL_LINEAR_MIPMAP_NEAREST,
+	TextureFilterType_Nearest				= GL_NEAREST,
+	TextureFilterType_NearestMipmapLinear	= GL_NEAREST_MIPMAP_LINEAR,
+	TextureFilterType_NearestMipmapNearest	= GL_NEAREST_MIPMAP_NEAREST
+};
 enum TextureWrapType : int
 {
 	TextureWrapType_ClampToBorder		= GL_CLAMP_TO_BORDER,
@@ -249,7 +263,9 @@ enum UniformBufferBinding : unsigned int
 	UniformBufferBinding_PointLights,
 	UniformBufferBinding_SpotLights,
 	UniformBufferBinding_AtmScatParam,
-	UniformBufferBinding_LensFlareParam
+	UniformBufferBinding_LensFlareParam,
+	UniformBufferBinding_AODataSet,
+	UniformBufferBinding_SSAOSampleBuffer
 };
 
 enum GBufferTextureType : unsigned int
@@ -264,19 +280,16 @@ enum GBufferTextureType : unsigned int
 	GBufferIntermediate,
 	GBufferTotalNumTextures,
 	GBufferInputTexture = GBufferTotalNumTextures,
-	GbufferOutputTexture
+	GbufferOutputTexture,
+	GbufferDepth
 };
 
-/*enum ObjectMaterialType : unsigned int
+enum AmbientOcclusionType : int
 {
-	ObjectMaterialType_Null = 0,
-	ObjectMaterialType_Glass,
-	ObjectMaterialType_Metal,
-	ObjectMaterialType_Plastic,
-	ObjectMaterialType_Rock,
-	ObjectMaterialType_Wood,
-	ObjectMaterialType_NumOfTypes
-};*/
+	AmbientOcclusionType_None = 0,
+	AmbientOcclusionType_SSAO,
+	AmbientOcclusionType_HBAO
+};
 
 enum AudioBusType : unsigned int
 {

+ 32 - 0
Praxis3D/Source/Config.cpp

@@ -148,6 +148,11 @@ void Config::init()
 	AddVariablePredef(m_graphicsVar, eye_adaption);
 	AddVariablePredef(m_graphicsVar, multisampling);
 	AddVariablePredef(m_graphicsVar, alpha_size);
+	AddVariablePredef(m_graphicsVar, ao_blur_num_of_samples);
+	AddVariablePredef(m_graphicsVar, ao_num_of_directions);
+	AddVariablePredef(m_graphicsVar, ao_num_of_samples);
+	AddVariablePredef(m_graphicsVar, ao_num_of_steps);
+	AddVariablePredef(m_graphicsVar, ao_type);
 	AddVariablePredef(m_graphicsVar, bloom_blur_passes);
 	AddVariablePredef(m_graphicsVar, dir_shadow_res_x);
 	AddVariablePredef(m_graphicsVar, dir_shadow_res_y);
@@ -164,6 +169,13 @@ void Config::init()
 	AddVariablePredef(m_graphicsVar, tonemap_method);
 	AddVariablePredef(m_graphicsVar, alpha_threshold);
 	AddVariablePredef(m_graphicsVar, ambient_light_intensity);
+	AddVariablePredef(m_graphicsVar, ao_hbao_bias);
+	AddVariablePredef(m_graphicsVar, ao_ssao_bias);
+	AddVariablePredef(m_graphicsVar, ao_blurSharpness);
+	AddVariablePredef(m_graphicsVar, ao_hbao_intensity);
+	AddVariablePredef(m_graphicsVar, ao_ssao_intensity);
+	AddVariablePredef(m_graphicsVar, ao_hbao_radius);
+	AddVariablePredef(m_graphicsVar, ao_ssao_radius);
 	AddVariablePredef(m_graphicsVar, emissive_multiplier);
 	AddVariablePredef(m_graphicsVar, emissive_threshold);
 	AddVariablePredef(m_graphicsVar, eye_adaption_rate);
@@ -355,6 +367,12 @@ void Config::init()
 	AddVariablePredef(m_rendererVar, bloom_composite_pass_frag_shader);
 	AddVariablePredef(m_rendererVar, blur_pass_vert_shader);
 	AddVariablePredef(m_rendererVar, blur_pass_frag_shader);
+	AddVariablePredef(m_rendererVar, hbao_blur_horizontal_frag_shader);
+	AddVariablePredef(m_rendererVar, hbao_blur_horizontal_vert_shader);
+	AddVariablePredef(m_rendererVar, hbao_blur_vertical_frag_shader);
+	AddVariablePredef(m_rendererVar, hbao_blur_vertical_vert_shader);
+	AddVariablePredef(m_rendererVar, hbao_pass_frag_shader);
+	AddVariablePredef(m_rendererVar, hbao_pass_vert_shader);
 	AddVariablePredef(m_rendererVar, lense_flare_comp_pass_vert_shader);
 	AddVariablePredef(m_rendererVar, lense_flare_comp_pass_frag_shader);
 	AddVariablePredef(m_rendererVar, lense_flare_pass_vert_shader);
@@ -370,6 +388,10 @@ void Config::init()
 	AddVariablePredef(m_rendererVar, lens_flare_dirt_texture);
 	AddVariablePredef(m_rendererVar, lens_flare_ghost_gradient_texture);
 	AddVariablePredef(m_rendererVar, lens_flare_starburst_texture);
+	AddVariablePredef(m_rendererVar, ssao_blur_frag_shader);
+	AddVariablePredef(m_rendererVar, ssao_blur_vert_shader);
+	AddVariablePredef(m_rendererVar, ssao_pass_frag_shader);
+	AddVariablePredef(m_rendererVar, ssao_pass_vert_shader);
 	AddVariablePredef(m_rendererVar, dir_light_quad_offset_x);
 	AddVariablePredef(m_rendererVar, dir_light_quad_offset_y);
 	AddVariablePredef(m_rendererVar, dir_light_quad_offset_z);
@@ -385,6 +407,7 @@ void Config::init()
 	AddVariablePredef(m_rendererVar, objects_loaded_per_frame);
 	AddVariablePredef(m_rendererVar, render_to_texture_buffer);
 	AddVariablePredef(m_rendererVar, shader_pool_size);
+	AddVariablePredef(m_rendererVar, ssao_num_of_samples);
 	AddVariablePredef(m_rendererVar, depth_test);
 	AddVariablePredef(m_rendererVar, face_culling);
 
@@ -403,6 +426,7 @@ void Config::init()
 	AddVariablePredef(m_shaderVar, modelViewMatUniform);
 	AddVariablePredef(m_shaderVar, modelViewProjectionMatUniform);
 	AddVariablePredef(m_shaderVar, transposeViewMatUniform);
+	AddVariablePredef(m_shaderVar, transposeInverseViewMatUniform);
 	AddVariablePredef(m_shaderVar, screenSizeUniform);
 	AddVariablePredef(m_shaderVar, screenNumOfPixelsUniform);
 	AddVariablePredef(m_shaderVar, deltaTimeMSUniform);
@@ -443,6 +467,7 @@ void Config::init()
 	AddVariablePredef(m_shaderVar, matPropertiesMapUniform);
 	AddVariablePredef(m_shaderVar, intermediateMapUniform);
 	AddVariablePredef(m_shaderVar, finalMapUniform);
+	AddVariablePredef(m_shaderVar, depthMapUniform);
 	AddVariablePredef(m_shaderVar, inputColorMapUniform);
 	AddVariablePredef(m_shaderVar, outputColorMapUniform);
 	AddVariablePredef(m_shaderVar, sunGlowTextureUniform);
@@ -456,6 +481,11 @@ void Config::init()
 	AddVariablePredef(m_shaderVar, heightTextureUniform);
 	AddVariablePredef(m_shaderVar, combinedTextureUniform);
 	AddVariablePredef(m_shaderVar, averageLuminanceTexture);
+	AddVariablePredef(m_shaderVar, noiseTexture);
+	AddVariablePredef(m_shaderVar, hbaoBlurHorizontalInvResDirection);
+	AddVariablePredef(m_shaderVar, hbaoBlurVerticalInvResDirection);
+	AddVariablePredef(m_shaderVar, hbaoBlurNumOfSamples);
+	AddVariablePredef(m_shaderVar, hbaoBlurSharpness);
 	AddVariablePredef(m_shaderVar, atmIrradianceTextureUniform);
 	AddVariablePredef(m_shaderVar, atmScatteringTextureUniform);
 	AddVariablePredef(m_shaderVar, atmSingleMieScatTextureUniform);
@@ -477,6 +507,8 @@ void Config::init()
 	AddVariablePredef(m_shaderVar, depthTypeUniform);
 	AddVariablePredef(m_shaderVar, eyeAdaptionRateUniform);
 	AddVariablePredef(m_shaderVar, eyeAdaptionIntBrightnessUniform);
+	AddVariablePredef(m_shaderVar, AODataSetBuffer);
+	AddVariablePredef(m_shaderVar, SSAOSampleBuffer);
 	AddVariablePredef(m_shaderVar, HDRSSBuffer);
 	AddVariablePredef(m_shaderVar, atmScatParamBuffer);
 	AddVariablePredef(m_shaderVar, lensFlareParametersBuffer);

+ 89 - 5
Praxis3D/Source/Config.h

@@ -33,6 +33,7 @@ enum DataType : uint32_t
 	DataType_CreateEntity,				// ComponentsConstructionInfo
 	DataType_DeleteEntity,				// EntityAndComponent
 	// Graphics
+	DataType_AmbientOcclusionData,		// AmbientOcclusionData
 	DataType_GUIPassFunctors,			// FunctorSequence
 	DataType_RenderToTexture,			// bool
 	DataType_RenderToTextureResolution, // glm::ivec2
@@ -165,6 +166,7 @@ namespace Systems
 			static constexpr BitMask Shared19	= (BitMask)1 << 19;
 			static constexpr BitMask Shared20	= (BitMask)1 << 20;
 			static constexpr BitMask Shared21	= (BitMask)1 << 21;
+			static constexpr BitMask Shared22	= (BitMask)1 << 22;
 			static constexpr BitMask Shared23	= (BitMask)1 << 23;
 			static constexpr BitMask Shared24	= (BitMask)1 << 24;
 			static constexpr BitMask Shared25	= (BitMask)1 << 25;
@@ -279,9 +281,15 @@ namespace Systems
 																IntermediateBuffer | FinalBuffer | RenderToTextureBuffer;
 
 			static constexpr BitMask AmbientIntensity		= Changes::Type::Graphics + Changes::Graphics::Scene + Changes::Common::Shared16;
-			static constexpr BitMask ZFar					= Changes::Type::Graphics + Changes::Graphics::Scene + Changes::Common::Shared17;
-			static constexpr BitMask ZNear					= Changes::Type::Graphics + Changes::Graphics::Scene + Changes::Common::Shared18;
-			static constexpr BitMask AllScene				= AmbientIntensity | ZFar | ZNear;
+			static constexpr BitMask AOIntensity			= Changes::Type::Graphics + Changes::Graphics::Scene + Changes::Common::Shared17;
+			static constexpr BitMask AOBlurSharpness		= Changes::Type::Graphics + Changes::Graphics::Scene + Changes::Common::Shared18;
+			static constexpr BitMask AOBias					= Changes::Type::Graphics + Changes::Graphics::Scene + Changes::Common::Shared19;
+			static constexpr BitMask AONumOfSamples			= Changes::Type::Graphics + Changes::Graphics::Scene + Changes::Common::Shared20;
+			static constexpr BitMask AORadius				= Changes::Type::Graphics + Changes::Graphics::Scene + Changes::Common::Shared21;
+			static constexpr BitMask AOType					= Changes::Type::Graphics + Changes::Graphics::Scene + Changes::Common::Shared22;
+			static constexpr BitMask ZFar					= Changes::Type::Graphics + Changes::Graphics::Scene + Changes::Common::Shared23;
+			static constexpr BitMask ZNear					= Changes::Type::Graphics + Changes::Graphics::Scene + Changes::Common::Shared24;
+			static constexpr BitMask AllScene				= AmbientIntensity | AOIntensity | AOBlurSharpness | AOBias | AONumOfSamples | AORadius | AOType | ZFar | ZNear;
 
 			static constexpr BitMask All					= AllCamera | AllLighting | AllBuffers | AllScene;
 		}
@@ -336,6 +344,7 @@ namespace Properties
 	Code(Keybindings,) \
 	Code(LoadInBackground,) \
 	Code(Name,) \
+	Code(None,) \
 	Code(Objects,) \
 	Code(ObjectPoolSize,) \
 	Code(Scene,) \
@@ -375,6 +384,9 @@ namespace Properties
 	Code(AmbientIntensity, ) \
 	Code(AmbientOcclusion, ) \
 	Code(Attenuation,) \
+	Code(Bias,) \
+	Code(BlurSamples,) \
+	Code(BlurSharpness,) \
 	Code(Camera,) \
 	Code(CameraComponent,) \
 	Code(ClampToBorder,) \
@@ -385,6 +397,7 @@ namespace Properties
 	Code(CutoffAngle,) \
 	Code(Diffuse,) \
 	Code(Direction,) \
+	Code(Directions,) \
 	Code(DirectionalLight,) \
 	Code(Emissive,) \
 	Code(EmissiveIntensity,) \
@@ -394,6 +407,7 @@ namespace Properties
 	Code(GeometryShader,) \
 	Code(Graphics,) \
 	Code(GraphicsObject,) \
+	Code(HBAO,) \
 	Code(Height,) \
 	Code(HeightScale,) \
 	Code(Intensity,) \
@@ -425,6 +439,7 @@ namespace Properties
 	Code(Repeat,) \
 	Code(RMHAO,) \
 	Code(Roughness,) \
+	Code(Samples,) \
 	Code(Shaders,) \
 	Code(ShaderComponent,) \
 	Code(ShaderPoolSize,) \
@@ -432,7 +447,9 @@ namespace Properties
 	Code(ShaderModelObject,) \
 	Code(SpotLight,) \
 	Code(SpotLightPoolSize,) \
+	Code(SSAO,) \
 	Code(Static,) \
+	Code(Steps,) \
 	Code(TessControlShader,) \
 	Code(TessEvaluationShader,) \
 	Code(TextureTilingFactor,) \
@@ -442,6 +459,7 @@ namespace Properties
 	Code(ZFar,) \
 	Code(ZNear,) \
 	/* Graphics rendering passes */ \
+	Code(AmbientOcclusionRenderPass,) \
 	Code(AtmScatteringRenderPass,) \
 	Code(BloomRenderPass,) \
 	Code(GeometryRenderPass,) \
@@ -866,6 +884,11 @@ public:
 			eye_adaption = false;
 			multisampling = true;
 			alpha_size = 8;
+			ao_blur_num_of_samples = 6;
+			ao_num_of_directions = 8;
+			ao_num_of_samples = 64;
+			ao_num_of_steps = 4;
+			ao_type = AmbientOcclusionType::AmbientOcclusionType_HBAO;
 			bloom_blur_passes = 5;
 			bloom_downscale_limit = 10;
 			bloom_mipmap_limit = 16;
@@ -886,6 +909,13 @@ public:
 			tonemap_method = 6;
 			alpha_threshold = 0.0f;
 			ambient_light_intensity = 0.3f;
+			ao_hbao_bias = 0.1f;
+			ao_ssao_bias = 0.025f;
+			ao_blurSharpness = 40.0f;
+			ao_hbao_intensity = 2.0f;
+			ao_ssao_intensity = 2.0f;
+			ao_hbao_radius = 2.0f;
+			ao_ssao_radius = 0.5f;
 			bloom_intensity = 1.0f;
 			bloom_knee = 0.1f;
 			bloom_threshold = 1.5f;
@@ -929,6 +959,11 @@ public:
 		bool eye_adaption;
 		bool multisampling;
 		int alpha_size;
+		int ao_blur_num_of_samples;
+		int ao_num_of_directions;
+		int ao_num_of_samples;
+		int ao_num_of_steps;
+		int ao_type;
 		int bloom_blur_passes;
 		int bloom_downscale_limit;
 		int bloom_mipmap_limit;
@@ -949,6 +984,13 @@ public:
 		int tonemap_method;
 		float alpha_threshold;
 		float ambient_light_intensity;
+		float ao_hbao_bias;
+		float ao_ssao_bias;
+		float ao_blurSharpness;
+		float ao_hbao_intensity;
+		float ao_ssao_intensity;
+		float ao_hbao_radius;
+		float ao_ssao_radius;
 		float bloom_intensity;
 		float bloom_knee;
 		float bloom_threshold;
@@ -1002,7 +1044,7 @@ public:
 			editor_inspector_button_width_multiplier = 1.5f;
 			editor_lua_variables_max_height = 200.0f;
 			editor_play_button_size = 30.0f;
-			editor_render_pass_max_height = 250.0f;
+			editor_render_pass_max_height = 270.0f;
 			gui_file_dialog_min_size_x = 400.0f;
 			gui_file_dialog_min_size_y = 200.0f;
 			gui_file_dialog_dir_color_R = 0.905f;
@@ -1152,7 +1194,7 @@ public:
 			makeLeftHanded = false;
 			triangulate = true;
 			removeComponent = false;
-			genNormals = true;
+			genNormals = false;
 			genSmoothNormals = true;
 			genUVCoords = true;
 			optimizeMeshes = true;
@@ -1278,6 +1320,12 @@ public:
 			bloom_upscale_comp_shader = "bloomUpscale.comp";
 			blur_pass_vert_shader = "blurPass.vert";
 			blur_pass_frag_shader = "blurPass.frag";
+			hbao_blur_horizontal_frag_shader = "ambientOcclusionBlurHBAOhorizontal.frag";
+			hbao_blur_horizontal_vert_shader = "ambientOcclusionBlurHBAO.vert";
+			hbao_blur_vertical_frag_shader = "ambientOcclusionBlurHBAOvertical.frag";
+			hbao_blur_vertical_vert_shader = "ambientOcclusionBlurHBAO.vert";
+			hbao_pass_frag_shader = "ambientOcclusionPassHBAO.frag";
+			hbao_pass_vert_shader = "ambientOcclusionPassHBAO.vert";
 			lense_flare_comp_pass_vert_shader = "lenseFlareCompositePass.vert";
 			lense_flare_comp_pass_frag_shader = "lenseFlareCompositePass.frag";
 			lense_flare_pass_vert_shader = "lenseFlarePass.vert";
@@ -1293,6 +1341,10 @@ public:
 			lens_flare_dirt_texture = "DirtMaskTexture.png";
 			lens_flare_ghost_gradient_texture = "p3d_lensFlareGhostColorGradient.png";
 			lens_flare_starburst_texture = "p3d_lensFlareStarburst.png";
+			ssao_blur_frag_shader = "ambientOcclusionBlurSSAO.frag";
+			ssao_blur_vert_shader = "ambientOcclusionBlurSSAO.vert";
+			ssao_pass_frag_shader = "ambientOcclusionPassSSAO.frag";
+			ssao_pass_vert_shader = "ambientOcclusionPassSSAO.vert";
 			dir_light_quad_offset_x = 0.0f;
 			dir_light_quad_offset_y = 0.0f;
 			dir_light_quad_offset_z = 0.0f;
@@ -1308,6 +1360,7 @@ public:
 			objects_loaded_per_frame = 1;
 			render_to_texture_buffer = GBufferTextureType::GBufferEmissive;
 			shader_pool_size = 10;
+			ssao_num_of_samples = 64;
 			depth_test = true;
 			face_culling = true;
 		}
@@ -1348,6 +1401,12 @@ public:
 		std::string bloom_upscale_comp_shader;
 		std::string blur_pass_vert_shader;
 		std::string blur_pass_frag_shader;
+		std::string hbao_blur_horizontal_frag_shader;
+		std::string hbao_blur_horizontal_vert_shader;
+		std::string hbao_blur_vertical_frag_shader;
+		std::string hbao_blur_vertical_vert_shader;
+		std::string hbao_pass_frag_shader;
+		std::string hbao_pass_vert_shader;
 		std::string lense_flare_comp_pass_vert_shader;
 		std::string lense_flare_comp_pass_frag_shader;
 		std::string lense_flare_pass_vert_shader;
@@ -1363,6 +1422,10 @@ public:
 		std::string lens_flare_dirt_texture;
 		std::string lens_flare_ghost_gradient_texture;
 		std::string lens_flare_starburst_texture;
+		std::string ssao_blur_frag_shader;
+		std::string ssao_blur_vert_shader;
+		std::string ssao_pass_frag_shader;
+		std::string ssao_pass_vert_shader;
 		float dir_light_quad_offset_x;
 		float dir_light_quad_offset_y;
 		float dir_light_quad_offset_z;
@@ -1378,6 +1441,7 @@ public:
 		int objects_loaded_per_frame;
 		int render_to_texture_buffer;
 		int shader_pool_size;
+		int ssao_num_of_samples;
 		bool depth_test;
 		bool face_culling;
 	};
@@ -1408,6 +1472,7 @@ public:
 			modelViewMatUniform = "modelViewMat";
 			modelViewProjectionMatUniform = "MVP";
 			transposeViewMatUniform = "transposeViewMat";
+			transposeInverseViewMatUniform = "transposeInverseViewMat";
 			screenSizeUniform = "screenSize";
 			screenNumOfPixelsUniform = "screenNumOfPixels";
 			deltaTimeMSUniform = "deltaTimeMS";
@@ -1452,6 +1517,7 @@ public:
 			matPropertiesMapUniform = "matPropertiesMap";
 			intermediateMapUniform = "intermediateMap";
 			finalMapUniform = "finalColorMap";
+			depthMapUniform = "depthMap";
 			inputColorMapUniform = "inputColorMap";
 			outputColorMapUniform = "outputColorMap";
 
@@ -1466,6 +1532,12 @@ public:
 			heightTextureUniform = "heightTexture";
 			combinedTextureUniform = "combinedTexture";
 			averageLuminanceTexture = "averageLuminanceTexture";
+			noiseTexture = "noiseTexture";
+
+			hbaoBlurHorizontalInvResDirection = "hbaoBlurHorizontalInvResDirection";
+			hbaoBlurVerticalInvResDirection = "hbaoBlurVerticalInvResDirection";
+			hbaoBlurNumOfSamples = "hbaoBlurNumOfSamples";
+			hbaoBlurSharpness = "hbaoBlurSharpness";
 
 			atmIrradianceTextureUniform = "atmIrradianceTexture";
 			atmScatteringTextureUniform = "atmScatteringTexture";
@@ -1496,6 +1568,8 @@ public:
 
 			eyeAdaptionRateUniform = "eyeAdaptionRate";
 			eyeAdaptionIntBrightnessUniform = "eyeAdaptionIntBrightness";
+			AODataSetBuffer = "AODataSetBuffer";
+			SSAOSampleBuffer = "SSAOSampleBuffer";
 			HDRSSBuffer = "HDRBuffer";
 			atmScatParamBuffer = "AtmScatParametersBuffer";
 			lensFlareParametersBuffer = "LensFlareParametersBuffer";
@@ -1513,6 +1587,7 @@ public:
 		std::string modelViewMatUniform;
 		std::string modelViewProjectionMatUniform;
 		std::string transposeViewMatUniform;
+		std::string transposeInverseViewMatUniform;
 		std::string screenSizeUniform;
 		std::string screenNumOfPixelsUniform;
 		std::string deltaTimeMSUniform;
@@ -1557,6 +1632,7 @@ public:
 		std::string matPropertiesMapUniform;
 		std::string intermediateMapUniform;
 		std::string finalMapUniform;
+		std::string depthMapUniform;
 		std::string inputColorMapUniform;
 		std::string outputColorMapUniform;
 
@@ -1571,6 +1647,12 @@ public:
 		std::string heightTextureUniform;
 		std::string combinedTextureUniform;
 		std::string averageLuminanceTexture;
+		std::string noiseTexture;
+
+		std::string hbaoBlurHorizontalInvResDirection;
+		std::string hbaoBlurVerticalInvResDirection;
+		std::string hbaoBlurNumOfSamples;
+		std::string hbaoBlurSharpness;
 
 		std::string atmIrradianceTextureUniform;
 		std::string atmScatteringTextureUniform;
@@ -1601,6 +1683,8 @@ public:
 
 		std::string eyeAdaptionRateUniform;
 		std::string eyeAdaptionIntBrightnessUniform;
+		std::string AODataSetBuffer;
+		std::string SSAOSampleBuffer;
 		std::string HDRSSBuffer;
 		std::string atmScatParamBuffer;
 		std::string lensFlareParametersBuffer;

+ 107 - 0
Praxis3D/Source/Containers.h

@@ -2,6 +2,7 @@
 
 #include <string>
 
+#include "Config.h"
 #include "CommonDefinitions.h"
 #include "Math.h"
 #include "PropertySet.h"
@@ -196,4 +197,110 @@ struct EntityAndComponent
 
 	EntityID m_entityID;
 	ComponentType m_componentType;
+};
+
+// Stores data used for tweaking the ambient occlusion effect
+struct AmbientOcclusionData
+{
+	AmbientOcclusionData()
+	{
+		setDefaultValues();
+		m_aoType = AmbientOcclusionType::AmbientOcclusionType_None;
+	}
+
+	void setDefaultValues()
+	{
+		m_aoNumOfDirections = Config::graphicsVar().ao_num_of_directions;
+		m_aoNumOfSamples = Config::graphicsVar().ao_num_of_samples;
+		m_aoNumOfSteps = Config::graphicsVar().ao_num_of_steps;
+		m_aoBlurNumOfSamples = Config::graphicsVar().ao_blur_num_of_samples;
+		m_aoBlurSharpness = Config::graphicsVar().ao_blurSharpness;
+		m_aoType = AmbientOcclusionData::ambientOcclusionTypeToAmbientOcclusionType(Config::graphicsVar().ao_type);
+
+		// Apply default AO type specific values
+		switch(m_aoType)
+		{
+			case AmbientOcclusionType_SSAO:
+				m_aoBias = Config::graphicsVar().ao_ssao_bias;
+				m_aoIntensity = Config::graphicsVar().ao_ssao_intensity;
+				m_aoRadius = Config::graphicsVar().ao_ssao_radius;
+				break;
+			case AmbientOcclusionType_HBAO:
+				m_aoBias = Config::graphicsVar().ao_hbao_bias;
+				m_aoIntensity = Config::graphicsVar().ao_hbao_intensity;
+				m_aoRadius = Config::graphicsVar().ao_hbao_radius;
+				break;
+		}
+	}
+
+	bool operator==(const AmbientOcclusionData &p_aoData) 
+	{ 
+		return	m_aoNumOfDirections == p_aoData.m_aoNumOfDirections &&
+				m_aoNumOfSamples	== p_aoData.m_aoNumOfSamples &&
+				m_aoNumOfSteps		== p_aoData.m_aoNumOfSteps &&
+				m_aoIntensity		== p_aoData.m_aoIntensity &&
+				m_aoBias			== p_aoData.m_aoBias &&
+				m_aoRadius			== p_aoData.m_aoRadius &&
+				m_aoBlurSharpness	== p_aoData.m_aoBlurSharpness &&
+				m_aoType			== p_aoData.m_aoType;
+	}
+
+	static Properties::PropertyID ambientOcclusionTypeToPropertyID(const AmbientOcclusionType p_aoType)
+	{
+		switch(p_aoType)
+		{
+			case AmbientOcclusionType::AmbientOcclusionType_None:
+			default:
+				return Properties::None;
+				break;
+			case AmbientOcclusionType::AmbientOcclusionType_SSAO:
+				return Properties::SSAO;
+				break;
+			case AmbientOcclusionType::AmbientOcclusionType_HBAO:
+				return Properties::HBAO;
+				break;
+		}
+	}
+	static AmbientOcclusionType propertyIDToAmbientOcclusionType(const Properties::PropertyID p_propertyID)
+	{
+		switch(p_propertyID)
+		{
+			case Properties::None:
+			default:
+				return AmbientOcclusionType::AmbientOcclusionType_None;
+				break;
+			case Properties::SSAO:
+				return AmbientOcclusionType::AmbientOcclusionType_SSAO;
+				break;
+			case Properties::HBAO:
+				return AmbientOcclusionType::AmbientOcclusionType_HBAO;
+				break;
+		}
+	}
+	static AmbientOcclusionType ambientOcclusionTypeToAmbientOcclusionType(const int p_aoType)
+	{
+		switch(p_aoType)
+		{
+			case AmbientOcclusionType::AmbientOcclusionType_None:
+			default:
+				return AmbientOcclusionType::AmbientOcclusionType_None;
+				break;
+			case AmbientOcclusionType::AmbientOcclusionType_SSAO:
+				return AmbientOcclusionType::AmbientOcclusionType_SSAO;
+				break;
+			case AmbientOcclusionType::AmbientOcclusionType_HBAO:
+				return AmbientOcclusionType::AmbientOcclusionType_HBAO;
+				break;
+		}
+	}
+
+	int m_aoNumOfDirections;
+	int m_aoNumOfSamples;
+	int m_aoNumOfSteps;
+	int m_aoBlurNumOfSamples;
+	float m_aoIntensity;
+	float m_aoBias;
+	float m_aoRadius;
+	float m_aoBlurSharpness;
+	AmbientOcclusionType m_aoType;
 };

+ 106 - 3
Praxis3D/Source/EditorWindow.cpp

@@ -780,7 +780,7 @@ void EditorWindow::update(const float p_deltaTime)
                         // Clear the component exist flags as they are reset every frame
                         m_selectedEntity.clearComponentExistFlags();
 
-                        // Calculate widget offset used to draw a label on the left and a widget on the right (opposite of how ImGui draws it)
+                        // Calculate widget offset used to draw a label on the left and a widget on ;he right (opposite of how ImGui draws it)
                         float inputWidgetOffset = ImGui::GetCursorPosX() + ImGui::CalcItemWidth() * 0.5f + ImGui::GetStyle().ItemInnerSpacing.x;
 
                         // Calculate the offset for the collapsing header that is drawn after the delete component button of each component type
@@ -4073,7 +4073,7 @@ void EditorWindow::drawSceneData(SceneData &p_sceneData, const bool p_sendChange
         }
     }
 
-    // Calculate rendering passes window height and cap it to a max height value
+    // Calculate audio banks window height and cap it to a max height value
     float audioBanksWindowHeight = (m_fontSize + m_imguiStyle.FramePadding.y * 2 + m_imguiStyle.ItemSpacing.y) * (p_sceneData.m_audioBanks.size() + 2);
     audioBanksWindowHeight = audioBanksWindowHeight > Config::GUIVar().editor_audio_banks_max_height ? Config::GUIVar().editor_audio_banks_max_height : audioBanksWindowHeight;
 
@@ -4165,11 +4165,113 @@ void EditorWindow::drawSceneData(SceneData &p_sceneData, const bool p_sendChange
         m_systemScene->getSceneLoader()->getChangeController()->sendChange(this, m_systemScene->getSceneLoader()->getSystemScene(Systems::Graphics), Systems::Changes::Graphics::ZNear);
     }
 
+    // Calculate ambient occlusion window height
+    float ambientOcclusionWindowHeight = (m_fontSize + m_imguiStyle.FramePadding.y * 2 + m_imguiStyle.ItemSpacing.y);
+    ambientOcclusionWindowHeight *= p_sceneData.m_aoData.m_aoType == AmbientOcclusionType::AmbientOcclusionType_None ? 2.0f : 10.0f;
+
+    if(ImGui::BeginChild("##AmbientOcclusionSettings", ImVec2(0.0f, ambientOcclusionWindowHeight), true))
+    {
+        ImGui::PushStyleVar(ImGuiStyleVar_SeparatorTextBorderSize, 0.0f);
+        ImGui::SeparatorText("Ambient occlusion:");
+        ImGui::PopStyleVar(); //ImGuiStyleVar_SeparatorTextBorderSize
+
+        bool aoDataChanged = false;
+        int aoType = p_sceneData.m_aoData.m_aoType;
+        drawLeftAlignedLabelText("Ambient occlusion type:", inputWidgetOffset);
+        if(ImGui::Combo("##AOTypeCombo", &aoType, &m_ambientOcclusionTypeText[0], (int)m_ambientOcclusionTypeText.size()))
+        {
+            p_sceneData.m_aoData.m_aoType = AmbientOcclusionData::ambientOcclusionTypeToAmbientOcclusionType(aoType);
+
+            if(p_sendChanges)
+                aoDataChanged = true;
+
+            // Apply default AO type specific values
+            switch(p_sceneData.m_aoData.m_aoType)
+            {
+                case AmbientOcclusionType_SSAO:
+                    p_sceneData.m_aoData.m_aoBias = Config::graphicsVar().ao_ssao_bias;
+                    p_sceneData.m_aoData.m_aoIntensity = Config::graphicsVar().ao_ssao_intensity;
+                    p_sceneData.m_aoData.m_aoRadius = Config::graphicsVar().ao_ssao_radius;
+                    break;
+                case AmbientOcclusionType_HBAO:
+                    p_sceneData.m_aoData.m_aoBias = Config::graphicsVar().ao_hbao_bias;
+                    p_sceneData.m_aoData.m_aoIntensity = Config::graphicsVar().ao_hbao_intensity;
+                    p_sceneData.m_aoData.m_aoRadius = Config::graphicsVar().ao_hbao_radius;
+                    break;
+            }
+        }
+
+        if(p_sceneData.m_aoData.m_aoType != AmbientOcclusionType::AmbientOcclusionType_None)
+        {
+            // Draw NUMBER OF DIRECTIONS
+            drawLeftAlignedLabelText("Number of directions:", inputWidgetOffset);
+            if(ImGui::InputInt("##AONumberOfDirectionsInputInt", &p_sceneData.m_aoData.m_aoNumOfDirections) && p_sendChanges)
+            {
+                aoDataChanged = true;
+            }
+
+            // Draw NUMBER OF STEPS
+            drawLeftAlignedLabelText("Number of steps:", inputWidgetOffset);
+            if(ImGui::InputInt("##AONumberOfStepsInputInt", &p_sceneData.m_aoData.m_aoNumOfSteps) && p_sendChanges)
+            {
+                aoDataChanged = true;
+            }
+
+            // Draw NUMBER OF SAMPLES
+            drawLeftAlignedLabelText("Number of samples:", inputWidgetOffset);
+            if(ImGui::InputInt("##AONumberOfSamplesInputInt", &p_sceneData.m_aoData.m_aoNumOfSamples) && p_sendChanges)
+            {
+                aoDataChanged = true;
+            }
+
+            // Draw NUMBER OF BLUR SAMPLES
+            drawLeftAlignedLabelText("Number of blur samples:", inputWidgetOffset);
+            if(ImGui::InputInt("##AONumberOfBlurSamplesInputInt", &p_sceneData.m_aoData.m_aoBlurNumOfSamples) && p_sendChanges)
+            {
+                aoDataChanged = true;
+            }
+
+            // Draw AO INTENSITY
+            drawLeftAlignedLabelText("Intensity:", inputWidgetOffset);
+            if(ImGui::DragFloat("##AOIntensityDrag", &p_sceneData.m_aoData.m_aoIntensity, 0.1f, 0.0f, 100000.0f, "%.3f") && p_sendChanges)
+            {
+                aoDataChanged = true;
+            }
+
+            // Draw AO BIAS
+            drawLeftAlignedLabelText("Bias:", inputWidgetOffset);
+            if(ImGui::DragFloat("##AOBiasDrag", &p_sceneData.m_aoData.m_aoBias, 0.0001f, 0.0f, 1.0f, "%.5f") && p_sendChanges)
+            {
+                aoDataChanged = true;
+            }
+
+            // Draw AO RADIUS
+            drawLeftAlignedLabelText("Radius:", inputWidgetOffset);
+            if(ImGui::DragFloat("##AORadiusDrag", &p_sceneData.m_aoData.m_aoRadius, 0.01f, 0.0f, 100000.0f, "%.3f") && p_sendChanges)
+            {
+                aoDataChanged = true;
+            }
+
+            // Draw AO BLUR SHARPNESS
+            drawLeftAlignedLabelText("Blur sharpness:", inputWidgetOffset);
+            if(ImGui::DragFloat("##AOBlurSharpnessDrag", &p_sceneData.m_aoData.m_aoBlurSharpness, 0.1f, 0.0f, 100000.0f, "%.3f") && p_sendChanges)
+            {
+                aoDataChanged = true;
+            }
+
+        }
+
+        if(aoDataChanged)
+            m_systemScene->getSceneLoader()->getChangeController()->sendData(m_systemScene->getSceneLoader()->getSystemScene(Systems::Graphics), DataType::DataType_AmbientOcclusionData, (void *)&p_sceneData.m_aoData, false);
+
+    }
+    ImGui::EndChild();
+
     // Calculate rendering passes window height and cap it to a max height value
     float renderPassWindowHeight = (m_fontSize + m_imguiStyle.FramePadding.y * 2 + m_imguiStyle.ItemSpacing.y) * (p_sceneData.m_renderingPasses.size() + 2);
     renderPassWindowHeight = renderPassWindowHeight > Config::GUIVar().editor_render_pass_max_height ? Config::GUIVar().editor_render_pass_max_height : renderPassWindowHeight;
 
-    if(ImGui::BeginChild("##RenderingPasses", ImVec2(0, renderPassWindowHeight), true))//, ImVec2(0, childWindowHeight), true, ImGuiWindowFlags_None)
+    if(ImGui::BeginChild("##RenderingPasses", ImVec2(0.0f, renderPassWindowHeight), true))//, ImVec2(0, childWindowHeight), true, ImGuiWindowFlags_None)
     {
         ImGui::PushStyleVar(ImGuiStyleVar_SeparatorTextBorderSize, 0.0f);
         ImGui::SeparatorText("Rendering passes:");
@@ -4473,6 +4575,7 @@ void EditorWindow::updateSceneData(SceneData &p_sceneData)
         p_sceneData.m_volume[i] = audioScene->getVolume(static_cast<AudioBusType>(i));
 
     // Set graphics data
+    p_sceneData.m_aoData = graphicsScene->getAmbientOcclusionData();
     p_sceneData.m_ambientIntensity = graphicsScene->getSceneObjects().m_ambientIntensity;
     p_sceneData.m_zFar = graphicsScene->getSceneObjects().m_zFar;
     p_sceneData.m_zNear = graphicsScene->getSceneObjects().m_zNear;

+ 82 - 69
Praxis3D/Source/EditorWindow.h

@@ -76,6 +76,7 @@ public:
 		for(unsigned int i = 0; i < RenderPassType::RenderPassType_NumOfTypes; i++)
 			m_renderingPassesTypeText.push_back(GetString(static_cast<RenderPassType>(i)));
 
+		m_ambientOcclusionTypeText = { "None", "SSAO", "HBAO" };
 		m_luaVariableTypeStrings = { "null", "bool", "int", "float", "double", "vec2i", "vec2f", "vec3f", "vec4f", "string", "propertyID" };
 		m_shaderTypeStrings = { "Compute", "Fragment", "Geometry", "Vertex", "Tessellation control", "Tessellation evaluation" };
 		m_tonemappingMethodText = { "None", "Simple reinhard", "Reinhard with white point", "Filmic tonemapping", "Uncharted 2", "Unreal 3", "ACES", "Lottes", "Uchimura" };
@@ -721,9 +722,13 @@ private:
 			for(unsigned int i = 0; i < AudioBusType::AudioBusType_NumOfTypes; i++)
 				m_volume[i] = 1.0f;
 
+			m_aoData.setDefaultValues();
+
 			m_ambientIntensity = 0.0f;
+
 			m_zFar = 0.0f;
 			m_zNear = 0.0f;
+
 			m_renderingPasses.push_back(RenderPassType::RenderPassType_Geometry);
 			m_renderingPasses.push_back(RenderPassType::RenderPassType_AtmScattering);
 			m_renderingPasses.push_back(RenderPassType::RenderPassType_Lighting);
@@ -745,6 +750,7 @@ private:
 		float m_volume[AudioBusType::AudioBusType_NumOfTypes];
 
 		// Graphics scene
+		AmbientOcclusionData m_aoData;
 		float m_ambientIntensity;
 		float m_zFar;
 		float m_zNear;
@@ -963,15 +969,18 @@ private:
 	{
 		switch(p_textureDataType)
 		{
-		case TextureDataType_Float:
-			return "Float";
-			break;
-		case TextureDataType_Int:
-			return "Integer";
-			break;
-		case TextureDataType_UnsignedByte:
-			return "Unsigned byte";
-			break;
+			case TextureDataType::TextureDataType_Short:
+				return "Short";
+				break;
+			case TextureDataType::TextureDataType_Float:
+				return "Float";
+				break;
+			case TextureDataType::TextureDataType_Int:
+				return "Integer";
+				break;
+			case TextureDataType::TextureDataType_UnsignedByte:
+				return "Unsigned byte";
+				break;
 		}
 		return "";
 	}
@@ -979,66 +988,69 @@ private:
 	{
 		switch(p_textureDataFormat)
 		{
-		case TextureDataFormat_R8:
-			return "R8";
-			break;
-		case TextureDataFormat_R16:
-			return "R16";
-			break;
-		case TextureDataFormat_R16F:
-			return "R16F";
-			break;
-		case TextureDataFormat_R32F:
-			return "R32F";
-			break;
-		case TextureDataFormat_RG8:
-			return "RG8";
-			break;
-		case TextureDataFormat_RG16:
-			return "RG16";
-			break;
-		case TextureDataFormat_RG16F:
-			return "RG16F";
-			break;
-		case TextureDataFormat_RG32F:
-			return "RG32F";
-			break;
-		case TextureDataFormat_RGB8:
-			return "RGB8";
-			break;
-		case TextureDataFormat_RGB16:
-			return "RGB16";
-			break;
-		case TextureDataFormat_RGB16F:
-			return "RGB16F";
-			break;
-		case TextureDataFormat_RGB32F:
-			return "RGB32F";
-			break;
-		case TextureDataFormat_RGBA8:
-			return "RGBA8";
-			break;
-		case TextureDataFormat_RGBA16:
-			return "RGBA16";
-			break;
-		case TextureDataFormat_RGBA16F:
-			return "RGBA16F";
-			break;
-		case TextureDataFormat_RGBA32F:
-			return "RGBA32F";
-			break;
-		case TextureDataFormat_R16I:
-			return "R16I";
-			break;
-		case TextureDataFormat_R32I:
-			return "R32I";
-			break;
-		case TextureDataFormat_R16UI:
-			return "R16UI";
-			break;
-		case TextureDataFormat_R32UI:
-			return "R32UI";
-			break;
+			case TextureDataFormat::TextureDataFormat_R8:
+				return "R8";
+				break;
+			case TextureDataFormat::TextureDataFormat_R16:
+				return "R16";
+				break;
+			case TextureDataFormat::TextureDataFormat_R16F:
+				return "R16F";
+				break;
+			case TextureDataFormat::TextureDataFormat_R32F:
+				return "R32F";
+				break;
+			case TextureDataFormat::TextureDataFormat_RG8:
+				return "RG8";
+				break;
+			case TextureDataFormat::TextureDataFormat_RG16:
+				return "RG16";
+				break;
+			case TextureDataFormat::TextureDataFormat_RG16F:
+				return "RG16F";
+				break;
+			case TextureDataFormat::TextureDataFormat_RG32F:
+				return "RG32F";
+				break;
+			case TextureDataFormat::TextureDataFormat_RGB8:
+				return "RGB8";
+				break;
+			case TextureDataFormat::TextureDataFormat_RGB16:
+				return "RGB16";
+				break;
+			case TextureDataFormat::TextureDataFormat_RGB16F:
+				return "RGB16F";
+				break;
+			case TextureDataFormat::TextureDataFormat_RGB32F:
+				return "RGB32F";
+				break;
+			case TextureDataFormat::TextureDataFormat_RGBA8:
+				return "RGBA8";
+				break;
+			case TextureDataFormat::TextureDataFormat_RGBA16:
+				return "RGBA16";
+				break;
+			case TextureDataFormat::TextureDataFormat_RGBA16SN:
+				return "RGBA16 SNORM";
+				break;
+			case TextureDataFormat::TextureDataFormat_RGBA16F:
+				return "RGBA16F";
+				break;
+			case TextureDataFormat::TextureDataFormat_RGBA32F:
+				return "RGBA32F";
+				break;
+			case TextureDataFormat::TextureDataFormat_R16I:
+				return "R16I";
+				break;
+			case TextureDataFormat::TextureDataFormat_R32I:
+				return "R32I";
+				break;
+			case TextureDataFormat::TextureDataFormat_R16UI:
+				return "R16UI";
+				break;
+			case TextureDataFormat::TextureDataFormat_R32UI:
+				return "R32UI";
+				break;
 		}
 		return "";
 	}
@@ -1121,6 +1133,7 @@ private:
 	ImVec2 m_buttonSizedByFont;
 
 	// String arrays and other data used for ImGui Combo inputs
+	std::vector<const char *> m_ambientOcclusionTypeText;
 	std::vector<const char *> m_physicalMaterialProperties;
 	std::vector<const char *> m_renderingPassesTypeText;
 	std::vector<const char *> m_luaVariableTypeStrings;

+ 1 - 0
Praxis3D/Source/ErrorCodes.h

@@ -162,6 +162,7 @@ DECLARE_ENUM(ErrorCode, ERROR_CODES)
     Code(Source_SkyPass,) \
     Code(Source_SoundComponent,) \
     Code(Source_SoundListenerComponent,) \
+    Code(Source_SSAOPass,) \
     Code(Source_TextureLoader,) \
     Code(Source_UniversalScene,) \
     Code(Source_Window,) \

+ 3 - 2
Praxis3D/Source/ErrorHandler.cpp

@@ -171,7 +171,8 @@ void ErrorHandler::log(ErrorType p_errorType, ErrorSource p_errorSource, std::st
 			if(!WindowLocator().get().spawnYesNoErrorBox(m_errorTypeStrings[p_errorType] + ": " + m_errorSources[p_errorSource], m_errorSources[p_errorSource] + ": " + p_error + ".\n\nWould you like to continue?"))
 				Config::m_engineVar.running = false;
 
-			m_console->displayMessage("\033[31m[" + m_errorTypeStrings[p_errorType] + "] [" + m_errorSources[p_errorSource] + "]: " + p_error + ".\033[0m");
+			//m_console->displayMessage("\033[31m[" + m_errorTypeStrings[p_errorType] + "] [" + m_errorSources[p_errorSource] + "]: " + p_error + ".\033[0m");
+			m_console->displayMessage("\033[31m[" + m_errorTypeStrings[p_errorType] + "] \033[1;36m[" + m_errorSources[p_errorSource] + "]\033[0;37m: " + p_error + ".\033[0m");
 			break;
 		}
 
@@ -199,7 +200,7 @@ void ErrorHandler::log(ErrorCode p_errorCode, ErrorSource p_errorSource, std::st
 		}
 	}
 	else
-		log(m_errorData[p_errorCode].m_errorType, p_errorSource, "\033[1;33m" + m_errorData[p_errorCode].m_errorString + "\033[0m: " + p_error);
+		log(m_errorData[p_errorCode].m_errorType, p_errorSource, m_errorData[p_errorCode].m_errorString + ": " + p_error);
 }
 
 void ErrorHandler::log(const ErrorCode p_errorCode, const std::string &p_objectName, const ErrorSource p_errorSource)

+ 3 - 23
Praxis3D/Source/GeometryBuffer.h

@@ -31,7 +31,7 @@ public:
 	virtual void initFinalPass();		// Bind the final buffer to 'read from' and the default screen buffer to 'write to'
 	
 	// Buffer binding functions
-	inline void bindBufferForReading(GBufferTextureType p_buffer, int p_activeTexture)
+	inline void bindBufferForReading(GBufferTextureType p_buffer, int p_activeTexture = 0)
 	{
 		glActiveTexture(GL_TEXTURE0 + p_activeTexture);
 		switch(p_buffer)
@@ -51,27 +51,8 @@ public:
 		case GBufferTextureType::GBufferIntermediate:
 			glBindTexture(GL_TEXTURE_2D, m_intermediateBuffer);
 			break;
-		}
-	}
-	inline void bindBufferForReading(GBufferTextureType p_buffer)
-	{
-		glActiveTexture(GL_TEXTURE0);
-		switch(p_buffer)
-		{
-		case GBufferTextureType::GBufferPosition:
-		case GBufferTextureType::GBufferDiffuse:
-		case GBufferTextureType::GBufferNormal:
-		case GBufferTextureType::GBufferEmissive:
-			glBindTexture(GL_TEXTURE_2D, m_GBTextures[p_buffer]);
-			break;
-		case GBufferTextureType::GBufferMatProperties:
-			glBindTexture(GL_TEXTURE_2D, m_GBTextures[p_buffer]);
-			break;
-		case GBufferTextureType::GBufferFinal:
-			glBindTexture(GL_TEXTURE_2D, m_finalBuffer);
-			break;
-		case GBufferTextureType::GBufferIntermediate:
-			glBindTexture(GL_TEXTURE_2D, m_intermediateBuffer);
+		case GBufferTextureType::GbufferDepth:
+			glBindTexture(GL_TEXTURE_2D, m_depthBuffer);
 			break;
 		}
 	}
@@ -212,4 +193,3 @@ protected:
 
 	GBufferTextureType m_finalPassBuffer;
 };
-

+ 35 - 0
Praxis3D/Source/GraphicsDataSets.h

@@ -2,6 +2,7 @@
 
 #include <algorithm>
 #include <iterator>
+#include <cmath>
 #include <variant>
 
 #include "Math.h"
@@ -370,6 +371,40 @@ struct SpotLightDataSet
 	float m_cutoffAngle;
 };
 
+struct AODataSet
+{
+	AODataSet()
+	{
+		m_RadiusToScreen = 0.0;
+		m_radius = 0.0;
+		m_NegInvR2 = 0.0f;
+		m_NDotVBias = 0.0f;
+
+		m_InvFullResolution = glm::vec2(0.0f, 0.0f);
+		m_AOMultiplier = 0.0f;
+		m_PowExponent = 0.0f;
+
+		m_bias = 0.0f;
+		m_numOfDirections = 0;
+		m_numOfSamples = 0;
+		m_numOfSteps = 0;
+	}
+
+	float m_RadiusToScreen;	// radius
+	float m_radius;
+	float m_NegInvR2;		// radius * radius
+	float m_NDotVBias;
+
+	glm::vec2 m_InvFullResolution;
+	float m_AOMultiplier;
+	float m_PowExponent;
+
+	float m_bias;
+	int m_numOfDirections;
+	int m_numOfSamples;
+	int m_numOfSteps;
+};
+
 struct HDRDataSet
 {
 	HDRDataSet()

+ 2 - 0
Praxis3D/Source/ModelLoader.cpp

@@ -32,6 +32,8 @@ ErrorCode Model::loadToMemory()
 		// Assign flags for assimp loader
 		unsigned int assimpFlags = 0;
 
+		if(Config::modelVar().calcTangentSpace)
+			assimpFlags |= aiProcess_CalcTangentSpace;
 		if(Config::modelVar().joinIdenticalVertices)
 			assimpFlags |= aiProcess_JoinIdenticalVertices;
 		if(Config::modelVar().makeLeftHanded)

+ 20 - 0
Praxis3D/Source/RenderPassBase.h

@@ -102,6 +102,26 @@ public:
 protected:
 	inline void setInitialized(const bool p_initialized) { m_initialized = p_initialized; }
 
+	// Calculates the maximum mipmap levels based on the image size and mipmap / downscale limits 
+	inline unsigned int calculateMipmapLevels(const unsigned int p_width, const unsigned int p_height, const unsigned int p_mipmapLimit, const unsigned int p_downscaleLimit) const
+	{
+		unsigned int width = p_width / 2;
+		unsigned int height = p_height / 2;
+		unsigned int mipLevels = 1;
+
+		for(unsigned int i = 0; i < p_mipmapLimit; i++)
+		{
+			width = width / 2;
+			height = height / 2;
+
+			if(width < p_downscaleLimit || height < p_downscaleLimit) break;
+
+			mipLevels++;
+		}
+
+		return mipLevels + 1;
+	}
+
 	unsigned int m_ID;
 	bool m_initialized;
 	RenderPassType m_renderPassType;

+ 25 - 16
Praxis3D/Source/RendererBackend.h

@@ -221,6 +221,8 @@ public:
 					const TextureFormat p_texFormat,
 					const TextureDataFormat p_texDataFormat,
 					const TextureDataType p_texDataType,
+					const TextureFilterType p_texMagFilter,
+					const TextureFilterType p_texMinFilter,
 					const bool p_enableMipmap,
 					const int p_mipmapLevel,
 					const unsigned int p_textureWidth,
@@ -228,7 +230,7 @@ public:
 					const void *p_data) :
 			m_handle(p_handle),
 			m_objectType(LoadObject_Texture2D),
-			m_objectData(p_name, p_texFormat, p_texDataFormat, p_texDataType, p_enableMipmap, p_mipmapLevel, p_textureWidth, p_textureHeight, p_data) { }
+			m_objectData(p_name, p_texFormat, p_texDataFormat, p_texDataType, p_texMagFilter, p_texMinFilter, p_enableMipmap, p_mipmapLevel, p_textureWidth, p_textureHeight, p_data) { }
 
 		LoadCommand(unsigned int &p_handle,
 					const TextureFormat p_texFormat,
@@ -306,6 +308,8 @@ public:
 							  const TextureFormat p_texFormat,
 							  const TextureDataFormat p_texDataFormat,
 							  const TextureDataType p_texDataType,
+							  const TextureFilterType p_texMagFilter,
+							  const TextureFilterType p_texMinFilter,
 							  const bool p_enableMipmap,
 							  const int p_mipmapLevel,
 							  const unsigned int p_textureWidth,
@@ -315,6 +319,8 @@ public:
 				m_texFormat(p_texFormat),
 				m_texDataFormat(p_texDataFormat),
 				m_texDataType(p_texDataType),
+				m_magnificationFilter(p_texMagFilter),
+				m_minificationFilter(p_texMinFilter),
 				m_enableMipmap(p_enableMipmap),
 				m_mipmapLevel(p_mipmapLevel),
 				m_textureWidth(p_textureWidth),
@@ -325,6 +331,8 @@ public:
 			const TextureFormat m_texFormat;
 			const TextureDataFormat m_texDataFormat;
 			const TextureDataType m_texDataType;
+			const TextureFilterType m_magnificationFilter;
+			const TextureFilterType m_minificationFilter;
 			const bool m_enableMipmap;
 			const int m_mipmapLevel;
 			const unsigned int m_textureWidth;
@@ -378,12 +386,14 @@ public:
 					   const TextureFormat p_texFormat,
 					   const TextureDataFormat p_texDataFormat,
 					   const TextureDataType p_texDataType,
+					   const TextureFilterType p_texMagFilter,
+					   const TextureFilterType p_texMinFilter,
 					   const bool p_enableMipmap,
 					   const int p_mipmapLevel,
 					   const unsigned int p_textureWidth,
 					   const unsigned int p_textureHeight,
 					   const void *p_data) :
-				m_tex2DData(p_name, p_texFormat, p_texDataFormat, p_texDataType, p_enableMipmap, p_mipmapLevel, p_textureWidth, p_textureHeight, p_data) { }
+				m_tex2DData(p_name, p_texFormat, p_texDataFormat, p_texDataType, p_texMagFilter, p_texMinFilter, p_enableMipmap, p_mipmapLevel, p_textureWidth, p_textureHeight, p_data) { }
 
 			ObjectData(const TextureFormat p_texFormat,
 					   const int p_mipmapLevel,
@@ -435,6 +445,12 @@ public:
 	void processDrawing(const ScreenSpaceDrawCommands &p_screenSpaceDrawCommands, const UniformFrameData &p_frameData);
 	void processDrawing(const ComputeDispatchCommands &p_computeDispatchCommands, const UniformFrameData &p_frameData);
 
+	inline void bindTextureForReadering(const unsigned int p_bindingLocation, const TextureLoader2D::Texture2DHandle &p_texture) const
+	{
+		glActiveTexture(GL_TEXTURE0 + p_bindingLocation);
+		glBindTexture(GL_TEXTURE_2D, p_texture.getHandle());
+	}
+
 	inline GeometryBuffer *getGeometryBuffer() { return m_gbuffer; }
 
 	inline unsigned int getFramebufferTextureHandle(GBufferTextureType p_bufferType) const { return m_gbuffer->getBufferTextureHandle(p_bufferType); }
@@ -882,22 +898,15 @@ protected:
 						p_command.m_objectData.m_tex2DData.m_texDataType,
 						p_command.m_objectData.m_tex2DData.m_data);
 
-					// Generate mipmaps if they are enabled, and set texture filtering to use mipmaps
+					// Generate mipmaps if they are enabled
 					if(p_command.m_objectData.m_tex2DData.m_enableMipmap)
-					{
 						glGenerateMipmap(GL_TEXTURE_2D);
-						// Texture filtering mode, when image is minimized
-						glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, Config::textureVar().gl_texture_minification_mipmap);
-						// Texture filtering mode, when image is magnified
-						glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, Config::textureVar().gl_texture_magnification_mipmap);
-					}
-					else
-					{
-						// Texture filtering mode, when image is minimized
-						glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, Config::textureVar().gl_texture_minification);
-						// Texture filtering mode, when image is magnified
-						glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, Config::textureVar().gl_texture_magnification);
-					}
+
+					// Texture filtering mode, when image is magnified
+					glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, p_command.m_objectData.m_tex2DData.m_magnificationFilter);
+
+					// Texture filtering mode, when image is minimized
+					glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, p_command.m_objectData.m_tex2DData.m_minificationFilter);
 
 					// Texture anisotropic filtering
 					glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, Config::textureVar().gl_texture_anisotropy);

+ 9 - 76
Praxis3D/Source/RendererFrontend.cpp

@@ -1,4 +1,5 @@
 
+#include "AmbientOcclusionPass.h"
 #include "AtmScatteringPass.h"
 #include "BloomCompositePass.h"
 #include "BloomPass.h"
@@ -29,82 +30,6 @@ RendererFrontend::RendererFrontend() : m_renderPassData(nullptr)
 
 	m_zFar = Config::graphicsVar().z_far;
 	m_zNear = Config::graphicsVar().z_near;
-
-	/*/ Set up the order of the rendering passes
-	m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_Geometry);
-	m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_AtmScattering);
-	m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_Lighting);
-	m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_AtmScattering);
-	//m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_HdrMapping);
-	m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_Bloom);
-	m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_Luminance);
-	//m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_Blur);
-	//m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_BloomComposite);
-	//m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_LenseFlare);
-	//m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_Blur);
-	//m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_LenseFlareComposite);
-	m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_Final);
-	m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_GUI);
-
-	// Make sure the entries of the rendering passes are set to nullptr
-	for(unsigned int i = 0; i < RenderPassType::RenderPassType_NumOfTypes; i++)
-		m_initializedRenderingPasses[i] = nullptr;
-
-	// Create rendering passes
-	for(decltype(m_renderingPassesTypes.size()) i = 0, size = m_renderingPassesTypes.size(); i < size; i++)
-	{
-		switch(m_renderingPassesTypes[i])
-		{
-		case RenderPassType_Geometry:
-			if(m_initializedRenderingPasses[RenderPassType_Geometry] == nullptr)
-				m_initializedRenderingPasses[RenderPassType_Geometry] = new GeometryPass(*this);
-			break;
-		case RenderPassType_Lighting:
-			if(m_initializedRenderingPasses[RenderPassType_Lighting] == nullptr)
-				m_initializedRenderingPasses[RenderPassType_Lighting] = new LightingPass(*this);
-			break;
-		case RenderPassType_AtmScattering:
-			if(m_initializedRenderingPasses[RenderPassType_AtmScattering] == nullptr)
-				m_initializedRenderingPasses[RenderPassType_AtmScattering] = new AtmScatteringPass(*this);
-			break;
-		case RenderPassType_HdrMapping:
-			if(m_initializedRenderingPasses[RenderPassType_HdrMapping] == nullptr)
-				m_initializedRenderingPasses[RenderPassType_HdrMapping] = new HdrMappingPass(*this);
-			break;
-		case RenderPassType_Blur:
-			if(m_initializedRenderingPasses[RenderPassType_Blur] == nullptr)
-				m_initializedRenderingPasses[RenderPassType_Blur] = new BlurPass(*this);
-			break;
-		case RenderPassType_Bloom:
-			if(m_initializedRenderingPasses[RenderPassType_Bloom] == nullptr)
-				m_initializedRenderingPasses[RenderPassType_Bloom] = new BloomPass(*this);
-			break;
-		case RenderPassType_BloomComposite:
-			if(m_initializedRenderingPasses[RenderPassType_BloomComposite] == nullptr)
-				m_initializedRenderingPasses[RenderPassType_BloomComposite] = new BloomCompositePass(*this);
-			break;
-		case RenderPassType_LenseFlare:
-			if(m_initializedRenderingPasses[RenderPassType_LenseFlare] == nullptr)
-				m_initializedRenderingPasses[RenderPassType_LenseFlare] = new LenseFlarePass(*this);
-			break;
-		case RenderPassType_LenseFlareComposite:
-			if(m_initializedRenderingPasses[RenderPassType_LenseFlareComposite] == nullptr)
-				m_initializedRenderingPasses[RenderPassType_LenseFlareComposite] = new LenseFlareCompositePass(*this);
-			break;
-		case RenderPassType_Luminance:
-			if(m_initializedRenderingPasses[RenderPassType_Luminance] == nullptr)
-				m_initializedRenderingPasses[RenderPassType_Luminance] = new LuminancePass(*this);
-			break;
-		case RenderPassType_Final:
-			if(m_initializedRenderingPasses[RenderPassType_Final] == nullptr)
-				m_initializedRenderingPasses[RenderPassType_Final] = new FinalPass(*this);
-			break;
-		case RenderPassType_GUI:
-			if(m_initializedRenderingPasses[RenderPassType_GUI] == nullptr)
-				m_initializedRenderingPasses[RenderPassType_GUI] = new GUIPass(*this);
-			break;
-		}
-	}*/
 }
 
 RendererFrontend::~RendererFrontend()
@@ -247,6 +172,11 @@ void RendererFrontend::setRenderingPasses(const RenderingPasses &p_renderingPass
 				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType_GUI]);
 				guiRenderPassSet = true;
 				break;
+			case RenderPassType_AmbientOcclusion:
+				if(m_allRenderPasses[RenderPassType_AmbientOcclusion] == nullptr)
+					m_allRenderPasses[RenderPassType_AmbientOcclusion] = new AmbientOcclusionPass(*this);
+				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType_AmbientOcclusion]);
+				break;
 		}
 	}
 
@@ -448,6 +378,9 @@ void RendererFrontend::renderFrame(SceneObjects &p_sceneObjects, const float p_d
 	// Convert the view matrix to row major for the atmospheric scattering shaders
 	m_frameData.m_transposeViewMatrix = glm::transpose(m_frameData.m_viewMatrix);
 
+	// Calculate the transpose inverse view matrix needed for converting normals (in the normal g-buffer) to view space
+	m_frameData.m_transposeInverseViewMatrix = glm::transpose(glm::inverse(m_frameData.m_viewMatrix));
+
 	// Set the camera position
 	m_frameData.m_cameraPosition = p_sceneObjects.m_cameraViewMatrix[3];
 

+ 4 - 0
Praxis3D/Source/RendererFrontend.h

@@ -25,6 +25,7 @@ class RendererFrontend
 	friend class FinalPass;
 	friend class ReflectionPass;
 	friend class SkyPass;
+	friend class AmbientOcclusionPass;
 public:
 	// A handle for a uniform or shader storage buffer
 	struct ShaderBuffer
@@ -62,6 +63,7 @@ public:
 	void setRenderFinalToTexture(const bool p_renderToTexture);
 	void setRenderToTextureResolution(const glm::ivec2 p_renderToTextureResolution);
 	void setRenderingPasses(const RenderingPasses &p_renderingPasses);
+	void setAmbientOcclusionData(const AmbientOcclusionData &p_aoData) { m_frameData.m_aoData = p_aoData; }
 
 	const RenderingPasses getRenderingPasses();
 
@@ -186,6 +188,8 @@ protected:
 									p_texture.getTextureFormat(),
 									p_texture.getTextureDataFormat(),
 									p_texture.getTextureDataType(),
+									p_texture.getMagnificationFilterType(),
+									p_texture.getMinificationFilterType(),
 									p_texture.getEnableMipmap(),
 									p_texture.getMipmapLevel(),
 									p_texture.getTextureWidth(),

+ 85 - 1
Praxis3D/Source/RendererScene.cpp

@@ -10,8 +10,11 @@
 RendererScene::RendererScene(RendererSystem *p_system, SceneLoader *p_sceneLoader) : SystemScene(p_system, p_sceneLoader, Properties::PropertyID::Renderer)
 {
 	m_renderTask = new RenderTask(this, p_system->getRenderer());
-	m_renderToTexture = false;
 	m_firstLoadingDone = false;
+
+	m_sceneAOData.setDefaultValues();
+
+	m_renderToTexture = false;
 	m_renderToTextureResolution = glm::ivec2(Config::graphicsVar().current_resolution_x, Config::graphicsVar().current_resolution_y);
 }
 
@@ -73,6 +76,44 @@ ErrorCode RendererScene::setup(const PropertySet &p_properties)
 		}
 	}
 
+	// Load ambient occlusion data
+	if(auto &aoProperty = p_properties.getPropertySetByID(Properties::AmbientOcclusion); aoProperty)
+	{
+		for(decltype(aoProperty.getNumProperties()) i = 0, size = aoProperty.getNumProperties(); i < size; i++)
+		{
+			switch(aoProperty[i].getPropertyID())
+			{
+				case Properties::Type:
+					m_sceneAOData.m_aoType = AmbientOcclusionData::propertyIDToAmbientOcclusionType(aoProperty[i].getID());
+					break;
+				case Properties::Bias:
+					m_sceneAOData.m_aoBias = aoProperty[i].getFloat();
+					break;
+				case Properties::Directions:
+					m_sceneAOData.m_aoNumOfDirections = aoProperty[i].getInt();
+					break;
+				case Properties::Radius:
+					m_sceneAOData.m_aoRadius = aoProperty[i].getFloat();
+					break;
+				case Properties::Intensity:
+					m_sceneAOData.m_aoIntensity = aoProperty[i].getFloat();
+					break;
+				case Properties::Samples:
+					m_sceneAOData.m_aoNumOfSamples = aoProperty[i].getInt();
+					break;
+				case Properties::Steps:
+					m_sceneAOData.m_aoNumOfSteps = aoProperty[i].getInt();
+					break;
+				case Properties::BlurSamples:
+					m_sceneAOData.m_aoBlurNumOfSamples = aoProperty[i].getInt();
+					break;
+				case Properties::BlurSharpness:
+					m_sceneAOData.m_aoBlurSharpness = aoProperty[i].getFloat();
+					break;
+			}
+		}
+	}
+
 	// Load the rendering passes
 	auto &renderPassesProperty = p_properties.getPropertySetByID(Properties::RenderPasses);
 	if(renderPassesProperty)
@@ -106,6 +147,9 @@ ErrorCode RendererScene::setup(const PropertySet &p_properties)
 					case Properties::FinalRenderPass:
 						m_renderingPasses.push_back(RenderPassType::RenderPassType_Final);
 						break;
+					case Properties::AmbientOcclusionRenderPass:
+						m_renderingPasses.push_back(RenderPassType::RenderPassType_AmbientOcclusion);
+						break;
 				}
 			}
 		}
@@ -124,6 +168,18 @@ void RendererScene::exportSetup(PropertySet &p_propertySet)
 	p_propertySet.addProperty(Properties::ZFar, m_sceneObjects.m_zFar);
 	p_propertySet.addProperty(Properties::ZNear, m_sceneObjects.m_zNear);
 
+	// Add ambient occlusion data
+	auto &aoPropertySet = p_propertySet.addPropertySet(Properties::AmbientOcclusion);
+	aoPropertySet.addProperty(Properties::Type, AmbientOcclusionData::ambientOcclusionTypeToPropertyID(m_sceneAOData.m_aoType));
+	aoPropertySet.addProperty(Properties::Bias, m_sceneAOData.m_aoBias);
+	aoPropertySet.addProperty(Properties::Radius, m_sceneAOData.m_aoRadius);
+	aoPropertySet.addProperty(Properties::Intensity, m_sceneAOData.m_aoIntensity);
+	aoPropertySet.addProperty(Properties::Directions, m_sceneAOData.m_aoNumOfDirections);
+	aoPropertySet.addProperty(Properties::Samples, m_sceneAOData.m_aoNumOfSamples);
+	aoPropertySet.addProperty(Properties::Steps, m_sceneAOData.m_aoNumOfSteps);
+	aoPropertySet.addProperty(Properties::BlurSamples, m_sceneAOData.m_aoBlurNumOfSamples);
+	aoPropertySet.addProperty(Properties::BlurSharpness, m_sceneAOData.m_aoBlurSharpness);
+
 	// Add object pool sizes
 	auto &objectPoolSizePropertySet = p_propertySet.addPropertySet(Properties::ObjectPoolSize);
 	objectPoolSizePropertySet.addProperty(Properties::CameraComponent, (int)worldScene->getPoolSize<CameraComponent>());
@@ -149,6 +205,17 @@ void RendererScene::activate()
 	{
 		rendererSystem->getRenderer().setRenderToTextureResolution(m_renderToTextureResolution);
 	}
+
+	// Set the AO data
+	rendererSystem->getRenderer().setAmbientOcclusionData(m_sceneAOData);
+}
+
+void RendererScene::deactivate()
+{
+	auto rendererSystem = static_cast<RendererSystem *>(m_system);
+
+	// Save the current AO data
+	m_sceneAOData = rendererSystem->getRenderer().getFrameData().m_aoData;
 }
 
 ErrorCode RendererScene::preload()
@@ -883,6 +950,23 @@ void RendererScene::receiveData(const DataType p_dataType, void *p_data, const b
 {
 	switch(p_dataType)
 	{
+		case DataType::DataType_AmbientOcclusionData:
+			{
+				auto *aoData = static_cast<AmbientOcclusionData *>(p_data);
+
+				// Set the new AO data
+				m_sceneAOData = *aoData;
+
+				// Send the new AO data to the renderer
+				auto rendererSystem = static_cast<RendererSystem *>(m_system);
+				rendererSystem->getRenderer().setAmbientOcclusionData(m_sceneAOData);
+
+				// Delete the received data if it has been marked for deletion (ownership transfered upon receiving)
+				if(p_deleteAfterReceiving)
+					delete aoData;
+			}
+			break;
+
 		case DataType::DataType_CreateComponent:
 			{
 				auto *componentInfo = static_cast<ComponentsConstructionInfo *>(p_data);

+ 9 - 2
Praxis3D/Source/RendererScene.h

@@ -127,6 +127,8 @@ public:
 
 	void activate();
 
+	void deactivate();
+
 	// Preloads all the resources in the scene (as opposed to loading them while rendering, in background threads)
 	ErrorCode preload();
 
@@ -233,6 +235,7 @@ public:
 	const inline SceneObjects &getSceneObjects() const { return m_sceneObjects; }
 	inline SceneObjects &getSceneObjects() { return m_sceneObjects; }
 	const inline RenderingPasses &getRenderingPasses() const { return m_renderingPasses; }
+	const inline AmbientOcclusionData &getAmbientOcclusionData() const { return m_sceneAOData; }
 	const glm::mat4 &getViewMatrix() const;
 	const glm::mat4 &getProjectionMatrix() const;
 
@@ -269,6 +272,9 @@ public:
 				case RenderPassType::RenderPassType_Final:
 					renderPassTypeProperty = Properties::FinalRenderPass;
 					break;
+				case RenderPassType::RenderPassType_AmbientOcclusion:
+					renderPassTypeProperty = Properties::AmbientOcclusionRenderPass;
+					break;
 			}
 
 			// Add the rendering pass array entry and rendering pass type
@@ -278,8 +284,6 @@ public:
 	static std::string getRenderingPassString(const RenderPassType p_renderPassType)
 	{
 		std::string returnString;
-
-
 	}
 
 private:
@@ -297,6 +301,9 @@ private:
 	// Contains a list of rendering passes that gets set in the renderer system
 	RenderingPasses m_renderingPasses;
 
+	// Ambient occlusion data
+	AmbientOcclusionData m_sceneAOData;
+
 	// Render-to-texture data
 	bool m_renderToTexture;
 	glm::ivec2 m_renderToTextureResolution;

+ 15 - 0
Praxis3D/Source/ShaderUniformUpdater.cpp

@@ -53,6 +53,7 @@ ErrorCode ShaderUniformUpdater::generateTextureUpdateList()
 	uniformList.push_back(new MatPropertiesMapUniform(m_shaderHandle));
 	uniformList.push_back(new IntermediateMapUniform(m_shaderHandle));
 	uniformList.push_back(new FinalMapUniform(m_shaderHandle));
+	uniformList.push_back(new DepthMapUniform(m_shaderHandle));
 	uniformList.push_back(new InputMapUniform(m_shaderHandle));
 	uniformList.push_back(new OutputMapUniform(m_shaderHandle));
 
@@ -73,6 +74,9 @@ ErrorCode ShaderUniformUpdater::generateTextureUpdateList()
 	uniformList.push_back(new EmissiveTextureUniform(m_shaderHandle));
 	uniformList.push_back(new CombinedTextureUniform(m_shaderHandle));
 
+	// Additional textures
+	uniformList.push_back(new NoiseTextureUniform(m_shaderHandle));
+
 	// Atmoshperic scattering textures
 	uniformList.push_back(new AtmIrradianceTextureUniform(m_shaderHandle));
 	uniformList.push_back(new AtmScatteringTextureUniform(m_shaderHandle));
@@ -110,6 +114,7 @@ ErrorCode ShaderUniformUpdater::generatePerFrameList()
 	uniformList.push_back(new ProjectionMatUniform(m_shaderHandle));
 	uniformList.push_back(new ViewProjectionMatUniform(m_shaderHandle));
 	uniformList.push_back(new TransposeViewMatUniform(m_shaderHandle));
+	uniformList.push_back(new TransposeInverseViewMatUniform(m_shaderHandle));
 	uniformList.push_back(new DirShadowMapMVPUniform(m_shaderHandle));
 	uniformList.push_back(new DirShadowMapBiasMVPUniform(m_shaderHandle));
 
@@ -147,6 +152,12 @@ ErrorCode ShaderUniformUpdater::generatePerFrameList()
 	uniformList.push_back(new EmissiveMultiplierUniform(m_shaderHandle));
 	uniformList.push_back(new LODParallaxMappingUniform(m_shaderHandle));
 
+	// Ambient occlusion
+	uniformList.push_back(new HBAOBlurNumOfSamples(m_shaderHandle));
+	uniformList.push_back(new HBAOBlurSharpness(m_shaderHandle));
+	uniformList.push_back(new HBAOBlurHorizontalInvResDirection(m_shaderHandle));
+	uniformList.push_back(new HBAOBlurVerticalInvResDirection(m_shaderHandle));
+
 	// Bloom
 	uniformList.push_back(new BloomDirtIntensityUniform(m_shaderHandle));
 	uniformList.push_back(new BloomIntensityUniform(m_shaderHandle));
@@ -225,6 +236,10 @@ ErrorCode ShaderUniformUpdater::generateUniformBlockList()
 	uniformBlockList.push_back(new PointLightBufferUniform(m_shaderHandle));
 	uniformBlockList.push_back(new SpotLightBufferUniform(m_shaderHandle));
 
+	// Ambient occlusion buffer
+	uniformBlockList.push_back(new AODataSetBufferUniform(m_shaderHandle));
+	uniformBlockList.push_back(new SSAOSampleBufferUniform(m_shaderHandle));
+
 	// Atmospheric scattering buffer
 	uniformBlockList.push_back(new AtmScatParametersUniform(m_shaderHandle));
 	

+ 141 - 9
Praxis3D/Source/ShaderUniforms.h

@@ -215,6 +215,18 @@ public:
 		glUniformMatrix4fv(m_uniformHandle, 1, GL_FALSE, &p_uniformData.m_frameData.m_transposeViewMatrix[0][0]);
 	}
 };
+class TransposeInverseViewMatUniform : public BaseUniform
+{
+public:
+	TransposeInverseViewMatUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().transposeInverseViewMatUniform, p_shaderHandle)
+	{
+	}
+
+	void update(const UniformData &p_uniformData)
+	{
+		glUniformMatrix4fv(m_uniformHandle, 1, GL_FALSE, &p_uniformData.m_frameData.m_transposeInverseViewMatrix[0][0]);
+	}
+};
 
 class ScreenSizeUniform : public BaseUniform
 {
@@ -844,6 +856,16 @@ public:
 		glUniform1i(m_uniformHandle, GBufferTextureType::GBufferFinal);
 	}
 };
+class DepthMapUniform : public BaseUniform
+{
+public:
+	DepthMapUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().depthMapUniform, p_shaderHandle) { }
+
+	void update(const UniformData &p_uniformData)
+	{
+		glUniform1i(m_uniformHandle, GBufferTextureType::GbufferDepth);
+	}
+};
 class InputMapUniform : public BaseUniform
 {
 public:
@@ -913,8 +935,7 @@ public:
 
 	void update(const UniformData &p_uniformData)
 	{
-		//glUniform1i(m_uniformHandle, p_uniformData.getNormalTexturePos());
-		glUniform1i(m_uniformHandle, MaterialType_Normal);
+		glUniform1i(m_uniformHandle, MaterialType::MaterialType_Normal);
 	}
 };
 class EmissiveTextureUniform : public BaseUniform
@@ -924,7 +945,7 @@ public:
 
 	void update(const UniformData &p_uniformData)
 	{
-		glUniform1i(m_uniformHandle, MaterialType_Emissive);
+		glUniform1i(m_uniformHandle, MaterialType::MaterialType_Emissive);
 	}
 };
 class CombinedTextureUniform : public BaseUniform
@@ -934,11 +955,102 @@ public:
 
 	void update(const UniformData &p_uniformData)
 	{
-		//glUniform1i(m_uniformHandle, p_uniformData.getCombinedTexturePos());
-		glUniform1i(m_uniformHandle, MaterialType_Combined);
+		glUniform1i(m_uniformHandle, MaterialType::MaterialType_Combined);
+	}
+};
+
+class NoiseTextureUniform : public BaseUniform
+{
+public:
+	NoiseTextureUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().noiseTexture, p_shaderHandle) { }
+
+	void update(const UniformData &p_uniformData)
+	{
+		glUniform1i(m_uniformHandle, MaterialType::MaterialType_Noise);
 	}
 };
 
+class HBAOBlurNumOfSamples : public BaseUniform
+{
+public:
+	HBAOBlurNumOfSamples(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().hbaoBlurNumOfSamples, p_shaderHandle), m_numOfSamples(0)
+	{
+	}
+
+	void update(const UniformData &p_uniformData)
+	{
+		if(m_numOfSamples != p_uniformData.m_frameData.m_aoData.m_aoBlurNumOfSamples)
+		{
+			m_numOfSamples = p_uniformData.m_frameData.m_aoData.m_aoBlurNumOfSamples;
+
+			glUniform1i(m_uniformHandle, m_numOfSamples);
+		}
+	}
+
+private:
+	int m_numOfSamples;
+}; 
+class HBAOBlurSharpness : public BaseUniform
+{
+public:
+	HBAOBlurSharpness(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().hbaoBlurSharpness, p_shaderHandle), m_blurSharpness(0.0f)
+	{
+	}
+
+	void update(const UniformData &p_uniformData)
+	{
+		if(m_blurSharpness != p_uniformData.m_frameData.m_aoData.m_aoBlurSharpness)
+		{
+			m_blurSharpness = p_uniformData.m_frameData.m_aoData.m_aoBlurSharpness;
+
+			glUniform1f(m_uniformHandle, m_blurSharpness);
+		}
+	}
+
+private:
+	float m_blurSharpness;
+};
+class HBAOBlurHorizontalInvResDirection : public BaseUniform
+{
+public:
+	HBAOBlurHorizontalInvResDirection(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().hbaoBlurHorizontalInvResDirection, p_shaderHandle), m_screenHeight(0)
+	{
+	}
+
+	void update(const UniformData &p_uniformData)
+	{
+		if(m_screenHeight != p_uniformData.m_frameData.m_screenSize.y)
+		{
+			m_screenHeight = p_uniformData.m_frameData.m_screenSize.y;
+
+			glUniform2f(m_uniformHandle, 0.0f, 1.0f / (float)m_screenHeight);
+		}
+	}
+
+private:
+	int m_screenHeight;
+};
+class HBAOBlurVerticalInvResDirection : public BaseUniform
+{
+public:
+	HBAOBlurVerticalInvResDirection(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().hbaoBlurVerticalInvResDirection, p_shaderHandle), m_screenWidth(0)
+	{
+	}
+
+	void update(const UniformData &p_uniformData)
+	{
+		if(m_screenWidth != p_uniformData.m_frameData.m_screenSize.x)
+		{
+			m_screenWidth = p_uniformData.m_frameData.m_screenSize.x;
+
+			glUniform2f(m_uniformHandle, 1.0f / (float)m_screenWidth, 0.0f);
+		}
+	}
+
+private:
+	int m_screenWidth;
+};
+
 class AtmIrradianceTextureUniform : public BaseUniform
 {
 public:
@@ -1282,7 +1394,7 @@ public:
 
 	void update(const UniformData &p_uniformData)
 	{
-		updateBlockBinding(UniformBufferBinding_PointLights);
+		updateBlockBinding(UniformBufferBinding::UniformBufferBinding_PointLights);
 	}
 };
 class SpotLightBufferUniform : public BaseUniformBlock
@@ -1292,7 +1404,7 @@ public:
 
 	void update(const UniformData &p_uniformData)
 	{
-		updateBlockBinding(UniformBufferBinding_SpotLights);
+		updateBlockBinding(UniformBufferBinding::UniformBufferBinding_SpotLights);
 	}
 };
 class AtmScatParametersUniform : public BaseUniformBlock
@@ -1302,7 +1414,7 @@ public:
 
 	void update(const UniformData &p_uniformData)
 	{
-		updateBlockBinding(UniformBufferBinding_AtmScatParam);
+		updateBlockBinding(UniformBufferBinding::UniformBufferBinding_AtmScatParam);
 	}
 };
 class LensFlareParametersUniform : public BaseUniformBlock
@@ -1312,7 +1424,27 @@ public:
 
 	void update(const UniformData &p_uniformData)
 	{
-		updateBlockBinding(UniformBufferBinding_LensFlareParam);
+		updateBlockBinding(UniformBufferBinding::UniformBufferBinding_LensFlareParam);
+	}
+};
+class AODataSetBufferUniform : public BaseUniformBlock
+{
+public:
+	AODataSetBufferUniform(unsigned int p_shaderHandle) : BaseUniformBlock(Config::shaderVar().AODataSetBuffer, p_shaderHandle) { }
+
+	void update(const UniformData &p_uniformData)
+	{
+		updateBlockBinding(UniformBufferBinding::UniformBufferBinding_AODataSet);
+	}
+};
+class SSAOSampleBufferUniform : public BaseUniformBlock
+{
+public:
+	SSAOSampleBufferUniform(unsigned int p_shaderHandle) : BaseUniformBlock(Config::shaderVar().SSAOSampleBuffer, p_shaderHandle) { }
+
+	void update(const UniformData &p_uniformData)
+	{
+		updateBlockBinding(UniformBufferBinding::UniformBufferBinding_SSAOSampleBuffer);
 	}
 };
 

+ 68 - 9
Praxis3D/Source/TextureLoader.h

@@ -58,11 +58,46 @@ public:
 	inline TextureDataType getTextureDataType()		const { return m_textureDataType; }
 	inline bool getMipmapEnabled()					const { return m_enableMipmap; }
 	inline int getMipmapLevel()						const { return m_mipmapLevel; }
-	inline const unsigned char *getPixelData()		const { return m_pixelData; }
+	inline const void *getPixelData()				const { return m_pixelData; }
+
+	inline void setPixelData(void *p_pixelData)
+	{
+		if(m_pixelData != nullptr)
+			delete m_pixelData;
+
+		m_pixelData = p_pixelData;
+	}
 
 	// Returns true of the texture was loaded from file
 	const inline bool isLoadedFromFile() const { return m_loadedFromFile; }
 
+	// Convert the GL filter type (int) to TextureFilterType enum
+	static inline TextureFilterType convertToTextureFilterType(const int p_textureFilterType)
+	{
+		switch(p_textureFilterType)
+		{
+			case TextureFilterType::TextureFilterType_Linear:
+			default:
+				return TextureFilterType::TextureFilterType_Linear;
+				break;
+			case TextureFilterType::TextureFilterType_LinearMipmapLinear:
+				return TextureFilterType::TextureFilterType_LinearMipmapLinear;
+				break;
+			case TextureFilterType::TextureFilterType_LinearMipmapNearest:
+				return TextureFilterType::TextureFilterType_LinearMipmapNearest;
+				break;
+			case TextureFilterType::TextureFilterType_Nearest:
+				return TextureFilterType::TextureFilterType_Nearest;
+				break;
+			case TextureFilterType::TextureFilterType_NearestMipmapLinear:
+				return TextureFilterType::TextureFilterType_NearestMipmapLinear;
+				break;
+			case TextureFilterType::TextureFilterType_NearestMipmapNearest:
+				return TextureFilterType::TextureFilterType_NearestMipmapNearest;
+				break;
+		}
+	}
+
 protected:
 	Texture2D(LoaderBase<TextureLoader2D, Texture2D> *p_loaderBase, std::string p_filename, size_t p_uniqueID, unsigned int p_handle) : UniqueObject(p_loaderBase, p_uniqueID, p_filename), m_handle(p_handle)
 	{
@@ -77,6 +112,17 @@ protected:
 		m_textureFormat = TextureFormat::TextureFormat_RGBA;
 		m_textureDataFormat = TextureDataFormat::TextureDataFormat_RGBA8;
 		m_textureDataType = TextureDataType::TextureDataType_UnsignedByte;
+
+		if(m_enableMipmap)
+		{
+			m_magnificationFilter = convertToTextureFilterType(Config::textureVar().gl_texture_magnification_mipmap);
+			m_minificationFilter = convertToTextureFilterType(Config::textureVar().gl_texture_minification_mipmap);
+		}
+		else
+		{
+			m_magnificationFilter = convertToTextureFilterType(Config::textureVar().gl_texture_magnification);
+			m_minificationFilter = convertToTextureFilterType(Config::textureVar().gl_texture_minification);
+		}
 	}
 
 	// Loads pixel data (using the filename) from HDD to RAM, re-factors it
@@ -124,7 +170,8 @@ protected:
 				m_size = m_textureWidth * m_textureHeight;
 				
 				// Texture data passed to the GPU must be in an unsigned char array format
-				m_pixelData = (unsigned char*)FreeImage_GetBits(m_bitmap);
+				//m_pixelData = (unsigned char *)FreeImage_GetBits(m_bitmap);
+				auto pixelData = FreeImage_GetBits(m_bitmap);
 
 				// Temp variable for swapping color channels
 				unsigned char blue = 0;
@@ -135,11 +182,13 @@ protected:
 				// FreeImage loads in BGR format, therefore swap of bytes is needed (Or usage of GL_BGR)
 				for(unsigned int i = 0; i < m_size; i++)
 				{
-					blue = m_pixelData[i * numChan + 0];						 // Store blue
-					m_pixelData[i * numChan + 0] = m_pixelData[i * numChan + 2]; // Set red
-					m_pixelData[i * numChan + 2] = blue;						 // Set blue
+					blue = pixelData[i * numChan + 0];						 // Store blue
+					pixelData[i * numChan + 0] = pixelData[i * numChan + 2]; // Set red
+					pixelData[i * numChan + 2] = blue;						 // Set blue
 				}
 
+				m_pixelData = pixelData;
+
 				setLoadedToMemory(true);
 				m_loadedFromFile = true;
 			}
@@ -202,7 +251,7 @@ protected:
 					// Copy the pixel data
 					// Width-index * texture-width + height-index is the translation from 2D array indices to 1D
 					// Multiply the indices by 4, because textures contain RGBA channels, and add the channel offset
-					m_pixelData[(destWidth * m_textureWidth + destHeight) * 4 + p_destChannel] =
+					((GLubyte *)m_pixelData)[(destWidth * m_textureWidth + destHeight) * 4 + p_destChannel] =
 						p_pixelData[(srcWidth * p_textureWidth + srcHeight) * 4 + p_sourceChannel];
 				}
 			}
@@ -219,12 +268,14 @@ protected:
 	TextureFormat m_textureFormat;
 	TextureDataFormat m_textureDataFormat;
 	TextureDataType m_textureDataType;
+	TextureFilterType m_magnificationFilter;
+	TextureFilterType m_minificationFilter;
 	bool m_loadedFromFile;
 	bool m_enableMipmap;
 	int m_mipmapLevel;
 
 	FIBITMAP* m_bitmap;
-	unsigned char *m_pixelData;
+	void *m_pixelData;
 	unsigned int m_size,
 				 m_handle,
 				 m_textureWidth,
@@ -263,7 +314,7 @@ public:
 			if(m_textureData->m_pixelData && p_textureHandle.m_textureData->m_pixelData)
 			{
 				// Set the color channel of the internal texture
-				m_textureData->setColorChannel(p_textureHandle.m_textureData->m_pixelData,
+				m_textureData->setColorChannel((GLubyte *)p_textureHandle.m_textureData->m_pixelData,
 											   p_textureHandle.m_textureData->m_textureWidth,
 											   p_textureHandle.m_textureData->m_textureHeight,
 											   p_destChannel, p_sourceChannel);
@@ -304,10 +355,18 @@ public:
 		inline TextureFormat getTextureFormat() const { return m_textureData->m_textureFormat; }
 		inline TextureDataFormat getTextureDataFormat() const { return m_textureData->m_textureDataFormat; }
 		inline TextureDataType getTextureDataType() const { return m_textureData->m_textureDataType; }
+		inline TextureFilterType getMagnificationFilterType() const { return m_textureData->m_magnificationFilter; }
+		inline TextureFilterType getMinificationFilterType() const { return m_textureData->m_minificationFilter; }
 		inline bool getEnableMipmap() const { return m_textureData->m_enableMipmap; }
-		inline const unsigned char *getPixelData() const { return m_textureData->m_pixelData; }
+		inline const void *getPixelData() const { return m_textureData->m_pixelData; }
 		const inline bool isLoadedFromFile() const { return m_textureData->m_loadedFromFile; }
 
+		// Setters
+		inline void setPixelData(void *p_pixelData) { m_textureData->setPixelData(p_pixelData); }
+		inline void setHandle(unsigned int p_handle) { m_textureData->m_handle = p_handle; }
+		inline void setMagnificationFilterType(const TextureFilterType p_filterType) { m_textureData->m_magnificationFilter = p_filterType; }
+		inline void setMinificationFilterType(const TextureFilterType p_filterType) { m_textureData->m_minificationFilter = p_filterType; }
+
 	private:
 		// Increment the reference counter when creating a handle
 		Texture2DHandle(Texture2D *p_textureData) : m_textureData(p_textureData) { m_textureData->incRefCounter(); }

+ 12 - 2
Praxis3D/Source/UniformData.h

@@ -8,9 +8,15 @@ struct UniformFrameData
 	UniformFrameData()
 	{
 		m_ambientIntensity = Config::graphicsVar().ambient_light_intensity;
+
+		m_deltaTime = 0.0f;
+
 		m_numPointLights = 0;
 		m_numSpotLights = 0;
-		m_deltaTime = 0.0f;
+
+		m_bloomTreshold = glm::vec4(0.0f);
+
+		m_texelSize = glm::vec2(1.0f);
 		m_mipLevel = 1;
 	}
 
@@ -28,7 +34,8 @@ struct UniformFrameData
 				m_viewMatrix,
 				m_viewProjMatrix,
 				m_atmScatProjMatrix,
-				m_transposeViewMatrix;
+				m_transposeViewMatrix,
+				m_transposeInverseViewMatrix;
 
 	// Parameters of directional light, since there can be only one of it
 	DirectionalLightDataSet m_directionalLight;
@@ -41,6 +48,9 @@ struct UniformFrameData
 	unsigned int m_numPointLights,
 				 m_numSpotLights;
 
+	// Ambient occlusion data
+	AmbientOcclusionData m_aoData;
+
 	// Bloom pass data
 	glm::vec4 m_bloomTreshold;
 

+ 2 - 2
Praxis3D/Source/Version.h

@@ -3,11 +3,11 @@
 #define VERSION_TO_STRING_HELPER(v) #v
 #define VERSION_TO_STRING(v) VERSION_TO_STRING_HELPER(v)
 
-#define PRAXIS3D_COMMIT_DATE 2023-12-16
+#define PRAXIS3D_COMMIT_DATE 2023-12-24
 
 #define PRAXIS3D_VERSION_MAJOR 0
 #define PRAXIS3D_VERSION_MINOR 1
-#define PRAXIS3D_VERSION_PATCH 2
+#define PRAXIS3D_VERSION_PATCH 3
 
 #define PRAXIS3D_VERSION ((PRAXIS3D_VERSION_MAJOR << 16) | (PRAXIS3D_VERSION_MINOR << 8) | PRAXIS3D_VERSION_PATCH)
 #define PRAXIS3D_VERSION_STRING VERSION_TO_STRING(PRAXIS3D_VERSION_MAJOR) "." VERSION_TO_STRING(PRAXIS3D_VERSION_MINOR) "." VERSION_TO_STRING(PRAXIS3D_VERSION_PATCH)

+ 48 - 0
Praxis3D/imgui.ini

@@ -1628,6 +1628,54 @@ Column 0  Sort=0v
 RefScale=13
 Column 0  Sort=0v
 
+[Table][0x87F14482,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xF154D250,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x78CA77CA,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x0E6FE118,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xED6A28D8,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x44E2AAA8,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x2B998A76,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xECAA2D55,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x454520DA,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x87BCCA55,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x29D355F6,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x0B0E6EF4,4]
+RefScale=13
+Column 0  Sort=0v
+
 [Docking][Data]
 DockSpace         ID=0x8B93E3BD Window=0xA787BDB4 Pos=0,69 Size=2335,1309 Split=X
   DockNode        ID=0x00000007 Parent=0x8B93E3BD SizeRef=1409,1011 Split=Y