Просмотр исходного кода

Implemented auto exposure (using luminance histogram calculated in compute shaders) and tonemapping
Added LuminancePass as a post-process effect
Added luminanceHistogram compute shader that calculates a luminance histogram (within defined range) of the screen
Added luminanceAverage compute shader that calculates the average luminance of the screen from the histogram
Added tonemapping shader that applies auto-exposure compensation based on screen luminance average and has multiple tonemapping algorithms that can be chosed on run-time
Added an additional option for buffer binding target type for buffers that are used in shaders
Added options for different data types, data formats and mipmapping for texture loading in RendererBackend
Added a way to create Texture2D without loading from a file (intended for rendering passes internal use between shaders)
Added more shader uniforms for new rendering passes
Added additional memory barrier types for compute shaders
Added more config options (for new effects) and error sources
Changed the way error codes are loaded to an error hash map for easier code maintenance
Removed old tonemapping and gamma-correction from finalPass shader. It now only performs the screen copying to the backbuffer

Paul A 3 лет назад
Родитель
Сommit
4db6d2bfc3
34 измененных файлов с 1057 добавлено и 124 удалено
  1. 2 2
      Praxis3D/Data/Maps/componentTest.pmap
  2. 16 17
      Praxis3D/Data/Shaders/bloomDownscale.comp
  3. 5 9
      Praxis3D/Data/Shaders/finalPass.frag
  4. 64 0
      Praxis3D/Data/Shaders/luminanceAverage.comp
  5. 75 0
      Praxis3D/Data/Shaders/luminanceHistogram.comp
  6. 307 0
      Praxis3D/Data/Shaders/tonemapping.frag
  7. 10 0
      Praxis3D/Data/Shaders/tonemapping.vert
  8. 2 1
      Praxis3D/Data/config.ini
  9. 1 0
      Praxis3D/Data/error-strings-eng.data
  10. 1 0
      Praxis3D/Praxis3D.vcxproj
  11. 3 0
      Praxis3D/Praxis3D.vcxproj.filters
  12. 1 1
      Praxis3D/Source/AtmScatteringPass.h
  13. 15 6
      Praxis3D/Source/BloomPass.h
  14. 11 8
      Praxis3D/Source/CommandBuffer.h
  15. 43 1
      Praxis3D/Source/CommonDefinitions.h
  16. 30 1
      Praxis3D/Source/Config.cpp
  17. 35 1
      Praxis3D/Source/Config.h
  18. 1 0
      Praxis3D/Source/ErrorCodes.h
  19. 9 2
      Praxis3D/Source/ErrorHandler.cpp
  20. 3 2
      Praxis3D/Source/FinalPass.h
  21. 1 44
      Praxis3D/Source/GeometryBuffer.cpp
  22. 1 1
      Praxis3D/Source/HdrMappingPass.h
  23. 1 1
      Praxis3D/Source/LenseFlarePass.h
  24. 3 2
      Praxis3D/Source/LightingPass.h
  25. 132 0
      Praxis3D/Source/LuminancePass.h
  26. 1 1
      Praxis3D/Source/PostProcessPass.h
  27. 4 0
      Praxis3D/Source/RendererBackend.cpp
  28. 43 14
      Praxis3D/Source/RendererBackend.h
  29. 6 0
      Praxis3D/Source/RendererFrontend.cpp
  30. 7 1
      Praxis3D/Source/RendererFrontend.h
  31. 12 1
      Praxis3D/Source/ShaderUniformUpdater.cpp
  32. 157 4
      Praxis3D/Source/ShaderUniforms.h
  33. 40 2
      Praxis3D/Source/TextureLoader.cpp
  34. 15 2
      Praxis3D/Source/TextureLoader.h

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

@@ -71,7 +71,7 @@
 				{
 					"LocalPosition": "0.0f, 0.5f, 0.0f",
 					"LocalRotation": "0.0f, 45.0f, 0.0f",
-					"LocalScale": "1.0f, 1.0f, 1.0f"
+					"LocalScale": "10.0f, 10.0f, 10.0f"
 				}
 			},
 			"Physics":
@@ -341,7 +341,7 @@
 				{
 					"Type": "DirectionalLight",
 					"Color": "1.0f, 0.0f, 0.0f",
-					"Intensity": "20.0f"
+					"Intensity": "10.0f"
 				}
 			}
 		},

+ 16 - 17
Praxis3D/Data/Shaders/bloomDownscale.comp

@@ -35,7 +35,9 @@ vec4 calculateQuadraticThreshold(vec4 p_color, float p_threshold, vec3 p_curve)
 // Calculate Luma of the the color
 float calculateLuma(vec3 p_color)
 {
-    return dot(p_color, vec3(0.2126729, 0.7151522, 0.0721750));
+	// Taken from RTR vol 4 pg. 278
+	return dot(p_color, vec3(0.2125, 0.7154, 0.0721));
+    //return dot(p_color, vec3(0.2126729, 0.7151522, 0.0721750));
 }
 
 // [Karis2013] proposed reducing the dynamic range before averaging
@@ -49,20 +51,19 @@ shared float sharedBufferR[TILE_PIXEL_COUNT];
 shared float sharedBufferG[TILE_PIXEL_COUNT];
 shared float sharedBufferB[TILE_PIXEL_COUNT];
 
-void storeLDS(int p_idx, vec4 p_color)
+void storeLDS(int p_index, vec4 p_color)
 {
-    sharedBufferR[p_idx] = p_color.r;
-    sharedBufferG[p_idx] = p_color.g;
-    sharedBufferB[p_idx] = p_color.b;
+    sharedBufferR[p_index] = p_color.r;
+    sharedBufferG[p_index] = p_color.g;
+    sharedBufferB[p_index] = p_color.b;
 }
 
-vec4 loadLDS(uint p_idx)
+vec4 loadLDS(uint p_index)
 {
-    return vec4(sharedBufferR[p_idx], sharedBufferG[p_idx], sharedBufferB[p_idx], 1.0);
+    return vec4(sharedBufferR[p_index], sharedBufferG[p_index], sharedBufferB[p_index], 1.0);
 }
 
 layout(local_size_x = GROUP_SIZE, local_size_y = GROUP_SIZE) in;
-
 void main()
 {
 	ivec2 texCoord = ivec2(gl_GlobalInvocationID);
@@ -106,19 +107,17 @@ void main()
     vec2 div = (1.0 / 4.0) * vec2(0.5, 0.125);
 
 	// Sum and average out the colors based on the weights
-    vec4 c =  calculateKarisAverage((D + E + I + J) * div.x);
-         c += calculateKarisAverage((A + B + G + F) * div.y);
-         c += calculateKarisAverage((B + C + H + G) * div.y);
-         c += calculateKarisAverage((F + G + L + K) * div.y);
-         c += calculateKarisAverage((G + H + M + L) * div.y);
+    vec4 finalColor =  calculateKarisAverage((D + E + I + J) * div.x);
+         finalColor += calculateKarisAverage((A + B + G + F) * div.y);
+         finalColor += calculateKarisAverage((B + C + H + G) * div.y);
+         finalColor += calculateKarisAverage((F + G + L + K) * div.y);
+         finalColor += calculateKarisAverage((G + H + M + L) * div.y);
 
 	// Use threshold for the full sized image
 	if(mipLevel == 0)
     {
-        c = calculateQuadraticThreshold(c, bloomTreshold.x, bloomTreshold.yzw);
+        finalColor = calculateQuadraticThreshold(finalColor, bloomTreshold.x, bloomTreshold.yzw);
     }
 
-	imageStore(outputColorMap, texCoord, c);
-	//imageStore(outputColorMap, pixel_coords, textureLod(inputColorMap, pixel_coords, mipLevel));
-	//imageStore(outputColorMap, pixel_coords, vec4(0.0, 1.0, 0.0, 1.0));
+	imageStore(outputColorMap, texCoord, finalColor);
 }

+ 5 - 9
Praxis3D/Data/Shaders/finalPass.frag

@@ -2,7 +2,7 @@
 
 //#define ENABLE_SIMPLE_TONE_MAPPING
 //#define ENABLE_REINHARD_TONE_MAPPING
-#define ENABLE_FILMIC_TONE_MAPPING
+//#define ENABLE_FILMIC_TONE_MAPPING
 
 out vec4 outputColor;
 
@@ -71,11 +71,11 @@ void main(void)
 	vec2 texCoord = calcTexCoord();
 	
 	// Perform gamma correction on the color from the final framebuffer
-	vec3 fragmentColor = texture(inputColorMap, texCoord).xyz * exposure;
+	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);
@@ -92,12 +92,8 @@ void main(void)
 	#endif
 	
 	// Perform gamma correction as the last step of the fragment color
-	fragmentColor = gammaCorrection(fragmentColor, gamma);
+	//fragmentColor = gammaCorrection(fragmentColor, gamma);
 	
 	// Write the color to the framebuffer
-	outputColor = vec4(fragmentColor, 1.0);
-	//outputColor = vec4(texture(inputColorMap, texCoord).xyz, 1.0);
-	//outputColor = vec4(1.0, 0.0, 1.0, 1.0);
-	//outputColor = vec4(texture(emissiveMap, texCoord).xyz, 1.0);
-	
+	outputColor = vec4(fragmentColor, 1.0);	
 }

+ 64 - 0
Praxis3D/Data/Shaders/luminanceAverage.comp

@@ -0,0 +1,64 @@
+#version 460
+
+#define GROUP_SIZE 256
+#define THREADS_X 256
+#define THREADS_Y 1
+#define THREADS_Z 1
+
+layout(binding = 0, r16f) uniform image2D averageLuminanceTexture;
+layout(binding = 1, std430) buffer histogramBuffer
+{
+	uint histogram[256];
+};
+
+uniform float deltaTimeS;
+uniform float minLogLuminance;
+uniform float logLuminanceRange;
+uniform uint screenNumOfPixels;
+
+// Shared
+shared uint histogramShared[GROUP_SIZE];
+
+layout (local_size_x = THREADS_X, local_size_y = THREADS_Y, local_size_z = THREADS_Z) in;
+void main() 
+{
+	// Get the count from the histogram buffer
+	uint countForThisBin = histogram[gl_LocalInvocationIndex];
+	histogramShared[gl_LocalInvocationIndex] = countForThisBin * gl_LocalInvocationIndex;
+
+	barrier();
+
+	// Reset the count stored in the buffer in anticipation of the next pass
+	histogram[gl_LocalInvocationIndex] = 0;
+
+	// This loop will perform a weighted count of the luminance range
+	for (uint cutoff = (GROUP_SIZE >> 1); cutoff > 0; cutoff >>= 1) 
+	{
+		if (uint(gl_LocalInvocationIndex) < cutoff) 
+		{
+			histogramShared[gl_LocalInvocationIndex] += histogramShared[gl_LocalInvocationIndex + cutoff];
+		}
+
+		barrier();
+	}
+	
+	// We only need to calculate this once, so only a single thread is needed.
+	if(gl_LocalInvocationIndex == 0) 
+	{
+		// Here we take our weighted sum and divide it by the number of pixels
+		// that had luminance greater than zero (since the index == 0, we can
+		// use countForThisBin to find the number of black pixels)
+		float weightedLogAverage = (histogramShared[0] / max(screenNumOfPixels - float(countForThisBin), 1.0)) - 1.0;
+
+		// Map from our histogram space to actual luminance
+		float weightedAvgLum = exp2(((weightedLogAverage / 254.0) * logLuminanceRange) + minLogLuminance);
+
+		float time = clamp(deltaTimeS * 1, 0.1, 1.0);//clamp(1.0 - exp(deltaTimeS * 1.1), 0.1, 1.0);
+
+		// The new stored value will be interpolated using the last frames value
+		// to prevent sudden shifts in the exposure.
+		float lumLastFrame = imageLoad(averageLuminanceTexture, ivec2(0, 0)).x;
+		float adaptedLum = lumLastFrame + (weightedAvgLum - lumLastFrame) * time;
+		imageStore(averageLuminanceTexture, ivec2(0, 0), vec4(adaptedLum, 0.0, 0.0, 0.0));
+	}
+}

+ 75 - 0
Praxis3D/Data/Shaders/luminanceHistogram.comp

@@ -0,0 +1,75 @@
+#version 460
+
+#define GROUP_SIZE 256
+#define THREADS_X 16
+#define THREADS_Y 16
+#define THREADS_Z 1
+#define EPSILON 0.000001
+
+layout(rgba16f) readonly uniform highp image2D inputColorMap;
+layout(std430, binding = 1) buffer histogramBuffer
+{
+	uint histogram[256];
+};
+
+uniform ivec2 screenSize;
+uniform float minLogLuminance;
+uniform float inverseLogLuminanceRange;
+
+// Shared histogram buffer used for storing intermediate sums for each work group
+shared uint histogramShared[GROUP_SIZE];
+
+float calcLuminance(vec3 p_color)
+{
+	// Taken from RTR vol 4 pg. 278
+	return dot(p_color, vec3(0.2125, 0.7154, 0.0721));
+    //return dot(color, vec3(0.2126, 0.7152, 0.0722));
+}
+
+// For a given color and luminance range, return the histogram bin index
+uint colorToBin(vec3 p_color, float p_minLogLuminance, float p_inverseLogLuminanceRange) 
+{
+	// Convert our RGB value to Luminance, see note for RGB_TO_LUM macro above
+	float luminance = calcLuminance(p_color);
+
+	// Avoid taking the log of zero
+	if (luminance < EPSILON) 
+	{
+		return 0;
+	}
+	
+	// Calculate the log_2 luminance and express it as a value in [0.0, 1.0]
+	// where 0.0 represents the minimum luminance, and 1.0 represents the max.
+	float logLuminance = clamp((log2(luminance) - p_minLogLuminance) * p_inverseLogLuminanceRange, 0.0, 1.0);
+
+	// Map [0, 1] to [1, 255]. The zeroth bin is handled by the epsilon check above.
+	return uint(logLuminance * 254.0 + 1.0);
+}
+
+layout (local_size_x = THREADS_X, local_size_y = THREADS_Y, local_size_z = THREADS_Z) in;
+void main() 
+{
+    ivec2 texCoord = ivec2(gl_GlobalInvocationID.xy);
+		
+	// Initialize the bin for this thread to 0
+	histogramShared[gl_LocalInvocationIndex] = 0;
+	barrier();
+
+	// Ignore threads that map to areas beyond the bounds of our HDR image
+	if (gl_GlobalInvocationID.x < screenSize.x && gl_GlobalInvocationID.y < screenSize.y)
+	{
+		vec3 hdrColor = imageLoad(inputColorMap, ivec2(texCoord)).xyz;
+		uint binIndex = colorToBin(hdrColor, minLogLuminance, inverseLogLuminanceRange);
+		// We use an atomic add to ensure we don't write to the same bin in our
+		// histogram from two different threads at the same time.
+		atomicAdd(histogramShared[binIndex], 1);
+	}
+
+	// Wait for all threads in the work group to reach this point before adding our
+	// local histogram to the global one
+	barrier();
+
+	// Technically there's no chance that two threads write to the same bin here,
+	// but different work groups might! So we still need the atomic add.
+	atomicAdd(histogram[gl_LocalInvocationIndex], histogramShared[gl_LocalInvocationIndex]);
+}

+ 307 - 0
Praxis3D/Data/Shaders/tonemapping.frag

@@ -0,0 +1,307 @@
+#version 430 core
+
+out vec4 outputColor;
+
+uniform ivec2 screenSize;
+uniform float gamma;
+uniform int tonemapMethod;
+
+uniform sampler2D inputColorMap;
+uniform sampler2D averageLuminanceTexture;
+
+vec2 calcTexCoord(void)
+{
+    return gl_FragCoord.xy / screenSize;
+}
+
+float log10(float p_x)
+{
+    //log10(x) = log(x) / log(10) = (1 / log(10)) * log(x)
+    const float d = 1.0 / log(10.0);
+    return d * log(p_x);
+}
+
+vec3 splatVec3(float p_x) 
+{ 
+	return vec3(p_x, p_x, p_x); 
+}
+
+// Convert RGB to CIE 1931 XYZ color space
+vec3 convertRGB2XYZ(vec3 p_rgb)
+{
+	// Reference:
+	// RGB/XYZ Matrices
+	// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
+	vec3 xyz;
+	xyz.x = dot(vec3(0.4124564, 0.3575761, 0.1804375), p_rgb);
+	xyz.y = dot(vec3(0.2126729, 0.7151522, 0.0721750), p_rgb);
+	xyz.z = dot(vec3(0.0193339, 0.1191920, 0.9503041), p_rgb);
+	return xyz;
+}
+
+// Convert XYZ to RGB color space
+vec3 convertXYZ2RGB(vec3 p_xyz)
+{
+	vec3 rgb;
+	rgb.x = dot(vec3( 3.2404542, -1.5371385, -0.4985314), p_xyz);
+	rgb.y = dot(vec3(-0.9692660,  1.8760108,  0.0415560), p_xyz);
+	rgb.z = dot(vec3( 0.0556434, -0.2040259,  1.0572252), p_xyz);
+	return rgb;
+}
+
+// Convert XYZ to Yxy color space
+vec3 convertXYZ2Yxy(vec3 p_xyz)
+{
+	// Reference:
+	// http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_xyY.html
+	float inv = 1.0 / dot(p_xyz, vec3(1.0, 1.0, 1.0));
+	return vec3(p_xyz.y, p_xyz.x * inv, p_xyz.y * inv);
+}
+
+// Convert Yxy to XYZ color space
+vec3 convertYxy2XYZ(vec3 p_Yxy)
+{
+	// Reference:
+	// http://www.brucelindbloom.com/index.html?Eqn_xyY_to_XYZ.html
+	vec3 xyz;
+	xyz.x = p_Yxy.x * p_Yxy.y / p_Yxy.z;
+	xyz.y = p_Yxy.x;
+	xyz.z = p_Yxy.x * (1.0 - p_Yxy.y - p_Yxy.z) / p_Yxy.z;
+	return xyz;
+}
+
+// Convert RGB to Yxy color space
+vec3 convertRGB2Yxy(vec3 p_rgb)
+{
+	return convertXYZ2Yxy(convertRGB2XYZ(p_rgb));
+}
+
+// Convert Yxy to RGB color space
+vec3 convertYxy2RGB(vec3 p_Yxy)
+{
+	return convertXYZ2RGB(convertYxy2XYZ(p_Yxy));
+}
+
+vec3 gammaCorrectionAccurate(vec3 p_color)
+{
+	vec3 lo  = p_color * 12.92;
+	vec3 hi  = pow(abs(p_color), splatVec3(1.0 / 2.4) ) * 1.055 - 0.055;
+	vec3 rgb = mix(hi, lo, vec3(lessThanEqual(p_color, splatVec3(0.0031308))));
+	return rgb;
+}
+
+vec3 gammaCorrection(vec3 p_color, float p_gamma)
+{
+	return pow(p_color, vec3(1.0 / p_gamma));
+}
+
+vec3 compensateByLuminance(vec3 p_color, float p_luminance)
+{
+	float KeyValue = 1.03 - (2.0 / (log10(p_luminance + 1.0) + 2.0));
+    float ExposureValue = log2(KeyValue / p_luminance);// + _ManualBias;
+    return p_color * exp2(ExposureValue);
+}
+
+// Simple reinhard tone mapping
+vec3 tonemap_reinhard(vec3 p_color)
+{
+	return p_color / (p_color + vec3(1.0));
+}
+
+// Simple reinhard tone mapping with white point
+vec3 tonemap_reinhardWhitePoint(vec3 p_color, float p_whiteSqr)
+{
+	return (p_color * (1.0 + p_color / p_whiteSqr)) / (1.0 + p_color);
+}
+	
+// Filmic tone mapping using an algorithm created by John Hable for Uncharted 2
+vec3 tonemap_filmic(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
+}
+
+// Filimic tonemapping from Uncharted 2
+vec3 tonemap_Uncharted2(vec3 p_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;
+	p_color = ((p_color * (A * p_color + C * B) + D * E) / (p_color * (A * p_color + B) + D * F)) - E / F;
+	float white = ((W * (A * W + C * B) + D * E) / (W * (A * W + B) + D * F)) - E / F;
+	p_color /= white;
+	return p_color;
+}
+
+// Unreal engine 3 tonemapping
+float tonemap_Unreal(float p_color) 
+{
+	// Unreal 3, Documentation: "Color Grading"
+	// Adapted to be close to Tonemap_ACES, with similar range
+	// Gamma 2.2 correction is baked in, don't use with sRGB conversion!
+	return p_color / (p_color + 0.155) * 1.019;
+}
+
+// ACES tonemapping
+float tonemap_ACES(float p_color) 
+{
+	// Narkowicz 2015, "ACES Filmic Tone Mapping Curve"
+	const float a = 2.51;
+	const float b = 0.03;
+	const float c = 2.43;
+	const float d = 0.59;
+	const float e = 0.14;
+	
+	return (p_color * (a * p_color + b)) / (p_color * (c * p_color + d) + e);
+}
+
+// Lottes 2016 tonemapping
+float tonemap_Lottes(float p_color) 
+{
+	// Lottes 2016, "Advanced Techniques and Optimization of HDR Color Pipelines"
+	const float a = 1.6;
+	const float d = 0.977;
+	const float hdrMax = 8.0;
+	const float midIn = 0.18;
+	const float midOut = 0.267;
+
+	// Can be precomputed
+	const float b = (-pow(midIn, a) + pow(hdrMax, a) * midOut) / ((pow(hdrMax, a * d) - pow(midIn, a * d)) * midOut);
+	const float c = (pow(hdrMax, a * d) * pow(midIn, a) - pow(hdrMax, a) * pow(midIn, a * d) * midOut) / ((pow(hdrMax, a * d) - pow(midIn, a * d)) * midOut);
+
+	return pow(p_color, a) / (pow(p_color, a * d) * b + c);
+}
+
+float calculateUchimura(float p_color, float p_maxBrightness, float p_contrast, float p_start, float p_length, float p_black, float p_pedestal)
+{
+	// Uchimura 2017, "HDR theory and practice"
+	// Math: https://www.desmos.com/calculator/gslcdxvipg
+	// Source: https://www.slideshare.net/nikuque/hdr-theory-and-practicce-jp
+	float l0 = ((p_maxBrightness - p_start) * p_length) / p_contrast;
+	float L0 = p_start - p_start / p_contrast;
+	float L1 = p_start + (1.0 - p_start) / p_contrast;
+	float S0 = p_start + l0;
+	float S1 = p_start + p_contrast * l0;
+	float C2 = (p_contrast * p_maxBrightness) / (p_maxBrightness - S1);
+	float CP = -C2 / p_maxBrightness;
+
+	float w0 = 1.0 - smoothstep(0.0, p_start, p_color);
+	float w2 = step(p_start + l0, p_color);
+	float w1 = 1.0 - w0 - w2;
+
+	float T = p_start * pow(p_color / p_start, p_black) + p_pedestal;
+	float S = p_maxBrightness - (p_maxBrightness - S1) * exp(CP * (p_color - S0));
+	float L = p_start + p_contrast * (p_color - p_start);
+
+	return T * w0 + L * w1 + S * w2;
+}
+
+// Uchimura 2017 tonemapping
+float tonemap_Uchimura(float p_color)
+{
+	const float P = 1.0;  // max display brightness
+	const float a = 1.0;  // contrast
+	const float m = 0.22; // linear section start
+	const float l = 0.4;  // linear section length
+	const float c = 1.33; // black
+	const float b = 0.0;  // pedestal
+	return calculateUchimura(p_color, P, a, m, l, c, b);
+}
+
+void main(void)
+{	
+	// Calculate screen-space texture coordinates, for buffer access
+	vec2 texCoord = calcTexCoord();
+
+	// Get the color of the current fragment
+	vec3 fragmentColor = texture2D(inputColorMap, texCoord).xyz;
+
+	// Get the average scene luminance
+	float luminance = texture2D(averageLuminanceTexture, vec2(0.0, 0.0)).r;
+	
+	// Perform exposure compensation by converting the color from RGB to Yxy color space and adjusting the luminosity component
+	vec3 colorYxy = convertRGB2Yxy(fragmentColor);
+	colorYxy.x /= (9.6 * luminance + 0.0001);
+	fragmentColor = convertYxy2RGB(colorYxy);
+	
+	// Perform tonemapping, choosing the method based on a uniform
+	switch(tonemapMethod) 
+	{
+		// No tonemapping
+		case 0:
+		
+		break;
+		
+		// Simple reinhard tonemapping
+		case 1:
+			fragmentColor = tonemap_reinhard(fragmentColor);
+			fragmentColor.x = tonemap_ACES(fragmentColor.x);
+			fragmentColor.y = tonemap_ACES(fragmentColor.y);
+			fragmentColor.z = tonemap_ACES(fragmentColor.z);
+		break;
+			
+		// Reinhard with white point tonemapping
+		case 2:	
+			float whitePoint = 3.0f;
+			whitePoint = whitePoint * whitePoint;
+			
+			fragmentColor = tonemap_reinhardWhitePoint(fragmentColor, whitePoint);
+		break;
+		
+		// Filmic tonemapping
+		case 3:
+			fragmentColor = tonemap_filmic(fragmentColor);
+		break;
+		
+		// Uncharted 2 tonemapping
+		case 4:
+			fragmentColor = tonemap_Uncharted2(fragmentColor);
+		break;
+		
+		// Unreal 3 tonemapping
+		case 5:
+			fragmentColor.x = tonemap_Unreal(fragmentColor.x);
+			fragmentColor.y = tonemap_Unreal(fragmentColor.y);
+			fragmentColor.z = tonemap_Unreal(fragmentColor.z);
+		break;
+		
+		// ACES tonemapping
+		case 6:
+			fragmentColor.x = tonemap_ACES(fragmentColor.x);
+			fragmentColor.y = tonemap_ACES(fragmentColor.y);
+			fragmentColor.z = tonemap_ACES(fragmentColor.z);
+		break;
+		
+		// Lottes tonemapping
+		case 7:
+			fragmentColor.x = tonemap_Lottes(fragmentColor.x);
+			fragmentColor.y = tonemap_Lottes(fragmentColor.y);
+			fragmentColor.z = tonemap_Lottes(fragmentColor.z);
+		break;
+		
+		// Uchimura tonemapping
+		case 8:
+			fragmentColor.x = tonemap_Uchimura(fragmentColor.x);
+			fragmentColor.y = tonemap_Uchimura(fragmentColor.y);
+			fragmentColor.z = tonemap_Uchimura(fragmentColor.z);
+		break;
+	}
+	
+	// Perform gamma correction
+	fragmentColor = gammaCorrectionAccurate(fragmentColor);
+
+	// Write the color to the framebuffer
+	outputColor = vec4(fragmentColor, 1.0);
+}

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

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

+ 2 - 1
Praxis3D/Data/config.ini

@@ -28,4 +28,5 @@ lens_flare_halo_threshold 2.0
 lens_flare_halo_radius 0.55
 lens_flare_ghost_count 6
 mouse_jaw 0.1
-mouse_pitch 0.1
+mouse_pitch 0.1
+tonemap_method 8

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

@@ -61,6 +61,7 @@
 		"Source_LightObject"							: "Light Object",
 		"Source_LightingPass"							: "Lighting Rendering Pass",
 		"Source_LuaComponent"							: "Lua Component",
+		"Source_LuminancePass"						: "Luminance Pass",
 		"Source_ModelComponent"						: "Model Component",
 		"Source_ModelLoader"							: "Model Loader",
 		"Source_ObjectDirectory"					: "Object Directory",

+ 1 - 0
Praxis3D/Praxis3D.vcxproj

@@ -276,6 +276,7 @@
     <ClInclude Include="Source\Loaders.h" />
     <ClInclude Include="Source\LuaComponent.h" />
     <ClInclude Include="Source\LuaScript.h" />
+    <ClInclude Include="Source\LuminancePass.h" />
     <ClInclude Include="Source\Math.h" />
     <ClInclude Include="Source\ModelComponent.h" />
     <ClInclude Include="Source\ModelLoader.h" />

+ 3 - 0
Praxis3D/Praxis3D.vcxproj.filters

@@ -806,6 +806,9 @@
     <ClInclude Include="Source\BloomPass.h">
       <Filter>Renderer\Render Passes\Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="Source\LuminancePass.h">
+      <Filter>Renderer\Render Passes\Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="Data\config.ini" />

+ 1 - 1
Praxis3D/Source/AtmScatteringPass.h

@@ -17,7 +17,7 @@ public:
 		m_useWhiteBalance(true),
 		kSunAngularRadius(0.00935 / 2.0),
 		kLengthUnitInMeters(1000.0),
-		m_atmParamBuffer(BufferType_Uniform, BufferUsageHint_DynamicDraw)
+		m_atmParamBuffer(BufferType_Uniform, BufferBindTarget_Uniform, BufferUsageHint_DynamicDraw)
 	{
 		kSunSolidAngle = PI * kSunAngularRadius * kSunAngularRadius;
 

+ 15 - 6
Praxis3D/Source/BloomPass.h

@@ -75,12 +75,15 @@ public:
 		unsigned int height = p_height / 2;
 		unsigned int mipLevels = 1;
 
-		for(unsigned int i = 0; i < Config::graphicsVar().bloom_mipmap_limit; i++)
+		unsigned int mipmapLimit = (unsigned int)Config::graphicsVar().bloom_mipmap_limit;
+		unsigned int downscaleLimit = (unsigned int)Config::graphicsVar().bloom_downscale_limit;
+
+		for(unsigned int i = 0; i < mipmapLimit; i++)
 		{
 			width = width / 2;
 			height = height / 2;
 
-			if(width < Config::graphicsVar().bloom_downscale_limit || height < Config::graphicsVar().bloom_downscale_limit) break;
+			if(width < downscaleLimit || height < downscaleLimit) break;
 
 			mipLevels++;
 		}
@@ -113,8 +116,11 @@ public:
 			// Bind the corresponding mipmap level of the image buffer
 			m_renderer.m_backend.getGeometryBuffer()->bindBufferToImageUnitForWriting(GeometryBuffer::GBufferFinal, 0, i + 1);
 
+			const unsigned int groupX = (unsigned int)glm::ceil(mipmapSize.x / 8.0);
+			const unsigned int groupY = (unsigned int)glm::ceil(mipmapSize.y / 8.0);
+
 			// Dispatch the compute shader
-			m_renderer.queueForDrawing(m_bloomDownscaleShader->getShaderHandle(), m_bloomDownscaleShader->getUniformUpdater(), p_sceneObjects.m_camera.m_spatialData.m_transformMat, glm::ceil(float(mipmapSize.x) / 8), glm::ceil(float(mipmapSize.y) / 8), 1, MemoryBarrierType::MemoryBarrierType_AccessAndFetchBarrier);
+			m_renderer.queueForDrawing(m_bloomDownscaleShader->getShaderHandle(), m_bloomDownscaleShader->getUniformUpdater(), p_sceneObjects.m_camera.m_spatialData.m_transformMat, groupX, groupY, 1, MemoryBarrierType::MemoryBarrierType_AccessAndFetchBarrier);
 			m_renderer.passComputeDispatchCommandsToBackend();
 			
 			// Half the mipmap size as we go up the mipmap levels
@@ -131,8 +137,8 @@ public:
 		for(unsigned int i = mipmapLevels - 1; i >= 1; i--)
 		{
 			// Recalculate the mipmap size as we go down the mipmap levels
-			mipmapSize.x = glm::max(1.0, glm::floor(float(imageWidth) / glm::pow(2.0, i - 1)));
-			mipmapSize.y = glm::max(1.0, glm::floor(float(imageHeight) / glm::pow(2.0, i - 1)));
+			mipmapSize.x = (unsigned int)glm::max(1.0, glm::floor(imageWidth / glm::pow(2u, i - 1.0)));
+			mipmapSize.y = (unsigned int)glm::max(1.0, glm::floor(imageHeight / glm::pow(2u, i - 1.0)));
 
 			// Assign the texel size and mipmap level so it can be sent to the shader
 			m_renderer.m_frameData.m_texelSize = 1.0f / glm::vec2(mipmapSize);
@@ -141,8 +147,11 @@ public:
 			// Bind the corresponding mipmap level of the image buffer
 			m_renderer.m_backend.getGeometryBuffer()->bindBufferToImageUnitForWriting(GeometryBuffer::GBufferFinal, 0, i - 1);
 
+			const unsigned int groupX = (unsigned int)glm::ceil(mipmapSize.x / 8.0);
+			const unsigned int groupY = (unsigned int)glm::ceil(mipmapSize.y / 8.0);
+
 			// Dispatch the compute shader
-			m_renderer.queueForDrawing(m_bloomUpscaleShader->getShaderHandle(), m_bloomUpscaleShader->getUniformUpdater(), p_sceneObjects.m_camera.m_spatialData.m_transformMat, glm::ceil(float(mipmapSize.x) / 8), glm::ceil(float(mipmapSize.y) / 8), 1, MemoryBarrierType::MemoryBarrierType_AccessAndFetchBarrier);
+			m_renderer.queueForDrawing(m_bloomUpscaleShader->getShaderHandle(), m_bloomUpscaleShader->getUniformUpdater(), p_sceneObjects.m_camera.m_spatialData.m_transformMat, groupX, groupY, 1, MemoryBarrierType::MemoryBarrierType_AccessAndFetchBarrier);
 			m_renderer.passComputeDispatchCommandsToBackend();
 		}
 	}

+ 11 - 8
Praxis3D/Source/CommandBuffer.h

@@ -133,6 +133,9 @@ public:
 			RendererBackend::LoadCommand(p_texture.getFilename(),
 										 p_texture.getHandleRef(),
 										 p_texture.getTextureFormat(),
+										 p_texture.getTextureDataFormat(),
+										 p_texture.getTextureDataType(),
+										 p_texture.getEnableMipmap(),
 										 p_texture.getMipmapLevel(),
 										 p_texture.getTextureWidth(),
 										 p_texture.getTextureHeight(),
@@ -153,19 +156,19 @@ public:
 				queueForLoading(p_objectData.m_materials[matType][i]);
 	}
 
-	inline void queueForUpdate(RendererFrontend::ShaderBuffer &p_lightBuffer)
+	inline void queueForUpdate(RendererFrontend::ShaderBuffer &p_shaderBuffer)
 	{
-		// Calculate the sort key
+		/*/ Calculate the sort key
 		RendererBackend::DrawCommands::value_type::first_type sortKey = 0;
 
 		m_commands.emplace_back(std::make_pair(
 			sortKey,
-			RendererBackend::LoadCommand(p_lightBuffer.m_handle,
-										 p_lightBuffer.m_bufferType,
-										 p_lightBuffer.m_bufferUsage,
-										 p_lightBuffer.m_bindingIndex,
-										 p_lightBuffer.m_size,
-										 (void*)0)));
+			RendererBackend::LoadCommand(p_shaderBuffer.m_handle,
+										 p_shaderBuffer.m_bufferType,
+										 p_shaderBuffer.m_bufferUsage,
+										 p_shaderBuffer.m_bindingIndex,
+										 p_shaderBuffer.m_size,
+										 (void*)0)));*/
 	}
 
 	Commands &getCommands()	{ return m_commands; }

+ 43 - 1
Praxis3D/Source/CommonDefinitions.h

@@ -26,7 +26,8 @@ enum MemoryBarrierType : unsigned int
 {
 	MemoryBarrierType_All,
 	MemoryBarrierType_ImageAccessBarrier,
-	MemoryBarrierType_AccessAndFetchBarrier
+	MemoryBarrierType_AccessAndFetchBarrier,
+	MemoryBarrierType_ShaderStorageBarrier
 };
 enum RenderPassType : unsigned int
 {
@@ -39,6 +40,7 @@ enum RenderPassType : unsigned int
 	RenderPassType_BloomComposite,
 	RenderPassType_LenseFlare,
 	RenderPassType_LenseFlareComposite,
+	RenderPassType_Luminance,
 	RenderPassType_Final,
 	RenderPassType_GUI,
 	RenderPassType_NumOfTypes
@@ -50,6 +52,13 @@ enum BufferType : unsigned int
 	BufferType_Array			= GL_ARRAY_BUFFER,
 	BufferType_ElementArray		= GL_ELEMENT_ARRAY_BUFFER,
 	BufferType_ShaderStorage	= GL_SHADER_STORAGE_BUFFER
+}; 
+enum BufferBindTarget : unsigned int
+{
+	BufferBindTarget_Uniform			= GL_UNIFORM_BUFFER,
+	BufferBindTarget_AtomicCounter		= GL_ATOMIC_COUNTER_BUFFER,
+	BufferBindTarget_TransformFeedback	= GL_TRANSFORM_FEEDBACK_BUFFER,
+	BufferBindTarget_ShaderStorage		= GL_SHADER_STORAGE_BUFFER
 };
 enum BufferUpdateType : unsigned int
 {
@@ -125,6 +134,10 @@ enum LensFlareTextureType : unsigned int
 	LensFlareTextureType_LenseDirt,
 	LensFlareTextureType_Starburst
 };
+enum LuminanceTextureType : unsigned int
+{
+	LensFlareTextureType_AverageLuminance = MaterialType_NumOfTypes_Extended
+};
 enum ShaderType : unsigned int
 {
 	// WARNING: do not change the order - enum entries are order-sensitive
@@ -150,6 +163,35 @@ enum TextureFormat : int
 	TextureFormat_RGB	= GL_RGB,
 	TextureFormat_RGBA	= GL_RGBA
 };
+enum TextureDataType : int
+{
+	TextureDataType_Float			= GL_FLOAT,
+	TextureDataType_Int				= GL_INT,
+	TextureDataType_UnsignedByte	= GL_UNSIGNED_BYTE
+};
+enum TextureDataFormat : int
+{
+	TextureDataFormat_R8		= GL_R8,
+	TextureDataFormat_R16		= GL_R16,
+	TextureDataFormat_R16F		= GL_R16F,
+	TextureDataFormat_R32F		= GL_R32F,
+	TextureDataFormat_RG8		= GL_RG8,
+	TextureDataFormat_RG16		= GL_RG16,
+	TextureDataFormat_RG16F		= GL_RG16F,
+	TextureDataFormat_RG32F		= GL_RG32F,
+	TextureDataFormat_RGB8		= GL_RGB8,
+	TextureDataFormat_RGB16		= GL_RGB16,
+	TextureDataFormat_RGB16F	= GL_RGB16F,
+	TextureDataFormat_RGB32F	= GL_RGB32F,
+	TextureDataFormat_RGBA8		= GL_RGBA8,
+	TextureDataFormat_RGBA16	= GL_RGBA16,
+	TextureDataFormat_RGBA16F	= GL_RGBA16F,
+	TextureDataFormat_RGBA32F	= GL_RGBA32F,
+	TextureDataFormat_R16I		= GL_R16I,
+	TextureDataFormat_R32I		= GL_R32I,
+	TextureDataFormat_R16UI		= GL_R16UI,
+	TextureDataFormat_R32UI		= GL_R32UI
+};
 enum UniformBufferBinding : unsigned int
 {
 	UniformBufferBinding_PointLights,

+ 30 - 1
Praxis3D/Source/Config.cpp

@@ -128,6 +128,9 @@ void Config::init()
 	AddVariablePredef(m_graphicsVar, max_num_spot_lights);
 	AddVariablePredef(m_graphicsVar, multisample_buffers);
 	AddVariablePredef(m_graphicsVar, multisample_samples);
+	AddVariablePredef(m_graphicsVar, rendering_res_x);
+	AddVariablePredef(m_graphicsVar, rendering_res_y);
+	AddVariablePredef(m_graphicsVar, tonemap_method);
 	AddVariablePredef(m_graphicsVar, alpha_threshold);
 	AddVariablePredef(m_graphicsVar, emissive_multiplier);
 	AddVariablePredef(m_graphicsVar, emissive_threshold);
@@ -153,6 +156,9 @@ void Config::init()
 	AddVariablePredef(m_graphicsVar, light_color_r);
 	AddVariablePredef(m_graphicsVar, light_color_g);
 	AddVariablePredef(m_graphicsVar, light_color_b);
+	AddVariablePredef(m_graphicsVar, LOD_prallax_mapping);
+	AddVariablePredef(m_graphicsVar, luminance_range_min);
+	AddVariablePredef(m_graphicsVar, luminance_range_max);
 	AddVariablePredef(m_graphicsVar, height_scale);
 	AddVariablePredef(m_graphicsVar, texture_tiling_factor);
 	AddVariablePredef(m_graphicsVar, z_far);
@@ -255,7 +261,15 @@ void Config::init()
 	AddVariablePredef(m_rendererVar, gaussian_blur_horizontal_frag_shader);
 	AddVariablePredef(m_rendererVar, gaussian_blur_horizontal_vert_shader);
 	AddVariablePredef(m_rendererVar, hdr_mapping_pass_frag_shader); 
-	AddVariablePredef(m_rendererVar, hdr_mapping_pass_vert_shader); 
+	AddVariablePredef(m_rendererVar, hdr_mapping_pass_vert_shader);
+	AddVariablePredef(m_rendererVar, luminance_average_comp_shader);
+	AddVariablePredef(m_rendererVar, luminance_histogram_comp_shader);
+	AddVariablePredef(m_rendererVar, tonemapping_vert_shader);
+	AddVariablePredef(m_rendererVar, tonemapping_frag_shader);
+	AddVariablePredef(m_rendererVar, bloom_composite_pass_vert_shader);
+	AddVariablePredef(m_rendererVar, bloom_composite_pass_frag_shader);
+	AddVariablePredef(m_rendererVar, bloom_downscale_comp_shader);
+	AddVariablePredef(m_rendererVar, bloom_upscale_comp_shader);
 	AddVariablePredef(m_rendererVar, bloom_composite_pass_vert_shader); 
 	AddVariablePredef(m_rendererVar, bloom_composite_pass_frag_shader);
 	AddVariablePredef(m_rendererVar, blur_pass_vert_shader);
@@ -308,6 +322,7 @@ void Config::init()
 	AddVariablePredef(m_shaderVar, modelViewProjectionMatUniform);
 	AddVariablePredef(m_shaderVar, transposeViewMatUniform);
 	AddVariablePredef(m_shaderVar, screenSizeUniform);
+	AddVariablePredef(m_shaderVar, screenNumOfPixelsUniform);
 	AddVariablePredef(m_shaderVar, deltaTimeMSUniform);
 	AddVariablePredef(m_shaderVar, deltaTimeSUniform);
 	AddVariablePredef(m_shaderVar, elapsedTimeUniform);
@@ -319,6 +334,10 @@ void Config::init()
 	AddVariablePredef(m_shaderVar, heightScaleUniform);
 	AddVariablePredef(m_shaderVar, combinedTextureUniform);
 	AddVariablePredef(m_shaderVar, textureTilingFactorUniform);
+	AddVariablePredef(m_shaderVar, LODParallaxUniform);
+	AddVariablePredef(m_shaderVar, texelSize);
+	AddVariablePredef(m_shaderVar, numOfTexels);
+	AddVariablePredef(m_shaderVar, mipLevel);
 	AddVariablePredef(m_shaderVar, dirLightColor);
 	AddVariablePredef(m_shaderVar, dirLightDirection);
 	AddVariablePredef(m_shaderVar, dirLightIntensity);
@@ -353,10 +372,18 @@ void Config::init()
 	AddVariablePredef(m_shaderVar, glossTextureUniform);
 	AddVariablePredef(m_shaderVar, heightTextureUniform);
 	AddVariablePredef(m_shaderVar, combinedTextureUniform);
+	AddVariablePredef(m_shaderVar, averageLuminanceTexture);
 	AddVariablePredef(m_shaderVar, atmIrradianceTextureUniform);
 	AddVariablePredef(m_shaderVar, atmScatteringTextureUniform);
 	AddVariablePredef(m_shaderVar, atmSingleMieScatTextureUniform);
 	AddVariablePredef(m_shaderVar, atmTransmittanceTextureUniform);
+	AddVariablePredef(m_shaderVar, bloomTreshold);
+	AddVariablePredef(m_shaderVar, bloomIntensity);
+	AddVariablePredef(m_shaderVar, bloomDirtIntensity);
+	AddVariablePredef(m_shaderVar, inverseLogLuminanceRange);
+	AddVariablePredef(m_shaderVar, logLuminanceRange);
+	AddVariablePredef(m_shaderVar, minLogLuminance);
+	AddVariablePredef(m_shaderVar, tonemapMethod);
 	AddVariablePredef(m_shaderVar, lensFlareDirtTextureUniform);
 	AddVariablePredef(m_shaderVar, lensFlareGhostGradientTextureUniform);
 	AddVariablePredef(m_shaderVar, lensFlareStarburstTextureUniform);
@@ -391,6 +418,8 @@ void Config::init()
 	AddVariablePredef(m_textureVar, gl_texture_anisotropy);
 	AddVariablePredef(m_textureVar, gl_texture_magnification);
 	AddVariablePredef(m_textureVar, gl_texture_minification);
+	AddVariablePredef(m_textureVar, gl_texture_magnification_mipmap);
+	AddVariablePredef(m_textureVar, gl_texture_minification_mipmap);
 	AddVariablePredef(m_textureVar, number_of_mipmaps);
 	AddVariablePredef(m_textureVar, generate_mipmaps);
 

+ 35 - 1
Praxis3D/Source/Config.h

@@ -804,6 +804,7 @@ public:
 			multisample_samples = 1;
 			rendering_res_x = 1600;
 			rendering_res_y = 900;
+			tonemap_method = 6;
 			alpha_threshold = 0.0f;
 			bloom_intensity = 1.0f;
 			bloom_knee = 0.1f;
@@ -834,6 +835,8 @@ public:
 			light_color_g = 1.0f;
 			light_color_b = 1.0f;
 			LOD_prallax_mapping = 100.0f;
+			luminance_range_min = 0.004f;
+			luminance_range_max = 11.3f;
 			height_scale = 0.0f;
 			texture_tiling_factor = 1.0f;
 			z_far = 8000.0f;
@@ -860,6 +863,7 @@ public:
 		int multisample_samples;
 		int rendering_res_x;
 		int rendering_res_y;
+		int tonemap_method;
 		float alpha_threshold;
 		float bloom_intensity;
 		float bloom_knee;
@@ -890,6 +894,8 @@ public:
 		float light_color_g;
 		float light_color_b;
 		float LOD_prallax_mapping;
+		float luminance_range_min;
+		float luminance_range_max;
 		float height_scale;
 		float texture_tiling_factor;
 		float z_far;
@@ -1079,6 +1085,10 @@ public:
 			gaussian_blur_horizontal_vert_shader = "gaussianBlurHorizontal.vert";
 			hdr_mapping_pass_frag_shader = "hdrMappingPass.frag";
 			hdr_mapping_pass_vert_shader = "hdrMappingPass.vert";
+			luminance_average_comp_shader = "luminanceAverage.comp";
+			luminance_histogram_comp_shader = "luminanceHistogram.comp";
+			tonemapping_vert_shader = "tonemapping.vert";
+			tonemapping_frag_shader = "tonemapping.frag";
 			bloom_composite_pass_vert_shader = "bloomCompositePass.vert";
 			bloom_composite_pass_frag_shader = "bloomCompositePass.frag";
 			bloom_downscale_comp_shader = "bloomDownscale.comp";
@@ -1144,6 +1154,10 @@ public:
 		std::string gaussian_blur_horizontal_vert_shader;
 		std::string hdr_mapping_pass_frag_shader;
 		std::string hdr_mapping_pass_vert_shader;
+		std::string luminance_average_comp_shader;
+		std::string luminance_histogram_comp_shader;
+		std::string tonemapping_vert_shader;
+		std::string tonemapping_frag_shader;
 		std::string bloom_composite_pass_vert_shader;
 		std::string bloom_composite_pass_frag_shader;
 		std::string bloom_downscale_comp_shader;
@@ -1210,6 +1224,7 @@ public:
 			modelViewProjectionMatUniform = "MVP";
 			transposeViewMatUniform = "transposeViewMat";
 			screenSizeUniform = "screenSize";
+			screenNumOfPixelsUniform = "screenNumOfPixels";
 			deltaTimeMSUniform = "deltaTimeMS";
 			deltaTimeSUniform = "deltaTimeS";
 			elapsedTimeUniform = "elapsedTime";
@@ -1222,6 +1237,7 @@ public:
 			textureTilingFactorUniform = "textureTilingFactor";
 			LODParallaxUniform = "parallaxMappingLOD";
 			texelSize = "texelSize";
+			numOfTexels = "numOfTexels";
 			mipLevel = "mipLevel";
 
 			dirLightColor = "directionalLight.m_color";
@@ -1263,6 +1279,7 @@ public:
 			glossTextureUniform = "glossTexture";
 			heightTextureUniform = "heightTexture";
 			combinedTextureUniform = "combinedTexture";
+			averageLuminanceTexture = "averageLuminanceTexture";
 
 			atmIrradianceTextureUniform = "atmIrradianceTexture";
 			atmScatteringTextureUniform = "atmScatteringTexture";
@@ -1273,6 +1290,11 @@ public:
 			bloomIntensity = "bloomIntensity";
 			bloomDirtIntensity = "bloomDirtIntensity";
 
+			inverseLogLuminanceRange = "inverseLogLuminanceRange";
+			logLuminanceRange = "logLuminanceRange";
+			minLogLuminance = "minLogLuminance";
+			tonemapMethod = "tonemapMethod";
+
 			lensFlareDirtTextureUniform = "lensDirtTexture";
 			lensFlareGhostGradientTextureUniform = "ghostGradientTexture";
 			lensFlareStarburstTextureUniform = "lenseStarburstTexture";
@@ -1305,6 +1327,7 @@ public:
 		std::string modelViewProjectionMatUniform;
 		std::string transposeViewMatUniform;
 		std::string screenSizeUniform;
+		std::string screenNumOfPixelsUniform;
 		std::string deltaTimeMSUniform;
 		std::string deltaTimeSUniform;
 		std::string elapsedTimeUniform;
@@ -1317,6 +1340,7 @@ public:
 		std::string textureTilingFactorUniform;
 		std::string LODParallaxUniform;
 		std::string texelSize;
+		std::string numOfTexels;
 		std::string mipLevel;
 
 		std::string dirLightColor;
@@ -1358,6 +1382,7 @@ public:
 		std::string glossTextureUniform;
 		std::string heightTextureUniform;
 		std::string combinedTextureUniform;
+		std::string averageLuminanceTexture;
 
 		std::string atmIrradianceTextureUniform;
 		std::string atmScatteringTextureUniform;
@@ -1368,6 +1393,11 @@ public:
 		std::string bloomIntensity;
 		std::string bloomDirtIntensity;
 
+		std::string inverseLogLuminanceRange;
+		std::string logLuminanceRange;
+		std::string minLogLuminance;
+		std::string tonemapMethod;
+
 		std::string lensFlareDirtTextureUniform;
 		std::string lensFlareGhostGradientTextureUniform;
 		std::string lensFlareStarburstTextureUniform;
@@ -1410,7 +1440,9 @@ public:
 			RMHAO_texture_format = GL_RGBA;
 			gl_texture_anisotropy = 16;
 			gl_texture_magnification = GL_LINEAR;
-			gl_texture_minification = GL_LINEAR_MIPMAP_LINEAR;
+			gl_texture_minification = GL_LINEAR;
+			gl_texture_magnification_mipmap = GL_LINEAR;
+			gl_texture_minification_mipmap = GL_LINEAR_MIPMAP_LINEAR;
 			number_of_mipmaps = 50;
 			generate_mipmaps = true;
 		}
@@ -1432,6 +1464,8 @@ public:
 		int gl_texture_anisotropy;
 		int gl_texture_magnification;
 		int gl_texture_minification;
+		int gl_texture_magnification_mipmap;
+		int gl_texture_minification_mipmap;
 		int number_of_mipmaps;
 		bool generate_mipmaps;
 	};

+ 1 - 0
Praxis3D/Source/ErrorCodes.h

@@ -108,6 +108,7 @@ DECLARE_ENUM(ErrorCode, ERROR_CODES)
     Code(Source_LightObject,) \
     Code(Source_LightingPass,) \
     Code(Source_LuaComponent,) \
+    Code(Source_LuminancePass,) \
     Code(Source_ModelComponent,) \
     Code(Source_ModelLoader,) \
     Code(Source_ObjectDirectory,) \

+ 9 - 2
Praxis3D/Source/ErrorHandler.cpp

@@ -53,8 +53,14 @@ ErrorHandler::ErrorHandler()
 	AssignErrorType(Invalid_object_id, Error);
 	AssignErrorType(Duplicate_object_id, Error);
 	
+	for(unsigned int i = 0; i < Source_NumberOfErrorSources; i++)
+	{
+		ErrorSource errorSource = static_cast<ErrorSource>(i);
+		m_errHashmap[GetString(errorSource)] = NumberOfErrorCodes + errorSource;
+	}
+
 	// Add error sources to the hash map, and offset them by number of error codes, because they share the same hash map	
-    m_errHashmap[GetString(Source_Unknown)]						= NumberOfErrorCodes + Source_Unknown;
+   /* m_errHashmap[GetString(Source_Unknown)] = NumberOfErrorCodes + Source_Unknown;
     m_errHashmap[GetString(Source_General)]						= NumberOfErrorCodes + Source_General;
     m_errHashmap[GetString(Source_AtmScatteringPass)]			= NumberOfErrorCodes + Source_AtmScatteringPass;
     m_errHashmap[GetString(Source_BloomCompositePass)]			= NumberOfErrorCodes + Source_BloomCompositePass;
@@ -81,6 +87,7 @@ ErrorHandler::ErrorHandler()
     m_errHashmap[GetString(Source_LightObject)]					= NumberOfErrorCodes + Source_LightObject;
 	m_errHashmap[GetString(Source_LightingPass)]				= NumberOfErrorCodes + Source_LightingPass;
 	m_errHashmap[GetString(Source_LuaComponent)]				= NumberOfErrorCodes + Source_LuaComponent;
+	m_errHashmap[GetString(Source_LuminancePass)]				= NumberOfErrorCodes + Source_LuminancePass;
 	m_errHashmap[GetString(Source_ModelComponent)]				= NumberOfErrorCodes + Source_ModelComponent;
     m_errHashmap[GetString(Source_ModelLoader)]					= NumberOfErrorCodes + Source_ModelLoader;
 	m_errHashmap[GetString(Source_ObjectDirectory)]				= NumberOfErrorCodes + Source_ObjectDirectory;
@@ -104,7 +111,7 @@ ErrorHandler::ErrorHandler()
     m_errHashmap[GetString(Source_Window)]						= NumberOfErrorCodes + Source_Window;
 	m_errHashmap[GetString(Source_World)]						= NumberOfErrorCodes + Source_World;
 	m_errHashmap[GetString(Source_WorldScene)]					= NumberOfErrorCodes + Source_WorldScene;
-	m_errHashmap[GetString(Source_WorldSystem)]					= NumberOfErrorCodes + Source_WorldSystem;
+	m_errHashmap[GetString(Source_WorldSystem)]					= NumberOfErrorCodes + Source_WorldSystem;*/
 
 	// Add error types to the hash map, and offset them by number of error codes and error sources, because they share the same hash map
 	m_errHashmap[GetString(Info)]		= NumberOfErrorCodes + Source_NumberOfErrorSources + Info;

+ 3 - 2
Praxis3D/Source/FinalPass.h

@@ -72,8 +72,9 @@ public:
 		// Bind final texture for reading so it can be accessed in the shaders
 		m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(GeometryBuffer::GBufferEmissive, GeometryBuffer::GBufferEmissive);
 		m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(p_renderPassData.getColorInputMap(), GeometryBuffer::GBufferInputTexture);
-		//m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(GeometryBuffer::GBufferFinal, GeometryBuffer::GBufferInputTexture);
-		//m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(GeometryBuffer::GBufferNormal, GeometryBuffer::GBufferInputTexture);
+
+		//glActiveTexture(GL_TEXTURE0 + GeometryBuffer::GBufferInputTexture);
+		//glBindTexture(GL_TEXTURE_2D, p_renderPassData.m_luminanceAverageTextureHandle_DELETE_THIS_AFTER_DEBUG);
 
 		// Set the default framebuffer to be drawn to
 		m_renderer.m_backend.getGeometryBuffer()->bindFramebufferForWriting(GeometryBuffer::FramebufferDefault);

+ 1 - 44
Praxis3D/Source/GeometryBuffer.cpp

@@ -263,47 +263,4 @@ void GeometryBuffer::initFinalPass()
 	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
 
 #endif // SETTING_USE_BLIT_FRAMEBUFFER
-}
-/*
-void GeometryBuffer::bindForReading(GBufferTextureType p_buffer, int p_activeTexture)
-{
-	glActiveTexture(GL_TEXTURE0 + p_activeTexture);
-	switch(p_buffer)
-	{
-	case GeometryBuffer::GBufferPosition:
-	case GeometryBuffer::GBufferDiffuse:
-	case GeometryBuffer::GBufferNormal:
-	case GeometryBuffer::GBufferEmissive:
-		glBindTexture(GL_TEXTURE_2D, m_GBTextures[p_buffer]);
-		break;
-	case GeometryBuffer::GBufferFinal:
-		glBindTexture(GL_TEXTURE_2D, m_finalBuffer);
-		break;
-	case GeometryBuffer::GBufferIntermediate:
-		glBindTexture(GL_TEXTURE_2D, m_intermediateBuffer);
-		break;
-	}
-}
-void GeometryBuffer::bindForReading(GBufferTextureType p_buffer)
-{
-	glActiveTexture(GL_TEXTURE0);
-	switch(p_buffer)
-	{
-	case GeometryBuffer::GBufferPosition:
-	case GeometryBuffer::GBufferDiffuse:
-	case GeometryBuffer::GBufferNormal:
-	case GeometryBuffer::GBufferEmissive:
-		glBindTexture(GL_TEXTURE_2D, m_GBTextures[p_buffer]);
-		break;
-	case GeometryBuffer::GBufferFinal:
-		glBindTexture(GL_TEXTURE_2D, m_finalBuffer);
-		break;
-	case GeometryBuffer::GBufferIntermediate:
-		glBindTexture(GL_TEXTURE_2D, m_intermediateBuffer);
-		break;
-	}
-}
-void GeometryBuffer::bindForWriting(GBufferTextureType p_buffer)
-{
-	glDrawBuffer(GL_COLOR_ATTACHMENT0 + p_buffer);
-}*/
+}

+ 1 - 1
Praxis3D/Source/HdrMappingPass.h

@@ -8,7 +8,7 @@ class HdrMappingPass : public RenderPass
 public:
 	HdrMappingPass(RendererFrontend &p_renderer) :
 		RenderPass(p_renderer),
-		m_HDRSSBuffer(BufferType_ShaderStorage, BufferUsageHint_DynamicCopy) 
+		m_HDRSSBuffer(BufferType_ShaderStorage, BufferBindTarget_Uniform, BufferUsageHint_DynamicCopy) 
 	{
 		m_hdrMappingShader = nullptr;
 	}

+ 1 - 1
Praxis3D/Source/LenseFlarePass.h

@@ -8,7 +8,7 @@ class LenseFlarePass : public RenderPass
 public:
 	LenseFlarePass(RendererFrontend &p_renderer) :
 		RenderPass(p_renderer), 
-		m_lensFlareParamBuffer(BufferType_Uniform, BufferUsageHint_DynamicDraw),
+		m_lensFlareParamBuffer(BufferType_Uniform, BufferBindTarget_Uniform, BufferUsageHint_DynamicDraw),
 		m_lensFlareGhostGradient(Loaders::texture2D().load(Config::rendererVar().lens_flare_ghost_gradient_texture))
 	{
 		m_lensFlareParam.m_lensFlaireDownsample = Config::graphicsVar().lens_flare_downsample;

+ 3 - 2
Praxis3D/Source/LightingPass.h

@@ -7,8 +7,9 @@ class LightingPass : public RenderPass
 public:
 	LightingPass(RendererFrontend &p_renderer) : 
 		RenderPass(p_renderer), 
-		m_pointLightBuffer(BufferType_Uniform, BufferUsageHint_DynamicDraw),
-		m_spotLightBuffer(BufferType_Uniform, BufferUsageHint_DynamicDraw) { }
+		m_pointLightBuffer(BufferType_Uniform, BufferBindTarget_Uniform, BufferUsageHint_DynamicDraw),
+		m_spotLightBuffer(BufferType_Uniform, BufferBindTarget_Uniform, BufferUsageHint_DynamicDraw),
+		m_shaderLightPass(nullptr) { }
 
 	~LightingPass() { }
 

+ 132 - 0
Praxis3D/Source/LuminancePass.h

@@ -0,0 +1,132 @@
+#pragma once
+
+#include "RenderPassBase.h"
+
+class LuminancePass : public RenderPass
+{
+public:
+	LuminancePass(RendererFrontend &p_renderer) :
+		RenderPass(p_renderer),
+		m_histogramBuffer(BufferType_ElementArray, BufferBindTarget_ShaderStorage, BufferUsageHint_DynamicDraw),
+		m_luminanceAverageTexture(Loaders::texture2D().create("LuminanceTexture", 1, 1, TextureFormat_Red, TextureDataFormat_R16F, TextureDataType_Float)),
+		m_luminanceHistogramShader(nullptr),
+		m_luminanceAverageShader(nullptr),
+		m_tonemappingShader(nullptr) { }
+
+	~LuminancePass() { }
+
+	ErrorCode init()
+	{
+		ErrorCode returnError = ErrorCode::Success;
+
+		m_name = "Luminance Pass";
+
+		// Create a property-set used to load luminance histogram compute shader
+		PropertySet luminanceHistogramProperty(Properties::Shaders);
+		luminanceHistogramProperty.addProperty(Properties::ComputeShader, Config::rendererVar().luminance_histogram_comp_shader);
+
+		// Create a property-set used to load luminance average compute shader
+		PropertySet luminanceAverageProperty(Properties::Shaders);
+		luminanceAverageProperty.addProperty(Properties::ComputeShader, Config::rendererVar().luminance_average_comp_shader);
+
+		// Create a property-set used to load tonemapping shader
+		PropertySet tonemappingProperty(Properties::Shaders);
+		tonemappingProperty.addProperty(Properties::VertexShader, Config::rendererVar().tonemapping_vert_shader);
+		tonemappingProperty.addProperty(Properties::FragmentShader, Config::rendererVar().tonemapping_frag_shader);
+
+		// Create shaders
+		m_luminanceHistogramShader = Loaders::shader().load(luminanceHistogramProperty);
+		m_luminanceAverageShader = Loaders::shader().load(luminanceAverageProperty);
+		m_tonemappingShader = Loaders::shader().load(tonemappingProperty);
+
+		// Load shaders to memory
+		ErrorCode luminanceHistogramError = m_luminanceHistogramShader->loadToMemory();
+		ErrorCode luminanceAverageError = m_luminanceAverageShader->loadToMemory();
+		ErrorCode tonemappingError = m_tonemappingShader->loadToMemory();
+
+		// Queue the shaders to be loaded to GPU
+		if(luminanceHistogramError == ErrorCode::Success)
+			m_renderer.queueForLoading(*m_luminanceHistogramShader);
+		else
+			returnError = luminanceHistogramError;
+
+		if(luminanceAverageError == ErrorCode::Success)
+			m_renderer.queueForLoading(*m_luminanceAverageShader);
+		else
+			returnError = luminanceAverageError;
+
+		if(tonemappingError == ErrorCode::Success)
+			m_renderer.queueForLoading(*m_tonemappingShader);
+		else
+			returnError = tonemappingError;
+
+		// Check for errors and log either a successful or a failed initialization
+		if(returnError == ErrorCode::Success)
+		{
+			// Set histogram buffer values
+			m_histogramBuffer.m_bindingIndex = 1;
+			m_histogramBuffer.m_size = sizeof(uint32_t) * 256;
+
+			// Queue histogram buffer and luminance texture to be created in video memory
+			m_renderer.queueForLoading(m_histogramBuffer);
+			m_renderer.queueForLoading(m_luminanceAverageTexture);
+
+			ErrHandlerLoc::get().log(ErrorCode::Initialize_success, ErrorSource::Source_LuminancePass);
+		}
+		else
+		{
+			ErrHandlerLoc::get().log(ErrorCode::Initialize_failure, ErrorSource::Source_LuminancePass);
+		}
+
+		return returnError;
+	}
+
+	void update(RenderPassData &p_renderPassData, const SceneObjects &p_sceneObjects, const float p_deltaTime)
+	{
+		// Get the screen image buffer size
+		const unsigned int imageWidth = m_renderer.m_backend.getGeometryBuffer()->getBufferWidth();
+		const unsigned int imageHeight = m_renderer.m_backend.getGeometryBuffer()->getBufferHeight();
+
+		// Calculate the number of groups that the compute shader should execute
+		unsigned int groupsX = static_cast<uint32_t>(glm::ceil(imageWidth / 16.0f));
+		unsigned int groupsY = static_cast<uint32_t>(glm::ceil(imageHeight / 16.0f));
+
+		m_renderer.m_backend.getGeometryBuffer()->bindBufferToImageUnitForReading(GeometryBuffer::GBufferFinal, GeometryBuffer::GBufferInputTexture, 0);
+
+		m_renderer.queueForDrawing(m_luminanceHistogramShader->getShaderHandle(), m_luminanceHistogramShader->getUniformUpdater(), p_sceneObjects.m_camera.m_spatialData.m_transformMat, groupsX, groupsY, 1, MemoryBarrierType::MemoryBarrierType_ShaderStorageBarrier);
+		m_renderer.passComputeDispatchCommandsToBackend();
+
+		//glActiveTexture(GL_TEXTURE0);
+		//glBindTexture(GL_TEXTURE_2D, m_luminanceAverageTexture.getHandle());
+
+		glBindImageTexture(0, m_luminanceAverageTexture.getHandle(), 0, GL_FALSE, 0, GL_READ_WRITE, GL_R16F);
+
+		m_renderer.queueForDrawing(m_luminanceAverageShader->getShaderHandle(), m_luminanceAverageShader->getUniformUpdater(), p_sceneObjects.m_camera.m_spatialData.m_transformMat, 1, 1, 1, MemoryBarrierType::MemoryBarrierType_ShaderStorageBarrier);
+		m_renderer.passComputeDispatchCommandsToBackend();
+
+
+		glDisable(GL_DEPTH_TEST);
+
+		glActiveTexture(GL_TEXTURE0 + LuminanceTextureType::LensFlareTextureType_AverageLuminance);
+		glBindTexture(GL_TEXTURE_2D, m_luminanceAverageTexture.getHandle());
+
+		m_renderer.m_backend.getGeometryBuffer()->bindBufferForReading(p_renderPassData.getColorInputMap(), GeometryBuffer::GBufferInputTexture);
+
+		m_renderer.m_backend.getGeometryBuffer()->bindBufferForWriting(p_renderPassData.getColorOutputMap());
+
+		// Queue and render a full screen quad using a final pass shader
+		m_renderer.queueForDrawing(m_tonemappingShader->getShaderHandle(), m_tonemappingShader->getUniformUpdater(), p_sceneObjects.m_camera.m_spatialData.m_transformMat);
+		m_renderer.passScreenSpaceDrawCommandsToBackend();
+
+		p_renderPassData.swapColorInputOutputMaps();
+	}
+
+private:
+	ShaderLoader::ShaderProgram *m_luminanceHistogramShader;
+	ShaderLoader::ShaderProgram *m_luminanceAverageShader;
+	ShaderLoader::ShaderProgram *m_tonemappingShader;
+
+	RendererFrontend::ShaderBuffer m_histogramBuffer;
+
+	TextureLoader2D::Texture2DHandle m_luminanceAverageTexture;
+};

+ 1 - 1
Praxis3D/Source/PostProcessPass.h

@@ -8,7 +8,7 @@ class PostProcessPass : public RenderPass
 public:
 	PostProcessPass(RendererFrontend &p_renderer) :
 		RenderPass(p_renderer), 
-		m_lensFlareParamBuffer(BufferType_Uniform, BufferUsageHint_DynamicDraw),
+		m_lensFlareParamBuffer(BufferType_Uniform, BufferBindTarget_Uniform, BufferUsageHint_DynamicDraw),
 		m_lensFlareGhostGradient(Loaders::texture2D().load(Config::rendererVar().lens_flare_ghost_gradient_texture))
 	{
 		m_lensFlareParam.m_lensFlaireDownsample = 0.0f;

+ 4 - 0
Praxis3D/Source/RendererBackend.cpp

@@ -178,6 +178,10 @@ void RendererBackend::processDrawing(const ComputeDispatchCommands &p_computeDis
 			case MemoryBarrierType::MemoryBarrierType_AccessAndFetchBarrier:
 				memoryBarrierBit = GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_TEXTURE_FETCH_BARRIER_BIT;
 				break;
+
+			case MemoryBarrierType::MemoryBarrierType_ShaderStorageBarrier:
+				memoryBarrierBit = GL_SHADER_STORAGE_BARRIER_BIT;
+				break;
 		}
 
 		// Define the memory barrier for the data in the compute shader

+ 43 - 14
Praxis3D/Source/RendererBackend.h

@@ -184,13 +184,14 @@ public:
 	{
 		LoadCommand(unsigned int &p_handle,
 					const BufferType p_bufferType,
+					const BufferBindTarget p_bufferBindTarget,
 					const BufferUsageHint p_bufferUsage,
 					const unsigned int p_bindingIndex,
 					const int64_t p_size,
 					const void *p_data) :
 			m_handle(p_handle),
 			m_objectType(LoadObject_Buffer),
-			m_objectData(p_bufferType, p_bufferUsage, p_bindingIndex, p_size, p_data) { }
+			m_objectData(p_bufferType, p_bufferBindTarget, p_bufferUsage, p_bindingIndex, p_size, p_data) { }
 
 		LoadCommand(const std::string &p_name, 
 					unsigned int &p_handle,
@@ -214,13 +215,16 @@ public:
 		LoadCommand(const std::string &p_name, 
 					unsigned int &p_handle,
 					const TextureFormat p_texFormat,
+					const TextureDataFormat p_texDataFormat,
+					const TextureDataType p_texDataType,
+					const bool p_enableMipmap,
 					const int p_mipmapLevel,
 					const unsigned int p_textureWidth,
 					const unsigned int p_textureHeight,
 					const void *p_data) :
 			m_handle(p_handle),
 			m_objectType(LoadObject_Texture2D),
-			m_objectData(p_name, p_texFormat, p_mipmapLevel, p_textureWidth, p_textureHeight, p_data) { }
+			m_objectData(p_name, p_texFormat, p_texDataFormat, p_texDataType, p_enableMipmap, p_mipmapLevel, p_textureWidth, p_textureHeight, p_data) { }
 
 		LoadCommand(unsigned int &p_handle,
 					const TextureFormat p_texFormat,
@@ -236,17 +240,20 @@ public:
 		struct BufferLoadData
 		{
 			BufferLoadData(const BufferType p_bufferType,
+						   const BufferBindTarget p_bufferBindTarget,
 						   const BufferUsageHint p_bufferUsage,
 						   const unsigned int p_bindingIndex,
 						   const int64_t p_size,
 						   const void *p_data) :
 				m_bufferType(p_bufferType),
+				m_bufferBindTarget(p_bufferBindTarget),
 				m_bufferUsage(p_bufferUsage),
 				m_bindingIndex(p_bindingIndex),
 				m_size(p_size),
 				m_data(p_data) { }
 
 			const BufferType m_bufferType;
+			const BufferBindTarget m_bufferBindTarget;
 			const BufferUsageHint m_bufferUsage;
 			const unsigned int m_bindingIndex;
 			const int64_t m_size;
@@ -293,12 +300,18 @@ public:
 		{
 			Texture2DLoadData(const std::string &p_name,
 							  const TextureFormat p_texFormat,
+							  const TextureDataFormat p_texDataFormat,
+							  const TextureDataType p_texDataType,
+							  const bool p_enableMipmap,
 							  const int p_mipmapLevel,
 							  const unsigned int p_textureWidth,
 							  const unsigned int p_textureHeight,
 							  const void *p_data) :
 				m_name(p_name),
 				m_texFormat(p_texFormat),
+				m_texDataFormat(p_texDataFormat),
+				m_texDataType(p_texDataType),
+				m_enableMipmap(p_enableMipmap),
 				m_mipmapLevel(p_mipmapLevel),
 				m_textureWidth(p_textureWidth),
 				m_textureHeight(p_textureHeight),
@@ -306,9 +319,12 @@ public:
 
 			const std::string &m_name;
 			const TextureFormat m_texFormat;
+			const TextureDataFormat m_texDataFormat;
+			const TextureDataType m_texDataType;
+			const bool m_enableMipmap;
+			const int m_mipmapLevel;
 			const unsigned int m_textureWidth;
 			const unsigned int m_textureHeight;
-			const int m_mipmapLevel;
 			const void *m_data;
 		};
 		struct CubemapLoadData
@@ -334,11 +350,12 @@ public:
 		union ObjectData
 		{
 			ObjectData(const BufferType p_bufferType,
+					   const BufferBindTarget p_bufferBindTarget,
 					   const BufferUsageHint p_bufferUsage,
 					   const unsigned int p_bindingIndex,
 					   const int64_t p_size,
 					   const void *p_data) :
-				m_bufferData(p_bufferType, p_bufferUsage, p_bindingIndex, p_size, p_data) { }
+				m_bufferData(p_bufferType, p_bufferBindTarget, p_bufferUsage, p_bindingIndex, p_size, p_data) { }
 
 			ObjectData(const std::string &p_name, 
 					   unsigned int(&p_buffers)[ModelBuffer_NumAllTypes],
@@ -355,11 +372,14 @@ public:
 
 			ObjectData(const std::string &p_name, 
 					   const TextureFormat p_texFormat,
+					   const TextureDataFormat p_texDataFormat,
+					   const TextureDataType p_texDataType,
+					   const bool p_enableMipmap,
 					   const int p_mipmapLevel,
 					   const unsigned int p_textureWidth,
 					   const unsigned int p_textureHeight,
 					   const void *p_data) :
-				m_tex2DData(p_name, p_texFormat, p_mipmapLevel, p_textureWidth, p_textureHeight, p_data) { }
+				m_tex2DData(p_name, p_texFormat, p_texDataFormat, p_texDataType, p_enableMipmap, p_mipmapLevel, p_textureWidth, p_textureHeight, p_data) { }
 
 			ObjectData(const TextureFormat p_texFormat,
 					   const int p_mipmapLevel,
@@ -619,7 +639,7 @@ protected:
 						 p_command.m_objectData.m_bufferData.m_bufferUsage);
 
 			// Bind the buffer to the binding index so it can be accessed in a shader
-			glBindBufferBase(p_command.m_objectData.m_bufferData.m_bufferType, 
+			glBindBufferBase(p_command.m_objectData.m_bufferData.m_bufferBindTarget, 
 							 p_command.m_objectData.m_bufferData.m_bindingIndex,
 							 p_command.m_handle);
 		}
@@ -844,22 +864,31 @@ protected:
 			glBindTexture(GL_TEXTURE_2D, p_command.m_handle);
 			glTexImage2D(GL_TEXTURE_2D,
 						 p_command.m_objectData.m_tex2DData.m_mipmapLevel,
-						 p_command.m_objectData.m_tex2DData.m_texFormat,
+						 p_command.m_objectData.m_tex2DData.m_texDataFormat,
 						 p_command.m_objectData.m_tex2DData.m_textureWidth, 
 						 p_command.m_objectData.m_tex2DData.m_textureHeight,
 						 0, 
 						 p_command.m_objectData.m_tex2DData.m_texFormat,
-						 GL_UNSIGNED_BYTE, 
+						 p_command.m_objectData.m_tex2DData.m_texDataType,
 						 p_command.m_objectData.m_tex2DData.m_data);
 
-			// Generate  mipmaps if they are enabled
-			if(Config::textureVar().generate_mipmaps)
+			// Generate mipmaps if they are enabled, and set texture filtering to use mipmaps
+			if(p_command.m_objectData.m_tex2DData.m_enableMipmap)
+			{
 				glGenerateMipmap(GL_TEXTURE_2D);
+				// Texture filtering mode, when image is minimized
+				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, Config::textureVar().gl_texture_minification_mipmap);
+				// Texture filtering mode, when image is magnified
+				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, Config::textureVar().gl_texture_magnification_mipmap);
+			}
+			else
+			{
+				// Texture filtering mode, when image is minimized
+				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, Config::textureVar().gl_texture_minification);
+				// Texture filtering mode, when image is magnified
+				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, Config::textureVar().gl_texture_magnification);
+			}
 
-			// Texture filtering mode, when image is minimized
-			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, Config::textureVar().gl_texture_minification);
-			// Texture filtering mode, when image is magnified
-			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, Config::textureVar().gl_texture_magnification);
 			// Texture anisotropic filtering
 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, Config::textureVar().gl_texture_anisotropy);
 		}

+ 6 - 0
Praxis3D/Source/RendererFrontend.cpp

@@ -8,6 +8,7 @@
 #include "LenseFlareCompositePass.h"
 #include "LenseFlarePass.h"
 #include "LightingPass.h"
+#include "LuminancePass.h"
 #include "FinalPass.h"
 #include "HdrMappingPass.h"
 #include "PostProcessPass.h"
@@ -24,6 +25,7 @@ RendererFrontend::RendererFrontend() : m_renderPassData(nullptr)
 	m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_AtmScattering);
 	//m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_HdrMapping);
 	m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_Bloom);
+	m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_Luminance);
 	//m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_Blur);
 	//m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_BloomComposite);
 	//m_renderingPassesTypes.push_back(RenderPassType::RenderPassType_LenseFlare);
@@ -77,6 +79,10 @@ RendererFrontend::RendererFrontend() : m_renderPassData(nullptr)
 			if(m_initializedRenderingPasses[RenderPassType_LenseFlareComposite] == nullptr)
 				m_initializedRenderingPasses[RenderPassType_LenseFlareComposite] = new LenseFlareCompositePass(*this);
 			break;
+		case RenderPassType_Luminance:
+			if(m_initializedRenderingPasses[RenderPassType_Luminance] == nullptr)
+				m_initializedRenderingPasses[RenderPassType_Luminance] = new LuminancePass(*this);
+			break;
 		case RenderPassType_Final:
 			if(m_initializedRenderingPasses[RenderPassType_Final] == nullptr)
 				m_initializedRenderingPasses[RenderPassType_Final] = new FinalPass(*this);

+ 7 - 1
Praxis3D/Source/RendererFrontend.h

@@ -20,6 +20,7 @@ class RendererFrontend
 	friend class LenseFlareCompositePass;
 	friend class LenseFlarePass;
 	friend class LightingPass;
+	friend class LuminancePass;
 	friend class PostProcessPass;
 	friend class FinalPass;
 	friend class ReflectionPass;
@@ -28,7 +29,7 @@ public:
 	// A handle for a uniform or shader storage buffer
 	struct ShaderBuffer
 	{
-		ShaderBuffer(BufferType p_bufferType, BufferUsageHint p_usageHint) : m_bufferType(p_bufferType), m_bufferUsage(p_usageHint)
+		ShaderBuffer(const BufferType p_bufferType, const BufferBindTarget p_bufferBindTarget, const BufferUsageHint p_usageHint) : m_bufferType(p_bufferType), m_bufferBindTarget(p_bufferBindTarget), m_bufferUsage(p_usageHint)
 		{
 			m_data = 0;
 			m_size = 0;
@@ -46,6 +47,7 @@ public:
 		void *m_data;
 
 		const BufferType m_bufferType;
+		const BufferBindTarget m_bufferBindTarget;
 		const BufferUsageHint m_bufferUsage;
 	};
 
@@ -173,6 +175,9 @@ protected:
 		m_loadCommands.emplace_back(p_texture.getFilename(),
 									p_texture.getHandleRef(),
 									p_texture.getTextureFormat(),
+									p_texture.getTextureDataFormat(),
+									p_texture.getTextureDataType(),
+									p_texture.getEnableMipmap(),
 									p_texture.getMipmapLevel(),
 									p_texture.getTextureWidth(),
 									p_texture.getTextureHeight(),
@@ -216,6 +221,7 @@ protected:
 	{
 		m_loadCommands.emplace_back(p_shaderBuffer.m_handle,
 			p_shaderBuffer.m_bufferType,
+			p_shaderBuffer.m_bufferBindTarget,
 			p_shaderBuffer.m_bufferUsage,
 			p_shaderBuffer.m_bindingIndex,
 			p_shaderBuffer.m_size,

+ 12 - 1
Praxis3D/Source/ShaderUniformUpdater.cpp

@@ -80,6 +80,9 @@ ErrorCode ShaderUniformUpdater::generateTextureUpdateList()
 	uniformList.push_back(new LensFlareDirtTextureUniform(m_shaderHandle));
 	uniformList.push_back(new LensFlareGhostGradientTextureUniform(m_shaderHandle));
 	uniformList.push_back(new LensFlareStarburstTextureUniform(m_shaderHandle));
+
+	// Luminance pass textures
+	uniformList.push_back(new AverageLuminanceTextureUniform(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
@@ -124,8 +127,9 @@ ErrorCode ShaderUniformUpdater::generatePerFrameList()
 	uniformList.push_back(new NumPointLightsUniform(m_shaderHandle));
 	uniformList.push_back(new NumSpotLightsUniform(m_shaderHandle));
 
-	// Screen size uniform
+	// Screen size uniforms
 	uniformList.push_back(new ScreenSizeUniform(m_shaderHandle));
+	uniformList.push_back(new ScreenNumOfPixelsUniform(m_shaderHandle));
 
 	// Misc
 	uniformList.push_back(new DeltaTimeMSUniform(m_shaderHandle));
@@ -142,6 +146,12 @@ ErrorCode ShaderUniformUpdater::generatePerFrameList()
 	uniformList.push_back(new BloomIntensityUniform(m_shaderHandle));
 	uniformList.push_back(new BloomTresholdUniform(m_shaderHandle));
 
+	// Luminance
+	uniformList.push_back(new InverseLogLuminanceRangeUniform(m_shaderHandle));
+	uniformList.push_back(new LogLuminanceRangeUniform(m_shaderHandle));
+	uniformList.push_back(new MinLogLuminanceUniform(m_shaderHandle));
+	uniformList.push_back(new TonemapMethodUniform(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(uniformList.size()) i = 0, size = uniformList.size(); i < size; i++)
@@ -175,6 +185,7 @@ ErrorCode ShaderUniformUpdater::generatePerModelList()
 	uniformList.push_back(new HeightScaleUniform(m_shaderHandle));
 	uniformList.push_back(new MipLevelUniform(m_shaderHandle));
 	uniformList.push_back(new TexelSizeUniform(m_shaderHandle));
+	uniformList.push_back(new NumOFTexelsUniform(m_shaderHandle));
 	uniformList.push_back(new TextureTilingFactorUniform(m_shaderHandle));
 
 	// Test uniforms, used for debugging, etc

+ 157 - 4
Praxis3D/Source/ShaderUniforms.h

@@ -204,10 +204,34 @@ public:
 
 	void update(const UniformData &p_uniformData)
 	{
-		glUniform2i(m_uniformHandle, 
-					p_uniformData.m_frameData.m_screenSize.x, 
-					p_uniformData.m_frameData.m_screenSize.y);
+		if(m_screenSize != p_uniformData.m_frameData.m_screenSize)
+		{
+			m_screenSize = p_uniformData.m_frameData.m_screenSize;
+
+			glUniform2i(m_uniformHandle, m_screenSize.x, m_screenSize.y);
+		}
 	}
+
+private:
+	glm::ivec2 m_screenSize;
+}; 
+class ScreenNumOfPixelsUniform : public BaseUniform
+{
+public:
+	ScreenNumOfPixelsUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().screenNumOfPixelsUniform, p_shaderHandle) { }
+
+	void update(const UniformData &p_uniformData)
+	{
+		if(m_screenSize != p_uniformData.m_frameData.m_screenSize)
+		{
+			m_screenSize = p_uniformData.m_frameData.m_screenSize;
+
+			glUniform1ui(m_uniformHandle, (unsigned int)(m_screenSize.x * m_screenSize.y));
+		}
+	}
+
+private:
+	glm::ivec2 m_screenSize;
 };
 class DeltaTimeMSUniform : public BaseUniform
 {
@@ -441,8 +465,34 @@ public:
 
 	void update(const UniformData &p_uniformData)
 	{
-		glUniform2f(m_uniformHandle, p_uniformData.m_frameData.m_texelSize.x, p_uniformData.m_frameData.m_texelSize.y);
+		if(m_texelSize != p_uniformData.m_frameData.m_texelSize)
+		{
+			m_texelSize = p_uniformData.m_frameData.m_texelSize;
+
+			glUniform2f(m_uniformHandle, m_texelSize.x, m_texelSize.y);
+		}
+	}
+private:
+	glm::vec2 m_texelSize;
+};
+class NumOFTexelsUniform : public BaseUniform
+{
+public:
+	NumOFTexelsUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().numOfTexels, p_shaderHandle)
+	{
+	}
+
+	void update(const UniformData &p_uniformData)
+	{
+		if(m_texelSize != p_uniformData.m_frameData.m_texelSize)
+		{
+			m_texelSize = p_uniformData.m_frameData.m_texelSize;
+
+			glUniform1ui(m_uniformHandle, (unsigned int)(m_texelSize.x * m_texelSize.y));
+		}
 	}
+private:
+	glm::vec2 m_texelSize;
 };
 class MipLevelUniform : public BaseUniform
 {
@@ -934,6 +984,109 @@ public:
 	{
 		glUniform1i(m_uniformHandle, LensFlareTextureType::LensFlareTextureType_Starburst);
 	}
+}; 
+
+class AverageLuminanceTextureUniform : public BaseUniform
+{
+public:
+	AverageLuminanceTextureUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().averageLuminanceTexture, p_shaderHandle)
+	{
+	}
+
+	void update(const UniformData &p_uniformData)
+	{
+		glUniform1i(m_uniformHandle, LuminanceTextureType::LensFlareTextureType_AverageLuminance);
+	}
+};
+class InverseLogLuminanceRangeUniform : public BaseUniform
+{
+public:
+	InverseLogLuminanceRangeUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().inverseLogLuminanceRange, p_shaderHandle), m_minLuminanceRange(0.0f), m_maxLuminanceRange(0.0f)
+	{
+	}
+
+	void update(const UniformData &p_uniformData)
+	{
+		if(m_minLuminanceRange != Config::graphicsVar().luminance_range_min || m_maxLuminanceRange != Config::graphicsVar().luminance_range_max)
+		{
+			m_minLuminanceRange = Config::graphicsVar().luminance_range_min;
+			m_maxLuminanceRange = Config::graphicsVar().luminance_range_max;
+
+			float minLogLuminance = glm::log2(m_minLuminanceRange);
+			float maxLogLuminance = glm::log2(m_maxLuminanceRange);
+			float inverseLogRangeLuminance = 1.0f / (maxLogLuminance - minLogLuminance);
+
+			glUniform1f(m_uniformHandle, inverseLogRangeLuminance);
+		}
+	}
+private:
+	float	m_minLuminanceRange,
+			m_maxLuminanceRange;
+};
+class LogLuminanceRangeUniform : public BaseUniform
+{
+public:
+	LogLuminanceRangeUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().logLuminanceRange, p_shaderHandle), m_minLuminanceRange(0.0f), m_maxLuminanceRange(0.0f)
+	{
+	}
+
+	void update(const UniformData &p_uniformData)
+	{
+		if(m_minLuminanceRange != Config::graphicsVar().luminance_range_min || m_maxLuminanceRange != Config::graphicsVar().luminance_range_max)
+		{
+			m_minLuminanceRange = Config::graphicsVar().luminance_range_min;
+			m_maxLuminanceRange = Config::graphicsVar().luminance_range_max;
+
+			float minLogLuminance = glm::log2(m_minLuminanceRange);
+			float maxLogLuminance = glm::log2(m_maxLuminanceRange);
+
+			glUniform1f(m_uniformHandle, maxLogLuminance - minLogLuminance);
+		}
+	}
+private:
+	float	m_minLuminanceRange,
+			m_maxLuminanceRange;
+};
+class MinLogLuminanceUniform : public BaseUniform
+{
+public:
+	MinLogLuminanceUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().minLogLuminance, p_shaderHandle), m_minLuminanceRange(0.0f)
+	{
+	}
+
+	void update(const UniformData &p_uniformData)
+	{
+		if(m_minLuminanceRange != Config::graphicsVar().luminance_range_min)
+		{
+			m_minLuminanceRange = Config::graphicsVar().luminance_range_min;
+
+			float minLogLuminance = glm::log2(m_minLuminanceRange);
+
+			glUniform1f(m_uniformHandle, minLogLuminance);
+		}
+	}
+private:
+	float m_minLuminanceRange;
+};
+class TonemapMethodUniform : public BaseUniform
+{
+public:
+	TonemapMethodUniform(unsigned int p_shaderHandle) : BaseUniform(Config::shaderVar().tonemapMethod, p_shaderHandle), m_tonemapMethod(-1)
+	{
+	}
+
+	void update(const UniformData &p_uniformData)
+	{
+		if(m_tonemapMethod != Config::graphicsVar().tonemap_method)
+		{
+			m_tonemapMethod = Config::graphicsVar().tonemap_method;
+
+			glUniform1i(m_uniformHandle, m_tonemapMethod);
+		}
+	}
+
+private:
+	int m_tonemapMethod;
 };
 
 class DynamicEnvironmentMapUniform : public BaseUniform

+ 40 - 2
Praxis3D/Source/TextureLoader.cpp

@@ -148,6 +148,7 @@ TextureLoader2D::Texture2DHandle TextureLoader2D::load(const std::string &p_file
 	// Return the new texture
 	return Texture2DHandle(returnTexture);
 }
+
 TextureLoader2D::Texture2DHandle TextureLoader2D::load(const std::string &p_filename, unsigned int p_textureHandle)
 {
 	Texture2D *returnTexture;
@@ -230,6 +231,43 @@ TextureLoader2D::Texture2DHandle TextureLoader2D::load(const std::string &p_file
 	return Texture2DHandle(returnTexture);
 }
 
+TextureLoader2D::Texture2DHandle TextureLoader2D::create(const std::string &p_name, const unsigned int p_width, const unsigned int p_height, const TextureFormat p_textureFormat, const TextureDataFormat p_textureDataFormat, const TextureDataType p_textureDataType, const bool p_createMipmap, const void *p_data)
+{
+	Texture2D *returnTexture;
+
+	// Make sure calls from other threads are locked, while current call is in progress
+	// This is needed to as the object that is being requested might be currently loading /
+	// being added to the pool. Mutex prevents duplicates being loaded, and same data being changed.
+	SpinWait::Lock lock(m_mutex);
+
+	// Go through the texture pool and check if the texture hasn't been already loaded (to avoid duplicates)
+	for(decltype(m_objectPool.size()) size = m_objectPool.size(), i = 0; i < size; i++)
+	{
+		if(*m_objectPool[i] == p_name)
+			return Texture2DHandle(m_objectPool[i]);
+	}
+
+	// Texture wasn't loaded before, so create a new one
+	// Assign default handle (as a placeholder to be used before the texture is loaded from HDD)
+	returnTexture = new Texture2D(this, p_name, m_objectPool.size(), m_default2DTexture->m_handle);
+
+	returnTexture->m_textureWidth = p_width;
+	returnTexture->m_textureHeight = p_height;
+	returnTexture->m_textureFormat = p_textureFormat;
+	returnTexture->m_textureDataFormat = p_textureDataFormat;
+	returnTexture->m_textureDataType = p_textureDataType;
+	returnTexture->m_enableMipmap = p_createMipmap;
+	returnTexture->m_pixelData = (unsigned char*)p_data;
+
+	returnTexture->setLoadedToMemory(true);
+
+	// Add the new texture to the list
+	m_objectPool.push_back(returnTexture);
+
+	// Return the new texture
+	return Texture2DHandle(returnTexture);
+}
+
 TextureLoaderCubemap::TextureLoaderCubemap()
 {
 	std::string defaultFilenames[CubemapFace_NumOfFaces];
@@ -268,7 +306,7 @@ ErrorCode TextureLoaderCubemap::init()
 	return ErrorCode::Success;
 }
 
-TextureLoaderCubemap::TextureCubemapHandle TextureLoaderCubemap::load(const std::string & p_filenamePosX, const std::string & p_filenameNegX, const std::string & p_filenamePosY, const std::string & p_filenameNegY, const std::string & p_filenamePosZ, const std::string & p_filenameNegZ, bool p_startBackgroundLoading)
+TextureLoaderCubemap::TextureCubemapHandle TextureLoaderCubemap::load(const std::string &p_filenamePosX, const std::string &p_filenameNegX, const std::string &p_filenamePosY, const std::string &p_filenameNegY, const std::string &p_filenamePosZ, const std::string &p_filenameNegZ, bool p_startBackgroundLoading)
 {
 	std::string filenames[CubemapFace_NumOfFaces];
 
@@ -330,7 +368,7 @@ TextureLoaderCubemap::TextureCubemapHandle TextureLoaderCubemap::load(const std:
 	return TextureCubemapHandle(returnTexture);
 }
 
-TextureLoaderCubemap::TextureCubemapHandle TextureLoaderCubemap::load(const std::string & p_filename, unsigned int p_textureHandle)
+TextureLoaderCubemap::TextureCubemapHandle TextureLoaderCubemap::load(const std::string &p_filename, unsigned int p_textureHandle)
 {
 	TextureCubemap *returnTexture;
 

+ 15 - 2
Praxis3D/Source/TextureLoader.h

@@ -50,12 +50,16 @@ class Texture2D : public LoaderBase<TextureLoader2D, Texture2D>::UniqueObject
 protected:
 	Texture2D(LoaderBase<TextureLoader2D, Texture2D> *p_loaderBase, std::string p_filename, size_t p_uniqueID, unsigned int p_handle) : UniqueObject(p_loaderBase, p_uniqueID, p_filename), m_handle(p_handle)
 	{
+		m_size = 0;
+		m_enableMipmap = Config::textureVar().generate_mipmaps;
 		m_mipmapLevel = 0;
 		m_textureWidth = 0;
 		m_textureHeight = 0;
 		m_pixelData = nullptr;
 		m_bitmap = nullptr;
-		m_textureFormat = TextureFormat_RGBA;
+		m_textureFormat = TextureFormat::TextureFormat_RGBA;
+		m_textureDataFormat = TextureDataFormat::TextureDataFormat_RGBA8;
+		m_textureDataType = TextureDataType::TextureDataType_UnsignedByte;
 	}
 
 	// Loads pixel data (using the filename) from HDD to RAM, re-factors it
@@ -87,11 +91,13 @@ protected:
 				if(samplesPerPixel == 3)
 				{
 					m_textureFormat = TextureFormat_RGB;
+					m_textureDataFormat = TextureDataFormat_RGB8;
 					m_bitmap = FreeImage_ConvertTo24Bits(m_bitmap);
 				}
 				else
 				{
 					m_textureFormat = TextureFormat_RGBA;
+					m_textureDataFormat = TextureDataFormat_RGBA8;
 					m_bitmap = FreeImage_ConvertTo32Bits(m_bitmap);
 				}
 
@@ -193,6 +199,9 @@ protected:
 
 protected:
 	TextureFormat m_textureFormat;
+	TextureDataFormat m_textureDataFormat;
+	TextureDataType m_textureDataType;
+	bool m_enableMipmap;
 	int m_mipmapLevel;
 
 	FIBITMAP* m_bitmap;
@@ -261,6 +270,9 @@ public:
 		inline int getMipmapLevel() const { return m_textureData->m_mipmapLevel; }
 		inline std::string getFilename() const { return m_textureData->m_filename; }
 		inline TextureFormat getTextureFormat() const { return m_textureData->m_textureFormat; }
+		inline TextureDataFormat getTextureDataFormat() const { return m_textureData->m_textureDataFormat; }
+		inline TextureDataType getTextureDataType() const { return m_textureData->m_textureDataType; }
+		inline bool getEnableMipmap() const { return m_textureData->m_enableMipmap; }
 
 		inline const bool isLoadedToVideoMemory() { return m_textureData->isLoadedToVideoMemory(); }
 
@@ -289,7 +301,8 @@ public:
 	Texture2DHandle load(const std::string &p_filename, MaterialType p_materialType, bool p_startBackgroundLoading = true);
 	Texture2DHandle load(const std::string &p_filename, unsigned int p_textureHandle);
 	Texture2DHandle load(const std::string &p_filename);
-	
+	Texture2DHandle create(const std::string &p_name, const unsigned int p_width, const unsigned int p_height, const TextureFormat p_textureFormat, const TextureDataFormat p_textureDataFormat, const TextureDataType p_textureDataType, const bool p_createMipmap = false, const void *p_data = NULL);
+
 	Texture2DHandle getDefaultTexture(MaterialType p_materialType = MaterialType::MaterialType_Diffuse)
 	{
 		Texture2D *returnTexture = m_default2DTexture;