diff --git a/Effects/HDR/bloom-threshold.eff b/Effects/HDR/bloom-threshold.eff
index 6d9ae5c35..96816aa4e 100644
--- a/Effects/HDR/bloom-threshold.eff
+++ b/Effects/HDR/bloom-threshold.eff
@@ -1,11 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
 <PropertyList>
   <name>Effects/HDR/bloom-threshold</name>
+  <parameters>
+    <exposure-compensation>
+      <use>/sim/rendering/hdr/exposure-compensation</use>
+    </exposure-compensation>
+    <bloom-threshold><use>/sim/rendering/hdr/bloom-threshold</use></bloom-threshold>
+  </parameters>
   <technique n="1">
     <pass>
       <program>
         <vertex-shader>Shaders/HDR/trivial.vert</vertex-shader>
         <fragment-shader>Shaders/HDR/bloom-threshold.frag</fragment-shader>
+        <fragment-shader>Shaders/HDR/exposure-include.frag</fragment-shader>
       </program>
       <uniform>
         <name>hdr_tex</name>
@@ -17,6 +24,16 @@
         <type>sampler-2d</type>
         <value type="int">1</value>
       </uniform>
+      <uniform>
+        <name>exposure_compensation</name>
+        <type>float</type>
+        <value><use>exposure-compensation</use></value>
+      </uniform>
+      <uniform>
+        <name>bloom_threshold</name>
+        <type>float</type>
+        <value><use>bloom-threshold</use></value>
+      </uniform>
     </pass>
   </technique>
 </PropertyList>
diff --git a/Effects/HDR/postprocess.eff b/Effects/HDR/postprocess.eff
index 5e8aa1cc8..f23d14a6c 100644
--- a/Effects/HDR/postprocess.eff
+++ b/Effects/HDR/postprocess.eff
@@ -1,11 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
 <PropertyList>
   <name>Effects/HDR/postprocess</name>
+  <parameters>
+    <exposure-compensation>
+      <use>/sim/rendering/hdr/exposure-compensation</use>
+    </exposure-compensation>
+    <bloom-magnitude><use>/sim/rendering/hdr/bloom-magnitude</use></bloom-magnitude>
+    <debug-ev100><use>/sim/rendering/hdr/debug/display-ev100</use></debug-ev100>
+  </parameters>
   <technique n="1">
     <pass>
       <program>
         <vertex-shader>Shaders/HDR/trivial.vert</vertex-shader>
         <fragment-shader>Shaders/HDR/postprocess.frag</fragment-shader>
+        <fragment-shader>Shaders/HDR/exposure-include.frag</fragment-shader>
       </program>
       <uniform>
         <name>hdr_tex</name>
@@ -22,6 +30,21 @@
         <type>sampler-2d</type>
         <value type="int">4</value>
       </uniform>
+      <uniform>
+        <name>exposure_compensation</name>
+        <type>float</type>
+        <value><use>exposure-compensation</use></value>
+      </uniform>
+      <uniform>
+        <name>bloom_magnitude</name>
+        <type>float</type>
+        <value><use>bloom-magnitude</use></value>
+      </uniform>
+      <uniform>
+        <name>debug_ev100</name>
+        <type>bool</type>
+        <value><use>debug-ev100</use></value>
+      </uniform>
     </pass>
   </technique>
 </PropertyList>
diff --git a/Shaders/HDR/adapt-luminance.frag b/Shaders/HDR/adapt-luminance.frag
index 2efeb64d8..e7cfa438b 100644
--- a/Shaders/HDR/adapt-luminance.frag
+++ b/Shaders/HDR/adapt-luminance.frag
@@ -13,7 +13,7 @@ const float TAU = 4.0;
 void main()
 {
     float prevLum = texelFetch(prev_lum_tex, ivec2(0), 0).r;
-    float currentLum = exp2(textureLod(current_lum_tex, vec2(0.5), 10.0).r);
+    float currentLum = exp(textureLod(current_lum_tex, vec2(0.5), 10.0).r);
     adaptedLum = prevLum + (currentLum - prevLum) *
         (1.0 - exp(-osg_DeltaFrameTime * TAU));
 }
diff --git a/Shaders/HDR/bloom-threshold.frag b/Shaders/HDR/bloom-threshold.frag
index d940ea821..e5a0bed5f 100644
--- a/Shaders/HDR/bloom-threshold.frag
+++ b/Shaders/HDR/bloom-threshold.frag
@@ -7,17 +7,18 @@ in vec2 texCoord;
 uniform sampler2D hdr_tex;
 uniform sampler2D lum_tex;
 
+uniform float bloom_threshold;
+
+vec3 applyExposure(vec3 color, float avgLuminance, float threshold);
+
 void main()
 {
     vec3 hdrColor = texture(hdr_tex, texCoord).rgb;
-    float avgLuminance = texture(lum_tex, texCoord).r;
-    // XXX: Maybe we should actually control the EV compensation value itself
-    // instead of hardcoding a factor?
-    float exposure = 1.0 / (200.0 * avgLuminance);
-    hdrColor *= exposure;
+    float avgLuminance = texelFetch(lum_tex, ivec2(0), 0).r;
 
-    if (dot(hdrColor, vec3(0.333)) <= 0.001)
+    vec3 exposedHdrColor = applyExposure(hdrColor, avgLuminance, bloom_threshold);
+    if (dot(exposedHdrColor, vec3(0.333)) <= 0.001)
 		fragColor = vec3(0.0);
     else
-        fragColor = hdrColor;
+        fragColor = exposedHdrColor;
 }
diff --git a/Shaders/HDR/exposure-include.frag b/Shaders/HDR/exposure-include.frag
new file mode 100644
index 000000000..12220828a
--- /dev/null
+++ b/Shaders/HDR/exposure-include.frag
@@ -0,0 +1,27 @@
+#version 330 core
+
+uniform float exposure_compensation;
+
+const float one_over_log10 = 1.0 / log(10.0);
+
+float log10(float x)
+{
+    return one_over_log10 * log(x);
+}
+
+// Custom exposure curve based on the one proposed on
+// 'Perceptual Effects in Real-time Tone Mapping'.
+// http://resources.mpi-inf.mpg.de/hdr/peffects/krawczyk05sccg.pdf
+float keyValue(float L)
+{
+    return 0.82 - 2.0 / (log10(L + 0.84) + 2.6);
+}
+
+vec3 applyExposure(vec3 color, float avgLuminance, float threshold)
+{
+    avgLuminance = max(avgLuminance, 0.001);
+    float linearExposure = keyValue(avgLuminance) / avgLuminance;
+    float exposure = log2(max(linearExposure, 0.0001));
+    exposure += exposure_compensation - threshold;
+    return color * exp2(exposure);
+}
diff --git a/Shaders/HDR/luminance.frag b/Shaders/HDR/luminance.frag
index 597ebf191..fb69556b7 100644
--- a/Shaders/HDR/luminance.frag
+++ b/Shaders/HDR/luminance.frag
@@ -9,5 +9,5 @@ uniform sampler2D hdr_tex;
 void main()
 {
     vec3 hdrColor = texture(hdr_tex, texCoord).rgb;
-    luminance = log2(max(dot(hdrColor, vec3(0.299, 0.587, 0.114)), 0.0001));
+    luminance = log(max(dot(hdrColor, vec3(0.299, 0.587, 0.114)), 0.0001));
 }
diff --git a/Shaders/HDR/postprocess.frag b/Shaders/HDR/postprocess.frag
index c7672889a..1a2e31472 100644
--- a/Shaders/HDR/postprocess.frag
+++ b/Shaders/HDR/postprocess.frag
@@ -10,51 +10,74 @@ uniform sampler2D bloom_tex;
 
 uniform vec2 fg_BufferSize;
 
-const float BLOOM_INTENSITY = 1.0;
-const float EXPOSURE_THRESHOLD = 0.0;
+uniform float bloom_magnitude;
+uniform bool debug_ev100;
 
+vec3 applyExposure(vec3 color, float avgLuminance, float threshold);
+
+vec3 getDebugColor(float value)
+{
+    float level = value*3.14159265/2.0;
+    vec3 col;
+    col.r = sin(level);
+    col.g = sin(level*2.0);
+    col.b = cos(level);
+    return col;
+}
+
+vec3 debugEV100(vec3 hdr, float avgLuminance)
+{
+    float level;
+    if (texCoord.y < 0.05) {
+        const float w = 0.001;
+        if (texCoord.x >= (0.5 - w) && texCoord.x <= (0.5 + w))
+            return vec3(1.0);
+        return getDebugColor(texCoord.x);
+    }
+    float luminance = max(dot(hdr, vec3(0.299, 0.587, 0.114)), 0.0001);
+    float ev100 = log2(luminance * 8.0);
+    float norm = ev100 / 12.0 + 0.5;
+    return getDebugColor(norm);
+}
+
+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;
 vec3 ACESFilm(vec3 x)
 {
-    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 clamp((x*(a*x+b))/(x*(c*x+d)+e), 0.0, 1.0);
 }
 
-float log10(float x)
+vec3 encodeSRGB(vec3 linearRGB)
 {
-    return (1.0 / log(10.0)) * log(x);
-}
-
-float autokey(float lum)
-{
-    return 1.03 - 2.0 / (2.0 + log10(lum + 1.0));
+    vec3 a = 12.92 * linearRGB;
+    vec3 b = 1.055 * pow(linearRGB, vec3(1.0 / 2.4)) - 0.055;
+    vec3 c = step(vec3(0.0031308), linearRGB);
+    return mix(a, b, c);
 }
 
 void main()
 {
     vec3 hdrColor = texture(hdr_tex, texCoord).rgb;
-
     float avgLuminance = texelFetch(lum_tex, ivec2(0), 0).r;
-    avgLuminance = max(avgLuminance, 0.001);
 
-    // Auto-expose
-    float linearExposure = autokey(avgLuminance) / avgLuminance;
-    float exposure = log2(max(linearExposure, 0.0001));
-    exposure -= EXPOSURE_THRESHOLD;
-    hdrColor *= exp2(exposure);
+    // Exposure
+    vec3 exposedHdrColor = applyExposure(hdrColor, avgLuminance, 0.0);
+    if (debug_ev100) {
+        fragColor = vec4(debugEV100(exposedHdrColor, avgLuminance), 1.0);
+        return;
+    }
 
     // Tonemap
-    vec3 color = ACESFilm(hdrColor);
+    vec3 color = ACESFilm(exposedHdrColor);
     // Gamma correction
-    color = pow(color, vec3(1.0/2.2));
+    color = encodeSRGB(color);
 
-    // Apply bloom
+    // Bloom
     vec3 bloom = texture(bloom_tex, texCoord).rgb;
-    bloom *= BLOOM_INTENSITY;
-    color += bloom;
+    color += bloom.rgb * bloom_magnitude;
 
     fragColor = vec4(color, 1.0);
 }
diff --git a/Shaders/HDR/ws30.frag b/Shaders/HDR/ws30.frag
index af7aaf35a..21a5a0c8d 100644
--- a/Shaders/HDR/ws30.frag
+++ b/Shaders/HDR/ws30.frag
@@ -19,6 +19,7 @@ uniform float tile_width;
 uniform float tile_height;
 
 vec2 encodeNormal(vec3 n);
+vec3 decodeSRGB(vec3 screenRGB);
 
 void main()
 {
@@ -45,10 +46,11 @@ void main()
 
     vec3 texel = texture(atlas, vec3(st, lc)).rgb;
 
-    gbuffer0.rgb = pow(texel, vec3(2.2)); // Gamma correction
+    gbuffer0.rgb = decodeSRGB(texel) * color.rgb;
     gbuffer0.a = 1.0;
     gbuffer1 = encodeNormal(normalVS);
-    gbuffer2 = vec4(0.0, 0.9, 0.0, 0.0);
+    float specularity = clamp(dot(specular.rgb, vec3(0.333)), 0.0, 1.0);
+    gbuffer2 = vec4(0.0, 1.0-specularity, 0.0, 0.0);
 }
 
 
diff --git a/defaults.xml b/defaults.xml
index ff7a0fcc1..b9ed5d57f 100644
--- a/defaults.xml
+++ b/defaults.xml
@@ -382,6 +382,14 @@ Started September 2000 by David Megginson, david@megginson.com
         <available type="bool" userarchive="n">false</available>
         <enabled type="bool" userarchive="y">true</enabled>
       </pilot-model>
+      <hdr>
+        <debug>
+          <display-ev100 type="bool">false</display-ev100>
+        </debug>
+        <exposure-compensation type="float">0.0</exposure-compensation>
+        <bloom-magnitude type="float">1.0</bloom-magnitude>
+        <bloom-threshold type="float">6.0</bloom-threshold>
+      </hdr>
     </rendering>
     <model-hz type="int">120</model-hz>
     <sound>