diff --git a/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl b/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl
index 46ce7d66526e4a6e04211770aed7a7d97d1275e8..1dd697b43d6fc4331722f18e518770c0003b6af3 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl
@@ -144,26 +144,26 @@ vec2 getScreenCoordinate(vec2 screenpos)
 //      Method #4: Spheremap Transform, Lambert Azimuthal Equal-Area projection
 vec3 getNorm(vec2 screenpos)
 {
-   vec2 enc = texture(normalMap, screenpos.xy).xy;
-   vec2 fenc = enc*4-2;
-   float f = dot(fenc,fenc);
-   float g = sqrt(1-f/4);
-   vec3 n;
-   n.xy = fenc*g;
-   n.z = 1-f/2;
-   return n;
+   vec2 f = texture(normalMap, screenpos.xy).xy;
+    f = f * 2.0 - 1.0;
+ 
+    // https://twitter.com/Stubbesaurus/status/937994790553227264
+    vec3 n = vec3( f.x, f.y, 1.0 - abs( f.x ) - abs( f.y ) );
+    float t = clamp( -n.z , 0.0, 1.0);
+    n.xy += vec2(n.x >= 0.0 ? -t : t, n.y >= 0.0 ? -t : t); 
+    return normalize( n );
 }
 
 vec3 getNormalFromPacked(vec4 packedNormalEnvIntensityFlags)
 {
-   vec2 enc = packedNormalEnvIntensityFlags.xy;
-   vec2 fenc = enc*4-2;
-   float f = dot(fenc,fenc);
-   float g = sqrt(1-f/4);
-   vec3 n;
-   n.xy = fenc*g;
-   n.z = 1-f/2;
-   return normalize(n); // TODO: Is this normalize redundant?
+    vec2 f = packedNormalEnvIntensityFlags.xy;
+    f = f * 2.0 - 1.0;
+
+    // https://twitter.com/Stubbesaurus/status/937994790553227264
+    vec3 n = vec3( f.x, f.y, 1.0 - abs( f.x ) - abs( f.y ) );
+    float t = clamp( -n.z , 0.0, 1.0);
+    n.xy += vec2(n.x >= 0.0 ? -t : t, n.y >= 0.0 ? -t : t); 
+    return normalize( n );
 }
 
 // return packedNormalEnvIntensityFlags since GBUFFER_FLAG_HAS_PBR needs .w
@@ -392,7 +392,7 @@ vec3 pbrIbl(vec3 diffuseColor,
             out vec3 specContrib)
 {
     // retrieve a scale and bias to F0. See [1], Figure 3
-    vec2 brdf = BRDF(clamp(nv, 0, 1), 1.0-perceptualRough);
+    vec2 brdf = BRDF(clamp(nv, 0, 1), perceptualRough);
     vec3 diffuseLight = irradiance;
     vec3 specularLight = radiance;
     
diff --git a/indra/newview/app_settings/shaders/class1/environment/encodeNormF.glsl b/indra/newview/app_settings/shaders/class1/environment/encodeNormF.glsl
index 6cd24455228f402580f9e9d068400b4d935728c6..20e68fae2596df21b0be42ee51586ad25bd5e76d 100644
--- a/indra/newview/app_settings/shaders/class1/environment/encodeNormF.glsl
+++ b/indra/newview/app_settings/shaders/class1/environment/encodeNormF.glsl
@@ -23,12 +23,20 @@
  * $/LicenseInfo$
  */
 
-// Lambert Azimuthal Equal-Area projection
-// See: https://aras-p.info/texts/CompactNormalStorage.html
-// Also see: A_bit_more_deferred_-_CryEngine3.ppt
+ // Octahedron normal vector encoding
+ // https://knarkowicz.wordpress.com/2014/04/16/octahedron-normal-vector-encoding/
+ //
+
+vec2 OctWrap( vec2 v )
+{
+    return ( 1.0 - abs( v.yx ) ) * vec2(v.x >= 0.0 ? 1.0 : -1.0, v.y >= 0.0 ? 1.0 : -1.0); 
+}
+
 vec2 encode_normal(vec3 n)
 {
-	float f = sqrt(8 * n.z + 8);
-	return n.xy / f + 0.5;
+    n /= ( abs( n.x ) + abs( n.y ) + abs( n.z ) );
+    n.xy = n.z >= 0.0 ? n.xy : OctWrap( n.xy );
+    n.xy = n.xy * 0.5 + 0.5;
+    return n.xy;
 }