Browse Source

Implemented Cascaded Shadow Mapping

Implemented Cascaded Shadow Mapping with PCF Poisson disk sampling, layered depth rendering, slope bias calculation, simple cascade sampling blending, shadow penumbra scaling based on distance (for shadow anti-aliasing) and fully customizable cascades and their parameters
Added shaders for CSM pass, including a geometry shader, that performs layered rendering to render all CSM depth buffers with one draw call
Added shaders for light pass with shadowmapping, that perform CSM with PCF Poisson sampling
Added shader selection and shader #define variable updates, based on whether the shadow mapping is enabled, in LightingPass
Added ShadowCascadeData and ShadowMappingData containers that hold all the CSM settings, in Containers
Added CascadedShadowMapDataSet container that holds CSM uniform data that is sent to the shaders, in GraphicsDataSets
Added shadow mapping settings in EditorWindow
Added CSM framebuffer that holds all the shadow cascade depth maps as a texture array (CSMFramebuffer)
Added a way to request the creation of CSM framebuffer, in RendererBackend
Added ShadowMappingPass that performs shadow depth rendering and calculates all CSM data
Added shadow mapping rendering pass handling in RendererFrontend, RendererScene
Added shadow mapping settings importing and exporting, in RendererScene
Added a way set #define variables in shader source code before shader compilation, in ShaderLoader
Added more uniform variables, in ShaderUniforms, ShaderUniformUpdater
Added more Config variables
Added more errors and error strings
Changed framebuffers to require UniformFrameData in their initialize method, in Framebuffer, GeometryBuffer
Fixed a bug of vertical scrollbar drawing over scene settings GUI elements, in EditorWindow
Fixed a bug of the game engine running on integrated (instead of dedicated) GPU on laptops, by specifically requesting a dedicated GPU from AMD and NVidia, in Engine
Fixed a bug of not using updated camera's projection z plane far and near values (and instead using default ones from Config), in RendererFrontend
Fixed a bug of an error window showing text color flags in plain text, in ErrorHandler
Updated engine version to v0.2.0
Paul A 1 year ago
parent
commit
ef5e832190
49 changed files with 2190 additions and 1679 deletions
  1. 43 2
      Praxis3D/Data/Maps/componentTest.pmap
  2. 5 0
      Praxis3D/Data/Shaders/csmPass.frag
  3. 33 0
      Praxis3D/Data/Shaders/csmPass.geom
  4. 12 0
      Praxis3D/Data/Shaders/csmPass.vert
  5. 3 78
      Praxis3D/Data/Shaders/finalPass.frag
  6. 0 434
      Praxis3D/Data/Shaders/lightPass - Copy.frag
  7. 0 147
      Praxis3D/Data/Shaders/lightPass 2.frag
  8. 0 904
      Praxis3D/Data/Shaders/lightPass 3.frag
  9. 4 0
      Praxis3D/Data/Shaders/lightPass.frag
  10. 446 0
      Praxis3D/Data/Shaders/lightPass_CSM.frag
  11. 1 1
      Praxis3D/Data/Shaders/lightPass_CSM.vert
  12. 6 0
      Praxis3D/Data/Shaders/shadowMapping.frag
  13. 12 0
      Praxis3D/Data/Shaders/shadowMapping.vert
  14. 3 0
      Praxis3D/Data/error-strings-eng.data
  15. 2 0
      Praxis3D/Praxis3D.vcxproj
  16. 6 0
      Praxis3D/Praxis3D.vcxproj.filters
  17. 1 1
      Praxis3D/Source/AmbientOcclusionPass.h
  18. 140 0
      Praxis3D/Source/CSMFramebuffer.h
  19. 17 5
      Praxis3D/Source/CommonDefinitions.h
  20. 21 0
      Praxis3D/Source/Config.cpp
  21. 57 2
      Praxis3D/Source/Config.h
  22. 46 0
      Praxis3D/Source/Containers.h
  23. 2 2
      Praxis3D/Source/DeferredRenderer.cpp
  24. 183 7
      Praxis3D/Source/EditorWindow.cpp
  25. 5 0
      Praxis3D/Source/EditorWindow.h
  26. 11 0
      Praxis3D/Source/Engine.cpp
  27. 3 0
      Praxis3D/Source/ErrorCodes.h
  28. 3 1
      Praxis3D/Source/ErrorHandler.cpp
  29. 4 1
      Praxis3D/Source/Framebuffer.h
  30. 3 2
      Praxis3D/Source/GeometryBuffer.cpp
  31. 2 2
      Praxis3D/Source/GeometryBuffer.h
  32. 23 0
      Praxis3D/Source/GeometryPass.h
  33. 14 4
      Praxis3D/Source/GraphicsDataSets.h
  34. 117 22
      Praxis3D/Source/LightingPass.h
  35. 44 9
      Praxis3D/Source/RendererBackend.cpp
  36. 5 0
      Praxis3D/Source/RendererBackend.h
  37. 16 18
      Praxis3D/Source/RendererFrontend.cpp
  38. 8 17
      Praxis3D/Source/RendererFrontend.h
  39. 149 8
      Praxis3D/Source/RendererScene.cpp
  40. 12 5
      Praxis3D/Source/RendererScene.h
  41. 37 0
      Praxis3D/Source/ShaderLoader.cpp
  42. 68 0
      Praxis3D/Source/ShaderLoader.h
  43. 11 1
      Praxis3D/Source/ShaderUniformUpdater.cpp
  44. 1 0
      Praxis3D/Source/ShaderUniformUpdater.h
  45. 63 1
      Praxis3D/Source/ShaderUniforms.h
  46. 455 0
      Praxis3D/Source/ShadowMappingPass.h
  47. 12 0
      Praxis3D/Source/UniformData.h
  48. 3 3
      Praxis3D/Source/Version.h
  49. 78 2
      Praxis3D/imgui.ini

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

@@ -406,7 +406,7 @@
 						},
 						{
 							"Name": "cameraSpeedMultiplier",
-							"Value": "100000.0f"
+							"Value": "100.0f"
 						}
 					]
 				}
@@ -960,6 +960,9 @@
 				},
 				"RenderPasses": 
 				[
+					{
+						"Type": "ShadowMappingPass"
+					},
 					{
 						"Type": "GeometryRenderPass"
 					},
@@ -987,7 +990,45 @@
 					{
 						"Type": "GUIRenderPass"
 					}
-				]
+				],
+				"ShadowMapping":
+				{
+					"PenumbraSize": "1.42",
+					"PenumbraScaleRange": "1.0f, 2000.0f",
+					"Resolution": "4096",
+					"ZClipping": "true",
+					"ZPlaneMultiplier": "10.0f",
+					"PCF":
+					{
+						"Samples": "16"
+					},
+					"Cascades":
+					[
+						{
+							"Divider": "50.0f",
+							"Distance": "8.0f",
+							"BiasMax" : "0.0005f"
+						},
+						{
+							"Divider": "25.0f",
+							"Distance": "16.0f",
+							"BiasMax" : "0.0005f"
+						},
+						{
+							"Divider": "10.0f",
+							"Distance": "64.0f",
+							"BiasMax" : "0.0005f"
+						},
+						{
+							"Divider": "2.0f",
+							"Distance": "128.0f"
+						},
+						{
+							"Divider": "1.0f",
+							"PenumbraScale": "2.0f"
+						}
+					]
+				}
 			},
 			"System": 
 			{

+ 5 - 0
Praxis3D/Data/Shaders/csmPass.frag

@@ -0,0 +1,5 @@
+#version 430 core
+
+void main()
+{             
+}

+ 33 - 0
Praxis3D/Data/Shaders/csmPass.geom

@@ -0,0 +1,33 @@
+#version 430 core
+
+#define NUM_OF_CASCADES 5
+#define NUM_OF_VERTICES 3
+
+layout(triangles, invocations = NUM_OF_CASCADES) in;
+layout(triangle_strip, max_vertices = NUM_OF_VERTICES) out;
+
+struct CascadedShadowMapDataSet
+{
+	mat4 m_lightSpaceMatrix;
+	
+	float m_cascadeplanedistance;
+	float m_maxBias;
+	float m_poissonSampleScale;
+	float m_penumbraScale;
+};
+
+layout (std140) uniform CSMDataSetBuffer
+{
+	CascadedShadowMapDataSet m_csmDataSet[NUM_OF_CASCADES];
+};
+
+void main()
+{          
+	for (int i = 0; i < NUM_OF_VERTICES; ++i)
+	{
+		gl_Position = m_csmDataSet[gl_InvocationID].m_lightSpaceMatrix * gl_in[i].gl_Position;
+		gl_Layer = gl_InvocationID;
+		EmitVertex();
+	}
+	EndPrimitive();
+}  

+ 12 - 0
Praxis3D/Data/Shaders/csmPass.vert

@@ -0,0 +1,12 @@
+#version 430 core
+
+// Mesh buffers
+layout(location = 0) in vec3 vertexPosition;
+
+uniform mat4 modelMat;
+uniform mat4 MVP;
+
+void main(void) 
+{
+	gl_Position = modelMat * vec4(vertexPosition, 1.0);
+}

+ 3 - 78
Praxis3D/Data/Shaders/finalPass.frag

@@ -1,99 +1,24 @@
 #version 430 core
 
-//#define ENABLE_SIMPLE_TONE_MAPPING
-//#define ENABLE_REINHARD_TONE_MAPPING
-//#define ENABLE_FILMIC_TONE_MAPPING
-
 out vec4 outputColor;
 
 uniform ivec2 screenSize;
-uniform float gamma;
 
-uniform sampler2D emissiveMap;
 uniform sampler2D inputColorMap;
 
-vec3 gammaCorrection(vec3 p_color, float p_gamma)
-{
-	return pow(p_color, vec3(1.0 / p_gamma));
-}
-
-vec3 simpleToneMapping(vec3 p_color)
-{
-    return exp(-1.0 / (2.72 * p_color + 0.15));
-}
-
-// Simple reinhard tone mapping
-vec3 reinhardToneMapping(vec3 p_color)
-{
-	return p_color / (p_color + vec3(1.0));
-}
-	
-// Filmic tone mapping using an algorithm created by John Hable for Uncharted 2
-vec3 filmicToneMapping(vec3 p_color)
-{
-	// http://www.gdcvault.com/play/1012459/Uncharted_2__HDR_Lighting
-	// http://filmicgames.com/archives/75 - the coefficients are from here
-	float A = 0.15; // Shoulder Strength
-	float B = 0.50; // Linear Strength
-	float C = 0.10; // Linear Angle
-	float D = 0.20; // Toe Strength
-	float E = 0.02; // Toe Numerator
-	float F = 0.30; // Toe Denominator
-	
-	return ((p_color * (A * p_color + C * B) + D * E) / (p_color * (A * p_color + B) + D * F)) - E / F; // E/F = Toe Angle
-}
-
-vec3 Uncharted2ToneMapping(vec3 color)
-{
-	float A = 0.15;
-	float B = 0.50;
-	float C = 0.10;
-	float D = 0.20;
-	float E = 0.02;
-	float F = 0.30;
-	float W = 11.2;
-	color = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F;
-	float white = ((W * (A * W + C * B) + D * E) / (W * (A * W + B) + D * F)) - E / F;
-	color /= white;
-	return color;
-}
-
 vec2 calcTexCoord(void)
 {
     return gl_FragCoord.xy / screenSize;
 }
 
 void main(void)
-{
-	float exposure = 0.5;
-	
+{	
 	// Calculate screen-space texture coordinates, for buffer access
 	vec2 texCoord = calcTexCoord();
 	
-	// Perform gamma correction on the color from the final framebuffer
+	// Get the color data from the final color map
 	vec3 fragmentColor = texture(inputColorMap, texCoord).xyz;
-	
-	// Add emissive color (which is generated in a blur pass)
-	//fragmentColor += texture(emissiveMap, texCoord).xyz;
-	
-	#ifdef ENABLE_TONE_MAPPING
-	// Perform simple tonemapping on the final color
-	fragmentColor = simpleToneMapping(fragmentColor);
-	#endif
-	
-	#ifdef ENABLE_REINHARD_TONE_MAPPING
-	// Perform reinhard tonemapping on the final color
-	fragmentColor = reinhardToneMapping(fragmentColor);
-	#endif
-	
-	#ifdef ENABLE_FILMIC_TONE_MAPPING
-	// Perform filmic tonemapping on the final color
-	fragmentColor = Uncharted2ToneMapping(fragmentColor);
-	#endif
-	
-	// Perform gamma correction as the last step of the fragment color
-	//fragmentColor = gammaCorrection(fragmentColor, gamma);
-	
+		
 	// Write the color to the framebuffer
 	outputColor = vec4(fragmentColor, 1.0);
 }

+ 0 - 434
Praxis3D/Data/Shaders/lightPass - Copy.frag

@@ -1,434 +0,0 @@
-#version 430 core
-
-#define AVG_INTENDED_BRIGHTNESS 0.5
-#define MIN_INTENDED_BRIGHTNESS 0.005
-#define MAX_INTENDED_BRIGHTNESS 5.0
-
-#define MAX_NUM_POINT_LIGHTS 20
-#define MAX_NUM_SPOT_LIGHTS 10
-#define PI 3.1415926535
-
-layout(location = 0) out vec4 emissiveBuffer;
-layout(location = 1) out vec3 finalBuffer;
-
-in float avgBrightness;
-
-struct DirectionalLight
-{
-    vec3 m_color;
-    vec3 m_direction;
-    float m_intensity;
-};
-
-struct PointLight
-{
-    vec3 m_color;
-    vec3 m_position;
-	
-    float m_attenConstant;
-    float m_attenLinear;
-    float m_attenQuad;
-    float m_intensity;
-};
-struct SpotLight
-{
-    vec3 m_color;
-    vec3 m_position;
-    vec3 m_direction;
-	
-    float m_attenConstant;
-    float m_attenLinear;
-    float m_attenQuad;
-	
-    float m_intensity;
-    float m_cutoffAngle;
-};
-
-uniform sampler2D positionMap;
-uniform sampler2D diffuseMap;
-uniform sampler2D normalMap;
-uniform sampler2D emissiveMap;
-uniform sampler2D matPropertiesMap;
-
-uniform samplerCube staticEnvMap;
-
-uniform mat4 modelViewMat;
-uniform mat4 viewMat;
-uniform vec3 cameraPosVec;
-uniform ivec2 screenSize;
-uniform float gamma;
-
-uniform int numPointLights;
-uniform int numSpotLights;
-
-uniform DirectionalLight directionalLight;
-
-// Using uniform buffer objects to pass light arrays and std140 layout for consistent variable spacing inside the buffer.
-// Array size is fixed, but is updated partially, with only the lights that are being used, passing number of lights as uniform.
-layout (std140) uniform PointLights
-{
-	PointLight pointLights[MAX_NUM_POINT_LIGHTS];
-};
-layout (std140) uniform SpotLights
-{
-	SpotLight spotLights[MAX_NUM_SPOT_LIGHTS];
-};
-
-vec3 worldPos;
-vec3 normal;
-vec3 fragmentToEye;
-
-float getBrightestColor(vec3 p_color)
-{
-	return max(p_color.x, max(p_color.y, p_color.z));
-}
-
-// Calculates a brightness value from a color
-float calcBrightness(vec3 p_color)
-{
-	return dot(p_color, vec3(0.2126, 0.7152, 0.0722));
-}
-
-// Adjusts an RGB color based on the average brightness (exposure)
-// by making overall brightness match the average intended brightness (AVG_INTENDED_BRIGHTNESS)
-vec3 brightnessMapping(vec3 p_color, float p_exposure)
-{
-	return p_color * clamp(AVG_INTENDED_BRIGHTNESS / p_exposure, MIN_INTENDED_BRIGHTNESS, MAX_INTENDED_BRIGHTNESS);
-}
-
-float saturate(float p_value)
-{
-	return clamp(p_value, 0.0f, 1.0f);
-}
-
-float G1V(float p_dotNV, float p_k)
-{
-    return 1.0f / (p_dotNV * (1.0f - p_k) + p_k);
-}
-
-/*float MicroFacetDistr_GGX(vec3 p_normal, vec3 p_halfVec, float p_roughnessSqrt)
-{
-	float NdotH = dot(p_normal, p_halfVec);
-	if(NdotH > 0.0)
-	{
-		float NdotHSqrt = NdotH * NdotH;
-		float microfacetDstrb = NdotHSqrt * p_roughnessSqrt + (1.0 - NdotHSqrt);
-		return (NdotH * p_roughnessSqrt) / (3.14 * microfacetDstrb * microfacetDstrb);
-	}
-	else
-		return 0.0;
-}
-
-float GeometryAtten_GGX(vec3 p_fragToEye, vec3 p_normal, vec3 p_halfVec, float p_roughnessSqrt)
-{
-    float VdotH = clamp(dot(p_fragToEye, p_halfVec), 0.0, 1.0);
-    float VdotN = clamp(dot(p_fragToEye, p_normal), 0.0, 1.0);
-	
-	float geoFactor = (VdotH / VdotN);
-	if(geoFactor > 0.0)
-	{
-		float VdotHSqrt = VdotH * VdotH;
-		float geoAtt = (1.0 - VdotHSqrt) / VdotHSqrt;
-		return 2.0 / (1.0 + sqrt(1.0 + p_roughnessSqrt * geoAtt));
-	}
-	else
-		return 0.0;
-}
-
-vec3 Fresnel_Schlick(float p_cosT, vec3 p_F0)
-{
-	return F0 + (vec3(1.0) - F0) * pow(1.0 - p_cosT, 5.0);
-}*/
-
-vec3 computePBRLighting(vec3 lightPos, vec3 lightColor, vec3 position, vec3 N, vec3 V, vec3 albedo, float roughness, vec3 F0) 
-{
-
-	float alpha = roughness*roughness;
-	vec3 L = normalize(lightPos - position);
-	vec3 H = normalize (V + L);
-
-	float dotNL = clamp (dot (N, L), 0.0, 1.0);
-	float dotNV = clamp (dot (N, V), 0.0, 1.0);
-	float dotNH = clamp (dot (N, H), 0.0, 1.0);
-	float dotLH = clamp (dot (L, H), 0.0, 1.0);
-
-	float D, vis;
-	vec3 F;
-
-	// NDF : GGX
-	float alphaSqr = alpha*alpha;
-	float pi = 3.1415926535;
-	float denom = dotNH * dotNH *(alphaSqr - 1.0) + 1.0;
-	D = alphaSqr / (pi * denom * denom);
-
-	// Fresnel (Schlick)
-	float dotLH5 = pow (1.0 - dotLH, 5.0);
-	F = F0 + (1.0 - F0)*(dotLH5);
-
-	// Visibility term (G) : Smith with Schlick's approximation
-	float k = alpha / 2.0;
-	vis = G1V (dotNL, k) * G1V (dotNV, k);
-
-	vec3 specular = /*dotNL **/ D * F * vis;
-
-	vec3 ambient = vec3(.01);
-
-	float invPi = 0.31830988618;
-	vec3 diffuse = (albedo * invPi);
-
-
-	return (diffuse + specular) * lightColor * dotNL ;
-}
-
-vec3 LightingFuncGGX_REF(vec3 p_normal, vec3 p_viewDir, vec3 p_lightDir, float p_roughnessSqrt, vec3 p_F0)
-{
-	// Calculate half vector between view and light directions
-    vec3 halfVector = normalize(p_viewDir + p_lightDir);
-	
-	// Calculate required dot products
-    float dotNL = saturate(dot(p_normal, p_lightDir));
-    float dotNV = saturate(dot(p_normal, p_viewDir));
-    float dotNH = saturate(dot(p_normal, halfVector));
-    float dotLH = saturate(dot(p_lightDir, halfVector));
-
-	// Calculate microfacet distributions (based on roughness)
-	// Using GGX normal distribution function
-    float RoughnessPow4 = p_roughnessSqrt * p_roughnessSqrt;
-    float denom = dotNH * dotNH * (RoughnessPow4 - 1.0) + 1.0f;
-    float D = RoughnessPow4 / (PI * denom * denom);
-
-	// Calculate fresnel effect 
-	// Using Schlick's approximation
-    float dotLH5 = pow(1.0f - dotLH, 5);
-	vec3 F = p_F0 + (vec3(1.0f) - p_F0) * (dotLH5);
-
-	// Calculate geometric attenuation (or visibility term - self shadowing of microfacets)
-	// Using Smith with Schlick's approximation
-    float k2 = p_roughnessSqrt / 2.0f;
-    float G = G1V(dotNL, k2) * G1V(dotNV, k2);
-	
-	// Multiple all the terms together
-	return dotNL * D * F * G;
-}
-
-float SpecGGX(vec3 N, vec3 V, vec3 L, float roughness, float F0 )
-{
-	float SqrRoughness = roughness*roughness;
-
-	vec3 H = normalize(V + L);
-
-	float NdotL = clamp(dot(N,L),0.0,1.0);
-	float NdotV = clamp(dot(N,V),0.0,1.0);
-	float NdotH = clamp(dot(N,H),0.0,1.0);
-	float LdotH = clamp(dot(L,H),0.0,1.0);
-
-	float RoughnessPow4 = SqrRoughness * SqrRoughness;
-	float denom = NdotH * NdotH * (RoughnessPow4 - 1.0) + 1.0;
-	float D = RoughnessPow4 / (PI * denom * denom);
-
-	float LdotH5 = 1.0 - LdotH;
-    LdotH5 = LdotH5 * LdotH5 * LdotH5 * LdotH5 * LdotH5;
-	float F = F0 + (1.0 - F0) * (LdotH5);
-
-	float k = SqrRoughness/2.0;
-	float G = G1V(NdotL, k) * G1V(NdotV, k);
-
-	float specular = NdotL * D * F * G;
-    
-	return specular;
-}
-
-vec3 calcLightColor(vec3 p_normal, vec3 p_fragToEye, vec3 p_lightColor, vec3 p_lightDirection, vec3 p_F0, float p_diffuseAmount, float p_roughnessSqrt)
-{	
-	// Get specular and diffuse lighting
-	vec3 specularColor = LightingFuncGGX_REF(p_normal, p_fragToEye, p_lightDirection, p_roughnessSqrt, p_F0);
-    vec3 diffuseColor = vec3(clamp(dot(p_normal, p_lightDirection), 0.0, 1.0));
-	
-	// Add specular and diffuse together, and multiply it by the color of the light
-	return (specularColor + diffuseColor * p_diffuseAmount) * p_lightColor;
-}
-
-/*vec3 calcLightColorOld(vec3 p_lightColor, vec3 p_lightDirection)
-{
-    // Get angle between normal and light direction
-    //float NdotL = max(dot(normal, -p_lightDirection), 0.0);
-	float NdotL = clamp( dot( normal, -p_lightDirection ), 0.0, 1.0 );
-    
-    float specular = 0.0;
-    if(NdotL > 0.0)
-    {
-        // Calculate neccessary values
-        vec3 halfVector = normalize(fragmentToEye - p_lightDirection );
-		
-		float NdotH = clamp( dot( normal, halfVector ), 0.0, 1.0 );
-		float NdotV = clamp( dot( normal, fragmentToEye ), 0.0, 1.0 );
-		float VdotH = clamp( dot( fragmentToEye, halfVector ), 0.0, 1.0 );
-		        
-        // Calculate geometric attenuation (self shadowing of microfacets)
-        float NH2 = 2.0 * NdotH;
-        float g1 = (NH2 * NdotV) / VdotH;
-        float g2 = (NH2 * NdotL) / VdotH;
-        float geoAtt = min(1.0, min(g1, g2));
-     	
-		// Calculate microfacet distributions (roughness)
-		// Using Beckmann distribution function
-        float r1 = 1.0 / ( 4.0 * roughnessSqrt * pow(NdotH, 4.0));
-        float r2 = (NdotH * NdotH - 1.0) / (roughnessSqrt * NdotH * NdotH);
-        float microfacetDstrb = r1 * exp(r2);
-        
-		// Calculate fresnel effect (using Schlick's approximation)
-        float fresnel = pow(1.0 - VdotH, 5.0);
-        fresnel *= (1.0 - F0);
-        fresnel += F0;
-        
-		// Calculate specular component
-        specular = max((fresnel * geoAtt * microfacetDstrb) / (NdotV * NdotL * 3.14), 0.0);
-        //specular = (fresnel * geoAtt * microfacetDstrb) / (NdotV * NdotL * 3.14);
-		
-		//F0:"NdotL * (cSpecular * Rs + cDiffuse * (1-f0))"
-		
-		// Combine specular and diffuse components
-		return p_lightColor * NdotL * (k + specular * (1.0 - k));
-		//return p_lightColor * NdotL * specular;//(k + specular * (1.0 - k));
-    }
-	else
-	{
-		return vec3(0.0, 0.0, 0.0);
-	}
-}*/
-
-vec2 calcTexCoord(void)
-{
-    return gl_FragCoord.xy / screenSize;
-}
-
-void main(void) 
-{
-	// Calculate screen-space texture coordinates, for buffer access
-	vec2 texCoord = calcTexCoord();
-	
-	// Get the emissive texture color
-	//vec3 emissiveColor = texture(emissiveMap, texCoord).xyz;
-	vec3 emissiveColor = pow(texture(emissiveMap, texCoord).xyz, vec3(gamma));
-	
-	//if(getBrightestColor(emissiveColor) > 0.05)
-	//{
-	//	finalBuffer = emissiveColor;
-	//	discard;
-	//}
-	
-	// Get diffuse color (full-bright) from diffuse buffer and convert it to linear space
-	vec3 diffuseColor = pow(texture(diffuseMap, texCoord).xyz, vec3(gamma));
-	// Get pixel's position in world space
-	vec3 worldPos = texture(positionMap, texCoord).xyz;
-	// Get normal (in world space) and normalize it to minimize floating point approximation errors
-	vec3 normal = normalize(texture(normalMap, texCoord).xyz);
-	// Get material properties
-	vec4 matProperties = texture(matPropertiesMap, texCoord).xyzw;
-	
-	// Extract roughness and metalness values
-	float roughnessVar = matProperties.x;
-	float metalness = matProperties.y;
-	
-	// Squaring roughness gives better visual results
-	float roughnessSqrt = roughnessVar * roughnessVar;
-	
-	// Calculate view direction (fragment to eye vector)
-	fragmentToEye = normalize(cameraPosVec - worldPos);
-	
-	float ior = mix(1.5, 40.5, metalness);
-	ior = 0.04;//40.5;
-	
-	float F0 = abs((1.0 - ior) / (1.0 + ior));
-	F0 = F0 * F0;
-	//vec3 F0vec = mix(vec3(F0), diffuseColor, metalness);
-	vec3 F0vec = mix(vec3(0.04), diffuseColor, metalness);
-		
-	// Fresnel for the diffuse color
-    float NdotV = clamp(dot(normal, fragmentToEye), 0.0, 1.0);
-	NdotV = pow(1.0 - NdotV, 5.0);
-	float diffuseAmount = 1.0 - (metalness + (1.0 - metalness) * (NdotV));
-	
-	
-	// ior = from 1.2 to 10.0
-	
-	// Declare final color of the fragment and add directional light to it
-	vec3 finalLightColor = calcLightColor(normal, fragmentToEye, directionalLight.m_color, normalize(-directionalLight.m_direction), F0vec, diffuseAmount, roughnessSqrt) * directionalLight.m_intensity;
-	
-	//vec3 finalLightColor = calcLightColor(normal, fragmentToEye, vec3(1.0, 1.0, 1.0), normalize(vec3(8.0, 10.0, 5.0)), F0vec, diffuseAmount, roughnessSqrt) * 1.0;
-	
-	for(int i = 0; i < numPointLights; i++)
-	{		
-		// Get light direction, extract length from it and normalize for usage as direction vector
-		vec3 lightDirection =  pointLights[i].m_position - worldPos;
-		float lightDistance = length(lightDirection);
-		lightDirection = normalize(lightDirection);
-		
-		// Add up constant, linear and quadratic attenuation
-		float attenuation = pointLights[i].m_attenConstant + 
-							pointLights[i].m_attenLinear * lightDistance + 
-							pointLights[i].m_attenQuad * lightDistance * lightDistance;
-							
-		// Light color multiplied by intensity and divided by attenuation
-		finalLightColor += (calcLightColor(normal, fragmentToEye, pointLights[i].m_color, lightDirection, F0vec, diffuseAmount, roughnessSqrt) * pointLights[i].m_intensity) / attenuation;
-	}
-	
-	for(int i = 0; i < numSpotLights; i++)
-	{			
-		// Calculate direction from position of light to current pixel
-		vec3 lightToFragment = normalize(worldPos - spotLights[i].m_position);
-		
-		// Get dot product of light direction and direction of light to pixel, and use it as a factor for light strength
-		float spotLightFactor = dot(lightToFragment, spotLights[i].m_direction);
-		
-		// Early bail if pixel is outside of the cone of spot light
-		if(spotLightFactor > spotLights[i].m_cutoffAngle)
-		{
-			// Get light direction, extract length from it and normalize for usage as direction vector
-			vec3 lightDirection =  spotLights[i].m_position - worldPos;
-			float lightDistance = length(lightDirection);
-			lightDirection = normalize(lightDirection);
-			
-			// Add up constant, linear and quadratic attenuation
-			float attenuation = spotLights[i].m_attenConstant + 
-								spotLights[i].m_attenLinear * lightDistance + 
-								spotLights[i].m_attenQuad * lightDistance * lightDistance;
-			
-			// Light color multiplied by intensity
-			vec3 lightColor = (calcLightColor(normal, fragmentToEye, spotLights[i].m_color, lightDirection, F0vec, diffuseAmount, roughnessSqrt) * spotLights[i].m_intensity);
-			
-			// Light restriction from cone
-			float coneAttenuation = (1.0 - (1.0 - spotLightFactor) * 1.0 / (1.0 - spotLights[i].m_cutoffAngle));
-			
-			finalLightColor += (lightColor / attenuation) * coneAttenuation;
-		}
-	}
-	
-	//emissiveBuffer = vec4(emissive, 0.0);	
-	
-	// Multiply the diffuse color by the amount of light the current pixel receives
-	diffuseColor = diffuseColor * finalLightColor;
-	
-	// Adjust the fragment brightness based on average
-	diffuseColor = brightnessMapping(diffuseColor, avgBrightness);
-	
-	// If a fragment brightness exceeds a value of 1.0 after an HDR Mapping, 
-	// add it to the emissive buffer, as a bloom effect
-	//emissiveBuffer = vec4(max(diffuseColor - vec3(1.0), vec3(0.0)), 1.0);
-	
-	if(calcBrightness(diffuseColor) > 1.0)
-		emissiveColor += diffuseColor;
-	
-	emissiveBuffer = vec4(emissiveColor, 1.0);
-	finalBuffer = diffuseColor + emissiveColor;
-	//finalBuffer = vec3(0.0, 1.0, 0.0);//diffuseColor * finalLightColor;
-	//finalBuffer = simpleToneMapping(diffuseColor * finalLightColor, 2.2);
-	//finalBuffer = vec3(roughnessVar, roughnessVar, roughnessVar);
-	//finalBuffer = vec4(texture(staticEnvMap, vec3(0.0, 0.0, 0.0)).xyz, 1.0);
-	//finalBuffer = vec4(metallic, metallic, metallic, 1.0);
-	//finalBuffer = vec4(pow(mix(diffuseColor, vec3(0.0, 0.0, 0.0), 1.0 - metallic) * finalLightColor, vec3(1.0 / 2.2)), 1.0);
-	//finalBuffer = vec4(pow(finalLightColor, vec3(1.0 / 2.2)), 1.0);
-	//finalBuffer = vec4(pow(diffuseColor, vec3(1.0 / 2.2)), 1.0);
-	//finalBuffer = vec4(texture(normalMap, texCoord).xyz, 1.0);
-	//finalBuffer = vec4(roughnessVar, roughnessVar, roughnessVar, 1.0);
-}

+ 0 - 147
Praxis3D/Data/Shaders/lightPass 2.frag

@@ -1,147 +0,0 @@
-#version 330
-
-#define MAX_NUM_POINT_LIGHTS 20
-#define MAX_NUM_SPOT_LIGHTS 10
-
-layout(location = 0) out vec4 emissiveBuffer;
-layout(location = 1) out vec4 finalBuffer;
-
-//in vec2 texCoord;
-
-uniform sampler2D positionMap;
-uniform sampler2D diffuseMap;
-uniform sampler2D normalMap;
-
-uniform ivec2 screenSize;
-
-uniform mat4 modelViewMat;
-uniform mat4 viewMat;
-uniform vec3 cameraPosVec;
-
-struct DirectionalLight
-{
-    vec3 m_color;
-    vec3 m_direction;
-    float m_intensity;
-};
-
-struct PointLight
-{
-    vec3 m_color;
-    vec3 m_position;
-	
-    float m_attenConstant;
-    float m_attenLinear;
-    float m_attenQuad;
-    float m_intensity;
-};
-struct SpotLight
-{
-    vec3 m_color;
-    vec3 m_position;
-    vec3 m_direction;
-	
-    float m_attenConstant;
-    float m_attenLinear;
-    float m_attenQuad;
-	
-    float m_intensity;
-    float m_cutoffAngle;
-};
-
-// Using uniform buffer objects to pass light arrays and std140 layout for consistent variable spacing inside the buffer.
-// Array size is fixed, but is updated partially, with only the lights that are being used, passing number of lights as uniform.
-layout (std140) uniform PointLights
-{
-	PointLight pointLights[MAX_NUM_POINT_LIGHTS];
-};
-layout (std140) uniform SpotLights
-{
-	SpotLight spotLights[MAX_NUM_SPOT_LIGHTS];
-};
-
-uniform DirectionalLight directionalLight;
-	
-vec2 calcTexCoord(void)
-{
-    return gl_FragCoord.xy / screenSize;
-    //return gl_FragCoord.xy / vec2(1600, 900);
-}
-
-void main(void) 
-{
-	// Calculate screen-space texture coordinates, for buffer access
-	vec2 texCoord = calcTexCoord();
-	
-	vec3 varNormal = texture(normalMap, texCoord).xyz;
-	
-	//vec3 varEyeDir = vec3(viewMat[2].xyz);
-	vec3 worldPos = texture(positionMap, texCoord).xyz;
-	
-//vec3 varEyeDir = normalize(worldPos - cameraPosVec);
-	vec3 varEyeDir = normalize(cameraPosVec - worldPos);
-	//vec3 varEyeDir = vec3(viewMat[3].xyz);
-	//vec3 varEyeDir = vec4(vec4(1.0, 0.0, 0.0, 1.0) * viewMat).xyz;
-	
-	//vec3 lightDirection = normalize(vec3(0.5, 0.0, 0.5));
-	//vec3 lightDirection = normalize(vec3(0.0, 1.0, 0.5));
-	vec3 lightDirection = directionalLight.m_direction;
-	
-	// set important material values
-    float roughnessValue = 0.3; // 0 : smooth, 1: rough
-    float F0 = 1.8; // fresnel reflectance at normal incidence
-    float k = 0.01; // fraction of diffuse reflection (specular reflection = 1 - k)
-    //vec3 lightColor = directionalLight.m_color;
-	vec3 lightColor = spotLights[1].m_color;
-    
-    // interpolating normals will change the length of the normal, so renormalize the normal.
-    vec3 normal = normalize(varNormal);
-    
-    // do the lighting calculation for each fragment.
-    float NdotL = max(dot(normal, lightDirection), 0.0);
-    
-    float specular = 0.0;
-    if(NdotL > 0.0)
-    {
-        vec3 eyeDir = normalize(varEyeDir);
-
-        // calculate intermediary values
-        vec3 halfVector = normalize(lightDirection + eyeDir);
-        float NdotH = max(dot(normal, halfVector), 0.0); 
-        float NdotV = max(dot(normal, eyeDir), 0.0); // note: this could also be NdotL, which is the same value
-        float VdotH = max(dot(eyeDir, halfVector), 0.0);
-        float mSquared = roughnessValue * roughnessValue;
-        
-        // geometric attenuation
-        float NH2 = 2.0 * NdotH;
-        float g1 = (NH2 * NdotV) / VdotH;
-        float g2 = (NH2 * NdotL) / VdotH;
-        float geoAtt = min(1.0, min(g1, g2));
-     
-        // roughness (or: microfacet distribution function)
-        // beckmann distribution function
-        float r1 = 1.0 / ( 4.0 * mSquared * pow(NdotH, 4.0));
-        float r2 = (NdotH * NdotH - 1.0) / (mSquared * NdotH * NdotH);
-        float roughness = r1 * exp(r2);
-        
-        // fresnel
-        // Schlick approximation
-        float fresnel = pow(1.0 - VdotH, 5.0);
-        fresnel *= (1.0 - F0);
-        fresnel += F0;
-        
-        specular = (fresnel * geoAtt * roughness) / (NdotV * NdotL * 3.14);
-    }
-
-	vec3 color = pow(texture(diffuseMap, texCoord).xyz, vec3(2.2));
-    vec3 finalValue = lightColor * NdotL * (k + specular * (1.0 - k));
-	
-	finalBuffer = vec4(pow(color * finalValue, vec3(1.0 / 2.2)), 1.0);
-	//finalBuffer = vec4(directionalLight.m_color, 1.0);
-	//finalBuffer = vec4(normalize(lightDirection + varEyeDir), 1.0);
-
-	// Get diffuse color (full-bright) from diffuse buffer
-	//vec3 color = texture(diffuseMap, texCoord).xyz;
-
-	//finalBuffer = vec4(pow(color, vec3(1.0 / 2.2)), 1.0);
-}

+ 0 - 904
Praxis3D/Data/Shaders/lightPass 3.frag

@@ -1,904 +0,0 @@
-#version 430 core
-
-#define MAX_NUM_POINT_LIGHTS 20
-#define MAX_NUM_SPOT_LIGHTS 10
-
-layout(location = 0) out vec4 emissiveBuffer;
-layout(location = 1) out vec3 finalBuffer;
-
-//in vec2 texCoord;
-
-struct DirectionalLight
-{
-    vec3 m_color;
-    vec3 m_direction;
-    float m_intensity;
-};
-
-struct PointLight
-{
-    vec3 m_color;
-    vec3 m_position;
-	
-    float m_attenConstant;
-    float m_attenLinear;
-    float m_attenQuad;
-    float m_intensity;
-};
-struct SpotLight
-{
-    vec3 m_color;
-    vec3 m_position;
-    vec3 m_direction;
-	
-    float m_attenConstant;
-    float m_attenLinear;
-    float m_attenQuad;
-	
-    float m_intensity;
-    float m_cutoffAngle;
-};
-
-uniform sampler2D positionMap;
-uniform sampler2D diffuseMap;
-uniform sampler2D normalMap;
-uniform sampler2D matPropertiesMap;
-
-uniform samplerCube staticEnvMap;
-
-uniform mat4 modelViewMat;
-uniform mat4 viewMat;
-uniform vec3 cameraPosVec;
-uniform ivec2 screenSize;
-
-uniform int numPointLights;
-uniform int numSpotLights;
-
-uniform DirectionalLight directionalLight;
-
-// Using uniform buffer objects to pass light arrays and std140 layout for consistent variable spacing inside the buffer.
-// Array size is fixed, but is updated partially, with only the lights that are being used, passing number of lights as uniform.
-layout (std140) uniform PointLights
-{
-	PointLight pointLights[MAX_NUM_POINT_LIGHTS];
-};
-layout (std140) uniform SpotLights
-{
-	SpotLight spotLights[MAX_NUM_SPOT_LIGHTS];
-};
-
-vec3 worldPos;
-vec3 normal;
-vec3 fragmentToEye;
-float roughnessSqrt;
-
-// set important material values
-float roughnessVar = 0.1; // 0.1	// 0 : smooth, 1: rough
-//float roughnessVar = 0.8;
-float F0 = 0.02;//0.171968833;//1.0; 			// fresnel reflectance at normal incidence
-vec3 F0vec;
-//float k = 0.01; 				// fraction of diffuse reflection (specular reflection = 1 - k)
-float k = 0.8; //0.5
-float metallic;
-vec3 envColor;
-
-float ref_at_norm_incidence = F0;
-vec3 viewer;
-
-float saturate(float p_value)
-{
-	return clamp(p_value, 0.0f, 1.0f);
-}
-
-float G1V(float p_dotNV, float p_k)
-{
-    return 1.0f / (p_dotNV * (1.0f - p_k) + p_k);
-}
-
-float MicroFacetDistr_GGX(vec3 p_normal, vec3 p_halfVec, float p_roughnessSqrt)
-{
-	float NdotH = dot(p_normal, p_halfVec);
-	if(NdotH > 0.0)
-	{
-		float NdotHSqrt = NdotH * NdotH;
-		float microfacetDstrb = NdotHSqrt * p_roughnessSqrt + (1.0 - NdotHSqrt);
-		return (NdotH * p_roughnessSqrt) / (3.14 * microfacetDstrb * microfacetDstrb);
-	}
-	else
-		return 0.0;
-}
-
-float GeometryAtten_GGX(vec3 p_fragToEye, vec3 p_normal, vec3 p_halfVec, float p_roughnessSqrt)
-{
-    float VdotH = clamp(dot(p_fragToEye, p_halfVec), 0.0, 1.0);
-    float VdotN = clamp(dot(p_fragToEye, p_normal), 0.0, 1.0);
-	
-	float geoFactor = (VdotH / VdotN);
-	if(geoFactor > 0.0)
-	{
-		float VdotHSqrt = VdotH * VdotH;
-		float geoAtt = (1.0 - VdotHSqrt) / VdotHSqrt;
-		return 2.0 / (1.0 + sqrt(1.0 + p_roughnessSqrt * geoAtt));
-	}
-	else
-		return 0.0;
-}
-
-vec3 Fresnel_Schlick(float p_cosT, vec3 p_F0)
-{
-	return F0 + (vec3(1.0) - F0) * pow(1.0 - p_cosT, 5.0);
-}
-
-vec3 calcLightColor2(vec3 p_lightColor, vec3 p_lightDirection)
-{
-	//float NoV = saturate( dot(normal, V) );
-	float NoV = abs( dot(normal, fragmentToEye) ) + 0.00001;//1e-5;
-
-	// Diffuse_Lambert
-	//Shared.DiffuseMul = DiffuseColor * (1.0 / 3.14);
-
-	// D_GGX, Vis_SmithJointApprox
-	float m = roughnessVar * roughnessVar;
-	float m2 = m * m;
-	float SpecularMul = (0.5 / 3.14) * m2;
-	float VisMad = ( 2 * NoV * ( 1 - m ) + m, NoV * m );
-	
-	// F_Schlick
-	//SpecularMul *= saturate( 50.0 * k );
-	return vec3(1.0);
-}
-
-vec3 calcLightColor3(vec3 p_lightColor, vec3 p_lightDirection)
-{
-	// Compute any aliases and intermediary values
-    // -------------------------------------------
-	
-	vec3 half_vector = normalize(fragmentToEye - p_lightDirection );
-    float NdotL        = clamp( dot( normal, -p_lightDirection ), 0.0, 1.0 );
-    float NdotH        = clamp( dot( normal, half_vector ), 0.0, 1.0 );
-    float NdotV        = clamp( dot( normal, viewer ), 0.0, 1.0 );
-    float VdotH        = clamp( dot( viewer, half_vector ), 0.0, 1.0 );
-    float r_sq         = roughnessVar * roughnessVar;
- 
-    // Evaluate the geometric term
-    // --------------------------------
-    float geo_numerator   = 2.0f * NdotH;
-    float geo_denominator = VdotH;
- 
-    float geo_b = (geo_numerator * NdotV ) / geo_denominator;
-    float geo_c = (geo_numerator * NdotL ) / geo_denominator;
-    float geo   = min( 1.0f, min( geo_b, geo_c ) );
- 
-    // Now evaluate the roughness term
-    // -------------------------------
-    float roughness;
-    
-	float roughness_a = 1.0f / ( 4.0f * r_sq * pow( NdotH, 4 ) );
-	float roughness_b = NdotH * NdotH - 1.0f;
-	float roughness_c = r_sq * NdotH * NdotH;
-	roughness = roughness_a * exp( roughness_b / roughness_c );
- 
-    // Next evaluate the Fresnel value
-    // -------------------------------
-    float fresnel = pow( 1.0f - VdotH, 5.0f );
-    fresnel *= ( 1.0f - F0 );
-    fresnel += F0;
- 
-    // Put all the terms together to compute
-    // the specular term in the equation
-    // -------------------------------------
-    float Rs_numerator   = ( fresnel * geo * roughness );
-    float Rs_denominator  = NdotV * NdotL;
-    float Rs             = Rs_numerator/ Rs_denominator;
-  
-    float specular = (fresnel * geo * roughness) / (NdotV * NdotL);
-    // Put all the parts together to generate
-    // the final colour
-    // --------------------------------------
-	
-	return p_lightColor * NdotL * specular;
-	
-    //float3 final = max(0.0f, NdotL) * (cSpecular * Rs + cDiffuse);
- 
-    // Return the result
-    // -----------------
-    //return float4( final, 1.0f );
-}
-
-vec3 computePBRLighting(vec3 lightPos, vec3 lightColor, vec3 position, vec3 N, vec3 V, vec3 albedo, float roughness, vec3 F0) 
-{
-
-	float alpha = roughness*roughness;
-	vec3 L = normalize(lightPos - position);
-	vec3 H = normalize (V + L);
-
-	float dotNL = clamp (dot (N, L), 0.0, 1.0);
-	float dotNV = clamp (dot (N, V), 0.0, 1.0);
-	float dotNH = clamp (dot (N, H), 0.0, 1.0);
-	float dotLH = clamp (dot (L, H), 0.0, 1.0);
-
-	float D, vis;
-	vec3 F;
-
-	// NDF : GGX
-	float alphaSqr = alpha*alpha;
-	float pi = 3.1415926535;
-	float denom = dotNH * dotNH *(alphaSqr - 1.0) + 1.0;
-	D = alphaSqr / (pi * denom * denom);
-
-	// Fresnel (Schlick)
-	float dotLH5 = pow (1.0 - dotLH, 5.0);
-	F = F0 + (1.0 - F0)*(dotLH5);
-
-	// Visibility term (G) : Smith with Schlick's approximation
-	float k = alpha / 2.0;
-	vis = G1V (dotNL, k) * G1V (dotNV, k);
-
-	vec3 specular = /*dotNL **/ D * F * vis;
-
-	vec3 ambient = vec3(.01);
-
-	float invPi = 0.31830988618;
-	vec3 diffuse = (albedo * invPi);
-
-
-	return (diffuse + specular) * lightColor * dotNL ;
-}
-
-/*float SpecGGX(vec3 N, vec3 V, vec3 L, float roughness, float F0)
-{
-	float SqrRoughness = roughness*roughness;
-
-	vec3 H = normalize(V+L);
-
-	float NdotL = clamp(dot(N,L),0.0,1.0);
-	float NdotV = clamp(dot(N,V),0.0,1.0);
-	float NdotH = clamp(dot(N,H),0.0,1.0);
-	float LdotH = clamp(dot(L,H),0.0,1.0);
-
-	// Geom term
-	float RoughnessPow4 = SqrRoughness*SqrRoughness;
-	float pi = 3.14159;
-	float denom = NdotH * NdotH *(RoughnessPow4-1.0) + 1.0;
-	float D = RoughnessPow4/(pi * denom * denom);
-
-	// Fresnel term 
-	float LdotH5 = 1.0-LdotH;
-    LdotH5 = LdotH5*LdotH5*LdotH5*LdotH5*LdotH5;
-	float F = F0 + (1.0-F0)*(LdotH5);
-
-	// Vis term 
-	float k = SqrRoughness/2.0;
-	float Vis = G1V(NdotL,k)*G1V(NdotV,k);
-
-	float specular = NdotL * D * F * Vis;
-    
-	return specular;
-}*/
-
-vec3 LightingFuncGGX_REF(vec3 N, vec3 V, vec3 L, float roughness, vec3 F0)
-{
-    float alpha = roughness;//roughness*roughness;
-
-    vec3 H = normalize(V+L);
-
-    float dotNL = saturate(dot(N,L));
-    float dotNV = saturate(dot(N,V));
-    float dotNH = saturate(dot(N,H));
-    float dotLH = saturate(dot(L,H));
-
-    //float F, D, vis;
-	float D, vis;
-	vec3 F;
-
-    // D
-    float alphaSqr = alpha*alpha;
-    float pi = 3.14159f;
-    float denom = dotNH * dotNH *(alphaSqr-1.0) + 1.0f;
-    D = alphaSqr/(pi * denom * denom);
-
-    // F
-    float dotLH5 = pow(1.0f - dotLH, 5);
-	F = F0 + (vec3(1.0f) - F0) * (dotLH5);
-    //F = F0 + (1.0-F0)*(dotLH5);
-
-    // V
-    float k2 = alpha / 2.0f;
-    vis = G1V(dotNL, k2) * G1V(dotNV, k2);
-	
-	float metallic2 = (1.0f - metallic);// / 2.0f;
-	
-	vec3 specular;
-	
-	//if(metallic == 1.0f)
-	//{
-		specular = dotNL * D * F * vis;
-	//}
-	//else
-	//{
-		//specular = (dotNL) * (metallic2 + D * F * vis * (1.0 - metallic2));
-	//}
-	
-    //vec3 specular = (dotNL) * (metallic2 + D * F * vis * (1.0 - metallic2));
-	
-	//specular = ((1.0 - dotNL) * (1.0 - metallic2)) + D * F * vis;
-	//specular = ((1.0 - dotNL) * (1.0 - metallic2)) + (metallic2 + D * F * vis * (1.0 - metallic2));
-	
-    return specular;
-}
-
-float SpecGGX(vec3 N, vec3 V, vec3 L, float roughness, float F0 )
-{
-	float SqrRoughness = roughness*roughness;
-
-	vec3 H = normalize(V+L);
-
-	float NdotL = clamp(dot(N,L),0.0,1.0);
-	float NdotV = clamp(dot(N,V),0.0,1.0);
-	float NdotH = clamp(dot(N,H),0.0,1.0);
-	float LdotH = clamp(dot(L,H),0.0,1.0);
-
-	// Geom term
-	float RoughnessPow4 = SqrRoughness*SqrRoughness;
-	float pi = 3.14159;
-	float denom = NdotH * NdotH *(RoughnessPow4-1.0) + 1.0;
-	float D = RoughnessPow4/(pi * denom * denom);
-
-	// Fresnel term 
-	float LdotH5 = 1.0-LdotH;
-    LdotH5 = LdotH5*LdotH5*LdotH5*LdotH5*LdotH5;
-	float F = F0 + (1.0-F0)*(LdotH5);
-
-	// Vis term 
-	float k = SqrRoughness/2.0;
-	float Vis = G1V(NdotL,k)*G1V(NdotV,k);
-
-	float specular = NdotL * D * F * Vis;
-    
-	return specular;
-}
-
-vec3 calcLight(vec3 p_normal, vec3 p_fragToEye, vec3 p_lightColor, vec3 p_lightDirection, vec3 p_F0, float p_roughnessSqrt)
-{	
-	vec3 lightDir = -p_lightDirection;
-		
-	//float spec = SpecGGX(p_normal, p_fragToEye, lightDir, roughnessVar, F0);
-	vec3 spec = LightingFuncGGX_REF(p_normal, p_fragToEye, lightDir, roughnessSqrt, F0vec);
-	
-    float dif = dot(p_normal, lightDir);
-		
-	// Fresnel
-    float NdotV = clamp(dot(p_normal, p_fragToEye), 0.0, 1.0);
-	NdotV = pow(1.0 - NdotV, 5.0);
-	float Fresnel = metallic + (1.0 - metallic) * (NdotV);
-
-    // Tint lights
-    vec3 SpecColor = spec * p_lightColor;
-    vec3 DiffColor = dif * p_lightColor * (1.0 - Fresnel);
-	
-    vec3 lightSum = max(((DiffColor + SpecColor)), vec3(0.0, 0.0, 0.0));
-    
-	// Fresnel 2
-    /*vec3 H = normalize(fragmentToEye + lightDir);
-    float dotLH = saturate(dot(lightDir,H));
-    float dotLH5 = pow(1.0f - dotLH, 5);
-	float F = F0 + (1.0 - F0) * (dotLH5);
-	F = metallic + (1.0 - metallic) * F;*/
-	
-    // Add GI
-    //const float	cAmbientMin = 0.04;    
-    //float		ambient = cAmbientMin * (IsInSphere);    
-    //vec3		ColorAmbient = vec3(ambient,ambient,ambient);
-    //vec3		GIReflexion = GetGIReflexion ( normal, roughness );
-    
-    
-    //ColorAmbient = GIReflexion * cAmbientMin;
-        
-    //vec3 lightSum = max(((DiffColor + SpecColor) * (1.0 - cAmbientMin)), vec3(0.0, 0.0, 0.0));
-    //return ( lightSum + ColorAmbient + ( Fresnel * GIReflexion ) ) * IsInSphere;
-	
-    return lightSum;
-	
-	/*
-
-    float dotNL = saturate(dot(p_normal, -p_lightDirection));
-	
-	//spec = LightingFuncGGX_REF(p_normal, p_fragToEye, -p_lightDirection, p_roughnessSqrt, F0vec);
-	
-    vec3 H = normalize(p_fragToEye + (-p_lightDirection));
-    float dotLH = saturate(dot(-p_lightDirection,H));
-    float dotLH5 = pow(1.0f - dotLH, 5);
-    //F = F0 + (1.0-F0)*(dotLH5);
-	vec3 F = F0vec + (1.0f - F0vec) * (dotLH5);
-	
-	float metallic2 = 1.0 - metallic;
-	
-	float diffuse = (1.0 - dotNL) * (1.0 - metallic);
-		//return p_lightColor * NdotL * (k + specular * (1.0 - k));
-	
-	//return p_lightColor * spec;*/
-
-	/*/ Calculate the specular contribution
-    float3 ks = 0;
-    float3 specular = GGX_Specular(specularCubemap, normal, viewVector, roughness, F0, ks );
-    float3 kd = (1 - ks) * (1 - metallic);
-    // Calculate the diffuse contribution
-    float3 irradiance = texCUBE(diffuseCubemap_Sampler, normal ).rgb;
-    float3 diffuse = materialColour * irradiance;
-
-    return float4( kd * diffuse + /*ks* / specular, 1);  */   
-	
-	//vec3 reflectionVec = reflect(-p_fragToEye, p_normal);
-    /*vec3 radiance = vec3(0);
-	
-	//float NdotV = clamp(dot(p_normal, p_fragToEye), 0.0, 1.0);
-	float NdotL = clamp(dot(p_normal, -p_lightDirection), 0.0, 1.0);
-	
-	//if(NdotL > 0.0)
-	{
-       // vec3 halfVector = normalize(p_lightDirection - p_fragToEye);
-        vec3 halfVector = normalize(p_fragToEye + p_lightDirection);
-		float NdotH = clamp(dot(p_normal, halfVector), 0.0, 1.0);
-		float VdotH = clamp(dot(p_fragToEye, halfVector), 0.0, 1.0);
-		
-		float cosT = clamp(dot(p_lightDirection, p_normal), 0.0, 1.0); // NdotL
-		float sinT = sqrt(1.0 - (cosT * cosT));
-		
-		vec3 fresnel = Fresnel_Schlick(VdotH, p_F0);
-		float geometry = GeometryAtten_GGX(p_fragToEye, p_normal, halfVector, p_roughnessSqrt) * GeometryAtten_GGX(p_lightDirection, p_normal, halfVector, p_roughnessSqrt);
-		//float geometry = GeometryAtten_GGX(p_lightDirection, p_normal, halfVector, p_roughnessSqrt);
-		float denominator = clamp(4.0 * (NdotV * NdotH + 0.05), 0.0, 1.0);
-		
-		//kS += fresnel;
-		
-		radiance = p_lightColor * geometry * fresnel * sinT / denominator;
-	}
-	
-	return radiance;*/
-}
-
-vec3 calcLightColor(vec3 p_lightColor, vec3 p_lightDirection)
-{
-    // Get angle between normal and light direction
-    //float NdotL = max(dot(normal, -p_lightDirection), 0.0);
-	float NdotL = clamp( dot( normal, -p_lightDirection ), 0.0, 1.0 );
-    
-    float specular = 0.0;
-    if(NdotL > 0.0)
-    {
-        // Calculate neccessary values
-        vec3 halfVector = normalize(fragmentToEye - p_lightDirection );
-		
-		float NdotH = clamp( dot( normal, halfVector ), 0.0, 1.0 );
-		float NdotV = clamp( dot( normal, fragmentToEye ), 0.0, 1.0 );
-		float VdotH = clamp( dot( fragmentToEye, halfVector ), 0.0, 1.0 );
-		
-		/*float NdotL = max(dot(normal, -p_lightDirection), 0.0);
-        float NdotH = max(dot(normal, halfVector), 0.0); 
-        float NdotV = max(dot(normal, fragmentToEye), 0.0);
-		float VdotH = max(dot(fragmentToEye, halfVector), 0.0);*/
-        
-        // Calculate geometric attenuation (self shadowing of microfacets)
-        float NH2 = 2.0 * NdotH;
-        float g1 = (NH2 * NdotV) / VdotH;
-        float g2 = (NH2 * NdotL) / VdotH;
-        float geoAtt = min(1.0, min(g1, g2));
-     	
-		// Calculate microfacet distributions (roughness)
-		// Using Beckmann distribution function
-        float r1 = 1.0 / ( 4.0 * roughnessSqrt * pow(NdotH, 4.0));
-        float r2 = (NdotH * NdotH - 1.0) / (roughnessSqrt * NdotH * NdotH);
-        float microfacetDstrb = r1 * exp(r2);
-        
-		// Calculate fresnel effect (using Schlick's approximation)
-        float fresnel = pow(1.0 - VdotH, 5.0);
-        fresnel *= (1.0 - F0);
-        fresnel += F0;
-        
-		// Calculate specular component
-        specular = max((fresnel * geoAtt * microfacetDstrb) / (NdotV * NdotL * 3.14), 0.0);
-        //specular = (fresnel * geoAtt * microfacetDstrb) / (NdotV * NdotL * 3.14);
-		
-		//F0:"NdotL * (cSpecular * Rs + cDiffuse * (1-f0))"
-		
-		// Combine specular and diffuse components
-		return p_lightColor * NdotL * (k + specular * (1.0 - k));
-		//return p_lightColor * NdotL * specular;//(k + specular * (1.0 - k));
-    }
-	else
-	{
-		return vec3(0.0, 0.0, 0.0);
-	}
-}
-
-vec2 calcTexCoord(void)
-{
-    return gl_FragCoord.xy / screenSize;
-}
-
-vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N)
-{
-	return vec3(0.0);
-    /*float a = Roughness * Roughness;
-    float Phi = 2 * PI * Xi.x;
-    float CosTheta = sqrt((1 - Xi.y) / (1 + (a*a - 1) * Xi.y));
-    float SinTheta = sqrt(1 - CosTheta * CosTheta);
-    vec3 H;
-    H.x = SinTheta * cos(Phi);
-    H.y = SinTheta * sin(Phi);
-    H.z = CosTheta;
-    vec3 UpVector = abs(N.z) < 0.999 ? vec3(0, 0, 1) : vec3(1, 0, 0);
-    vec3 TangentX = normalize(cross(UpVector, N));
-    vec3 TangentY = cross(N, TangentX);
-
-    // Tangent to world space
-    return TangentX * H.x + TangentY * H.y + N * H.z;*/
-}
-
-vec3 PrefilterEnvMap( float Roughness , vec3 R )
-{
-	return vec3(0.0);
-    /*vec3 N = R;
-    vec3 V = R;
-    vec3 PrefilteredColor = vec3(0);
-    float TotalWeight = 0.0000001f;
-    const uint NumSamples = 1024;
-
-    for( uint i = 0; i < NumSamples; i++ )
-    {
-        vec2 Xi = vec2(RandomNumBuffer[i * 2 + 0], RandomNumBuffer[i * 2 + 1]);
-        vec3 H = ImportanceSampleGGX( Xi, Roughness , N );
-        vec3 L = 2 * dot( V, H ) * H - V;
-        float NoL = saturate( dot( N, L ) );
-
-        if( NoL > 0 )
-        {
-            PrefilteredColor += EnvMap.SampleLevel( linearSampler, L, 0 ).rgb * NoL;
-            TotalWeight += NoL;
-        }
-    }
-    return PrefilteredColor / TotalWeight;*/
-}
-
-vec3 S(vec2 p_vec)
-{
-	return vec3(1.0);
-}
-
- float F(float VoH)
- {
-	return 1.0f;
- }
- float G(float NoL, float NoV, float NoH, float VoH)
- {
-	return 1.0f;
- }
-	
-// Hammersley function (return random low-discrepency points)
-vec2 Hammersley(uint i, uint N)
-{
-  return vec2(
-    float(i) / float(N),
-    float(bitfieldReverse(i)) * 2.3283064365386963e-10
-  );
-}
-
-// Computes the exact mip-map to reference for the specular contribution.
-// Accessing the proper mip-map allows us to approximate the integral for this
-// angle of incidence on the current object.
-float compute_lod(uint NumSamples, float NoH)
-{
-	return 0.0;
-	//float dist = D(NoH); // Defined elsewhere as subroutine
-	//return 0.5 * (log2(float(Dimensions.x * Dimensions.y) / NumSamples) - log2(dist));
-}
- 
-// Calculates the specular influence for a surface at the current fragment
-// location. This is an approximation of the lighting integral itself.
-vec3 radiance(vec3 N, vec3 V)
-{
-  // Precalculate rotation for +Z Hemisphere to microfacet normal.
-  //vec3 UpVector = abs(N.z) < 0.999 ? ZAxis : XAxis;
-  vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
-  vec3 TangentX = normalize(cross( UpVector, N ));
-  vec3 TangentY = cross(N, TangentX);
- 
-  // Note: I ended up using abs() for situations where the normal is
-  // facing a little away from the view to still accept the approximation.
-  // I believe this is due to a separate issue with normal storage, so
-  // you may only need to saturate() each dot value instead of abs().
-  float NoV = abs(dot(N, V));
- 
-  // Approximate the integral for lighting contribution.
-  vec3 fColor = vec3(0.0);
-  const uint NumSamples = 20;
-  for (uint i = 0; i < NumSamples; ++i)
-  {
-    vec2 Xi = Hammersley(i, NumSamples);
-    vec3 Li = S(Xi); // Defined elsewhere as subroutine
-    vec3 H  = normalize(Li.x * TangentX + Li.y * TangentY + Li.z * N);
-    vec3 L  = normalize(-reflect(V, H));
- 
-    // Calculate dot products for BRDF
-    float NoL = abs(dot(N, L));
-    float NoH = abs(dot(N, H));
-    float VoH = abs(dot(V, H));
-    float lod = compute_lod(NumSamples, NoH);
- 
-    float F_ = F(VoH); // Defined elsewhere as subroutine
-    float G_ = G(NoL, NoV, NoH, VoH); // Defined elsewhere as subroutine
-    vec3 LColor = textureLod(staticEnvMap, L, lod).rgb;
- 
-    // Since the sample is skewed towards the Distribution, we don't need
-    // to evaluate all of the factors for the lighting equation. Also note
-    // that this function is calculating the specular portion, so we absolutely
-    // do not add any more diffuse here.
-    fColor += F_ * G_ * LColor * VoH / (NoH * NoV);
-  }
- 
-  // Average the results
-  return fColor / float(NumSamples);
-}
- 
-vec3 main2()
-{
-  vec3 V = fragmentToEye;//normalize((-fragmentToEye * cameraPosVec).xyz);
-  vec3 color;
- 
-  // No object, instead display the environment.
-  /*if (depth() == 1.0)
-  {
-    color = textureLod(staticEnvMap, -V, 0.0).rgb;
-  }*/
- 
-  // Object, approximate the ambient light.
-  //else
-  //{
-    vec3 N = normalize((-fragmentToEye * normal).xyz);
-    vec3 L = normalize(-reflect(V, N));
-    float NoV = saturate(dot(N, V));
-    float NoL = saturate(dot(N, L));
- 
-    // Calculate different portions of the color
-    vec3 irrMap = textureLod(staticEnvMap, N, 0.0).rgb;
-    vec3 Kdiff  = irrMap /** baseColor()*/ / 3.14159265358979323846;
-    vec3 Kspec  = radiance(N, V);
- 
-    // Mix the materials
-    color = Kdiff;//BlendMaterial(Kdiff, Kspec);
-  //}
-	return color;
-  //fFragColor = vec4(color, 1.0);
-}
-
-void main(void) 
-{
-	// Calculate screen-space texture coordinates, for buffer access
-	vec2 texCoord = calcTexCoord();
-	
-	// Get diffuse color (full-bright) from diffuse buffer and gamma-correct it
-	vec3 diffuseColor = pow(texture(diffuseMap, texCoord).xyz, vec3(2.2));
-	// Get pixel's position in world space
-	vec3 worldPos = texture(positionMap, texCoord).xyz;
-	// Get normal (in world space) and normalize it to minimize floating point approximation errors
-	vec3 normal = normalize(texture(normalMap, texCoord).xyz);
-	// Get material properties
-	vec4 matProperties = texture(matPropertiesMap, texCoord).xyzw;
-	
-	// Extract roughness and metalness values
-	roughnessVar = matProperties.x;
-	metallic = matProperties.y;
-	
-	// Calculate view direction (fragment to eye vector)
-	fragmentToEye = normalize(cameraPosVec - worldPos);
-	
-	ref_at_norm_incidence = F0;
-	viewer = fragmentToEye;
-		
-	//fragmentToEye = normalize(worldPos - cameraPosVec);
-	//roughnessSqrt = roughnessVar;// * roughnessVar;
-
-	//k = 1.0 - positionAndGloss.w;
-	//roughnessVar = (normalAndSpecular.w + positionAndGloss.w) / 2.0;
-	roughnessSqrt = roughnessVar * roughnessVar;
-	
-	// ior = from 1.2 to 10.0
-	
-	//envColor = pow(textureLod(staticEnvMap, R, 0).xyz, vec3(2.2));
-	float ior = mix(1.5, 40.5, metallic);
-	ior = 4.5;
-	
-	F0 = abs((1.0 - ior) / (1.0 + ior));
-	F0 = F0 * F0;
-	F0vec = vec3(F0);
-	F0vec = mix(F0vec, diffuseColor, metallic);
-	
-	
-	//vec3 lightDir = -p_lightDirection;
-
-	//float spec = SpecGGX(p_normal, p_fragToEye, lightDir, roughnessVar, F0);
-	//vec3 lightDir = -normalize(directionalLight.m_direction);
-	//vec3 p_lightColor = vec3(1.0);
-	
-	//vec3 spec = SpecGGX(normal, fragmentToEye, R, roughnessVar, F0) * envColor;
-	
-    //float dif = dot(normal, lightDir);
-	//float dif = dot(p_normal, -p_lightDirection);
-	
-	// Fresnel
-    //float NdotV = clamp(dot(normal, fragmentToEye), 0.0, 1.0);
-	//NdotV = pow(1.0 - NdotV, 5.0);    
-	//float Fresnel = metallic + (1.0 - metallic) * (NdotV);
-
-    //vec3 H = normalize(fragmentToEye + R);
-    //float dotLH = saturate(dot(R,H));
-    //float dotLH5 = pow(1.0f - dotLH, 5);
-	//float F = F0 + (1.0 - F0) * (dotLH5);
-	//F = metallic + (1.0 - metallic) * F;
-	//vec3 F = F0vec + (vec3(1.0) - F0vec) * (dotLH5);
-	
-    // Tint lights
-    //vec3 SpecColor = spec * p_lightColor;
-    //vec3 DiffColor = dif * p_lightColor * (1.0 - Fresnel);
-	
-	
-    //float dif = dot(normal, R);
-    //vec3 DiffColor = dif * diffEnvColor * (1.0 - Fresnel);
-	
-	//envColor *= spec;
-	
-    // envColor = max(((/*diffuseColor*DiffColor +*/ envColor*spec)), vec3(0.0, 0.0, 0.0));
-		
-	// Fresnel
-    float NdotV = clamp(dot(normal, fragmentToEye), 0.0, 1.0);
-	NdotV = pow(1.0 - NdotV, 5.0);
-	float Fresnel = metallic + (1.0 - metallic) * (NdotV);
-	
-    //vec3 lightSum = max(((DiffColor + SpecColor)), vec3(0.0, 0.0, 0.0));
-	vec3 lightSum;
-	
-    vec3 I = normalize(worldPos - cameraPosVec);
-    vec3 R = normalize(reflect(I, normal));
-	
-	
-	vec3 diffEnvColor = (pow(textureLod(staticEnvMap, R, 9).xyz, vec3(2.2)) + 
-						pow(textureLod(staticEnvMap, R, 10).xyz, vec3(2.2)) +
-						pow(textureLod(staticEnvMap, R, 11).xyz, vec3(2.2))) / 3.0;
-						
-	int texLOD = 0;//int(10 * roughnessSqrt);
-	envColor = mix(pow(textureLod(staticEnvMap, R, 0).xyz, vec3(2.2)), pow(textureLod(staticEnvMap, R, 10).xyz, vec3(2.2)), 0.0);
-	
-	vec3 spec = min(LightingFuncGGX_REF(normal, fragmentToEye, R, roughnessSqrt, diffuseColor), 1.0);
-	
-    vec3 SpecColor = spec * envColor;
-    vec3 DiffColor = dot(normal, R) * diffEnvColor * (1.0 - Fresnel);
-		
-	vec3 Rnew = R;
-	
-	lightSum = main2();
-	
-	//Rnew = normalize(R + vec3(0.1, 0.0, 0.0));
-	//lightSum = calcLight(normal, fragmentToEye, pow(textureLod(staticEnvMap, Rnew, 0).xyz, vec3(2.2)), -R, vec3(F0), roughnessSqrt);
-	//lightSum = pow(textureLod(staticEnvMap, Rnew, 0).xyz, vec3(2.2)) * LightingFuncGGX_REF(normal, -I, Rnew, roughnessSqrt, F0vec);
-	//Rnew = normalize(R + vec3(0.08, 0.0, 0.0));
-	//lightSum += pow(textureLod(staticEnvMap, Rnew, 0).xyz, vec3(2.2)) * LightingFuncGGX_REF(normal, -I, Rnew, roughnessSqrt, F0vec);
-	//Rnew = normalize(R + vec3(-0.08, 0.0, 0.0));
-	//lightSum += pow(textureLod(staticEnvMap, Rnew, 0).xyz, vec3(2.2)) * LightingFuncGGX_REF(normal, -I, Rnew, roughnessSqrt, F0vec);
-	//Rnew = normalize(R + vec3(0.0, 0.08, 0.0));
-	//lightSum += pow(textureLod(staticEnvMap, Rnew, 0).xyz, vec3(2.2)) * LightingFuncGGX_REF(normal, -I, Rnew, roughnessSqrt, F0vec);
-	//Rnew = normalize(R + vec3(0.0, -0.08, 0.0));
-	//lightSum += pow(textureLod(staticEnvMap, Rnew, 0).xyz, vec3(2.2)) * LightingFuncGGX_REF(normal, -I, Rnew, roughnessSqrt, F0vec);
-	//Rnew = normalize(R + vec3(0.0, 0.0, 0.08));
-	//lightSum += pow(textureLod(staticEnvMap, Rnew, 0).xyz, vec3(2.2)) * LightingFuncGGX_REF(normal, -I, Rnew, roughnessSqrt, F0vec);
-	//Rnew = normalize(R + vec3(0.0, 0.0, -0.08));
-	//lightSum += pow(textureLod(staticEnvMap, Rnew, 0).xyz, vec3(2.2)) * LightingFuncGGX_REF(normal, -I, Rnew, roughnessSqrt, F0vec);
-	
-	//lightSum /= 6;
-	
-	//envColor += dif;
-	
-    //vec3 lightSum = max(((DiffColor + SpecColor)), vec3(0.0, 0.0, 0.0));
-    //float dotLH5 = pow(1.0f - dotLH, 5);
-    //F = F0 + (1.0-F0)*(dotLH5);
-	//F = F0 + (1.0f - F0) * (dotLH5);
-	
-    // Tint lights
-    //vec3 SpecColor = spec * p_lightColor;
-    //vec3 DiffColor = dif * p_lightColor * (1.0 - Fresnel);
-	//spec = max(((DiffColor + SpecColor)), vec3(0.0, 0.0, 0.0)) * directionalLight.m_intensity;
-	
-	//vec3 spec = LightingFuncGGX_REF(normal, fragmentToEye, -normalize(directionalLight.m_direction), roughnessSqrt, F0vec);
-	
-	//envColor = spec * pow(textureLod(staticEnvMap, R, 0).xyz, vec3(2.2));
-	//envColor = pow(textureLod(staticEnvMap, R, 5).xyz, vec3(2.2));
-	
-	//envColor = mix(pow(textureLod(staticEnvMap, R, 5).xyz, vec3(2.2)), pow(textureLod(staticEnvMap, R, 6).xyz, vec3(2.2)), 0.5);
-	
-	//float spec = SpecGGX(p_normal, p_fragToEye, R, roughnessVar, F0);
-	//envColor *= 
-	
-	//float reflectance = 0.5;
-	//F0vec = 0.16 * reflectance * reflectance * (1.0 - metallic) + diffuseColor * metallic;
-	
-	//float roughness = 1.0 - smoothness*smoothness;
-    //vec3 F0 = 0.16*reflectance*reflectance * (1.0-metalMask) + baseColor*metalMask;
-    //vec3 albedo = baseColor;
-	
-	//float3 F0 = abs ((1.0 - ior) / (1.0 + ior));
-	//F0 = F0 * F0;
-	//F0 = lerp(F0, materialColour.rgb, metallic);
-	
-	//k = 1.0 - normalAndSpecular.w;
-	//k = 0.2;
-	//roughnessVar = 0.1;// - ((positionAndGloss.w + normalAndSpecular.w) / 2.0);
-	
-	
-	// Declare final color of the fragment and add directional light to it
-	vec3 finalLightColor = calcLight(normal, fragmentToEye, directionalLight.m_color, normalize(directionalLight.m_direction), vec3(F0), roughnessSqrt) * directionalLight.m_intensity;// + directionalLight.m_color * 0.005;
-	//vec3 finalLightColor = vec3(0.0);
-	
-	for(int i = 0; i < numPointLights; i++)
-	{		
-		// Get light direction, extract length from it and normalize for usage as direction vector
-		vec3 lightDirection = worldPos - pointLights[i].m_position;
-		//vec3 lightDirection =  pointLights[i].m_position - worldPos;
-		float lightDistance = length(lightDirection);
-		lightDirection = normalize(lightDirection);
-		
-		// Add up constant, linear and quadratic attenuation
-		float attenuation = pointLights[i].m_attenConstant + 
-							pointLights[i].m_attenLinear * lightDistance + 
-							pointLights[i].m_attenQuad * lightDistance * lightDistance;
-						 
-		// Light color multiplied by intensity and divided by attenuation
-		
-		//finalLightColor += (calcLightColor(pointLights[i].m_color, lightDirection) * pointLights[i].m_intensity) / attenuation;
-		finalLightColor += (calcLight(normal, fragmentToEye, pointLights[i].m_color, lightDirection, vec3(F0), roughnessSqrt) * pointLights[i].m_intensity) / attenuation;
-		//finalLightColor += (computePBRLighting(pointLights[i].m_position, pointLights[i].m_color, worldPos, normal, fragmentToEye, diffuseColor, roughnessVar, F0vec) * pointLights[i].m_intensity) / attenuation;
-	}
-	
-	for(int i = 0; i < numSpotLights; i++)
-	{			
-		// Calculate direction from position of light to current pixel
-		vec3 lightToFragment = normalize(worldPos - spotLights[i].m_position);
-		
-		// Get dot product of light direction and direction of light to pixel, and use it as a factor for light strength
-		float spotLightFactor = dot(lightToFragment, spotLights[i].m_direction);
-		
-		// Early bail if pixel is outside of the cone of spot light
-		if(spotLightFactor > spotLights[i].m_cutoffAngle)
-		{
-			// Get light direction, extract length from it and normalize for usage as direction vector
-			vec3 lightDirection = worldPos - spotLights[i].m_position;
-			//vec3 lightDirection =  spotLights[i].m_position - worldPos;
-			float lightDistance = length(lightDirection);
-			lightDirection = normalize(lightDirection);
-			
-			// Add up constant, linear and quadratic attenuation
-			float attenuation = spotLights[i].m_attenConstant + 
-								spotLights[i].m_attenLinear * lightDistance + 
-								spotLights[i].m_attenQuad * lightDistance * lightDistance;
-			
-			// Light color multiplied by intensity
-			//finalLightColor += (calcLight(normal, fragmentToEye, pointLights[i].m_color, lightDirection, vec3(F0), roughnessSqrt) * pointLights[i].m_intensity) / attenuation;
-			vec3 lightColor = (calcLight(normal, fragmentToEye, spotLights[i].m_color, lightDirection, vec3(F0), roughnessSqrt) * spotLights[i].m_intensity);
-			//vec3 lightColor = (calcLightColor(spotLights[i].m_color, lightDirection) * spotLights[i].m_intensity);
-			
-			// Light restriction from cone
-			float coneAttenuation = (1.0 - (1.0 - spotLightFactor) * 1.0 / (1.0 - spotLights[i].m_cutoffAngle));
-			
-			finalLightColor += (lightColor / attenuation) * coneAttenuation;
-		}
-	}
-		
-	// Multiply the diffuse color by the amount of light the current pixel receives and gamma correct it
-	finalBuffer = pow(diffuseColor * finalLightColor, vec3(1.0 / 2.2));
-	//finalBuffer = vec3(roughnessVar, roughnessVar, roughnessVar);
-	//finalBuffer = vec4(texture(staticEnvMap, vec3(0.0, 0.0, 0.0)).xyz, 1.0);
-	//finalBuffer = vec4(metallic, metallic, metallic, 1.0);
-	//finalBuffer = vec4(pow(mix(diffuseColor, vec3(0.0, 0.0, 0.0), 1.0 - metallic) * finalLightColor, vec3(1.0 / 2.2)), 1.0);
-	//finalBuffer = vec4(pow(finalLightColor, vec3(1.0 / 2.2)), 1.0);
-	//finalBuffer = vec4(pow(diffuseColor, vec3(1.0 / 2.2)), 1.0);
-	//finalBuffer = vec4(texture(normalMap, texCoord).xyz, 1.0);
-	//finalBuffer = vec4(roughnessVar, roughnessVar, roughnessVar, 1.0);
-}

+ 4 - 0
Praxis3D/Data/Shaders/lightPass.frag

@@ -59,6 +59,7 @@ uniform sampler2D diffuseMap;
 uniform sampler2D normalMap;
 uniform sampler2D emissiveMap;
 uniform sampler2D matPropertiesMap;
+uniform sampler2DArray cascadedShadowMap;
 
 //uniform samplerCube staticEnvMap;
 
@@ -319,6 +320,9 @@ void main(void)
 		}
 	}
 	
+	float pcfDepth = texture(cascadedShadowMap, vec3(texCoord, 0)).r;
+	
 	colorBuffer = vec4(finalLightColor + emissiveColor, 1.0);
+	//colorBuffer = vec4(pcfDepth, pcfDepth, pcfDepth, 1.0);
 	//colorBuffer = vec4(matProperties.z, matProperties.z, matProperties.z, 1.0);
 }

+ 446 - 0
Praxis3D/Data/Shaders/lightPass_CSM.frag

@@ -0,0 +1,446 @@
+#version 430 core
+
+#define AVG_INTENDED_BRIGHTNESS 0.5
+#define MIN_INTENDED_BRIGHTNESS 0.001
+#define MAX_INTENDED_BRIGHTNESS 100.0
+
+#define MAX_NUM_POINT_LIGHTS 20
+#define MAX_NUM_SPOT_LIGHTS 10
+#define PI 3.1415926535
+
+#define NUM_OF_CASCADES 1
+#define NUM_OF_PCF_SAMPLES 16
+
+const float g_pcfSampleWeight = 1.0 / NUM_OF_PCF_SAMPLES;
+
+const vec3 g_sunNoonColor = vec3(1.0, 1.0, 1.0);
+const vec3 g_sunSetColor = vec3(1.0, 0.6, 0.2);
+
+const float g_sunNoonIntensityMod = 1.0;
+const float g_sunSetIntensityMod = 0.0;
+
+// Poisson Disk PCF sampling
+const vec2 poissonDisk[16] = vec2[]
+( 
+   vec2( -0.94201624, -0.39906216 ), 
+   vec2( 0.94558609, -0.76890725 ), 
+   vec2( -0.094184101, -0.92938870 ), 
+   vec2( 0.34495938, 0.29387760 ), 
+   vec2( -0.91588581, 0.45771432 ), 
+   vec2( -0.81544232, -0.87912464 ), 
+   vec2( -0.38277543, 0.27676845 ), 
+   vec2( 0.97484398, 0.75648379 ), 
+   vec2( 0.44323325, -0.97511554 ), 
+   vec2( 0.53742981, -0.47373420 ), 
+   vec2( -0.26496911, -0.41893023 ), 
+   vec2( 0.79197514, 0.19090188 ), 
+   vec2( -0.24188840, 0.99706507 ), 
+   vec2( -0.81409955, 0.91437590 ), 
+   vec2( 0.19984126, 0.78641367 ), 
+   vec2( 0.14383161, -0.14100790 ) 
+);
+
+layout(location = 0) out vec4 colorBuffer;
+
+in float avgBrightness;
+
+struct DirectionalLight
+{
+    vec3 m_color;
+    vec3 m_direction;
+    float m_intensity;
+};
+
+struct PointLight
+{
+    vec3 m_color;
+    vec3 m_position;
+	
+    float m_attenConstant;
+    float m_attenLinear;
+    float m_attenQuad;
+    float m_intensity;
+};
+struct SpotLight
+{
+    vec3 m_color;
+    vec3 m_position;
+    vec3 m_direction;
+	
+    float m_attenConstant;
+    float m_attenLinear;
+    float m_attenQuad;
+	
+    float m_intensity;
+    float m_cutoffAngle;
+};
+
+struct CascadedShadowMapDataSet
+{
+	mat4 m_lightSpaceMatrix;
+	
+	float m_cascadeplanedistance;
+	float m_maxBias;
+	float m_poissonSampleScale;
+	float m_penumbraScale;
+};
+
+uniform sampler2D positionMap;
+uniform sampler2D diffuseMap;
+uniform sampler2D normalMap;
+uniform sampler2D emissiveMap;
+uniform sampler2D matPropertiesMap;
+uniform sampler2DArray csmDepthMap;
+
+uniform mat4 lightMatrixTest;
+uniform mat4 modelViewMat;
+uniform mat4 viewMat;
+uniform vec3 cameraPosVec;
+uniform vec2 projPlaneRange; // x - zFar, y - zNear
+uniform vec2 csmPenumbraScaleRange;
+uniform ivec2 screenSize;
+uniform float gamma;
+
+uniform int numPointLights;
+uniform int numSpotLights;
+
+uniform float ambientLightIntensity;
+uniform DirectionalLight directionalLight;
+
+// Using uniform buffer objects to pass light arrays and std140 layout for consistent variable spacing inside the buffer.
+// Array size is fixed, but is updated partially, with only the lights that are being used, passing number of lights as uniform.
+layout (std140) uniform PointLights
+{
+	PointLight pointLights[MAX_NUM_POINT_LIGHTS];
+};
+layout (std140) uniform SpotLights
+{
+	SpotLight spotLights[MAX_NUM_SPOT_LIGHTS];
+};
+
+layout (std140) uniform CSMDataSetBuffer
+{
+	CascadedShadowMapDataSet m_csmDataSet[NUM_OF_CASCADES];
+};
+
+layout (std430, binding = 0) buffer HDRBuffer
+{
+	float screenBrightness;
+};
+
+vec2 calcTexCoord(void)
+{
+    return gl_FragCoord.xy / screenSize;
+}
+
+// Returns a random number based on a vec3 and an int.
+float calcRandom(vec3 p_seed, int p_variable)
+{
+	vec4 seed = vec4(p_seed, p_variable);
+	float dotProduct = dot(seed, vec4(12.9898, 78.233, 45.164, 94.673));
+	return fract(sin(dotProduct) * 43758.5453);
+}
+
+// Linear interpolation in [0 1] range
+float scaleLinear(float p_value, vec2 p_valueDomain) 
+{
+	return (p_value - p_valueDomain.x) / (p_valueDomain.y - p_valueDomain.x);
+}
+
+// Linear interpolation in the given range
+float scaleLinear(float p_value, vec2 p_valueDomain, vec2 p_valueRange) 
+{
+	return mix(p_valueRange.x, p_valueRange.y, scaleLinear(p_value, p_valueDomain));
+}
+
+float getBrightestColor(vec3 p_color)
+{
+	return max(p_color.x, max(p_color.y, p_color.z));
+}
+
+// Calculates a brightness value from a color
+float calcBrightness(vec3 p_color)
+{
+	return dot(p_color, vec3(0.2126, 0.7152, 0.0722));
+}
+
+// Adjusts an RGB color based on the average brightness (exposure)
+// by making overall brightness match the average intended brightness (AVG_INTENDED_BRIGHTNESS)
+vec3 brightnessMapping(vec3 p_color, float p_exposure)
+{
+	return p_color * clamp(AVG_INTENDED_BRIGHTNESS / p_exposure, MIN_INTENDED_BRIGHTNESS, MAX_INTENDED_BRIGHTNESS);
+}
+
+float saturate(float p_value)
+{
+	return clamp(p_value, 0.0f, 1.0f);
+}
+
+// Calculates microfacet distribution (based on roughness)
+// Using GGX normal distribution function
+float DistributionGGX(vec3 p_normal, vec3 p_halfVector, float p_roughness)
+{
+	float roughnessSquaredSquared = p_roughness * p_roughness * p_roughness * p_roughness;
+    float NdotH = max(dot(p_normal, p_halfVector), 0.0);
+    float NdotH2 = NdotH * NdotH;
+	
+    float denominator = (NdotH2 * (roughnessSquaredSquared - 1.0) + 1.0);
+    denominator = PI * denominator * denominator;
+	
+    return roughnessSquaredSquared / denominator;
+}
+
+
+// Geometry attenuation (Smith with Schlick's approximation)
+float GeometrySchlickGGX(float p_NdotV, float p_roughness)
+{
+    float roughness = (p_roughness + 1.0);
+    float k = (roughness * roughness) / 8.0;
+	
+    //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)
+float GeometrySmith(vec3 p_normal, vec3 p_fragToEye, vec3 L, float p_roughness)
+{
+    float NdotV = max(dot(p_normal, p_fragToEye), 0.0);
+    float NdotL = max(dot(p_normal, L), 0.0);
+    float ggx2  = GeometrySchlickGGX(NdotV, p_roughness);
+    float ggx1  = GeometrySchlickGGX(NdotL, p_roughness);
+	
+    return ggx1 * ggx2;
+}
+
+// Calculates fresnel effect using Schlick's approximation
+vec3 fresnelSchlick(float p_cosTheta, vec3 p_F0)
+{
+	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, float p_shadowFactor)
+{	
+	/*/ Get specular and diffuse lighting
+	vec3 specularColor = LightingFuncGGX_REF(p_normal, p_fragToEye, p_lightDirection, p_roughnessSqrt, p_F0);
+    vec3 diffuseColor = vec3(clamp(dot(p_normal, p_lightDirection), 0.0, 1.0));
+	
+	// Add specular and diffuse together, and multiply it by the color of the light
+	return (specularColor + diffuseColor * p_diffuseAmount) * p_lightColor;*/
+	
+	// Calculate per-light radiance
+	vec3 halfVector = normalize(p_fragToEye + p_lightDirection);
+	float attenuation = 1.0 / (p_lightDistance * p_lightDistance);
+	vec3 radiance = p_lightColor * attenuation;
+	
+	// Calculate Cook-Torrance BRDF
+	float NDF = DistributionGGX(p_normal, halfVector, p_roughness);
+	float G = GeometrySmith(p_normal, p_fragToEye, p_lightDirection, p_roughness);
+	vec3 F = fresnelSchlick(clamp(dot(halfVector, p_fragToEye), 0.0, 1.0), p_F0);
+	
+	vec3 kS = F;
+	vec3 kD = vec3(1.0) - kS;
+	kD *= 1.0 - p_metalic;
+	
+	vec3 numerator = NDF * G * F;
+	float denominator = 4.0 * max(dot(p_normal, p_fragToEye), 0.0) * max(dot(p_normal, p_lightDirection), 0.0);
+	vec3 specular = numerator / max(denominator, 0.001);
+	
+	// Combine diffuse, specular, radiance with albedo color and return it
+	float NdotL = max(dot(p_normal, p_lightDirection), 0.0);
+	
+	// Add light color
+	vec3 lightColor = (kD * p_albedoColor / PI + specular) * radiance * NdotL * p_shadowFactor;
+	
+	// Add ambient light
+	lightColor += radiance * p_ambientOcclusion * ambientLightIntensity * (p_albedoColor) * (1.0 - p_metalic);
+	
+	return lightColor;
+}
+
+float calcCascadedShadow(vec3 p_worldPos, vec3 p_normal, vec3 p_lightDirection)
+{
+    vec4 fragPosViewSpace = viewMat * vec4(p_worldPos, 1.0);
+    float depthValue = abs(fragPosViewSpace.z);
+
+    // Calculate cascade layer
+    int layer = NUM_OF_CASCADES;
+    for (int i = 0; i < NUM_OF_CASCADES; ++i)
+    {
+        if (depthValue < m_csmDataSet[i].m_cascadeplanedistance)
+        {
+            layer = i;
+            break;
+        }
+    }
+
+	// Get fragment position in light space
+    vec4 fragPosLightSpace = m_csmDataSet[layer].m_lightSpaceMatrix * vec4(p_worldPos, 1.0);
+	
+    // Perform perspective divide
+    vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
+	
+    // Transform to [0,1] range
+    projCoords = projCoords * 0.5 + 0.5;
+
+    // Get depth of current fragment from light's perspective
+    float currentDepth = projCoords.z;
+
+    // Keep the shadow at 0.0 when outside the far_plane region of the light's frustum.
+    if (currentDepth > 1.0)
+    {
+        return 0.0;
+    }
+	
+    // Calculate bias based on slope
+	float bias = 0.05 * tan(acos(dot(p_normal, p_lightDirection)));
+	bias = clamp(bias, 0.0, m_csmDataSet[layer].m_maxBias);
+	
+	// Calculate the poisson sampling scale
+	float samplingScale = scaleLinear(depthValue, vec2(projPlaneRange.y, projPlaneRange.x), csmPenumbraScaleRange) * m_csmDataSet[layer].m_penumbraScale;
+	
+	// Start the fragment as fully lit
+    float shadow = 1.0;
+	
+    // Perform PCF
+	for(int i = 0 ; i < NUM_OF_PCF_SAMPLES; i++)
+	{		
+		// A random sample, based on the pixel's position in world space.
+		// The position is rounded to the millimeter to avoid too much aliasing
+		int index = int(16.0 * calcRandom(floor(p_worldPos.xyz * 1000.0), i)) % 16;
+		
+		// Get the fragments depth from the depth map
+		float pcfDepth = texture(csmDepthMap, vec3(projCoords.xy + poissonDisk[index] / m_csmDataSet[layer].m_poissonSampleScale * samplingScale, layer)).r;
+		
+		// Check if the fragment is in the shadow (while applying bias)
+		shadow -= (currentDepth - bias) > pcfDepth ? g_pcfSampleWeight : 0.0;
+	}
+	
+    return shadow;
+}
+
+void main(void) 
+{
+	// Calculate screen-space texture coordinates, for buffer access
+	vec2 texCoord = calcTexCoord();
+	// Get the emissive texture color and convert it to linear space
+	vec3 emissiveColor = pow(texture(emissiveMap, texCoord).xyz, vec3(gamma));
+	// Get diffuse color (full-bright) from diffuse buffer and convert it to linear space
+	vec3 diffuseColor = pow(texture(diffuseMap, texCoord).xyz, vec3(gamma));
+	// Get pixel's position in world space
+	vec3 worldPos = texture(positionMap, texCoord).xyz;
+	// Get normal (in world space) and normalize it to minimize floating point approximation errors
+	vec3 normal = normalize(texture(normalMap, texCoord).xyz);
+	// Get material properties
+	vec4 matProperties = texture(matPropertiesMap, texCoord).xyzw;
+	// Calculate view direction (fragment to eye vector)
+	vec3 fragmentToEye = normalize(cameraPosVec - worldPos);
+	
+	// Extract roughness, metalness and ambient occlusion values
+	float roughnessSqrt = matProperties.x;
+	float metalic = matProperties.y;
+	float ambientOcclusion = matProperties.z;
+
+	// Calculate F0, with minimum IOR as 0.04
+	vec3 f0 = mix(vec3(0.04), diffuseColor, metalic);
+	
+	// Normalize direction light direction vector
+	vec3 dirLightDirection =  normalize(directionalLight.m_direction);
+	
+	// Get dot product between the up vector (perpendicular to the ground) and directional light direction
+	float dirLightFactor = dot(dirLightDirection, vec3(0.0, 1.0, 0.0)) + 0.03;
+	
+	// Initialize final color variable of this fragment
+	vec3 finalLightColor = vec3(0.0);
+	
+	// Calculate directional light only if it is pointing from above the horizon
+	if(dirLightFactor > 0.0)
+	{
+		float dirLightFactorSqrt = dirLightFactor;//sqrt(dirLightFactor);
+		vec3 dirLightColor = mix(g_sunSetColor, g_sunNoonColor, dirLightFactorSqrt);
+		float dirLightIntensity = directionalLight.m_intensity * mix(g_sunSetIntensityMod, g_sunNoonIntensityMod, dirLightFactorSqrt);
+	
+		// Add ambient lighting
+		//finalLightColor += diffuseColor * dirLightColor * dirLightIntensity * ambientLightIntensity * ambientOcclusion * (1.0 - metalic * metalic);
+	
+		float shadowFactor = calcCascadedShadow(worldPos, normal, dirLightDirection);
+		
+		// Add directional lighting
+		finalLightColor += calcLightColor(
+			diffuseColor, 
+			normal, 
+			fragmentToEye, 
+			dirLightColor, 
+			dirLightDirection, 
+			1.0, 
+			f0, 
+			roughnessSqrt, 
+			metalic,
+			ambientOcclusion,
+			shadowFactor) * dirLightIntensity;// * min(1.0, (dirLightFactor /* 100.0*/) + 0.02);
+	}
+		
+	for(int i = 0; i < numPointLights; i++)
+	{		
+		// Get light direction, extract length from it and normalize for usage as direction vector
+		vec3 lightDirection =  pointLights[i].m_position - worldPos;
+		float lightDistance = length(lightDirection);
+		lightDirection = normalize(lightDirection);
+		
+		// Light color multiplied by intensity and divided by attenuation
+		finalLightColor += (calcLightColor(
+			diffuseColor, 
+			normal, 
+			fragmentToEye, 
+			pointLights[i].m_color, 
+			lightDirection, 
+			lightDistance, 
+			f0, 
+			roughnessSqrt, 
+			metalic, 
+			ambientOcclusion,
+			1.0) * pointLights[i].m_intensity);
+	}
+	
+	for(int i = 0; i < numSpotLights; i++)
+	{			
+		// Calculate direction from position of light to current pixel
+		vec3 lightToFragment = normalize(worldPos - spotLights[i].m_position);
+		
+		// Get dot product of light direction and direction of light to pixel, and use it as a factor for light strength
+		float spotLightFactor = dot(lightToFragment, spotLights[i].m_direction);
+		
+		// Early bail if pixel is outside of the cone of spot light
+		if(spotLightFactor > spotLights[i].m_cutoffAngle)
+		{
+			// Get light direction, extract length from it and normalize for usage as direction vector
+			vec3 lightDirection =  spotLights[i].m_position - worldPos;
+			float lightDistance = length(lightDirection);
+			lightDirection = normalize(lightDirection);
+			
+			// Light color multiplied by intensity
+			vec3 lightColor = (calcLightColor(
+				diffuseColor, 
+				normal, 
+				fragmentToEye, 
+				spotLights[i].m_color, 
+				lightDirection, 
+				lightDistance, 
+				f0, 
+				roughnessSqrt, 
+				metalic, 
+				ambientOcclusion,
+				1.0) * spotLights[i].m_intensity);
+			
+			// Light restriction from cone
+			float coneAttenuation = (1.0 - (1.0 - spotLightFactor) * 1.0 / (1.0 - spotLights[i].m_cutoffAngle));
+			
+			finalLightColor += lightColor * coneAttenuation;
+		}
+	}
+			
+	colorBuffer = vec4(finalLightColor + emissiveColor, 1.0);
+}

+ 1 - 1
Praxis3D/Data/Shaders/lightPass - Copy.vert → Praxis3D/Data/Shaders/lightPass_CSM.vert

@@ -18,5 +18,5 @@ void main(void)
 	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.0, 1.0);
+	gl_Position = vec4(texCoord * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 1.0, 1.0);
 }

+ 6 - 0
Praxis3D/Data/Shaders/shadowMapping.frag

@@ -0,0 +1,6 @@
+#version 430 core
+
+void main()
+{             
+    // gl_FragDepth = gl_FragCoord.z;
+}

+ 12 - 0
Praxis3D/Data/Shaders/shadowMapping.vert

@@ -0,0 +1,12 @@
+#version 430 core
+
+// Mesh buffers
+layout(location = 0) in vec3 vertexPosition;
+
+uniform mat4 lightMatrixTest;
+uniform mat4 modelMat;
+
+void main()
+{
+    gl_Position = lightMatrixTest * modelMat * vec4(vertexPosition, 1.0);
+}

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

@@ -40,6 +40,8 @@
 		"Shader_creation_failed"						: "Shader handle creation has failed",
 		"Shader_link_failed"								: "Shader linking has failed",
 		"Shader_loading_failed"							: "Shader has failed to load",
+		"Shader_source_empty"								: "Shader source code is not present",
+		"Shader_variable_not_found"					: "Shader variable was not found",
 		"GameObjects_missing"								: "GameObjects missing from the map file",
 		"Number_of_meshes_missmatch"				: "Number of meshes is bigger than a mesh property array",
 		"Texture_not_found"									: "Texture was not found",
@@ -108,6 +110,7 @@
 		"Source_ScriptObject"							: "Script Object",
 		"Source_ShaderComponent"					: "Shader Component",
 		"Source_ShaderLoader"							: "Shader Loader",
+		"Source_ShadowMappingPass"				: "Shadow Mapping Pass",
 		"Source_SkyObject"								: "Sky Object",
 		"Source_SkyPass"									: "Sky Rendering Pass",
 		"Source_SoundComponent"						: "Sound Component",

+ 2 - 0
Praxis3D/Praxis3D.vcxproj

@@ -257,6 +257,7 @@
     <ClInclude Include="Source\Config.h" />
     <ClInclude Include="Source\ConfigLoader.h" />
     <ClInclude Include="Source\Containers.h" />
+    <ClInclude Include="Source\CSMFramebuffer.h" />
     <ClInclude Include="Source\DebugMoveScript.h" />
     <ClInclude Include="Source\DebugRotateScript.h" />
     <ClInclude Include="Source\DebugUIScript.h" />
@@ -316,6 +317,7 @@
     <ClInclude Include="Source\ModelComponent.h" />
     <ClInclude Include="Source\ModelLoader.h" />
     <ClInclude Include="Source\ModelGraphicsObjects.h" />
+    <ClInclude Include="Source\ShadowMappingPass.h" />
     <ClInclude Include="Source\SoundComponent.h" />
     <ClInclude Include="Source\NullObjects.h" />
     <ClInclude Include="Source\NullSystemObjects.h" />

+ 6 - 0
Praxis3D/Praxis3D.vcxproj.filters

@@ -953,6 +953,12 @@
     <ClInclude Include="Source\AmbientOcclusionPass.h">
       <Filter>Renderer\Render Passes\Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="Source\ShadowMappingPass.h">
+      <Filter>Renderer\Render Passes\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Source\CSMFramebuffer.h">
+      <Filter>Renderer\Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="Data\config.ini" />

+ 1 - 1
Praxis3D/Source/AmbientOcclusionPass.h

@@ -29,7 +29,7 @@ public:
 	{
 		ErrorCode returnError = ErrorCode::Success;
 
-		m_name = "SSAO Rendering Pass";
+		m_name = "Ambient Occlusion Rendering Pass";
 
 		// Create the HBAO pass shader
 		{

+ 140 - 0
Praxis3D/Source/CSMFramebuffer.h

@@ -0,0 +1,140 @@
+#pragma once
+
+#include <vector>
+
+#include "Config.h"
+#include "ErrorCodes.h"
+#include "Framebuffer.h"
+
+class CSMFramebuffer : public Framebuffer
+{
+public:
+	CSMFramebuffer(const UniformFrameData &p_frameData) : Framebuffer(p_frameData.m_shadowMappingData.m_csmResolution, p_frameData.m_shadowMappingData.m_csmResolution)
+	{
+		m_depthBuffers = 0;
+		m_numOfCascades = 0;
+	}
+	~CSMFramebuffer()
+	{
+		if(m_depthBuffers != 0)
+			glDeleteTextures(1, &m_depthBuffers);
+	}
+
+	// Generates buffers, set's up FBO
+	ErrorCode init(const UniformFrameData &p_frameData)
+	{
+		ErrorCode returnCode = ErrorCode::Success;
+
+		if(!initialized())
+		{
+			m_numOfCascades = int(p_frameData.m_shadowMappingData.m_shadowCascadePlaneDistances.size());
+
+			// Create the FBO
+			glGenFramebuffers(1, &m_FBO);
+			glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_FBO);
+
+			glGenTextures(1, &m_depthBuffers);
+			createDepthBuffers();
+
+			glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+			glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+			glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+			glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+
+			constexpr float bordercolor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
+			glTexParameterfv(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BORDER_COLOR, bordercolor);
+
+			glBindFramebuffer(GL_FRAMEBUFFER, m_FBO);
+			glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, m_depthBuffers, 0);
+			glDrawBuffer(GL_NONE);
+			glReadBuffer(GL_NONE);
+			
+			// Check for errors and return an error in case of one
+			m_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+			if(m_status != GL_FRAMEBUFFER_COMPLETE)
+				returnCode = ErrorCode::Geometrybuffer_failed;
+
+			GLenum glError = glGetError();
+			if(glError != GL_NO_ERROR)
+			{
+				std::cout << "csm framebuffer gl error" << std::endl;
+			}
+		}
+		return returnCode;
+	}
+
+	// Set the size of all buffers
+	virtual void setBufferSize(unsigned int p_bufferWidth, unsigned int p_bufferHeight)
+	{
+		m_bufferWidth = p_bufferWidth;
+		m_bufferHeight = p_bufferHeight;
+		createDepthBuffers();
+	}	
+	
+	void setBufferSizeAndNumOfCascades(unsigned int p_bufferWidth, unsigned int p_bufferHeight, unsigned int p_numOfCascades)
+	{
+		m_bufferWidth = p_bufferWidth;
+		m_bufferHeight = p_bufferHeight;
+		m_numOfCascades = int(p_numOfCascades);
+		createDepthBuffers();
+	}
+
+	// Set the size of an individual buffer
+	virtual void setBufferSize(GLuint p_buffer, unsigned int p_bufferWidth, unsigned int p_bufferHeight)
+	{
+		switch(p_buffer)
+		{
+			case CSMBufferTextureType::CSMBufferTextureType_CSMDepthMap:
+				m_bufferWidth = p_bufferWidth;
+				m_bufferHeight = p_bufferHeight;
+				createDepthBuffers();
+				break;
+		}
+	}
+
+	void setNumOfCascades(unsigned int p_numOfCascades)
+	{
+		if(m_numOfCascades != p_numOfCascades)
+		{
+			m_numOfCascades = int(p_numOfCascades);
+			createDepthBuffers();
+		}
+	}
+
+	virtual void initFrame()
+	{
+		glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_FBO);
+		glViewport(0, 0, m_bufferWidth, m_bufferHeight);
+		glDepthMask(GL_TRUE);
+		glEnable(GL_DEPTH_TEST);		// Enable depth testing, as this is much like a regular forward render pass
+		glClear(GL_DEPTH_BUFFER_BIT);	// Make sure to clear the depth buffer for the new frame
+		glDisable(GL_CULL_FACE);
+		//glDepthFunc(GL_LEQUAL);
+		//glCullFace(GL_FRONT);			// peter panning
+	}
+
+	// Buffer binding functions
+	inline void bindBufferForReading(CSMBufferTextureType p_buffer, int p_activeTexture = 0)
+	{
+		glActiveTexture(GL_TEXTURE0 + p_activeTexture);
+		switch(p_buffer)
+		{
+			case CSMBufferTextureType::CSMBufferTextureType_CSMDepthMap:
+				glBindTexture(GL_TEXTURE_2D_ARRAY, m_depthBuffers);
+				break;
+		}
+	}
+
+private:
+	inline void createDepthBuffers()
+	{
+		glBindTexture(GL_TEXTURE_2D_ARRAY, m_depthBuffers);
+		glTexImage3D(
+			GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT32F, m_bufferWidth, m_bufferHeight, m_numOfCascades,
+			0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
+	}
+
+	GLuint m_depthBuffers;
+
+	int m_numOfCascades;
+};

+ 17 - 5
Praxis3D/Source/CommonDefinitions.h

@@ -73,6 +73,7 @@ enum MemoryBarrierType : unsigned int
 };
 #define RENDER_PASS_TYPE(Code) \
 	Code(RenderPassType_Geometry, = 0) \
+	Code(RenderPassType_ShadowMapping,) \
 	Code(RenderPassType_Lighting,) \
 	Code(RenderPassType_AtmScattering,) \
 	Code(RenderPassType_HdrMapping,) \
@@ -260,14 +261,20 @@ enum TextureWrapType : int
 };
 enum UniformBufferBinding : unsigned int
 {
-	UniformBufferBinding_PointLights,
+	UniformBufferBinding_PointLights = 0,
 	UniformBufferBinding_SpotLights,
 	UniformBufferBinding_AtmScatParam,
 	UniformBufferBinding_LensFlareParam,
 	UniformBufferBinding_AODataSet,
-	UniformBufferBinding_SSAOSampleBuffer
+	UniformBufferBinding_SSAOSampleBuffer,
+	UniformBufferBinding_CSMMatrixBuffer
 };
 
+enum FramebufferType : unsigned int
+{
+	FramebufferType_GBuffer = 0,
+	FramebufferType_CSMBuffer
+};
 enum GBufferTextureType : unsigned int
 {
 	GBufferPosition,
@@ -278,10 +285,15 @@ enum GBufferTextureType : unsigned int
 	GBufferFinal,
 	GBufferNumTextures = GBufferFinal,
 	GBufferIntermediate,
-	GBufferTotalNumTextures,
-	GBufferInputTexture = GBufferTotalNumTextures,
+	GBufferInputTexture,
 	GbufferOutputTexture,
-	GbufferDepth
+	GbufferDepth,
+	GBufferTotalNumTextures
+};
+
+enum CSMBufferTextureType : unsigned int
+{
+	CSMBufferTextureType_CSMDepthMap = GBufferTextureType::GBufferTotalNumTextures
 };
 
 enum AmbientOcclusionType : int

+ 21 - 0
Praxis3D/Source/Config.cpp

@@ -176,6 +176,10 @@ void Config::init()
 	AddVariablePredef(m_graphicsVar, ao_ssao_intensity);
 	AddVariablePredef(m_graphicsVar, ao_hbao_radius);
 	AddVariablePredef(m_graphicsVar, ao_ssao_radius);
+	AddVariablePredef(m_graphicsVar, bloom_intensity);
+	AddVariablePredef(m_graphicsVar, bloom_knee);
+	AddVariablePredef(m_graphicsVar, bloom_threshold);
+	AddVariablePredef(m_graphicsVar, bloom_dirt_intensity);
 	AddVariablePredef(m_graphicsVar, emissive_multiplier);
 	AddVariablePredef(m_graphicsVar, emissive_threshold);
 	AddVariablePredef(m_graphicsVar, eye_adaption_rate);
@@ -367,6 +371,9 @@ 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, csm_pass_frag_shader);
+	AddVariablePredef(m_rendererVar, csm_pass_geom_shader);
+	AddVariablePredef(m_rendererVar, csm_pass_vert_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);
@@ -377,6 +384,8 @@ void Config::init()
 	AddVariablePredef(m_rendererVar, lense_flare_comp_pass_frag_shader);
 	AddVariablePredef(m_rendererVar, lense_flare_pass_vert_shader);
 	AddVariablePredef(m_rendererVar, lense_flare_pass_frag_shader);
+	AddVariablePredef(m_rendererVar, light_pass_csm_vert_shader);
+	AddVariablePredef(m_rendererVar, light_pass_csm_frag_shader);
 	AddVariablePredef(m_rendererVar, light_pass_vert_shader);
 	AddVariablePredef(m_rendererVar, light_pass_frag_shader);
 	AddVariablePredef(m_rendererVar, final_pass_vert_shader);
@@ -392,12 +401,18 @@ void Config::init()
 	AddVariablePredef(m_rendererVar, ssao_blur_vert_shader);
 	AddVariablePredef(m_rendererVar, ssao_pass_frag_shader);
 	AddVariablePredef(m_rendererVar, ssao_pass_vert_shader);
+	AddVariablePredef(m_rendererVar, csm_max_shadow_bias);
+	AddVariablePredef(m_rendererVar, csm_penumbra_size);
+	AddVariablePredef(m_rendererVar, csm_penumbra_size_scale_min);
+	AddVariablePredef(m_rendererVar, csm_penumbra_size_scale_max);
 	AddVariablePredef(m_rendererVar, dir_light_quad_offset_x);
 	AddVariablePredef(m_rendererVar, dir_light_quad_offset_y);
 	AddVariablePredef(m_rendererVar, dir_light_quad_offset_z);
 	AddVariablePredef(m_rendererVar, dir_light_quad_rotation_x);
 	AddVariablePredef(m_rendererVar, dir_light_quad_rotation_y);
 	AddVariablePredef(m_rendererVar, dir_light_quad_rotation_z);
+	AddVariablePredef(m_rendererVar, csm_num_of_pcf_samples);
+	AddVariablePredef(m_rendererVar, csm_resolution);
 	AddVariablePredef(m_rendererVar, depth_test_func);
 	AddVariablePredef(m_rendererVar, face_culling_mode);
 	AddVariablePredef(m_rendererVar, heightmap_combine_channel);
@@ -444,6 +459,7 @@ void Config::init()
 	AddVariablePredef(m_shaderVar, texelSize);
 	AddVariablePredef(m_shaderVar, numOfTexels);
 	AddVariablePredef(m_shaderVar, mipLevel);
+	AddVariablePredef(m_shaderVar, projPlaneRange);
 	AddVariablePredef(m_shaderVar, ambientLightIntensity);
 	AddVariablePredef(m_shaderVar, dirLightColor);
 	AddVariablePredef(m_shaderVar, dirLightDirection);
@@ -470,6 +486,8 @@ void Config::init()
 	AddVariablePredef(m_shaderVar, depthMapUniform);
 	AddVariablePredef(m_shaderVar, inputColorMapUniform);
 	AddVariablePredef(m_shaderVar, outputColorMapUniform);
+	AddVariablePredef(m_shaderVar, csmDepthMapUniform);
+	AddVariablePredef(m_shaderVar, csmPenumbraScaleRange);
 	AddVariablePredef(m_shaderVar, sunGlowTextureUniform);
 	AddVariablePredef(m_shaderVar, skyMapTextureUniform);
 	AddVariablePredef(m_shaderVar, dirShadowMapTextureUniform);
@@ -509,12 +527,15 @@ void Config::init()
 	AddVariablePredef(m_shaderVar, eyeAdaptionIntBrightnessUniform);
 	AddVariablePredef(m_shaderVar, AODataSetBuffer);
 	AddVariablePredef(m_shaderVar, SSAOSampleBuffer);
+	AddVariablePredef(m_shaderVar, CSMDataSetBuffer);
 	AddVariablePredef(m_shaderVar, HDRSSBuffer);
 	AddVariablePredef(m_shaderVar, atmScatParamBuffer);
 	AddVariablePredef(m_shaderVar, lensFlareParametersBuffer);
 	AddVariablePredef(m_shaderVar, testMatUniform);
 	AddVariablePredef(m_shaderVar, testVecUniform);
 	AddVariablePredef(m_shaderVar, testFloatUniform);
+	AddVariablePredef(m_shaderVar, define_numOfCascades);
+	AddVariablePredef(m_shaderVar, define_numOfPCFSamples);
 
 	// Texture variables
 	AddVariablePredef(m_textureVar, default_texture);

+ 57 - 2
Praxis3D/Source/Config.h

@@ -37,6 +37,7 @@ enum DataType : uint32_t
 	DataType_GUIPassFunctors,			// FunctorSequence
 	DataType_RenderToTexture,			// bool
 	DataType_RenderToTextureResolution, // glm::ivec2
+	DataType_ShadowMappingData,			// ShadowMappingData
 	DataType_LoadShader,				// ShaderLoader::ShaderProgram
 	DataType_LoadTexture2D,				// TextureLoader2D::Texture2DHandle
 	DataType_UnloadTexture2D,			// TextureLoader2D::Texture2DHandle
@@ -385,10 +386,12 @@ namespace Properties
 	Code(AmbientOcclusion, ) \
 	Code(Attenuation,) \
 	Code(Bias,) \
+	Code(BiasMax,) \
 	Code(BlurSamples,) \
 	Code(BlurSharpness,) \
 	Code(Camera,) \
 	Code(CameraComponent,) \
+	Code(Cascades,) \
 	Code(ClampToBorder,) \
 	Code(ClampToEdge,) \
 	Code(Color,) \
@@ -399,6 +402,8 @@ namespace Properties
 	Code(Direction,) \
 	Code(Directions,) \
 	Code(DirectionalLight,) \
+	Code(Distance,) \
+	Code(Divider,) \
 	Code(Emissive,) \
 	Code(EmissiveIntensity,) \
 	Code(EnvironmentMapDynamic,) \
@@ -427,6 +432,10 @@ namespace Properties
 	Code(NegativeZ,) \
 	Code(Normal,) \
 	Code(ParallaxHeightScale,) \
+	Code(PenumbraScale,) \
+	Code(PenumbraSize,) \
+	Code(PenumbraScaleRange,) \
+	Code(PCF,) \
 	Code(PointLight,) \
 	Code(PointLightPoolSize,) \
 	Code(PositiveX,) \
@@ -437,6 +446,7 @@ namespace Properties
 	Code(Rendering,) \
 	Code(RenderPasses,) \
 	Code(Repeat,) \
+	Code(Resolution,) \
 	Code(RMHAO,) \
 	Code(Roughness,) \
 	Code(Samples,) \
@@ -445,6 +455,7 @@ namespace Properties
 	Code(ShaderPoolSize,) \
 	Code(ShaderGraphicsObject,) \
 	Code(ShaderModelObject,) \
+	Code(ShadowMapping,) \
 	Code(SpotLight,) \
 	Code(SpotLightPoolSize,) \
 	Code(SSAO,) \
@@ -456,17 +467,20 @@ namespace Properties
 	Code(TextureScale,) \
 	Code(VertexShader,) \
 	Code(WrapMode,) \
+	Code(ZClipping,) \
 	Code(ZFar,) \
 	Code(ZNear,) \
+	Code(ZPlaneMultiplier,) \
 	/* Graphics rendering passes */ \
 	Code(AmbientOcclusionRenderPass,) \
 	Code(AtmScatteringRenderPass,) \
 	Code(BloomRenderPass,) \
+	Code(FinalRenderPass,) \
 	Code(GeometryRenderPass,) \
 	Code(GUIRenderPass,) \
 	Code(LightingRenderPass,) \
 	Code(LuminanceRenderPass,) \
-	Code(FinalRenderPass,) \
+	Code(ShadowMappingPass,) \
 	/* GUI */ \
 	Code(EditorWindow,) \
 	Code(GUI,) \
@@ -969,6 +983,8 @@ public:
 		int bloom_mipmap_limit;
 		int current_resolution_x;
 		int current_resolution_y;
+		//int csm_num_of_levels;
+		//int csm_resolution;
 		int dir_shadow_res_x;
 		int dir_shadow_res_y;
 		int lens_flare_blur_passes;
@@ -1044,7 +1060,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 = 270.0f;
+			editor_render_pass_max_height = 275.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;
@@ -1320,6 +1336,9 @@ public:
 			bloom_upscale_comp_shader = "bloomUpscale.comp";
 			blur_pass_vert_shader = "blurPass.vert";
 			blur_pass_frag_shader = "blurPass.frag";
+			csm_pass_frag_shader = "csmPass.frag";
+			csm_pass_geom_shader = "csmPass.geom";
+			csm_pass_vert_shader = "csmPass.vert";
 			hbao_blur_horizontal_frag_shader = "ambientOcclusionBlurHBAOhorizontal.frag";
 			hbao_blur_horizontal_vert_shader = "ambientOcclusionBlurHBAO.vert";
 			hbao_blur_vertical_frag_shader = "ambientOcclusionBlurHBAOvertical.frag";
@@ -1330,6 +1349,8 @@ public:
 			lense_flare_comp_pass_frag_shader = "lenseFlareCompositePass.frag";
 			lense_flare_pass_vert_shader = "lenseFlarePass.vert";
 			lense_flare_pass_frag_shader = "lenseFlarePass.frag";
+			light_pass_csm_vert_shader = "lightPass_CSM.vert";
+			light_pass_csm_frag_shader = "lightPass_CSM.frag";
 			light_pass_vert_shader = "lightPass.vert";
 			light_pass_frag_shader = "lightPass.frag";
 			final_pass_vert_shader = "finalPass.vert";
@@ -1345,12 +1366,18 @@ public:
 			ssao_blur_vert_shader = "ambientOcclusionBlurSSAO.vert";
 			ssao_pass_frag_shader = "ambientOcclusionPassSSAO.frag";
 			ssao_pass_vert_shader = "ambientOcclusionPassSSAO.vert";
+			csm_max_shadow_bias = 0.0005f;
+			csm_penumbra_size = 1.42f;
+			csm_penumbra_size_scale_min = 1.0f;
+			csm_penumbra_size_scale_max = 2000.0f;
 			dir_light_quad_offset_x = 0.0f;
 			dir_light_quad_offset_y = 0.0f;
 			dir_light_quad_offset_z = 0.0f;
 			dir_light_quad_rotation_x = 180.0f;
 			dir_light_quad_rotation_y = 0.0f;
 			dir_light_quad_rotation_z = 0.0f;
+			csm_num_of_pcf_samples = 16;
+			csm_resolution = 2048;
 			depth_test_func = GL_LESS;
 			face_culling_mode = GL_BACK;
 			heightmap_combine_channel = 3;
@@ -1401,6 +1428,9 @@ public:
 		std::string bloom_upscale_comp_shader;
 		std::string blur_pass_vert_shader;
 		std::string blur_pass_frag_shader;
+		std::string csm_pass_frag_shader;
+		std::string csm_pass_geom_shader;
+		std::string csm_pass_vert_shader;
 		std::string hbao_blur_horizontal_frag_shader;
 		std::string hbao_blur_horizontal_vert_shader;
 		std::string hbao_blur_vertical_frag_shader;
@@ -1411,6 +1441,8 @@ public:
 		std::string lense_flare_comp_pass_frag_shader;
 		std::string lense_flare_pass_vert_shader;
 		std::string lense_flare_pass_frag_shader;
+		std::string light_pass_csm_vert_shader;
+		std::string light_pass_csm_frag_shader;
 		std::string light_pass_vert_shader;
 		std::string light_pass_frag_shader;
 		std::string final_pass_vert_shader;
@@ -1426,12 +1458,18 @@ public:
 		std::string ssao_blur_vert_shader;
 		std::string ssao_pass_frag_shader;
 		std::string ssao_pass_vert_shader;
+		float csm_max_shadow_bias;
+		float csm_penumbra_size;
+		float csm_penumbra_size_scale_min;
+		float csm_penumbra_size_scale_max;
 		float dir_light_quad_offset_x;
 		float dir_light_quad_offset_y;
 		float dir_light_quad_offset_z;
 		float dir_light_quad_rotation_x;
 		float dir_light_quad_rotation_y;
 		float dir_light_quad_rotation_z;
+		int csm_num_of_pcf_samples;
+		int csm_resolution;
 		int depth_test_func;
 		int face_culling_mode;
 		int heightmap_combine_channel;
@@ -1489,6 +1527,7 @@ public:
 			texelSize = "texelSize";
 			numOfTexels = "numOfTexels";
 			mipLevel = "mipLevel";
+			projPlaneRange = "projPlaneRange";
 
 			ambientLightIntensity = "ambientLightIntensity";
 			dirLightColor = "directionalLight.m_color";
@@ -1521,6 +1560,9 @@ public:
 			inputColorMapUniform = "inputColorMap";
 			outputColorMapUniform = "outputColorMap";
 
+			csmDepthMapUniform = "csmDepthMap";
+			csmPenumbraScaleRange = "csmPenumbraScaleRange";
+
 			sunGlowTextureUniform = "sunGlowMap";
 			skyMapTextureUniform = "skyMap";
 			dirShadowMapTextureUniform = "dirShadowMap";
@@ -1570,6 +1612,7 @@ public:
 			eyeAdaptionIntBrightnessUniform = "eyeAdaptionIntBrightness";
 			AODataSetBuffer = "AODataSetBuffer";
 			SSAOSampleBuffer = "SSAOSampleBuffer";
+			CSMDataSetBuffer = "CSMDataSetBuffer";
 			HDRSSBuffer = "HDRBuffer";
 			atmScatParamBuffer = "AtmScatParametersBuffer";
 			lensFlareParametersBuffer = "LensFlareParametersBuffer";
@@ -1577,6 +1620,9 @@ public:
 			testMatUniform = "testMat";
 			testVecUniform = "testVec";
 			testFloatUniform = "testFloat";
+
+			define_numOfCascades = "NUM_OF_CASCADES";
+			define_numOfPCFSamples = "NUM_OF_PCF_SAMPLES";
 		}
 
 		std::string atmScatProjMatUniform;
@@ -1604,6 +1650,7 @@ public:
 		std::string texelSize;
 		std::string numOfTexels;
 		std::string mipLevel;
+		std::string projPlaneRange;
 
 		std::string ambientLightIntensity;
 		std::string dirLightColor;
@@ -1635,6 +1682,9 @@ public:
 		std::string depthMapUniform;
 		std::string inputColorMapUniform;
 		std::string outputColorMapUniform;
+		
+		std::string csmDepthMapUniform;
+		std::string csmPenumbraScaleRange;
 
 		std::string sunGlowTextureUniform;
 		std::string skyMapTextureUniform;
@@ -1685,6 +1735,7 @@ public:
 		std::string eyeAdaptionIntBrightnessUniform;
 		std::string AODataSetBuffer;
 		std::string SSAOSampleBuffer;
+		std::string CSMDataSetBuffer;
 		std::string HDRSSBuffer;
 		std::string atmScatParamBuffer;
 		std::string lensFlareParametersBuffer;
@@ -1692,6 +1743,10 @@ public:
 		std::string testMatUniform;
 		std::string testVecUniform;
 		std::string testFloatUniform;
+
+		// Shader #define variable names
+		std::string define_numOfCascades;
+		std::string define_numOfPCFSamples;
 	};
 	struct TextureVariables
 	{

+ 46 - 0
Praxis3D/Source/Containers.h

@@ -303,4 +303,50 @@ struct AmbientOcclusionData
 	float m_aoRadius;
 	float m_aoBlurSharpness;
 	AmbientOcclusionType m_aoType;
+};
+
+// Stores a single CSM cascade settings
+struct ShadowCascadeData
+{
+	ShadowCascadeData() : m_cascadeFarDistance(1.0f), m_maxBias(Config::rendererVar().csm_max_shadow_bias), m_distanceIsDivider(true), m_penumbraScale(1.0f) { }
+	ShadowCascadeData(const float p_cascadeDistance, const bool p_distanceIsDivider, const float p_maxBias, const float p_penumbraScale) : m_cascadeFarDistance(p_cascadeDistance), m_distanceIsDivider(p_distanceIsDivider), m_maxBias(p_maxBias), m_penumbraScale(p_penumbraScale) { }
+
+	// Float value holds either cascade distance or a divider
+	float m_cascadeFarDistance;
+
+	// Maximum bias value that the slope bias calculation result is clamped at
+	float m_maxBias;
+
+	// PCF Poisson sampling "spread" gets multiplied by penumbra scale
+	float m_penumbraScale;
+
+	// Bool value indicates whether the float is a literal distance unit or a divider (meant to divide z-far to get the distance unit)
+	// true - divider; false - literal distance unit
+	bool m_distanceIsDivider;
+};
+
+// Stores shadow mapping settings
+struct ShadowMappingData
+{
+	ShadowMappingData()
+	{
+		m_penumbraScaleRange = glm::vec2(Config::rendererVar().csm_penumbra_size_scale_min, Config::rendererVar().csm_penumbra_size_scale_max);
+		m_csmCascadePlaneZMultiplier = 10.0f;
+		m_penumbraSize = Config::rendererVar().csm_penumbra_size;
+		m_csmResolution = Config::rendererVar().csm_resolution;
+		m_numOfPCFSamples = Config::rendererVar().csm_num_of_pcf_samples;
+		m_shadowMappingEnabled = false;
+		m_zClipping = false;
+	}
+
+	std::vector<ShadowCascadeData> m_shadowCascadePlaneDistances;
+
+	// Cascaded shadow mapping data
+	glm::vec2 m_penumbraScaleRange;		// Min and Max range of penumbra size scaling based on fragment distance from the camera
+	float m_csmCascadePlaneZMultiplier;	// Multiplier for CSM cascade planes z clip distance
+	float m_penumbraSize;				// Shadow edge softness
+	unsigned int m_csmResolution;		// Resolution of CSM shadow map depth buffer
+	unsigned int m_numOfPCFSamples;		// Number of shadow map samples taken and averaged out during the Percentage-Closer Filtering
+	bool m_shadowMappingEnabled;		// Is the shadow mapping pass enabled
+	bool m_zClipping;					// Turn on z-clipping when rendering geometry for CSM depth pass (fixes VERY distant objects getting clipped out of the orthogonal shadow plane)
 };

+ 2 - 2
Praxis3D/Source/DeferredRenderer.cpp

@@ -36,10 +36,10 @@ ErrorCode DeferredRenderer::init()
 	m_screenSize.y = Config::graphicsVar().current_resolution_y;
 	
 	// Initialize gbuffer (and also pass the screen size to be used as the buffer size)
-	m_gbuffer = new GeometryBuffer((unsigned int) m_screenSize.x, (unsigned int) m_screenSize.y);
+	//m_gbuffer = new GeometryBuffer((unsigned int) m_screenSize.x, (unsigned int) m_screenSize.y);
 
 	// Initialize gbuffer and check if it was successful
-	if(ErrHandlerLoc::get().ifSuccessful(m_gbuffer->init(), returnCode))
+	//if(ErrHandlerLoc::get().ifSuccessful(m_gbuffer->init(), returnCode))
 	{
 		// Create a property-set used to load geometry shader
 		PropertySet geomShaderProperties(Properties::Shaders);

+ 183 - 7
Praxis3D/Source/EditorWindow.cpp

@@ -2524,40 +2524,42 @@ void EditorWindow::update(const float p_deltaTime)
                     drawSceneData(m_currentSceneData, true);
 
                     // Calculate widget offset used to draw a label on the left and a widget on the right (opposite of how ImGui draws it)
-                    float inputWidgetOffset = ImGui::GetCursorPosX() + ImGui::CalcItemWidth() * 0.5f + ImGui::GetStyle().ItemInnerSpacing.x;
+                    float inputWidgetOffset = ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x * 0.5f + ImGui::GetStyle().ItemInnerSpacing.x;
 
                     // Center the separator text
                     ImGui::PushStyleVar(ImGuiStyleVar_SeparatorTextAlign, ImVec2(0.5f, 0.5f));
                     ImGui::SeparatorText("Luminance settings:");
 
                     // Draw TONEMAP METHOD
-                    drawLeftAlignedLabelText("Tonemap method:", inputWidgetOffset);
+                    drawLeftAlignedLabelText("Tonemap method:", inputWidgetOffset, ImGui::GetContentRegionAvail().x - inputWidgetOffset);
                     ImGui::Combo("##TonemapMethodPicker", &Config::m_graphicsVar.tonemap_method, &m_tonemappingMethodText[0], (int)m_tonemappingMethodText.size());
 
                     // Draw LUMINANCE RANGE MIN
-                    drawLeftAlignedLabelText("Luminance range min:", inputWidgetOffset);
+                    drawLeftAlignedLabelText("Luminance range min:", inputWidgetOffset, ImGui::GetContentRegionAvail().x - inputWidgetOffset);
                     ImGui::DragFloat("##LuminanceRangeMinDrag", &Config::m_graphicsVar.luminance_range_min, 0.001f, 0.0f, 100.0f, "%.5f");
 
                     // Draw LUMINANCE RANGE MAX
-                    drawLeftAlignedLabelText("Luminance range max:", inputWidgetOffset);
+                    drawLeftAlignedLabelText("Luminance range max:", inputWidgetOffset, ImGui::GetContentRegionAvail().x - inputWidgetOffset);
                     ImGui::DragFloat("##LuminanceRangeMaxDrag", &Config::m_graphicsVar.luminance_range_max, 0.001f, 0.0f, 100.0f, "%.5f");
 
                     // Draw LUMINANCE MULTIPLIER
-                    drawLeftAlignedLabelText("Luminance multiplier:", inputWidgetOffset);
+                    drawLeftAlignedLabelText("Luminance multiplier:", inputWidgetOffset, ImGui::GetContentRegionAvail().x - inputWidgetOffset);
                     ImGui::DragFloat("##LuminanceMultiplierDrag", &Config::m_graphicsVar.luminance_multiplier, 0.001f, 0.0f, 100.0f, "%.5f");
 
                     ImGui::SeparatorText("Graphics settings:");
 
                     // Draw PARALLAX LOD
-                    drawLeftAlignedLabelText("Parallax LOD:", inputWidgetOffset);
+                    drawLeftAlignedLabelText("Parallax LOD:", inputWidgetOffset, ImGui::GetContentRegionAvail().x - inputWidgetOffset);
                     ImGui::DragFloat("##ParallaxLODDrag", &Config::m_graphicsVar.LOD_parallax_mapping, 0.1f, 0.0f, 100000.0f, "%.5f");
 
                     ImGui::SeparatorText("Renderer settings:");
 
                     // Draw OBJECTS LOADER PER FRAME
-                    drawLeftAlignedLabelText("Object loads per frame:", inputWidgetOffset);
+                    drawLeftAlignedLabelText("Object loads per frame:", inputWidgetOffset, ImGui::GetContentRegionAvail().x - inputWidgetOffset);
                     ImGui::InputInt("##ObjectsLoadedPerFrameInput", &Config::m_rendererVar.objects_loaded_per_frame);
 
+                    ImGui::NewLine();
+
                     ImGui::PopStyleVar(); //ImGuiStyleVar_SeparatorTextAlign
                     ImGui::EndTabItem();
                 }
@@ -4149,6 +4151,7 @@ void EditorWindow::drawSceneData(SceneData &p_sceneData, const bool p_sendChange
     if(ImGui::DragFloat("##AmbientLightIntensityDrag", &p_sceneData.m_ambientIntensity, 0.001f, 0.0f, 100000.0f, "%.5f") && p_sendChanges)
     {
         m_systemScene->getSceneLoader()->getChangeController()->sendChange(this, m_systemScene->getSceneLoader()->getSystemScene(Systems::Graphics), Systems::Changes::Graphics::AmbientIntensity);
+        p_sceneData.m_modified = true;
     }
     
     // Draw Z FAR
@@ -4156,6 +4159,7 @@ void EditorWindow::drawSceneData(SceneData &p_sceneData, const bool p_sendChange
     if(ImGui::DragFloat("##ZBufferFarDrag", &p_sceneData.m_zFar, 0.1f, 0.0f, 100000.0f, "%.5f") && p_sendChanges)
     {
         m_systemScene->getSceneLoader()->getChangeController()->sendChange(this, m_systemScene->getSceneLoader()->getSystemScene(Systems::Graphics), Systems::Changes::Graphics::ZFar);
+        p_sceneData.m_modified = true;
     }
 
     // Draw Z NEAR
@@ -4163,6 +4167,7 @@ void EditorWindow::drawSceneData(SceneData &p_sceneData, const bool p_sendChange
     if(ImGui::DragFloat("##ZBufferNearDrag", &p_sceneData.m_zNear, 0.0001f, 0.0f, 100000.0f, "%.5f") && p_sendChanges)
     {
         m_systemScene->getSceneLoader()->getChangeController()->sendChange(this, m_systemScene->getSceneLoader()->getSystemScene(Systems::Graphics), Systems::Changes::Graphics::ZNear);
+        p_sceneData.m_modified = true;
     }
 
     // Calculate ambient occlusion window height
@@ -4262,8 +4267,178 @@ void EditorWindow::drawSceneData(SceneData &p_sceneData, const bool p_sendChange
         }
 
         if(aoDataChanged)
+        {
             m_systemScene->getSceneLoader()->getChangeController()->sendData(m_systemScene->getSceneLoader()->getSystemScene(Systems::Graphics), DataType::DataType_AmbientOcclusionData, (void *)&p_sceneData.m_aoData, false);
+            p_sceneData.m_modified = true;
+        }
+
+    }
+    ImGui::EndChild();
+
+    // Calculate ambient occlusion window height
+    float shadowMappingWindowHeight = (m_fontSize + m_imguiStyle.FramePadding.y * 2 + m_imguiStyle.ItemSpacing.y);
+    shadowMappingWindowHeight *= p_sceneData.m_shadowMappingData.m_shadowMappingEnabled ? 10 + 3 * p_sceneData.m_shadowMappingData.m_shadowCascadePlaneDistances.size() : 2.0f;
+    shadowMappingWindowHeight += m_imguiStyle.ItemSpacing.y;
+
+    if(ImGui::BeginChild("##CascadedShadowMappingSettings", ImVec2(0.0f, shadowMappingWindowHeight), true))
+    {
+        ImGui::PushStyleVar(ImGuiStyleVar_SeparatorTextBorderSize, 0.0f);
+        ImGui::SeparatorText("Cascaded shadow mapping:");
+        ImGui::PopStyleVar(); //ImGuiStyleVar_SeparatorTextBorderSize
+
+        bool csmDataChanged = false;
+
+        // Draw ENABLED
+        drawLeftAlignedLabelText("Enabled:", inputWidgetOffset);
+        if(ImGui::Checkbox("##CSMEnabledCheckbox", &p_sceneData.m_shadowMappingData.m_shadowMappingEnabled) && p_sendChanges)
+        {
+            csmDataChanged = true;
+        }
 
+        // Show the rest of the shadow mapping settings only of the shadow mapping is enabled
+        if(p_sceneData.m_shadowMappingData.m_shadowMappingEnabled)
+        {
+            // Draw DEPTH MAP Z-CLIPPING
+            drawLeftAlignedLabelText("Depth map Z-clipping:", inputWidgetOffset);
+            if(ImGui::Checkbox("##CSMZClippingCheckbox", &p_sceneData.m_shadowMappingData.m_zClipping) && p_sendChanges)
+            {
+                csmDataChanged = true;
+            }
+
+            // Draw PENUMBRA SIZE
+            drawLeftAlignedLabelText("Penumbra size:", inputWidgetOffset);
+            if(ImGui::DragFloat("##CSMPenumbraSizeDrag", &p_sceneData.m_shadowMappingData.m_penumbraSize, 0.01f, 0.01f, 10.0f, "%.3f") && p_sendChanges)
+            {
+                csmDataChanged = true;
+            }
+
+            // Draw PENUMBRA SCALE RANGE
+            drawLeftAlignedLabelText("Penumbra scale range:", inputWidgetOffset);
+            if(ImGui::DragFloat2("##CSMPenumbraScaleRangeDrag", glm::value_ptr(p_sceneData.m_shadowMappingData.m_penumbraScaleRange), 1.0f, 1.0f, 10000.0f) && p_sendChanges)
+            {
+                csmDataChanged = true;
+            }
+
+            // Draw RESOLUTION
+            drawLeftAlignedLabelText("Resolution:", inputWidgetOffset);
+            if(int resolution = (int)p_sceneData.m_shadowMappingData.m_csmResolution; ImGui::DragInt("##CSMResolutionDrag", &resolution, 64.0f, 64, 10240))
+            {
+                p_sceneData.m_shadowMappingData.m_csmResolution = (unsigned int)resolution;
+
+                if(p_sendChanges)
+                    csmDataChanged = true;
+            }
+
+            // Draw Z-PLANE MULTIPLIER
+            drawLeftAlignedLabelText("Z-plane multiplier:", inputWidgetOffset);
+            if(ImGui::DragFloat("##CSMZPlaneMultiplierDrag", &p_sceneData.m_shadowMappingData.m_csmCascadePlaneZMultiplier, 0.1f, 1.0f, 10000.0f, "%.1f") && p_sendChanges)
+            {
+                csmDataChanged = true;
+            }
+
+            // Draw PCF SAMPLES
+            drawLeftAlignedLabelText("PCF samples:", inputWidgetOffset);
+            if(int numOfPCFSamples = (int)p_sceneData.m_shadowMappingData.m_numOfPCFSamples; ImGui::InputInt("##CSMPCFSamplesInputInt", &numOfPCFSamples) && p_sendChanges)
+            {
+                if(numOfPCFSamples > 0)
+                {
+                    p_sceneData.m_shadowMappingData.m_numOfPCFSamples = (unsigned int)numOfPCFSamples;
+                    csmDataChanged = true;
+                }
+            }
+
+            // Calculate CSM cascades window height
+            float cascadesWindowHeight = (m_fontSize + m_imguiStyle.FramePadding.y * 2 + m_imguiStyle.ItemSpacing.y);
+            cascadesWindowHeight *= 2 + 3 * p_sceneData.m_shadowMappingData.m_shadowCascadePlaneDistances.size();
+
+            if(ImGui::BeginChild("##CSMCascades", ImVec2(0, cascadesWindowHeight), true))
+            {
+                // Calculate widget offset used to draw a label on the left and a widget on the right (opposite of how ImGui draws it)
+                float inputWidgetOffsetCascades = ImGui::GetCursorPosX() + ImGui::CalcItemWidth() * 0.5f + ImGui::GetStyle().ItemInnerSpacing.x;
+
+                ImGui::PushStyleVar(ImGuiStyleVar_SeparatorTextBorderSize, 0.0f);
+                ImGui::SeparatorText("CSM cascades:");
+                ImGui::PopStyleVar(); //ImGuiStyleVar_SeparatorTextBorderSize
+
+                for(decltype(p_sceneData.m_shadowMappingData.m_shadowCascadePlaneDistances.size()) size = p_sceneData.m_shadowMappingData.m_shadowCascadePlaneDistances.size(), i = 0; i < size; i++)
+                {
+                    // Calculate item width to fit both DISTANCE and DISTANCE TYPE items on the same line
+                    auto itemWidth = (ImGui::GetContentRegionAvail().x - inputWidgetOffset) / 2.0f;
+
+                    // Draw CASCADE DISTANCE
+                    drawLeftAlignedLabelText((Utilities::toString(i + 1) + ". Cascade distance:").c_str(), inputWidgetOffset, itemWidth);
+                    if(ImGui::DragFloat(("##CascadesDistanceDrag" + Utilities::toString(i)).c_str(), &p_sceneData.m_shadowMappingData.m_shadowCascadePlaneDistances[i].m_cascadeFarDistance, 1.0f, 0.0f, 10000.0f, "%.1f") && p_sendChanges)
+                    {
+                        csmDataChanged = true;
+                    }
+
+                    // Draw CASCADE DISTANCE UNIT TYPE
+                    int distanceType = p_sceneData.m_shadowMappingData.m_shadowCascadePlaneDistances[i].m_distanceIsDivider ? 1 : 0;
+                    ImGui::SameLine();
+                    ImGui::SetNextItemWidth(itemWidth - calcTextSizedButtonSize(1) - m_imguiStyle.FramePadding.x * 4);
+                    if(ImGui::Combo(("##CascadesDistanceTypeCombo" + Utilities::toString(i)).c_str(), &distanceType, &m_cascadeDistanceTypeText[0], (int)m_cascadeDistanceTypeText.size()))
+                    {
+                        p_sceneData.m_shadowMappingData.m_shadowCascadePlaneDistances[i].m_distanceIsDivider = (distanceType == 1);
+                        csmDataChanged = true;
+                    }
+
+                    // Draw DELETE button
+                    ImGui::SameLine(calcTextSizedButtonOffset(1));
+                    if(drawTextSizedButton(m_buttonTextures[ButtonTextureType::ButtonTextureType_DeleteEntry], "##CSMCascadeDeleteButton" + Utilities::toString(i), "Remove shadow cascade entry"))
+                    {
+                        p_sceneData.m_shadowMappingData.m_shadowCascadePlaneDistances.erase(p_sceneData.m_shadowMappingData.m_shadowCascadePlaneDistances.begin() + i);
+                        size = p_sceneData.m_shadowMappingData.m_shadowCascadePlaneDistances.size();
+                        i--;
+
+                        if(p_sendChanges)
+                            csmDataChanged = true;
+                    }
+
+                    // Draw ADD button
+                    ImGui::SameLine(calcTextSizedButtonOffset(0));
+                    if(drawTextSizedButton(m_buttonTextures[ButtonTextureType::ButtonTextureType_Add], "CSMCascadeAddButton" + Utilities::toString(i), "Add a shadow cascade entry"))
+                    {
+                        p_sceneData.m_shadowMappingData.m_shadowCascadePlaneDistances.insert(p_sceneData.m_shadowMappingData.m_shadowCascadePlaneDistances.begin() + i + 1, ShadowCascadeData());
+                        size = p_sceneData.m_shadowMappingData.m_shadowCascadePlaneDistances.size();
+
+                        if(p_sendChanges)
+                            csmDataChanged = true;
+                    }
+
+                    // Draw BIAX MAX
+                    drawLeftAlignedLabelText("   Bias max:", inputWidgetOffset, itemWidth);
+                    if(ImGui::DragFloat(("##CascadesBiasMaxDrag" + Utilities::toString(i)).c_str(), &p_sceneData.m_shadowMappingData.m_shadowCascadePlaneDistances[i].m_maxBias, 0.000001f, 0.0f, 1.0f, "%.5f") && p_sendChanges)
+                    {
+                        csmDataChanged = true;
+                    }
+
+                    // Draw PENUMBRA SCALE
+                    drawLeftAlignedLabelText("   Penumbra scale:", inputWidgetOffset, itemWidth);
+                    if(ImGui::DragFloat(("##CascadesPenumbraScaleDrag" + Utilities::toString(i)).c_str(), &p_sceneData.m_shadowMappingData.m_shadowCascadePlaneDistances[i].m_penumbraScale, 0.001f, 0.0001f, 1000.0f, "%.3f") && p_sendChanges)
+                    {
+                        csmDataChanged = true;
+                    }
+                }
+
+                // Draw ADD button
+                ImGui::SetCursorPosX(calcTextSizedButtonOffset(0));
+                if(drawTextSizedButton(m_buttonTextures[ButtonTextureType::ButtonTextureType_Add], "##CSMCascadeAddAtEndButton", "Add a shadow cascade entry"))
+                {
+                    p_sceneData.m_shadowMappingData.m_shadowCascadePlaneDistances.push_back(ShadowCascadeData());
+
+                    if(p_sendChanges)
+                        csmDataChanged = true;
+                }
+            }
+            ImGui::EndChild();
+        }
+
+        // If CSM data was changed, send the new data to the renderer and mark the scene data as modified
+        if(csmDataChanged)
+        {
+            m_systemScene->getSceneLoader()->getChangeController()->sendData(m_systemScene->getSceneLoader()->getSystemScene(Systems::Graphics), DataType::DataType_ShadowMappingData, (void *)&p_sceneData.m_shadowMappingData, false);
+            p_sceneData.m_modified = true;
+        }
     }
     ImGui::EndChild();
 
@@ -4576,6 +4751,7 @@ void EditorWindow::updateSceneData(SceneData &p_sceneData)
 
     // Set graphics data
     p_sceneData.m_aoData = graphicsScene->getAmbientOcclusionData();
+    p_sceneData.m_shadowMappingData = graphicsScene->getShadowMappingData();
     p_sceneData.m_ambientIntensity = graphicsScene->getSceneObjects().m_ambientIntensity;
     p_sceneData.m_zFar = graphicsScene->getSceneObjects().m_zFar;
     p_sceneData.m_zNear = graphicsScene->getSceneObjects().m_zNear;

+ 5 - 0
Praxis3D/Source/EditorWindow.h

@@ -77,6 +77,7 @@ public:
 			m_renderingPassesTypeText.push_back(GetString(static_cast<RenderPassType>(i)));
 
 		m_ambientOcclusionTypeText = { "None", "SSAO", "HBAO" };
+		m_cascadeDistanceTypeText = { "Units", "Divider" };
 		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" };
@@ -729,7 +730,9 @@ private:
 			m_zFar = 0.0f;
 			m_zNear = 0.0f;
 
+			m_renderingPasses.push_back(RenderPassType::RenderPassType_ShadowMapping);
 			m_renderingPasses.push_back(RenderPassType::RenderPassType_Geometry);
+			m_renderingPasses.push_back(RenderPassType::RenderPassType_AmbientOcclusion);
 			m_renderingPasses.push_back(RenderPassType::RenderPassType_AtmScattering);
 			m_renderingPasses.push_back(RenderPassType::RenderPassType_Lighting);
 			m_renderingPasses.push_back(RenderPassType::RenderPassType_AtmScattering);
@@ -754,6 +757,7 @@ private:
 		float m_ambientIntensity;
 		float m_zFar;
 		float m_zNear;
+		ShadowMappingData m_shadowMappingData;
 		RenderingPasses m_renderingPasses;
 
 		// Physics scene
@@ -1134,6 +1138,7 @@ private:
 
 	// String arrays and other data used for ImGui Combo inputs
 	std::vector<const char *> m_ambientOcclusionTypeText;
+	std::vector<const char *> m_cascadeDistanceTypeText;
 	std::vector<const char *> m_physicalMaterialProperties;
 	std::vector<const char *> m_renderingPassesTypeText;
 	std::vector<const char *> m_luaVariableTypeStrings;

+ 11 - 0
Praxis3D/Source/Engine.cpp

@@ -17,6 +17,17 @@
 #include "WindowLocator.h"
 #include "WorldSystem.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+	__declspec(dllexport) uint32_t NvOptimusEnablement = 1;
+	__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
+
+#ifdef __cplusplus
+}
+#endif
+
 int Engine::m_instances = 0;
 
 #define ENUMTOSTRING(ENUM) #ENUM

+ 3 - 0
Praxis3D/Source/ErrorCodes.h

@@ -83,6 +83,8 @@ DECLARE_ENUM(ErrorType, ERROR_TYPES)
 	Code(Shader_creation_failed,) \
 	Code(Shader_link_failed,) \
 	Code(Shader_loading_failed,) \
+	Code(Shader_source_empty,) \
+	Code(Shader_variable_not_found,) \
 	/* Scene loader errors */ \
 	Code(GameObjects_missing,) \
 	Code(Number_of_meshes_missmatch,) \
@@ -158,6 +160,7 @@ DECLARE_ENUM(ErrorCode, ERROR_CODES)
     Code(Source_ScriptObject,) \
     Code(Source_ShaderComponent,) \
     Code(Source_ShaderLoader,) \
+    Code(Source_ShadowMappingPass,) \
     Code(Source_SkyObject,) \
     Code(Source_SkyPass,) \
     Code(Source_SoundComponent,) \

+ 3 - 1
Praxis3D/Source/ErrorHandler.cpp

@@ -60,6 +60,8 @@ ErrorHandler::ErrorHandler()
 	AssignErrorType(Shader_creation_failed, Error);
 	AssignErrorType(Shader_link_failed, Error);
 	AssignErrorType(Shader_loading_failed, Error);
+	AssignErrorType(Shader_source_empty, Warning);
+	AssignErrorType(Shader_variable_not_found, Warning);
 	AssignErrorType(GameObjects_missing, Error);
 	AssignErrorType(Number_of_meshes_missmatch, Warning);
 	AssignErrorType(Texture_not_found, Warning); 
@@ -139,7 +141,7 @@ void ErrorHandler::log(ErrorCode p_errorCode, ErrorSource p_errorSource)
 		}
 	}
 	else
-		log(m_errorData[p_errorCode].m_errorType, p_errorSource, "\033[1;33m" + m_errorData[p_errorCode].m_errorString + "\033[0m");
+		log(m_errorData[p_errorCode].m_errorType, p_errorSource, m_errorData[p_errorCode].m_errorString);
 }
 void ErrorHandler::log(ErrorType p_errorType, ErrorSource p_errorSource, std::string p_error)
 {

+ 4 - 1
Praxis3D/Source/Framebuffer.h

@@ -2,6 +2,8 @@
 
 #include <GL/glew.h>
 
+#include "UniformData.h"
+
 class Framebuffer
 {
 public:
@@ -38,13 +40,14 @@ public:
 
 	GLuint getStatus() { return m_status; }
 
-	virtual ErrorCode init() = 0;
+	virtual ErrorCode init(const UniformFrameData &p_frameData) = 0;
 	
 	inline unsigned int getBufferWidth() const { return m_bufferWidth; }
 	inline unsigned int getBufferHeight() const { return m_bufferHeight; }
 	
 	// Set the size of all buffers
 	virtual void setBufferSize(unsigned int p_bufferWidth, unsigned int p_bufferHeight) = 0;
+
 	// Set the size of an individual buffer
 	virtual void setBufferSize(GLuint p_buffer, unsigned int p_bufferWidth, unsigned int p_bufferHeight) = 0;
 

+ 3 - 2
Praxis3D/Source/GeometryBuffer.cpp

@@ -1,7 +1,7 @@
 #include "Config.h"
 #include "GeometryBuffer.h"
 
-GeometryBuffer::GeometryBuffer(unsigned int p_bufferWidth, unsigned int p_bufferHeight) : Framebuffer(p_bufferWidth, p_bufferHeight)
+GeometryBuffer::GeometryBuffer(const UniformFrameData &p_frameData) : Framebuffer(p_frameData.m_screenSize.x, p_frameData.m_screenSize.y)
 {
 
 	m_intermediateBuffer = 0;
@@ -39,7 +39,7 @@ GeometryBuffer::~GeometryBuffer()
 		glDeleteTextures(1, &m_finalBuffer);
 }
 
-ErrorCode GeometryBuffer::init()
+ErrorCode GeometryBuffer::init(const UniformFrameData &p_frameData)
 {
 	ErrorCode returnCode = ErrorCode::Success;
 
@@ -221,6 +221,7 @@ void GeometryBuffer::setBufferSize(GLuint p_buffer, unsigned int p_bufferWidth,
 void GeometryBuffer::initFrame()
 {
 	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_FBO);
+	glViewport(0, 0, m_bufferWidth, m_bufferHeight);
 	glDrawBuffer(GL_COLOR_ATTACHMENT0 + GBufferIntermediate);	// Bind intermediate buffer
 	glClear(GL_COLOR_BUFFER_BIT);								// and clear it
 	glDrawBuffer(GL_COLOR_ATTACHMENT0 + GBufferFinal);			// Bind final buffer

+ 2 - 2
Praxis3D/Source/GeometryBuffer.h

@@ -15,13 +15,13 @@ public:
 
 	typedef unsigned int GBufferTexture;
 
-	GeometryBuffer(unsigned int p_bufferWidth, unsigned int p_bufferHeight);
+	GeometryBuffer(const UniformFrameData &p_frameData);
 	~GeometryBuffer();
 	
 	//virtual void bindReadBuffer(GBufferTextureType p_type) { glReadBuffer(GL_COLOR_ATTACHMENT0 + p_type); }
 
 	// Generates buffers, set's up FBO
-	virtual ErrorCode init();
+	virtual ErrorCode init(const UniformFrameData &p_frameData);
 	virtual void setBufferSize(unsigned int p_bufferWidth, unsigned int p_bufferHeight);
 	virtual void setBufferSize(GLuint p_buffer, unsigned int p_bufferWidth, unsigned int p_bufferHeight);
 

+ 23 - 0
Praxis3D/Source/GeometryPass.h

@@ -47,7 +47,30 @@ public:
 
 	void update(RenderPassData &p_renderPassData, const SceneObjects &p_sceneObjects, const float p_deltaTime)
 	{
+		// Prepare the geometry buffer for a new frame and a geometry pass
+		m_renderer.m_backend.getGeometryBuffer()->initFrame();
+
 		glDepthMask(GL_TRUE);
+		glEnable(GL_DEPTH_TEST);		// Enable depth testing, as this is much like a regular forward render pass
+		glClear(GL_DEPTH_BUFFER_BIT);	// Make sure to clear the depth buffer for the new frame
+
+		// Set depth test function
+		glDepthFunc(Config::rendererVar().depth_test_func);
+		//glDisable(GL_CULL_FACE);
+
+		// Enable / disable face culling
+		if(Config::rendererVar().face_culling)
+		{
+			glEnable(GL_CULL_FACE);
+
+			// Set face culling mode
+			glCullFace(Config::rendererVar().face_culling_mode);
+		}
+		else
+			glDisable(GL_CULL_FACE);
+
+
+		//glDepthMask(GL_TRUE);
 		glDepthFunc(GL_LEQUAL);
 
 		// Set input and output color maps for this frame

+ 14 - 4
Praxis3D/Source/GraphicsDataSets.h

@@ -405,12 +405,22 @@ struct AODataSet
 	int m_numOfSteps;
 };
 
+struct CascadedShadowMapDataSet
+{
+	CascadedShadowMapDataSet() : m_cascadePlaneDistance(0.0f), m_maxBias(0.0f), m_poissonSampleScale(0.0f), m_penumbraScale(1.0f) { }
+	CascadedShadowMapDataSet(const glm::mat4 &p_lightSpaceMatrix, const float p_cascadePlaneDistance, const float p_maxBias, const float p_poissonSampleScale, const float p_penumbraScale) :
+		m_lightSpaceMatrix(p_lightSpaceMatrix), m_cascadePlaneDistance(p_cascadePlaneDistance), m_maxBias(p_maxBias), m_poissonSampleScale(p_poissonSampleScale), m_penumbraScale(p_penumbraScale) { }
+
+	glm::mat4 m_lightSpaceMatrix;
+	float m_cascadePlaneDistance;
+	float m_maxBias;
+	float m_poissonSampleScale;
+	float m_penumbraScale;
+};
+
 struct HDRDataSet
 {
-	HDRDataSet()
-	{
-		m_screenBrightness = 0.5f;
-	}
+	HDRDataSet() : m_screenBrightness(0.5f) { }
 
 	float m_screenBrightness;
 };

+ 117 - 22
Praxis3D/Source/LightingPass.h

@@ -10,14 +10,18 @@ public:
 		m_pointLightBuffer(BufferType_Uniform, BufferBindTarget_Uniform, BufferUsageHint_DynamicDraw),
 		m_spotLightBuffer(BufferType_Uniform, BufferBindTarget_Uniform, BufferUsageHint_DynamicDraw),
 		m_shaderLightPass(nullptr),
+		m_shaderLightCSMPass(nullptr),
 		m_maxNumPointLights(decltype(m_pointLights.size())(Config::graphicsVar().max_num_point_lights)),
-		m_maxNumSpotLights(decltype(m_spotLights.size())(Config::graphicsVar().max_num_spot_lights)) { }
+		m_maxNumSpotLights(decltype(m_spotLights.size())(Config::graphicsVar().max_num_spot_lights)),
+		m_numOfCascades(0),
+		m_numOfPCFSamples(1),
+		m_shadowMappingEnabled(false) { }
 
 	~LightingPass() { }
 
 	ErrorCode init()
 	{
-		ErrorCode returnError;
+		ErrorCode returnError = ErrorCode::Success;
 
 		m_name = "Lighting Rendering Pass";
 
@@ -36,21 +40,61 @@ public:
 		m_emissiveAndOutputBuffers[0] = m_renderer.m_backend.getGeometryBuffer()->getBufferLocation(GBufferTextureType::GBufferEmissive);
 		m_emissiveAndOutputBuffers[1] = m_renderer.m_backend.getGeometryBuffer()->getBufferLocation(GBufferTextureType::GBufferFinal);
 
-		// Create a property-set used to load lighting shader
-		PropertySet lightShaderProperties(Properties::Shaders);
-		lightShaderProperties.addProperty(Properties::VertexShader, Config::rendererVar().light_pass_vert_shader);
-		lightShaderProperties.addProperty(Properties::FragmentShader, Config::rendererVar().light_pass_frag_shader);
+		// Get the current shadow mapping data
+		const auto &shadowMappingData = m_renderer.m_frameData.m_shadowMappingData;
 
-		// Create shaders
-		m_shaderLightPass = Loaders::shader().load(lightShaderProperties);
+		// Get the current number of shadow cascades
+		m_numOfCascades = (unsigned int)shadowMappingData.m_shadowCascadePlaneDistances.size();
+		m_numOfPCFSamples = shadowMappingData.m_numOfPCFSamples;
+		m_shadowMappingEnabled = shadowMappingData.m_shadowMappingEnabled;
 
-		// Load shaders to memory
-		returnError = m_shaderLightPass->loadToMemory();
+		// Load lighting pass shader
+		{
+			// Create a property-set used to load lighting shader
+			PropertySet lightShaderProperties(Properties::Shaders);
 
-		if(returnError == ErrorCode::Success)
+			lightShaderProperties.addProperty(Properties::VertexShader, Config::rendererVar().light_pass_vert_shader);
+			lightShaderProperties.addProperty(Properties::FragmentShader, Config::rendererVar().light_pass_frag_shader);
+
+			// Create the shader
+			m_shaderLightPass = Loaders::shader().load(lightShaderProperties);
+
+			// Load the shader to memory
+			if(ErrorCode shaderError = m_shaderLightPass->loadToMemory(); shaderError == ErrorCode::Success)
+			{
+				// Queue the shader to be loaded to GPU
+				m_renderer.queueForLoading(*m_shaderLightPass);
+			}
+			else
+				returnError = shaderError;
+		}
+
+		// Load lighting with cascaded shadow mapping pass shader
 		{
-			// Queue the shaders to be loaded to GPU
-			m_renderer.queueForLoading(*m_shaderLightPass);
+			// Create a property-set used to load lighting shader
+			PropertySet lightShaderProperties(Properties::Shaders);
+
+			lightShaderProperties.addProperty(Properties::VertexShader, Config::rendererVar().light_pass_csm_vert_shader);
+			lightShaderProperties.addProperty(Properties::FragmentShader, Config::rendererVar().light_pass_csm_frag_shader);
+
+			// Create the shader
+			m_shaderLightCSMPass = Loaders::shader().load(lightShaderProperties);
+
+			// Load the shader to memory
+			if(ErrorCode shaderError = m_shaderLightCSMPass->loadToMemory(); shaderError == ErrorCode::Success)
+			{
+				if(m_numOfCascades > 0)
+					if(ErrorCode shaderVariableError = m_shaderLightCSMPass->setDefineValue(ShaderType::ShaderType_Fragment, Config::shaderVar().define_numOfCascades, m_numOfCascades); shaderVariableError != ErrorCode::Success)
+						ErrHandlerLoc::get().log(shaderVariableError, Config::shaderVar().define_numOfCascades, ErrorSource::Source_LightingPass);
+
+				if(ErrorCode shaderVariableError = m_shaderLightCSMPass->setDefineValue(ShaderType::ShaderType_Fragment, Config::shaderVar().define_numOfPCFSamples, m_numOfPCFSamples); shaderVariableError != ErrorCode::Success)
+					ErrHandlerLoc::get().log(shaderVariableError, Config::shaderVar().define_numOfPCFSamples, ErrorSource::Source_LightingPass);
+
+				// Queue the shader to be loaded to GPU
+				m_renderer.queueForLoading(*m_shaderLightCSMPass);
+			}
+			else
+				returnError = shaderError;
 		}
 
 		// Queue light buffers to be created
@@ -148,6 +192,13 @@ public:
 		m_spotLightBuffer.m_updateSize = sizeof(SpotLightDataSet) * m_spotLights.size();
 		m_spotLightBuffer.m_data = (void*)m_spotLights.data();
 
+		// Queue light buffer updates (so that new values that were just setup are sent to the GPU)
+		m_renderer.queueForUpdate(m_pointLightBuffer);
+		m_renderer.queueForUpdate(m_spotLightBuffer);
+
+		// Pass update commands so they are executed 
+		m_renderer.passUpdateCommandsToBackend();
+
 		// Bind textures for reading
 		m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(GBufferTextureType::GBufferPosition, GBufferTextureType::GBufferPosition);
 		m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(GBufferTextureType::GBufferDiffuse, GBufferTextureType::GBufferDiffuse);
@@ -155,18 +206,57 @@ public:
 		m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(GBufferTextureType::GBufferEmissive, GBufferTextureType::GBufferEmissive);
 		m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(GBufferTextureType::GBufferMatProperties, GBufferTextureType::GBufferMatProperties);
 
-		// Bind texture for writing
-		m_renderer.m_backend.getGeometryBuffer()->bindBufferForWriting(p_renderPassData.getColorOutputMap());
+		// Get the current shadow mapping data
+		const auto &shadowMappingData = m_renderer.m_frameData.m_shadowMappingData;
 
-		// Queue light buffer updates (so that new values that were just setup are sent to the GPU)
-		m_renderer.queueForUpdate(m_pointLightBuffer);
-		m_renderer.queueForUpdate(m_spotLightBuffer);
+		ShaderLoader::ShaderProgram *lightPassShader = m_shaderLightCSMPass;
 
-		// Pass update commands so they are executed 
-		m_renderer.passUpdateCommandsToBackend();
+		if(shadowMappingData.m_shadowMappingEnabled && !shadowMappingData.m_shadowCascadePlaneDistances.empty())
+		{
+			// If the number of shadow cascades has changed, set the new define inside the shader source and recompile the shader
+			if(m_numOfCascades != (unsigned int)shadowMappingData.m_shadowCascadePlaneDistances.size() || m_numOfPCFSamples != shadowMappingData.m_numOfPCFSamples)
+			{
+				m_numOfCascades = (unsigned int)shadowMappingData.m_shadowCascadePlaneDistances.size();
+				m_numOfPCFSamples = shadowMappingData.m_numOfPCFSamples;
+
+				if(m_numOfCascades > 0)
+				{
+					ErrorCode shaderVariableError = m_shaderLightCSMPass->setDefineValue(ShaderType::ShaderType_Fragment, Config::shaderVar().define_numOfCascades, m_numOfCascades);
+
+					if(shaderVariableError == ErrorCode::Success)
+					{
+						shaderVariableError = m_shaderLightCSMPass->setDefineValue(ShaderType::ShaderType_Fragment, Config::shaderVar().define_numOfPCFSamples, m_numOfPCFSamples);
+
+						if(shaderVariableError == ErrorCode::Success)
+						{
+							// Queue the shader to be loaded to GPU
+							m_shaderLightCSMPass->resetLoadedToVideoMemoryFlag();
+							m_renderer.queueForLoading(*m_shaderLightCSMPass);
+							m_renderer.passLoadCommandsToBackend();
+
+							// Make sure to update uniform block bindings, since light CSM shader uses CSM data set uniform that is owned by the shadow map pass, and might be created later
+							m_shaderLightCSMPass->m_uniformUpdater->updateBlockBindingPoints();
+						}
+						else
+							ErrHandlerLoc::get().log(shaderVariableError, Config::shaderVar().define_numOfPCFSamples, ErrorSource::Source_LightingPass);
+					}
+					else
+						ErrHandlerLoc::get().log(shaderVariableError, Config::shaderVar().define_numOfCascades, ErrorSource::Source_LightingPass);
+				}
+			}
+
+			m_renderer.m_backend.getCSMFramebuffer()->bindBufferForReading(CSMBufferTextureType::CSMBufferTextureType_CSMDepthMap, CSMBufferTextureType::CSMBufferTextureType_CSMDepthMap);
+
+			m_shaderLightCSMPass->m_uniformUpdater->updateBlockBindingPoints();
+		}
+		else
+			lightPassShader = m_shaderLightPass;
+
+		// Bind texture for writing
+		m_renderer.m_backend.getGeometryBuffer()->bindBufferForWriting(p_renderPassData.getColorOutputMap());
 
 		// Queue the screen space triangle, using lighting shader, to be drawn
-		m_renderer.queueForDrawing(m_shaderLightPass->getShaderHandle(), m_shaderLightPass->getUniformUpdater(), p_sceneObjects.m_cameraViewMatrix);
+		m_renderer.queueForDrawing(lightPassShader->getShaderHandle(), lightPassShader->getUniformUpdater(), p_sceneObjects.m_cameraViewMatrix);
 
 		// Pass the draw command so it is executed
 		m_renderer.passScreenSpaceDrawCommandsToBackend();
@@ -175,7 +265,8 @@ public:
 	}
 
 private:
-	ShaderLoader::ShaderProgram	*m_shaderLightPass;
+	ShaderLoader::ShaderProgram *m_shaderLightPass;
+	ShaderLoader::ShaderProgram	*m_shaderLightCSMPass;
 
 	// Buffer handles used for binding
 	std::vector<GeometryBuffer::GBufferTexture> m_emissiveAndOutputBuffers;
@@ -190,4 +281,8 @@ private:
 
 	decltype(m_pointLights.size()) m_maxNumPointLights;
 	decltype(m_spotLights.size()) m_maxNumSpotLights;
+
+	unsigned int m_numOfCascades;
+	unsigned int m_numOfPCFSamples;
+	bool m_shadowMappingEnabled;
 };

+ 44 - 9
Praxis3D/Source/RendererBackend.cpp

@@ -5,22 +5,23 @@ RendererBackend::SingleTriangle RendererBackend::m_fullscreenTriangle;
 RendererBackend::RendererBackend()
 {
 	m_gbuffer = nullptr;
+	m_csmBuffer = nullptr;
 }
 
 RendererBackend::~RendererBackend()
 {
-	delete m_gbuffer;
+	if(m_gbuffer != nullptr)
+		delete m_gbuffer;
+	if(m_csmBuffer != nullptr)
+		delete m_csmBuffer;
 }
 
 ErrorCode RendererBackend::init(const UniformFrameData &p_frameData)
 {
 	ErrorCode returnCode = ErrorCode::Success;
 
-	// Initialize gbuffer (and also pass the screen size to be used as the buffer size)
-	m_gbuffer = new GeometryBuffer((unsigned int)p_frameData.m_screenSize.x, (unsigned int)p_frameData.m_screenSize.y);
-
-	// Check if the gbuffer initialization was successful
-	if(ErrHandlerLoc::get().ifSuccessful(m_gbuffer->init(), returnCode))
+	// Initialize gbuffer and check if the gbuffer initialization was successful
+	if(ErrHandlerLoc::get().ifSuccessful(createFramebuffer(FramebufferType::FramebufferType_GBuffer, p_frameData), returnCode))
 	{
 		// Load fullscreen triangle (used to render post-processing effects)
 		m_fullscreenTriangle.load();
@@ -39,15 +40,49 @@ ErrorCode RendererBackend::init(const UniformFrameData &p_frameData)
 
 		//glDepthFunc(GL_LESS);
 
-		// Set face culling mode
-		glCullFace(Config::rendererVar().face_culling_mode);
-
 		// Set depth test function
 		glDepthFunc(Config::rendererVar().depth_test_func);
 	}
 	return returnCode;
 }
 
+ErrorCode RendererBackend::createFramebuffer(const FramebufferType p_frambufferType, const UniformFrameData &p_frameData)
+{
+	ErrorCode returnError = ErrorCode::Success;
+
+	switch(p_frambufferType)
+	{
+		case FramebufferType_GBuffer:
+		{
+			if(m_gbuffer != nullptr)
+				delete m_gbuffer;
+
+			// Initialize gbuffer (and also pass the screen size to be used as the buffer size)
+			m_gbuffer = new GeometryBuffer(p_frameData);
+
+			// Check if the gbuffer initialization was successful
+			returnError = m_gbuffer->init(p_frameData);
+			//if(ErrHandlerLoc::get().ifSuccessful(m_gbuffer->init(), returnError))
+		}
+		break;
+
+		case FramebufferType_CSMBuffer:
+		{
+			if(m_csmBuffer != nullptr)
+				delete m_csmBuffer;
+
+			m_csmBuffer = new CSMFramebuffer(p_frameData);
+
+			returnError = m_csmBuffer->init(p_frameData);
+
+		}
+		break;
+
+	}
+
+	return returnError;
+}
+
 void RendererBackend::processUpdate(const BufferUpdateCommands &p_updateCommands, const UniformFrameData &p_frameData)
 {
 	for(decltype(p_updateCommands.size()) i = 0, size = p_updateCommands.size(); i < size; i++)

+ 5 - 0
Praxis3D/Source/RendererBackend.h

@@ -4,6 +4,7 @@
 #include <stdint.h>
 
 #include "Config.h"
+#include "CSMFramebuffer.h"
 #include "GeometryBuffer.h"
 #include "Loaders.h"
 #include "ShaderUniformUpdater.h"
@@ -429,6 +430,8 @@ public:
 
 	ErrorCode init(const UniformFrameData &p_frameData);
 
+	ErrorCode createFramebuffer(const FramebufferType p_frambufferType, const UniformFrameData &p_frameData);
+
 	void setScreenSize(const UniformFrameData &p_frameData)
 	{
 		// Set gbuffer textures size
@@ -451,6 +454,7 @@ public:
 		glBindTexture(GL_TEXTURE_2D, p_texture.getHandle());
 	}
 
+	inline CSMFramebuffer *getCSMFramebuffer() { return m_csmBuffer; }
 	inline GeometryBuffer *getGeometryBuffer() { return m_gbuffer; }
 
 	inline unsigned int getFramebufferTextureHandle(GBufferTextureType p_bufferType) const { return m_gbuffer->getBufferTextureHandle(p_bufferType); }
@@ -1014,4 +1018,5 @@ protected:
 	static SingleTriangle m_fullscreenTriangle;
 
 	GeometryBuffer *m_gbuffer;
+	CSMFramebuffer *m_csmBuffer;
 };

+ 16 - 18
Praxis3D/Source/RendererFrontend.cpp

@@ -16,6 +16,7 @@
 #include "PostProcessPass.h"
 #include "ReflectionPass.h"
 #include "RendererFrontend.h"
+#include "ShadowMappingPass.h"
 #include "SkyPass.h"
 
 RendererFrontend::RendererFrontend() : m_renderPassData(nullptr)
@@ -27,9 +28,6 @@ RendererFrontend::RendererFrontend() : m_renderPassData(nullptr)
 	m_renderingPassesSet = false;
 	for(unsigned int i = 0; i < RenderPassType::RenderPassType_NumOfTypes; i++)
 		m_allRenderPasses[i] = nullptr;
-
-	m_zFar = Config::graphicsVar().z_far;
-	m_zNear = Config::graphicsVar().z_near;
 }
 
 RendererFrontend::~RendererFrontend()
@@ -106,6 +104,7 @@ void RendererFrontend::setRenderingPasses(const RenderingPasses &p_renderingPass
 	m_activeRenderPasses.clear();
 
 	bool guiRenderPassSet = false;
+	bool shadowMappingPassSet = false;
 
 	for(decltype(p_renderingPasses.size()) i = 0, size = p_renderingPasses.size(); i < size; i++)
 	{
@@ -116,6 +115,12 @@ void RendererFrontend::setRenderingPasses(const RenderingPasses &p_renderingPass
 					m_allRenderPasses[RenderPassType_Geometry] = new GeometryPass(*this);
 				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType_Geometry]);
 				break;
+			case RenderPassType_ShadowMapping:
+				if(m_allRenderPasses[RenderPassType_ShadowMapping] == nullptr)
+					m_allRenderPasses[RenderPassType_ShadowMapping] = new ShadowMappingPass(*this);
+				m_activeRenderPasses.push_back(m_allRenderPasses[RenderPassType_ShadowMapping]);
+				shadowMappingPassSet = true;
+				break;
 			case RenderPassType_Lighting:
 				if(m_allRenderPasses[RenderPassType_Lighting] == nullptr)
 					m_allRenderPasses[RenderPassType_Lighting] = new LightingPass(*this);
@@ -187,6 +192,9 @@ void RendererFrontend::setRenderingPasses(const RenderingPasses &p_renderingPass
 		if(m_guiRenderWasEnabled)
 			Config::m_GUIVar.gui_render = true;
 
+	// Set the shadow mapping enabled flag
+	m_frameData.m_shadowMappingData.m_shadowMappingEnabled = shadowMappingPassSet;
+
 	for(decltype(m_activeRenderPasses.size()) size = m_activeRenderPasses.size(), i = 0; i < size; i++)
 	{
 		if(!m_activeRenderPasses[i]->isInitialized())
@@ -259,11 +267,12 @@ void RendererFrontend::renderFrame(SceneObjects &p_sceneObjects, const float p_d
 		}
 	}
 
-	// If Z-buffer near or far values have changed, flag projection matrix for updating
-	if(m_zFar != p_sceneObjects.m_zFar || m_zNear != p_sceneObjects.m_zNear)
+	// If Z-buffer near or far or FOV values have changed, flag projection matrix for updating
+	if(m_frameData.m_zFar != p_sceneObjects.m_zFar || m_frameData.m_zNear != p_sceneObjects.m_zNear || m_frameData.m_fov != Config::graphicsVar().fov)
 	{
-		m_zFar = p_sceneObjects.m_zFar;
-		m_zNear = p_sceneObjects.m_zNear;
+		m_frameData.m_zFar = p_sceneObjects.m_zFar;
+		m_frameData.m_zNear = p_sceneObjects.m_zNear;
+		m_frameData.m_fov = Config::graphicsVar().fov;
 		projectionMatrixNeedsUpdating = true;
 	}
 
@@ -390,17 +399,6 @@ void RendererFrontend::renderFrame(SceneObjects &p_sceneObjects, const float p_d
 	// Set ambient intensity multiplier
 	m_frameData.m_ambientIntensity = p_sceneObjects.m_ambientIntensity;
 
-	// Prepare the geometry buffer for a new frame and a geometry pass
-	m_backend.getGeometryBuffer()->initFrame();
-	
-	glDepthMask(GL_TRUE);
-	glEnable(GL_DEPTH_TEST);		// Enable depth testing, as this is much like a regular forward render pass
-	glClear(GL_DEPTH_BUFFER_BIT);	// Make sure to clear the depth buffer for the new frame
-
-	// Set depth test function
-	glDepthFunc(Config::rendererVar().depth_test_func);
-	//glDisable(GL_CULL_FACE);
-
 	for(decltype(m_activeRenderPasses.size()) i = 0, size = m_activeRenderPasses.size(); i < size; i++)
 	{
 		m_activeRenderPasses[i]->update(*m_renderPassData, p_sceneObjects, p_deltaTime);

+ 8 - 17
Praxis3D/Source/RendererFrontend.h

@@ -10,10 +10,12 @@ struct RenderPassData;
 
 class RendererFrontend
 {
+	friend class AmbientOcclusionPass;
 	friend class AtmScatteringPass;
 	friend class BloomCompositePass;
 	friend class BloomPass;
 	friend class BlurPass;
+	friend class FinalPass;
 	friend class GeometryPass;
 	friend class GUIPass;
 	friend class HdrMappingPass;
@@ -22,10 +24,9 @@ class RendererFrontend
 	friend class LightingPass;
 	friend class LuminancePass;
 	friend class PostProcessPass;
-	friend class FinalPass;
 	friend class ReflectionPass;
+	friend class ShadowMappingPass;
 	friend class SkyPass;
-	friend class AmbientOcclusionPass;
 public:
 	// A handle for a uniform or shader storage buffer
 	struct ShaderBuffer
@@ -64,8 +65,10 @@ public:
 	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; }
+	void setShadowMappingData(const ShadowMappingData &p_shadowMappingData) { m_frameData.m_shadowMappingData = p_shadowMappingData; }
 
 	const RenderingPasses getRenderingPasses();
+	const ShadowMappingData &getShadowMappingData() const { return m_frameData.m_shadowMappingData; }
 
 	// Renders a complete frame
 	void renderFrame(SceneObjects &p_sceneObjects, const float p_deltaTime);
@@ -326,32 +329,20 @@ protected:
 	// Recalculates the projection matrix
 	inline void updateProjectionMatrix()
 	{
-		//m_frameData.m_projMatrix = glm::perspective(
-		//	Config::graphicsVar().fov, 
-		//	(float) m_frameData.m_screenSize.x / (float) m_frameData.m_screenSize.y, 
-		//	Config::graphicsVar().z_near,
-		//	Config::graphicsVar().z_far);
-
 		m_frameData.m_projMatrix = glm::perspectiveFov(
-			glm::radians(Config::graphicsVar().fov),
+			glm::radians(m_frameData.m_fov),
 			(float)m_frameData.m_screenSize.x,
 			(float)m_frameData.m_screenSize.y,
-			Config::graphicsVar().z_near,
-			Config::graphicsVar().z_far);
+			m_frameData.m_zNear,
+			m_frameData.m_zFar);
 
 		m_frameData.m_atmScatProjMatrix = Math::perspectiveRadian(Config::graphicsVar().fov,
 																m_frameData.m_screenSize.x,
 																m_frameData.m_screenSize.y);
-
-		//m_frameData.m_atmScatProjMatrix.perspectiveRadian(	Config::graphicsVar().fov,
-		//													m_frameData.m_screenSize.x,
-		//													m_frameData.m_screenSize.y);
 	}
 
 	bool m_renderingPassesSet;
 	bool m_guiRenderWasEnabled;
-	float m_zFar;
-	float m_zNear;
 
 	// Renderer backend, serves as an interface layer to GPU
 	RendererBackend m_backend;

+ 149 - 8
Praxis3D/Source/RendererScene.cpp

@@ -126,12 +126,18 @@ ErrorCode RendererScene::setup(const PropertySet &p_properties)
 			{
 				switch(typeProperty.getID())
 				{
+					case Properties::AmbientOcclusionRenderPass:
+						m_renderingPasses.push_back(RenderPassType::RenderPassType_AmbientOcclusion);
+						break;
 					case Properties::AtmScatteringRenderPass:
 						m_renderingPasses.push_back(RenderPassType::RenderPassType_AtmScattering);
 						break;
 					case Properties::BloomRenderPass:
 						m_renderingPasses.push_back(RenderPassType::RenderPassType_Bloom);
 						break;
+					case Properties::FinalRenderPass:
+						m_renderingPasses.push_back(RenderPassType::RenderPassType_Final);
+						break;
 					case Properties::GeometryRenderPass:
 						m_renderingPasses.push_back(RenderPassType::RenderPassType_Geometry);
 						break;
@@ -144,17 +150,96 @@ ErrorCode RendererScene::setup(const PropertySet &p_properties)
 					case Properties::LuminanceRenderPass:
 						m_renderingPasses.push_back(RenderPassType::RenderPassType_Luminance);
 						break;
-					case Properties::FinalRenderPass:
-						m_renderingPasses.push_back(RenderPassType::RenderPassType_Final);
-						break;
-					case Properties::AmbientOcclusionRenderPass:
-						m_renderingPasses.push_back(RenderPassType::RenderPassType_AmbientOcclusion);
+					case Properties::ShadowMappingPass:
+						m_renderingPasses.push_back(RenderPassType::RenderPassType_ShadowMapping);
 						break;
 				}
 			}
 		}
 	}
 
+	// Load shadow mapping data
+	if(auto &shadowMappingProperty = p_properties.getPropertySetByID(Properties::ShadowMapping); shadowMappingProperty)
+	{
+		for(decltype(shadowMappingProperty.getNumProperties()) i = 0, size = shadowMappingProperty.getNumProperties(); i < size; i++)
+		{
+			switch(shadowMappingProperty[i].getPropertyID())
+			{
+				// Set CSM penumbra size (shadow edge softness)
+				case Properties::PenumbraSize:
+					m_shadowMappingData.m_penumbraSize = shadowMappingProperty[i].getFloat();
+					break;
+
+					// Set CSM penumbra size scale range
+				case Properties::PenumbraScaleRange:
+					m_shadowMappingData.m_penumbraScaleRange = shadowMappingProperty[i].getVec2f();
+					break;
+
+				// Set CSM resolution property
+				case Properties::Resolution:
+					m_shadowMappingData.m_csmResolution = (unsigned int)shadowMappingProperty[i].getInt();
+					break;
+
+				// Set CSM cascade plane z clip distance multiplier
+				case Properties::ZClipping:
+					m_shadowMappingData.m_zClipping = shadowMappingProperty[i].getBool();
+					break;
+
+				// Set CSM cascade plane z clip distance multiplier
+				case Properties::ZPlaneMultiplier:
+					m_shadowMappingData.m_csmCascadePlaneZMultiplier = shadowMappingProperty[i].getFloat();
+					break;
+			}
+		}
+
+		// Find PCF property set
+		if(auto &pcfProperty = shadowMappingProperty.getPropertySetByID(Properties::PCF); pcfProperty)
+		{
+			// Find the Samples property
+			if(auto &samplesProperty = pcfProperty.getPropertyByID(Properties::Samples); samplesProperty)
+			{
+				// Get the number of samples and make sure it is not zero or lower
+				int samples = samplesProperty.getInt();
+				if(samples > 0)
+					m_shadowMappingData.m_numOfPCFSamples = (unsigned int)samples;
+			}
+		}
+
+		// Find Cascades property set
+		if(auto &cascadesProperty = shadowMappingProperty.getPropertySetByID(Properties::Cascades); cascadesProperty)
+		{
+			// Go over each array entry of Cascades property set
+			for(decltype(cascadesProperty.getNumPropertySets()) i = 0, size = cascadesProperty.getNumPropertySets(); i < size; i++)
+			{
+				ShadowCascadeData cascadeData;
+
+				// If the Distance property is present, add the shadow cascade distance and set the divider flag to FALSE
+				// Otherwise, if the Divider property is present, add the divider value and set the divider flag to TRUE
+				if(auto &distanceProperty = cascadesProperty.getPropertySetUnsafe(i).getPropertyByID(Properties::Distance); distanceProperty)
+				{
+					cascadeData.m_cascadeFarDistance = distanceProperty.getFloat();
+					cascadeData.m_distanceIsDivider = false;
+				}
+				else
+					if(auto &dividerProperty = cascadesProperty.getPropertySetUnsafe(i).getPropertyByID(Properties::Divider); dividerProperty)
+					{
+						cascadeData.m_cascadeFarDistance = dividerProperty.getFloat();
+						cascadeData.m_distanceIsDivider = true;
+					}
+
+				// Find and add the max bias value
+				if(auto &biasMaxProperty = cascadesProperty.getPropertySetUnsafe(i).getPropertyByID(Properties::BiasMax); biasMaxProperty)
+					cascadeData.m_maxBias = biasMaxProperty.getFloat();
+
+				// Find and add the penumbra scale value
+				if(auto &penumbraScaleProperty = cascadesProperty.getPropertySetUnsafe(i).getPropertyByID(Properties::PenumbraScale); penumbraScaleProperty)
+					cascadeData.m_penumbraScale = penumbraScaleProperty.getFloat();
+
+				m_shadowMappingData.m_shadowCascadePlaneDistances.push_back(cascadeData);
+			}
+		}
+	}
+
 	return ErrorCode::Success;
 }
 
@@ -187,16 +272,46 @@ void RendererScene::exportSetup(PropertySet &p_propertySet)
 	objectPoolSizePropertySet.addProperty(Properties::ModelComponent, (int)worldScene->getPoolSize<ModelComponent>());
 	objectPoolSizePropertySet.addProperty(Properties::ShaderComponent, (int)worldScene->getPoolSize<ShaderComponent>());
 
+	// Add rendering passes
 	exportRenderingPasses(p_propertySet, m_renderingPasses);
+
+	// Add shadow mapping data
+	auto &shadowMappingPropertySet = p_propertySet.addPropertySet(Properties::ShadowMapping);
+
+	shadowMappingPropertySet.addProperty(Properties::PenumbraSize, m_shadowMappingData.m_penumbraSize);
+	shadowMappingPropertySet.addProperty(Properties::PenumbraScaleRange, m_shadowMappingData.m_penumbraScaleRange);
+	shadowMappingPropertySet.addProperty(Properties::Resolution, (int)m_shadowMappingData.m_csmResolution);
+	shadowMappingPropertySet.addProperty(Properties::ZClipping, m_shadowMappingData.m_zClipping);
+	shadowMappingPropertySet.addProperty(Properties::ZPlaneMultiplier, m_shadowMappingData.m_csmCascadePlaneZMultiplier);
+
+	auto &pcfPropertySet = shadowMappingPropertySet.addPropertySet(Properties::PCF);
+	pcfPropertySet.addProperty(Properties::Samples, (int)m_shadowMappingData.m_numOfPCFSamples);
+
+	// Go over each CSM cascade plane
+	auto &cascadesPropertySet = shadowMappingPropertySet.addPropertySet(Properties::Cascades);
+	for(decltype(m_shadowMappingData.m_shadowCascadePlaneDistances.size()) i = 0, size = m_shadowMappingData.m_shadowCascadePlaneDistances.size(); i < size; i++)
+	{
+		// Set the export cascade type property to either Divider or Distance, based on the divider flag
+		Properties::PropertyID cascadeTypeProperty = m_shadowMappingData.m_shadowCascadePlaneDistances[i].m_distanceIsDivider ? Properties::Divider : Properties::Distance;
+
+		// Add the cascade plane array entry
+		auto &cascadeArrayEntryProperty = cascadesPropertySet.addPropertySet(Properties::ArrayEntry);
+
+		// Add distance or divider property
+		cascadeArrayEntryProperty.addProperty(cascadeTypeProperty, m_shadowMappingData.m_shadowCascadePlaneDistances[i].m_cascadeFarDistance);
+
+		// Add max bias property
+		cascadeArrayEntryProperty.addProperty(Properties::BiasMax, m_shadowMappingData.m_shadowCascadePlaneDistances[i].m_maxBias);
+
+		// Add penumbra scale property
+		cascadeArrayEntryProperty.addProperty(Properties::PenumbraScale, m_shadowMappingData.m_shadowCascadePlaneDistances[i].m_penumbraScale);
+	}
 }
 
 void RendererScene::activate()
 {
 	auto rendererSystem = static_cast<RendererSystem *>(m_system);
 
-	// Set rendering passes
-	rendererSystem->setRenderingPasses(m_renderingPasses);
-
 	// Set whether render the scene to texture or the whole screen
 	rendererSystem->getRenderer().setRenderFinalToTexture(m_renderToTexture);
 
@@ -208,6 +323,15 @@ void RendererScene::activate()
 
 	// Set the AO data
 	rendererSystem->getRenderer().setAmbientOcclusionData(m_sceneAOData);
+
+	// Set the shadow mapping data
+	rendererSystem->getRenderer().setShadowMappingData(m_shadowMappingData);
+
+	// Set rendering passes
+	rendererSystem->setRenderingPasses(m_renderingPasses);
+
+	// Update the internal shadow mapping enabled flag, as it is set based on the rendering passes that are given to the renderer system
+	m_shadowMappingData.m_shadowMappingEnabled = rendererSystem->getRenderer().getShadowMappingData().m_shadowMappingEnabled;
 }
 
 void RendererScene::deactivate()
@@ -1068,6 +1192,23 @@ void RendererScene::receiveData(const DataType p_dataType, void *p_data, const b
 			}
 			break;
 
+		case DataType::DataType_ShadowMappingData:
+			{
+				auto *shadowMappingData = static_cast<ShadowMappingData *>(p_data);
+
+				// Set the new shadow mapping data
+				m_shadowMappingData = *shadowMappingData;
+
+				// Send the new shadow mapping data to the renderer
+				auto rendererSystem = static_cast<RendererSystem *>(m_system);
+				rendererSystem->getRenderer().setShadowMappingData(m_shadowMappingData);
+
+				// Delete the received data if it has been marked for deletion (ownership transfered upon receiving)
+				if(p_deleteAfterReceiving)
+					delete shadowMappingData;
+			}
+			break;
+
 		case DataType::DataType_LoadShader:
 			{
 				ShaderLoader::ShaderProgram *shaderProgram = static_cast<ShaderLoader::ShaderProgram *>(p_data);

+ 12 - 5
Praxis3D/Source/RendererScene.h

@@ -236,6 +236,7 @@ public:
 	inline SceneObjects &getSceneObjects() { return m_sceneObjects; }
 	const inline RenderingPasses &getRenderingPasses() const { return m_renderingPasses; }
 	const inline AmbientOcclusionData &getAmbientOcclusionData() const { return m_sceneAOData; }
+	const inline ShadowMappingData &getShadowMappingData() const { return m_shadowMappingData; }
 	const glm::mat4 &getViewMatrix() const;
 	const glm::mat4 &getProjectionMatrix() const;
 
@@ -251,12 +252,18 @@ public:
 			Properties::PropertyID renderPassTypeProperty = Properties::Null;
 			switch(renderPassType)
 			{
+				case RenderPassType::RenderPassType_AmbientOcclusion:
+					renderPassTypeProperty = Properties::AmbientOcclusionRenderPass;
+					break;
 				case RenderPassType::RenderPassType_AtmScattering:
 					renderPassTypeProperty = Properties::AtmScatteringRenderPass;
 					break;
 				case RenderPassType::RenderPassType_Bloom:
 					renderPassTypeProperty = Properties::BloomRenderPass;
 					break;
+				case RenderPassType::RenderPassType_Final:
+					renderPassTypeProperty = Properties::FinalRenderPass;
+					break;
 				case RenderPassType::RenderPassType_Geometry:
 					renderPassTypeProperty = Properties::GeometryRenderPass;
 					break;
@@ -269,11 +276,8 @@ public:
 				case RenderPassType::RenderPassType_Luminance:
 					renderPassTypeProperty = Properties::LuminanceRenderPass;
 					break;
-				case RenderPassType::RenderPassType_Final:
-					renderPassTypeProperty = Properties::FinalRenderPass;
-					break;
-				case RenderPassType::RenderPassType_AmbientOcclusion:
-					renderPassTypeProperty = Properties::AmbientOcclusionRenderPass;
+				case RenderPassType::RenderPassType_ShadowMapping:
+					renderPassTypeProperty = Properties::ShadowMappingPass;
 					break;
 			}
 
@@ -304,6 +308,9 @@ private:
 	// Ambient occlusion data
 	AmbientOcclusionData m_sceneAOData;
 
+	// Shadow mapping data
+	ShadowMappingData m_shadowMappingData;
+
 	// Render-to-texture data
 	bool m_renderToTexture;
 	glm::ivec2 m_renderToTextureResolution;

+ 37 - 0
Praxis3D/Source/ShaderLoader.cpp

@@ -156,3 +156,40 @@ ShaderLoader::ShaderProgram::~ShaderProgram()
 {
 	delete m_uniformUpdater;
 }
+
+ErrorCode ShaderLoader::ShaderProgram::setVariableDefinition(const ShaderVariableDefinition &p_variable)
+{
+	ErrorCode returnError = ErrorCode::Success;
+
+	// Get the source code for the given shader
+	std::string &shaderSource = m_shaderSource[p_variable.m_shaderType];
+
+	// Check if the source code is present
+	if(!shaderSource.empty())
+	{
+		// Find the variable starting position in the source code
+		auto variableStartPosition = shaderSource.find(p_variable.m_variableName);
+
+		// Check if the variable was found
+		if(variableStartPosition != std::string::npos)
+		{
+			// Find the variable end position, by looking for the "next line" symbol, as that determines the end of the #define
+			auto variableEndPosition = shaderSource.find("\n", variableStartPosition + 1);
+
+			// Check if the end position was found
+			if(variableEndPosition != std::string::npos)
+			{
+				// Replace the variable and its value with the given one
+				shaderSource.replace(variableStartPosition, variableEndPosition - variableStartPosition, p_variable.m_variableName + " " + p_variable.m_variableValue);
+			}
+			else
+				returnError = ErrorCode::Shader_variable_not_found;
+		}
+		else
+			returnError = ErrorCode::Shader_variable_not_found;
+	}
+	else
+		returnError = ErrorCode::Shader_source_empty;
+
+	return returnError;
+}

+ 68 - 0
Praxis3D/Source/ShaderLoader.h

@@ -18,6 +18,22 @@ class ShaderLoader
 {
 	friend class ShaderProgram;
 public:
+	// Holds data on #define variable replacements in the shader source code
+	struct ShaderVariableDefinition
+	{
+		ShaderVariableDefinition(const ShaderType p_shaderType, const std::string &p_variableName, const std::string &p_value)
+			: m_shaderType(p_shaderType), m_variableName(p_variableName), m_variableValue(p_value) { }
+
+		bool operator==(const ShaderVariableDefinition &p_variable)
+		{
+			return	m_shaderType == p_variable.m_shaderType && 
+					m_variableName == p_variable.m_variableName;
+		}
+
+		ShaderType m_shaderType;
+		std::string m_variableName;
+		std::string m_variableValue;
+	};
 	class Shader
 	{
 		friend class ShaderLoader;
@@ -147,6 +163,7 @@ public:
 		friend class ShaderLoader;
 		friend class RendererFrontend;
 		friend class RendererScene;
+		friend class LightingPass;
 
 	public:
 		inline void addShader(ShaderType p_shaderType, const std::string &p_filename)
@@ -196,6 +213,12 @@ public:
 						}
 					}
 				}
+
+				// Re-set all the previously set variable definitions
+				for(const auto &variable : m_variableDefinitions)
+				{
+					setVariableDefinition(variable);
+				}
 			}
 
 			return returnError;
@@ -224,6 +247,30 @@ public:
 		
 		// Setters
 		inline void setShaderFilename(const ShaderType p_shaderType, const std::string &p_filename) { m_shaderFilename[p_shaderType] = p_filename; }
+		inline void resetLoadedToVideoMemoryFlag() { m_loadedToVideoMemory = false; }
+
+		// Functions for setting the #define variable values inside the shader code; must either be called before loading the shader to video memory (compiling) or the shader must be reloaded for the new value to take effect
+		ErrorCode setDefineValue(const ShaderType p_shaderType, const std::string &p_variableName, const std::string &p_value)
+		{
+			ErrorCode returnError = ErrorCode::Success;
+
+			// Create variable
+			ShaderVariableDefinition variable(p_shaderType, p_variableName, p_value);
+
+			// Try to set the variable
+			returnError = setVariableDefinition(variable);
+
+			// If the variable was set, save it
+			if(returnError == ErrorCode::Success)
+				saveVariableDefinition(variable);
+
+			return returnError;
+		}
+		inline ErrorCode setDefineValue(const ShaderType p_shaderType, const std::string &p_variableName, const bool p_value)			{ return setDefineValue(p_shaderType, p_variableName, Utilities::toString(p_value)); }
+		inline ErrorCode setDefineValue(const ShaderType p_shaderType, const std::string &p_variableName, const int p_value)			{ return setDefineValue(p_shaderType, p_variableName, Utilities::toString(p_value)); }
+		inline ErrorCode setDefineValue(const ShaderType p_shaderType, const std::string &p_variableName, const unsigned int p_value)	{ return setDefineValue(p_shaderType, p_variableName, Utilities::toString(p_value)); }
+		inline ErrorCode setDefineValue(const ShaderType p_shaderType, const std::string &p_variableName, const float p_value)			{ return setDefineValue(p_shaderType, p_variableName, Utilities::toString(p_value)); }
+		inline ErrorCode setDefineValue(const ShaderType p_shaderType, const std::string &p_variableName, const double p_value)			{ return setDefineValue(p_shaderType, p_variableName, Utilities::toString(p_value)); }
 
 		// Comparator operators
 		const inline bool operator==(const std::string &p_filename) const { return (m_combinedFilename == p_filename); }
@@ -271,6 +318,25 @@ public:
 		~ShaderProgram();
 
 		void setLoadedToVideoMemory(bool p_loaded) { m_loadedToVideoMemory = p_loaded; }
+		void saveVariableDefinition(const ShaderVariableDefinition &p_variable)
+		{
+			bool variableSet = false;
+
+			// If the variable definition already exist, update it
+			for(auto &variable : m_variableDefinitions)
+			{
+				if(p_variable == variable)
+				{
+					variable.m_variableValue = p_variable.m_variableValue;
+					variableSet = true;
+				}
+			}
+
+			// If the variable definition didn't exist, create a new one
+			if(!variableSet)
+				m_variableDefinitions.push_back(p_variable);
+		}
+		ErrorCode setVariableDefinition(const ShaderVariableDefinition &p_variable);
 
 		bool	m_defaultShader, 
 				m_loadedToMemory,
@@ -287,6 +353,8 @@ public:
 		unsigned int m_programHandle;
 
 		ShaderUniformUpdater *m_uniformUpdater;
+
+		std::vector<ShaderVariableDefinition> m_variableDefinitions;
 		
 		// Holds a default shader handle that the program handle can be tested against
 		static unsigned int m_defaultProgramHandle;

+ 11 - 1
Praxis3D/Source/ShaderUniformUpdater.cpp

@@ -45,7 +45,7 @@ ErrorCode ShaderUniformUpdater::generateTextureUpdateList()
 	// Make a vector of uniform classes and populate it
 	std::vector<BaseUniform*> uniformList;
 
-	// Framebuffer texture uniforms
+	// Geometry framebuffer texture uniforms
 	uniformList.push_back(new PositionMapUniform(m_shaderHandle));
 	uniformList.push_back(new DiffuseMapUniform(m_shaderHandle));
 	uniformList.push_back(new NormalMapUniform(m_shaderHandle));
@@ -57,6 +57,9 @@ ErrorCode ShaderUniformUpdater::generateTextureUpdateList()
 	uniformList.push_back(new InputMapUniform(m_shaderHandle));
 	uniformList.push_back(new OutputMapUniform(m_shaderHandle));
 
+	// Cascaded shadow map framebuffer texture uniforms
+	uniformList.push_back(new CSMDepthMapUniform(m_shaderHandle));
+
 	// Cubemap texture uniforms
 	uniformList.push_back(new DynamicEnvironmentMapUniform(m_shaderHandle));
 	uniformList.push_back(new StaticEnvironmentMapUniform(m_shaderHandle));
@@ -121,6 +124,7 @@ ErrorCode ShaderUniformUpdater::generatePerFrameList()
 	// Camera uniforms
 	uniformList.push_back(new CameraPosVecUniform(m_shaderHandle));
 	uniformList.push_back(new CameraTargetVecUniform(m_shaderHandle));
+	uniformList.push_back(new ProjPlaneRangeUniform(m_shaderHandle));
 
 	// Distance based fog uniforms
 	uniformList.push_back(new FogDensityUniform(m_shaderHandle));
@@ -163,6 +167,9 @@ ErrorCode ShaderUniformUpdater::generatePerFrameList()
 	uniformList.push_back(new BloomIntensityUniform(m_shaderHandle));
 	uniformList.push_back(new BloomTresholdUniform(m_shaderHandle));
 
+	// CSM
+	uniformList.push_back(new CSMPenumbraScaleRangeUniform(m_shaderHandle));
+	
 	// Luminance
 	uniformList.push_back(new InverseLogLuminanceRangeUniform(m_shaderHandle));
 	uniformList.push_back(new LogLuminanceRangeUniform(m_shaderHandle));
@@ -246,6 +253,9 @@ ErrorCode ShaderUniformUpdater::generateUniformBlockList()
 	// Lens flare effect parameters buffer
 	uniformBlockList.push_back(new LensFlareParametersUniform(m_shaderHandle));
 
+	// Cascaded shadow mapping matrix buffer
+	uniformBlockList.push_back(new CSMMatrixBufferUniform(m_shaderHandle));
+
 	// Go through each uniform and check if it is valid
 	// If it is, add it to the update list, if not, delete it
 	for(decltype(uniformBlockList.size()) i = 0, size = uniformBlockList.size(); i < size; i++)

+ 1 - 0
Praxis3D/Source/ShaderUniformUpdater.h

@@ -13,6 +13,7 @@
 class ShaderUniformUpdater
 {
 	friend class RendererBackend;
+	friend class LightingPass;
 public:
 	ShaderUniformUpdater(ShaderLoader::ShaderProgram &p_shader) : m_shader(p_shader)
 	{

+ 63 - 1
Praxis3D/Source/ShaderUniforms.h

@@ -42,7 +42,7 @@ class BaseUniformBlock
 {
 public:
 	BaseUniformBlock(const std::string &p_name, const unsigned int p_shaderHandle)
-		: m_name(p_name), m_shaderHandle(p_shaderHandle)
+		: m_name(p_name), m_shaderHandle(p_shaderHandle), m_uniformHandle(-1)
 	{
 		// Get the uniform location (returns -1 in case it is not present in the shader)
 		m_uniformHandle = glGetUniformBlockIndex(m_shaderHandle, p_name.c_str());
@@ -264,6 +264,26 @@ public:
 private:
 	glm::ivec2 m_screenSize;
 };
+class ProjPlaneRangeUniform : public BaseUniform
+{
+public:
+	ProjPlaneRangeUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().projPlaneRange, p_shaderHandle), m_zFar(0.0f), m_zNear(0.0f) { }
+
+	void update(const UniformData &p_uniformData)
+	{
+		if(m_zFar != p_uniformData.m_frameData.m_zFar || m_zNear != p_uniformData.m_frameData.m_zNear)
+		{
+			m_zFar = p_uniformData.m_frameData.m_zFar;
+			m_zNear = p_uniformData.m_frameData.m_zNear;
+
+			glUniform2f(m_uniformHandle, m_zFar, m_zNear);
+		}
+	}
+
+private:
+	float m_zFar;
+	float m_zNear;
+};
 class DeltaTimeMSUniform : public BaseUniform
 {
 public:
@@ -786,6 +806,7 @@ public:
 	}
 };
 
+// Gbuffer textures
 class PositionMapUniform : public BaseUniform
 {
 public:
@@ -887,6 +908,18 @@ public:
 	}
 };
 
+// CSM textures
+class CSMDepthMapUniform : public BaseUniform
+{
+public:
+	CSMDepthMapUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().csmDepthMapUniform, p_shaderHandle) { }
+
+	void update(const UniformData &p_uniformData)
+	{
+		glUniform1i(m_uniformHandle, CSMBufferTextureType::CSMBufferTextureType_CSMDepthMap);
+	}
+};
+
 /* Unused */ class SunGlowTextureUniform : public BaseUniform
 {
 public:
@@ -1051,6 +1084,25 @@ private:
 	int m_screenWidth;
 };
 
+class CSMPenumbraScaleRangeUniform : public BaseUniform
+{
+public:
+	CSMPenumbraScaleRangeUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().csmPenumbraScaleRange, p_shaderHandle) { }
+
+	void update(const UniformData &p_uniformData)
+	{
+		if(m_penumbraScaleRange != p_uniformData.m_frameData.m_shadowMappingData.m_penumbraScaleRange)
+		{
+			m_penumbraScaleRange = p_uniformData.m_frameData.m_shadowMappingData.m_penumbraScaleRange;
+
+			glUniform2f(m_uniformHandle, m_penumbraScaleRange.x, m_penumbraScaleRange.y);
+		}
+	}
+
+private:
+	glm::vec2 m_penumbraScaleRange;
+};
+
 class AtmIrradianceTextureUniform : public BaseUniform
 {
 public:
@@ -1447,6 +1499,16 @@ public:
 		updateBlockBinding(UniformBufferBinding::UniformBufferBinding_SSAOSampleBuffer);
 	}
 };
+class CSMMatrixBufferUniform : public BaseUniformBlock
+{
+public:
+	CSMMatrixBufferUniform(unsigned int p_shaderHandle) : BaseUniformBlock(Config::shaderVar().CSMDataSetBuffer, p_shaderHandle) { }
+
+	void update(const UniformData &p_uniformData)
+	{
+		updateBlockBinding(UniformBufferBinding::UniformBufferBinding_CSMMatrixBuffer);
+	}
+};
 
 class HDRShaderStorageBuffer : public BaseShaderStorageBlock
 {

+ 455 - 0
Praxis3D/Source/ShadowMappingPass.h

@@ -0,0 +1,455 @@
+#pragma once
+
+#include <glm/gtc/matrix_transform.hpp>
+#include <glm/gtc/type_ptr.hpp>
+#include <random>
+
+#include "RenderPassBase.h"
+
+class ShadowMappingPass : public RenderPass
+{
+public:
+	ShadowMappingPass(RendererFrontend &p_renderer) :
+		RenderPass(p_renderer, RenderPassType::RenderPassType_ShadowMapping),
+		m_csmPassShader(nullptr),
+		m_csmDataSetUniformBuffer(BufferType_Uniform, BufferBindTarget_Uniform, BufferUsageHint_DynamicDraw)
+	{
+		m_numOfCascades = (unsigned int)p_renderer.m_frameData.m_shadowMappingData.m_shadowCascadePlaneDistances.size();
+		m_csmResolution = p_renderer.m_frameData.m_shadowMappingData.m_csmResolution;
+	}
+
+	~ShadowMappingPass() 
+	{
+	}
+
+	ErrorCode init()
+	{
+		ErrorCode returnError = ErrorCode::Success;
+
+		m_name = "Shadow Mapping Pass";
+
+		const auto &shadowMappingData = m_renderer.m_frameData.m_shadowMappingData;
+
+		// Initialize the CSM framebuffer
+		m_renderer.m_backend.createFramebuffer(FramebufferType::FramebufferType_CSMBuffer, m_renderer.getFrameData());
+
+		// Get the current number of shadow cascades
+		m_numOfCascades = (unsigned int)shadowMappingData.m_shadowCascadePlaneDistances.size();
+
+		// Create the Cascaded Shadow Mapping pass shader
+		{
+			// Create a property-set used to load the shader
+			PropertySet shaderProperties(Properties::Shaders);
+			shaderProperties.addProperty(Properties::FragmentShader, Config::rendererVar().csm_pass_frag_shader);
+			shaderProperties.addProperty(Properties::GeometryShader, Config::rendererVar().csm_pass_geom_shader);
+			shaderProperties.addProperty(Properties::VertexShader, Config::rendererVar().csm_pass_vert_shader);
+
+			// Create the shader
+			m_csmPassShader = Loaders::shader().load(shaderProperties);
+
+			// Load the shader to memory
+			if(ErrorCode shaderError = m_csmPassShader->loadToMemory(); shaderError == ErrorCode::Success)
+			{
+				if(ErrorCode shaderVariableError = m_csmPassShader->setDefineValue(ShaderType::ShaderType_Geometry, Config::shaderVar().define_numOfCascades, m_numOfCascades); shaderVariableError != ErrorCode::Success)
+					ErrHandlerLoc::get().log(shaderVariableError, Config::shaderVar().define_numOfCascades, ErrorSource::Source_ShadowMappingPass);
+
+				// Queue the shader to be loaded to GPU
+				m_renderer.queueForLoading(*m_csmPassShader);
+			}
+			else
+				returnError = shaderError;
+		}
+
+		// Initialize CSM dataset
+		m_csmDataSet.reserve(m_numOfCascades);
+		updateCSMDataSet(shadowMappingData);
+
+		// Set data for CSM buffer
+		m_csmDataSetUniformBuffer.m_bindingIndex = UniformBufferBinding::UniformBufferBinding_CSMMatrixBuffer;
+		m_csmDataSetUniformBuffer.m_size = sizeof(CascadedShadowMapDataSet) * m_csmDataSet.size();
+		m_csmDataSetUniformBuffer.m_data = (void *)&m_csmDataSet[0];
+
+		// Load the CSM buffer
+		m_renderer.queueForLoading(m_csmDataSetUniformBuffer);
+
+		// 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)
+	{
+		// Get the current shadow mapping data
+		const auto &shadowMappingData = m_renderer.m_frameData.m_shadowMappingData;
+
+		if(p_sceneObjects.m_processDrawing && shadowMappingData.m_shadowMappingEnabled && !shadowMappingData.m_shadowCascadePlaneDistances.empty())
+		{
+			// If the number of shadow cascades has changed, set the new define inside the shader source and recompile the shader
+			// and resize the CSM framebuffer
+			if(m_numOfCascades != shadowMappingData.m_shadowCascadePlaneDistances.size())
+			{
+				m_numOfCascades = (unsigned int)shadowMappingData.m_shadowCascadePlaneDistances.size();
+
+				m_renderer.m_backend.getCSMFramebuffer()->setNumOfCascades(m_numOfCascades);
+
+				if(m_numOfCascades > 0)
+				{
+					if(ErrorCode shaderVariableError = m_csmPassShader->setDefineValue(ShaderType::ShaderType_Geometry, Config::shaderVar().define_numOfCascades, m_numOfCascades); shaderVariableError == ErrorCode::Success)
+					{
+						m_csmPassShader->resetLoadedToVideoMemoryFlag();
+
+						// Queue the shader to be loaded to GPU
+						m_renderer.queueForLoading(*m_csmPassShader);
+						m_renderer.passLoadCommandsToBackend();
+					}
+					else
+						ErrHandlerLoc::get().log(shaderVariableError, Config::shaderVar().define_numOfCascades, ErrorSource::Source_ShadowMappingPass);
+				}
+			}
+
+			// If the number of cascades has changed, update the CSM framebuffer
+			if(m_csmResolution != shadowMappingData.m_csmResolution)
+			{
+				m_csmResolution = shadowMappingData.m_csmResolution;
+				m_renderer.m_backend.getCSMFramebuffer()->setBufferSize(m_csmResolution, m_csmResolution);
+			}
+
+			// Update the CSM data set
+			updateCSMDataSet(shadowMappingData);
+
+			// Send the CSM data set to the GPU
+			m_csmDataSetUniformBuffer.m_updateSize = sizeof(CascadedShadowMapDataSet) * m_csmDataSet.size();
+			m_csmDataSetUniformBuffer.m_size = sizeof(CascadedShadowMapDataSet) * m_csmDataSet.size();
+			m_csmDataSetUniformBuffer.m_data = (void *)&m_csmDataSet[0];
+			m_renderer.queueForUpdate(m_csmDataSetUniformBuffer);
+			m_renderer.passUpdateCommandsToBackend();
+
+			// Prepare CSM framebuffer for rendering
+			m_renderer.m_backend.getCSMFramebuffer()->initFrame();
+
+			// Enable z clipping
+			if(shadowMappingData.m_zClipping)
+				glEnable(GL_DEPTH_CLAMP);
+
+			// Get known shader details
+			auto csmShaderHandle = m_csmPassShader->getShaderHandle();
+			auto &csmUniformUpdater = m_csmPassShader->getUniformUpdater();
+
+			// Iterate over all objects to be rendered with CSM shader
+			for(auto entity : p_sceneObjects.m_models)
+			{
+				ModelComponent &model = p_sceneObjects.m_models.get<ModelComponent>(entity);
+				if(model.isObjectActive())
+				{
+					SpatialComponent &spatialData = p_sceneObjects.m_models.get<SpatialComponent>(entity);
+					auto &modelData = model.getModelData();
+
+					for(decltype(modelData.size()) i = 0, size = modelData.size(); i < size; i++)
+					{
+						m_renderer.queueForDrawing(modelData[i],
+							csmShaderHandle,
+							csmUniformUpdater,
+							spatialData.getSpatialDataChangeManager().getWorldTransformWithScale(),
+							m_renderer.m_viewProjMatrix);
+					}
+				}
+			}
+
+			// Iterate over all objects to be rendered with a custom shader
+			//for(auto entity : p_sceneObjects.m_modelsWithShaders)
+			//{
+			//	ModelComponent &model = p_sceneObjects.m_modelsWithShaders.get<ModelComponent>(entity);
+			//	if(model.isObjectActive())
+			//	{
+			//		SpatialComponent &spatialData = p_sceneObjects.m_modelsWithShaders.get<SpatialComponent>(entity);
+			//		ShaderComponent &shader = p_sceneObjects.m_modelsWithShaders.get<ShaderComponent>(entity);
+			//		if(shader.isLoadedToVideoMemory())
+			//		{
+			//			auto &modelData = model.getModelData();
+
+			//			for(decltype(modelData.size()) i = 0, size = modelData.size(); i < size; i++)
+			//			{
+			//				m_renderer.queueForDrawing(modelData[i],
+			//					shader.getShaderData()->m_shader.getShaderHandle(),
+			//					shader.getShaderData()->m_shader.getUniformUpdater(),
+			//					spatialData.getSpatialDataChangeManager().getWorldTransformWithScale(),
+			//					m_renderer.m_viewProjMatrix);
+			//			}
+			//		}
+			//	}
+			//}
+
+			// Pass all the draw commands to be executed
+			m_renderer.passDrawCommandsToBackend();
+
+			// Disable z clipping if it was turned on before
+			if(shadowMappingData.m_zClipping)
+				glDisable(GL_DEPTH_CLAMP);
+		}
+	}
+
+private:
+	void calculateSplitPositions(std::vector<float> &p_splits, const float p_numOfSplits, const float p_zNear, const float p_zFar)
+	{
+		// Practical split scheme:
+		//
+		// CLi = n*(f/n)^(i/numsplits)
+		// CUi = n + (f-n)*(i/numsplits)
+		// Ci = CLi*(lambda) + CUi*(1-lambda)
+		//
+		// lambda scales between logarithmic and uniform
+		//
+
+		const float planeSplitWeight = 0.85f;
+
+		p_splits.clear();
+
+		for(int i = 0; i < p_numOfSplits; i++)
+		{
+			float fIDM = i / (float)p_numOfSplits;
+			float fLog = p_zNear * powf(p_zFar / p_zNear, fIDM);
+			float fUniform = p_zNear + (p_zFar - p_zNear) * fIDM;
+			p_splits.push_back(fLog * planeSplitWeight + fUniform * (1 - planeSplitWeight));
+		}
+
+		// make sure border values are accurate
+		p_splits.front() = p_zNear;
+		p_splits.back() = p_zFar;
+	}
+
+	std::vector<glm::vec4> getFrustumCornersWorldSpace(const glm::mat4 &p_projView)
+	{
+		std::vector<glm::vec4> returnFrustumCorners;
+
+		// Get the inverse view-projection matrix required for converting position to world space
+		const auto inv = glm::inverse(p_projView);
+
+		// Go over each frustum clip plane (X, Y and Z)
+		for(unsigned int x = 0; x < 2; ++x)
+		{
+			for(unsigned int y = 0; y < 2; ++y)
+			{
+				for(unsigned int z = 0; z < 2; ++z)
+				{
+					// Get the frustum corner position and convert it to world space
+					const glm::vec4 pt = inv * glm::vec4(2.0f * x - 1.0f, 2.0f * y - 1.0f, 2.0f * z - 1.0f, 1.0f);
+					returnFrustumCorners.push_back(pt / pt.w);
+				}
+			}
+		}
+
+		return returnFrustumCorners;
+	}
+	glm::mat4 calcLightSpaceMatrix(const ShadowMappingData &p_shadowMappingData, const float p_zNear, const float p_zFar)
+	{
+		// Calculate the camera projection matrix with the given z near and far clip distances
+		const auto cameraProjection = glm::perspectiveFov(
+			glm::radians(m_renderer.m_frameData.m_fov), 
+			(float)m_renderer.m_frameData.m_screenSize.x, 
+			(float)m_renderer.m_frameData.m_screenSize.y, 
+			p_zNear, 
+			p_zFar);
+
+		// Get the frustum corners of the camera projection
+		const auto frustumCorners = getFrustumCornersWorldSpace(cameraProjection * m_renderer.m_frameData.m_viewMatrix);
+
+		// Calculate frustum plane center position
+		glm::vec3 center = glm::vec3(0, 0, 0);
+		for(const auto &position : frustumCorners)
+		{
+			center += glm::vec3(position);
+		}
+		center /= frustumCorners.size();
+
+		// Get the direction light direction
+		const glm::vec3 lightDir = glm::normalize(m_renderer.m_frameData.m_directionalLight.m_direction);
+
+		// Calculate the light-view matrix (view matrix from the perspective of the directional light)
+		const auto lightView = glm::lookAt(center + lightDir, center, glm::vec3(0.0f, 1.0f, 0.0f));
+
+		float minX = std::numeric_limits<float>::max();
+		float maxX = std::numeric_limits<float>::lowest();
+		float minY = std::numeric_limits<float>::max();
+		float maxY = std::numeric_limits<float>::lowest();
+		float minZ = std::numeric_limits<float>::max();
+		float maxZ = std::numeric_limits<float>::lowest();
+
+		// Calculate the directional light orthogonal projection frustum corners
+		for(const auto &position : frustumCorners)
+		{
+			const auto trf = lightView * position;
+			minX = std::min(minX, trf.x);
+			maxX = std::max(maxX, trf.x);
+			minY = std::min(minY, trf.y);
+			maxY = std::max(maxY, trf.y);
+			minZ = std::min(minZ, trf.z);
+			maxZ = std::max(maxZ, trf.z);
+		}
+
+		// Scale the directional light orthogonal projection frustum z plane clip distances
+		if(minZ < 0)
+		{
+			minZ *= p_shadowMappingData.m_csmCascadePlaneZMultiplier;
+		}
+		else
+		{
+			minZ /= p_shadowMappingData.m_csmCascadePlaneZMultiplier;
+		}
+		if(maxZ < 0)
+		{
+			maxZ /= p_shadowMappingData.m_csmCascadePlaneZMultiplier;
+		}
+		else
+		{
+			maxZ *= p_shadowMappingData.m_csmCascadePlaneZMultiplier;
+		}
+
+		// Calculate the orthogonal light projection from the given frustum corners
+		const glm::mat4 lightProjection = glm::ortho(minX, maxX, minY, maxY, minZ, maxZ);
+
+		// Calculate the complete light space matrix (light view-projection)
+		return lightProjection * lightView;
+	}
+
+	glm::mat4 calcLightSpaceMatrix2(const float p_zNear, const float p_zFar)
+	{
+		glm::vec3 lightDir = glm::normalize(m_renderer.m_frameData.m_directionalLight.m_direction);
+
+		//p.SetCamera(m_pGameCamera->GetPos(), m_pGameCamera->GetTarget(), m_pGameCamera->GetUp());
+		glm::mat4 Cam = m_renderer.m_frameData.m_viewProjMatrix;
+		glm::mat4 CamInv = glm::inverse(m_renderer.m_frameData.m_viewProjMatrix);
+
+		//const auto lightView = glm::lookAt(center + lightDir, center, glm::vec3(0.0f, 1.0f, 0.0f));
+		//p.SetCamera(Vector3f(0.0f, 0.0f, 0.0f), m_dirLight.Direction, Vector3f(0.0f, 1.0f, 0.0f));
+		glm::mat4 LightM = glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), lightDir, glm::vec3(0.0f, 1.0f, 0.0f));
+
+		float ar = (float)m_renderer.m_frameData.m_screenSize.y / (float)m_renderer.m_frameData.m_screenSize.x;
+		float tanHalfHFOV = tanf(glm::radians(m_renderer.m_frameData.m_fov / 2.0f));
+		float tanHalfVFOV = tanf(glm::radians((m_renderer.m_frameData.m_fov * ar) / 2.0f));
+
+		//printf("ar %f tanHalfHFOV %f tanHalfVFOV %f\n", ar, tanHalfHFOV, tanHalfVFOV);
+
+		//for(uint i = 0; i < NUM_CASCADES; i++)
+		{
+			float xn = p_zNear * tanHalfHFOV;
+			float xf = p_zFar * tanHalfHFOV;
+			float yn = p_zNear * tanHalfVFOV;
+			float yf = p_zFar * tanHalfVFOV;
+
+			//printf("xn %f xf %f\n", xn, xf);
+			//printf("yn %f yf %f\n", yn, yf);
+
+			glm::vec4 frustumCorners[8] = {
+				// near face
+				glm::vec4(xn,   yn, p_zNear, 1.0),
+				glm::vec4(-xn,  yn, p_zNear, 1.0),
+				glm::vec4(xn,  -yn, p_zNear, 1.0),
+				glm::vec4(-xn, -yn, p_zNear, 1.0),
+
+				// far face
+				glm::vec4(xf,   yf, p_zFar, 1.0),
+				glm::vec4(-xf,  yf, p_zFar, 1.0),
+				glm::vec4(xf,  -yf, p_zFar, 1.0),
+				glm::vec4(-xf, -yf, p_zFar, 1.0)
+			};
+
+			glm::vec4 frustumCornersL[8];
+
+			float minX = std::numeric_limits<float>::max();
+			float maxX = std::numeric_limits<float>::min();
+			float minY = std::numeric_limits<float>::max();
+			float maxY = std::numeric_limits<float>::min();
+			float minZ = std::numeric_limits<float>::max();
+			float maxZ = std::numeric_limits<float>::min();
+
+			for(unsigned int j = 0; j < 8; j++)
+			{
+				//printf("Frustum: ");
+				glm::vec4 vW = CamInv * frustumCorners[j];
+				//vW.Print();
+				//printf("Light space: ");
+				frustumCornersL[j] = LightM * vW;
+				//frustumCornersL[j].Print();
+				//printf("\n");
+
+				minX = std::min(minX, frustumCornersL[j].x);
+				maxX = std::max(maxX, frustumCornersL[j].x);
+				minY = std::min(minY, frustumCornersL[j].y);
+				maxY = std::max(maxY, frustumCornersL[j].y);
+				minZ = std::min(minZ, frustumCornersL[j].z);
+				maxZ = std::max(maxZ, frustumCornersL[j].z);
+			}
+
+			//printf("BB: %f %f %f %f %f %f\n", minX, maxX, minY, maxY, minZ, maxZ);
+
+			//m_shadowOrthoProjInfo[i].r = maxX;
+			//m_shadowOrthoProjInfo[i].l = minX;
+			//m_shadowOrthoProjInfo[i].b = minY;
+			//m_shadowOrthoProjInfo[i].t = maxY;
+			//m_shadowOrthoProjInfo[i].f = maxZ;
+			//m_shadowOrthoProjInfo[i].n = minZ;
+
+			const glm::mat4 lightView = glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), lightDir, glm::vec3(0.0f, 1.0f, 0.0f));
+			const glm::mat4 lightProjection = glm::ortho(minX, maxX, minY, maxY, minZ, maxZ);
+			return lightProjection * lightView;
+		}
+	}
+
+	void updateCSMDataSet(const ShadowMappingData &p_shadowMappingData)
+	{
+		// Clear the old data
+		m_csmDataSet.clear();
+
+		float firstPlaneZFar = 1.0f;
+
+		if(!p_shadowMappingData.m_shadowCascadePlaneDistances.empty())
+			firstPlaneZFar = p_shadowMappingData.m_shadowCascadePlaneDistances[0].m_distanceIsDivider ?
+				m_renderer.getFrameData().m_zFar / p_shadowMappingData.m_shadowCascadePlaneDistances[0].m_cascadeFarDistance :
+				p_shadowMappingData.m_shadowCascadePlaneDistances[0].m_cascadeFarDistance;
+
+		const float initialPoissonSampleScale = 1.0f / (p_shadowMappingData.m_penumbraSize / 1000.0f);
+
+		for(decltype(p_shadowMappingData.m_shadowCascadePlaneDistances.size()) i = 0, size = p_shadowMappingData.m_shadowCascadePlaneDistances.size(); i < size; i++)
+		{
+			// If the divider flag is set, calculate the cascade z-near distance by diving the camera's z-far distance;
+			// Otherwise, set the cascade distance directly
+			const float planeZFar = p_shadowMappingData.m_shadowCascadePlaneDistances[i].m_distanceIsDivider ?
+				m_renderer.getFrameData().m_zFar / p_shadowMappingData.m_shadowCascadePlaneDistances[i].m_cascadeFarDistance :
+				p_shadowMappingData.m_shadowCascadePlaneDistances[i].m_cascadeFarDistance;
+
+			// Get plane z-near, which is the previous plane z-far; for the first entry, use camera z-near
+			const float planeZNear = i == 0 ? m_renderer.m_frameData.m_zNear : m_csmDataSet.back().m_cascadePlaneDistance;
+
+			const float poissonSampleScale = initialPoissonSampleScale * (planeZFar / firstPlaneZFar);
+
+			// Add the light space matrix and cascade plane distance to the CSM dataset
+			m_csmDataSet.emplace_back(
+				calcLightSpaceMatrix(p_shadowMappingData, planeZNear, planeZFar), 
+				planeZFar, 
+				p_shadowMappingData.m_shadowCascadePlaneDistances[i].m_maxBias, 
+				poissonSampleScale, 
+				p_shadowMappingData.m_shadowCascadePlaneDistances[i].m_penumbraScale);
+		}
+	}
+
+	// CSM settings
+	unsigned int m_numOfCascades;
+	unsigned int m_csmResolution;
+	std::vector<std::pair<float, float>> m_csmCascade;
+
+	// Cascaded shadow map pass shaders
+	ShaderLoader::ShaderProgram *m_csmPassShader;
+
+	// CSM uniform buffer
+	RendererFrontend::ShaderBuffer m_csmDataSetUniformBuffer;
+
+	// CSM dataset
+	std::vector<CascadedShadowMapDataSet> m_csmDataSet;
+};

+ 12 - 0
Praxis3D/Source/UniformData.h

@@ -14,6 +14,10 @@ struct UniformFrameData
 		m_numPointLights = 0;
 		m_numSpotLights = 0;
 
+		m_zFar = Config::graphicsVar().z_far;
+		m_zNear = Config::graphicsVar().z_near;
+		m_fov = Config::graphicsVar().fov;
+
 		m_bloomTreshold = glm::vec4(0.0f);
 
 		m_texelSize = glm::vec2(1.0f);
@@ -48,12 +52,20 @@ struct UniformFrameData
 	unsigned int m_numPointLights,
 				 m_numSpotLights;
 
+	// Projection data
+	float m_zFar;
+	float m_zNear;
+	float m_fov;
+
 	// Ambient occlusion data
 	AmbientOcclusionData m_aoData;
 
 	// Bloom pass data
 	glm::vec4 m_bloomTreshold;
 
+	// Cascaded shadow mapping data
+	ShadowMappingData m_shadowMappingData;
+
 	// Misc
 	glm::vec2 m_texelSize;
 	int m_mipLevel;

+ 3 - 3
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-24
+#define PRAXIS3D_COMMIT_DATE 2024-01-10
 
 #define PRAXIS3D_VERSION_MAJOR 0
-#define PRAXIS3D_VERSION_MINOR 1
-#define PRAXIS3D_VERSION_PATCH 3
+#define PRAXIS3D_VERSION_MINOR 2
+#define PRAXIS3D_VERSION_PATCH 0
 
 #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)

+ 78 - 2
Praxis3D/imgui.ini

@@ -203,7 +203,7 @@ Size=240,71
 Collapsed=0
 
 [Window][Open scene##OpenSceneFileDialog]
-Pos=599,114
+Pos=733,115
 Size=876,586
 Collapsed=0
 
@@ -213,7 +213,7 @@ Size=855,618
 Collapsed=0
 
 [Window][##AddEntityPopup]
-Pos=260,1097
+Pos=262,399
 Size=400,135
 Collapsed=0
 
@@ -1676,6 +1676,82 @@ Column 0  Sort=0v
 RefScale=13
 Column 0  Sort=0v
 
+[Table][0xA39D3C78,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xAE03508A,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x0CED853E,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xBAFCE26F,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x0ABC7CDF,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x48C7FC26,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x14231648,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x51CD04FC,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x3F72A664,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x52788278,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x24DD14AA,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xA103C7CB,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x880EFC2B,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xFEAB6AF9,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x5CED447F,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x02E28BC8,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0xDAB0CC37,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x4EAACEE4,4]
+RefScale=13
+Column 0  Sort=0v
+
+[Table][0x976BDEEE,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