diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp
index 71303b0517ffff84a344d021a5d9e6b1ca59b61b..2dbff3ffd0126de5a77854afbcf1641edc5a7513 100644
--- a/indra/llrender/llgl.cpp
+++ b/indra/llrender/llgl.cpp
@@ -1367,6 +1367,8 @@ void LLGLManager::initExtensions()
     }
 #endif
 
+    // NOTE: version checks against mGLVersion should bias down by 0.01 because of F32 errors
+    
     // OpenGL 4.x capabilities
     mHasCubeMapArray = mGLVersion >= 3.99f; 
     mHasTransformFeedback = mGLVersion >= 3.99f;
@@ -1377,6 +1379,8 @@ void LLGLManager::initExtensions()
 	glGetIntegerv(GL_MAX_ELEMENTS_INDICES, (GLint*) &mGLMaxIndexRange);
 	glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint*) &mGLMaxTextureSize);
 
+    mInited = TRUE;
+
 #if (LL_WINDOWS || LL_LINUX) && !LL_MESA_HEADLESS
 	LL_DEBUGS("RenderInit") << "GL Probe: Getting symbols" << LL_ENDL;
 	
@@ -1400,7 +1404,14 @@ void LLGLManager::initExtensions()
     wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)GLH_EXT_GET_PROC_ADDRESS("wglCreateContextAttribsARB");
 #endif
 
+
+    // Load entire OpenGL API through GetProcAddress, leaving sections beyond mGLVersion unloaded
+
     // GL_VERSION_1_2
+    if (mGLVersion < 1.19f)
+    {
+        return;
+    }
     glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawRangeElements");
     glTexImage3D = (PFNGLTEXIMAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glTexImage3D");
     glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glTexSubImage3D");
@@ -1408,6 +1419,10 @@ void LLGLManager::initExtensions()
 
 
     // GL_VERSION_1_3
+    if (mGLVersion < 1.29f)
+    {
+        return;
+    }
     glActiveTexture = (PFNGLACTIVETEXTUREPROC)GLH_EXT_GET_PROC_ADDRESS("glActiveTexture");
     glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glSampleCoverage");
     glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)GLH_EXT_GET_PROC_ADDRESS("glCompressedTexImage3D");
@@ -1456,6 +1471,10 @@ void LLGLManager::initExtensions()
     glMultTransposeMatrixd = (PFNGLMULTTRANSPOSEMATRIXDPROC)GLH_EXT_GET_PROC_ADDRESS("glMultTransposeMatrixd");
 
     // GL_VERSION_1_4
+    if (mGLVersion < 1.39f)
+    {
+        return;
+    }
     glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC)GLH_EXT_GET_PROC_ADDRESS("glBlendFuncSeparate");
     glMultiDrawArrays = (PFNGLMULTIDRAWARRAYSPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiDrawArrays");
     glMultiDrawElements = (PFNGLMULTIDRAWELEMENTSPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiDrawElements");
@@ -1503,6 +1522,10 @@ void LLGLManager::initExtensions()
     glWindowPos3sv = (PFNGLWINDOWPOS3SVPROC)GLH_EXT_GET_PROC_ADDRESS("glWindowPos3sv");
 
     // GL_VERSION_1_5
+    if (mGLVersion < 1.49f)
+    {
+        return;
+    }
     glGenQueries = (PFNGLGENQUERIESPROC)GLH_EXT_GET_PROC_ADDRESS("glGenQueries");
     glDeleteQueries = (PFNGLDELETEQUERIESPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteQueries");
     glIsQuery = (PFNGLISQUERYPROC)GLH_EXT_GET_PROC_ADDRESS("glIsQuery");
@@ -1524,6 +1547,10 @@ void LLGLManager::initExtensions()
     glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetBufferPointerv");
 
     // GL_VERSION_2_0
+    if (mGLVersion < 1.9f)
+    {
+        return;
+    }
     glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC)GLH_EXT_GET_PROC_ADDRESS("glBlendEquationSeparate");
     glDrawBuffers = (PFNGLDRAWBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawBuffers");
     glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC)GLH_EXT_GET_PROC_ADDRESS("glStencilOpSeparate");
@@ -1619,6 +1646,10 @@ void LLGLManager::initExtensions()
     glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribPointer");
 
     // GL_VERSION_2_1
+    if (mGLVersion < 2.09f)
+    {
+        return;
+    }
     glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix2x3fv");
     glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix3x2fv");
     glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix2x4fv");
@@ -1627,6 +1658,10 @@ void LLGLManager::initExtensions()
     glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix4x3fv");
 
     // GL_VERSION_3_0
+    if (mGLVersion < 2.99f)
+    {
+        return;
+    }
     glColorMaski = (PFNGLCOLORMASKIPROC)GLH_EXT_GET_PROC_ADDRESS("glColorMaski");
     glGetBooleani_v = (PFNGLGETBOOLEANI_VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetBooleani_v");
     glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetIntegeri_v");
@@ -1713,6 +1748,10 @@ void LLGLManager::initExtensions()
     glIsVertexArray = (PFNGLISVERTEXARRAYPROC)GLH_EXT_GET_PROC_ADDRESS("glIsVertexArray");
 
     // GL_VERSION_3_1
+    if (mGLVersion < 3.09f)
+    {
+        return;
+    }
     glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawArraysInstanced");
     glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawElementsInstanced");
     glTexBuffer = (PFNGLTEXBUFFERPROC)GLH_EXT_GET_PROC_ADDRESS("glTexBuffer");
@@ -1727,6 +1766,10 @@ void LLGLManager::initExtensions()
     glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)GLH_EXT_GET_PROC_ADDRESS("glUniformBlockBinding");
 
     // GL_VERSION_3_2
+    if (mGLVersion < 3.19f)
+    {
+        return;
+    }
     glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawElementsBaseVertex");
     glDrawRangeElementsBaseVertex = (PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawRangeElementsBaseVertex");
     glDrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawElementsInstancedBaseVertex");
@@ -1748,6 +1791,10 @@ void LLGLManager::initExtensions()
     glSampleMaski = (PFNGLSAMPLEMASKIPROC)GLH_EXT_GET_PROC_ADDRESS("glSampleMaski");
 
     // GL_VERSION_3_3
+    if (mGLVersion < 3.29f)
+    {
+        return;
+    }
     glBindFragDataLocationIndexed = (PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)GLH_EXT_GET_PROC_ADDRESS("glBindFragDataLocationIndexed");
     glGetFragDataIndex = (PFNGLGETFRAGDATAINDEXPROC)GLH_EXT_GET_PROC_ADDRESS("glGetFragDataIndex");
     glGenSamplers = (PFNGLGENSAMPLERSPROC)GLH_EXT_GET_PROC_ADDRESS("glGenSamplers");
@@ -1808,6 +1855,10 @@ void LLGLManager::initExtensions()
     glSecondaryColorP3uiv = (PFNGLSECONDARYCOLORP3UIVPROC)GLH_EXT_GET_PROC_ADDRESS("glSecondaryColorP3uiv");
 
     // GL_VERSION_4_0
+    if (mGLVersion < 3.99f)
+    {
+        return;
+    }
     glMinSampleShading = (PFNGLMINSAMPLESHADINGPROC)GLH_EXT_GET_PROC_ADDRESS("glMinSampleShading");
     glBlendEquationi = (PFNGLBLENDEQUATIONIPROC)GLH_EXT_GET_PROC_ADDRESS("glBlendEquationi");
     glBlendEquationSeparatei = (PFNGLBLENDEQUATIONSEPARATEIPROC)GLH_EXT_GET_PROC_ADDRESS("glBlendEquationSeparatei");
@@ -1856,6 +1907,10 @@ void LLGLManager::initExtensions()
     glGetQueryIndexediv = (PFNGLGETQUERYINDEXEDIVPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryIndexediv");
 
     // GL_VERSION_4_1
+    if (mGLVersion < 4.09f)
+    {
+        return;
+    }
     glReleaseShaderCompiler = (PFNGLRELEASESHADERCOMPILERPROC)GLH_EXT_GET_PROC_ADDRESS("glReleaseShaderCompiler");
     glShaderBinary = (PFNGLSHADERBINARYPROC)GLH_EXT_GET_PROC_ADDRESS("glShaderBinary");
     glGetShaderPrecisionFormat = (PFNGLGETSHADERPRECISIONFORMATPROC)GLH_EXT_GET_PROC_ADDRESS("glGetShaderPrecisionFormat");
@@ -1946,6 +2001,10 @@ void LLGLManager::initExtensions()
     glGetDoublei_v = (PFNGLGETDOUBLEI_VPROC)GLH_EXT_GET_PROC_ADDRESS("glGetDoublei_v");
 
     // GL_VERSION_4_2
+    if (mGLVersion < 4.19f)
+    {
+        return;
+    }
     glDrawArraysInstancedBaseInstance = (PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawArraysInstancedBaseInstance");
     glDrawElementsInstancedBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawElementsInstancedBaseInstance");
     glDrawElementsInstancedBaseVertexBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawElementsInstancedBaseVertexBaseInstance");
@@ -1960,6 +2019,10 @@ void LLGLManager::initExtensions()
     glDrawTransformFeedbackStreamInstanced = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawTransformFeedbackStreamInstanced");
 
     // GL_VERSION_4_3
+    if (mGLVersion < 4.29f)
+    {
+        return;
+    }
     glClearBufferData = (PFNGLCLEARBUFFERDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glClearBufferData");
     glClearBufferSubData = (PFNGLCLEARBUFFERSUBDATAPROC)GLH_EXT_GET_PROC_ADDRESS("glClearBufferSubData");
     glDispatchCompute = (PFNGLDISPATCHCOMPUTEPROC)GLH_EXT_GET_PROC_ADDRESS("glDispatchCompute");
@@ -2005,6 +2068,10 @@ void LLGLManager::initExtensions()
     glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC)GLH_EXT_GET_PROC_ADDRESS("glGetObjectPtrLabel");
 
     // GL_VERSION_4_4
+    if (mGLVersion < 4.39f)
+    {
+        return;
+    }
     glBufferStorage = (PFNGLBUFFERSTORAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glBufferStorage");
     glClearTexImage = (PFNGLCLEARTEXIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glClearTexImage");
     glClearTexSubImage = (PFNGLCLEARTEXSUBIMAGEPROC)GLH_EXT_GET_PROC_ADDRESS("glClearTexSubImage");
@@ -2016,6 +2083,10 @@ void LLGLManager::initExtensions()
     glBindVertexBuffers = (PFNGLBINDVERTEXBUFFERSPROC)GLH_EXT_GET_PROC_ADDRESS("glBindVertexBuffers");
 
     // GL_VERSION_4_5
+    if (mGLVersion < 4.49f)
+    {
+        return;
+    }
     glClipControl = (PFNGLCLIPCONTROLPROC)GLH_EXT_GET_PROC_ADDRESS("glClipControl");
     glCreateTransformFeedbacks = (PFNGLCREATETRANSFORMFEEDBACKSPROC)GLH_EXT_GET_PROC_ADDRESS("glCreateTransformFeedbacks");
     glTransformFeedbackBufferBase = (PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC)GLH_EXT_GET_PROC_ADDRESS("glTransformFeedbackBufferBase");
@@ -2140,15 +2211,16 @@ void LLGLManager::initExtensions()
     glTextureBarrier = (PFNGLTEXTUREBARRIERPROC)GLH_EXT_GET_PROC_ADDRESS("glTextureBarrier");
 
     // GL_VERSION_4_6
+    if (mGLVersion < 4.59f)
+    {
+        return;
+    }
     glSpecializeShader = (PFNGLSPECIALIZESHADERPROC)GLH_EXT_GET_PROC_ADDRESS("glSpecializeShader");
     glMultiDrawArraysIndirectCount = (PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiDrawArraysIndirectCount");
     glMultiDrawElementsIndirectCount = (PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC)GLH_EXT_GET_PROC_ADDRESS("glMultiDrawElementsIndirectCount");
     glPolygonOffsetClamp = (PFNGLPOLYGONOFFSETCLAMPPROC)GLH_EXT_GET_PROC_ADDRESS("glPolygonOffsetClamp");
     
-    LL_DEBUGS("RenderInit") << "GL Probe: Got symbols" << LL_ENDL;
 #endif
-
-    mInited = TRUE;
 }
 
 void rotate_quat(LLQuaternion& rotation)
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index a1f5e907300af80f2212acce4bbad924fedbae83..01271ddc28106bd92b84048bd3ff2ac97ceec771 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -9647,7 +9647,7 @@
   <key>RenderPBR</key>
   <map>
     <key>Comment</key>
-    <string>Use PBR rendering pipeline (Physically Based Rendering).</string>
+    <string>DEPRECATED - only true supported - Use PBR rendering pipeline (Physically Based Rendering).</string>
     <key>Persist</key>
     <integer>1</integer>
     <key>Type</key>
@@ -10328,7 +10328,7 @@
   <key>RenderReflectionProbeDetail</key>
   <map>
     <key>Comment</key>
-    <string>Detail of reflections.</string>
+    <string>Detail of reflections. (-1 - Disabled, 0 - Static Only, 1 - Static + Dynamic, 2 - Realtime)</string>
     <key>Persist</key>
     <integer>1</integer>
     <key>Type</key>
diff --git a/indra/newview/app_settings/shaders/class1/deferred/fullbrightShinyV.glsl b/indra/newview/app_settings/shaders/class1/deferred/fullbrightShinyV.glsl
index 5264b3b716698f45a948b4bcb5a700965c4005ff..0e461b400480f7aec3b753364c69e66e921130f7 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/fullbrightShinyV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/fullbrightShinyV.glsl
@@ -73,12 +73,7 @@ void main()
     vary_position = pos.xyz;
 	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
 
-#ifndef HAS_REFLECTION_PROBES	
-    vec3 ref = reflect(pos.xyz, -norm);
-	vary_texcoord1 = transpose(normal_matrix) * ref.xyz;
-#else
     vary_texcoord1 = norm;
-#endif
 
 	calcAtmospherics(pos.xyz);
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/genbrdflutF.glsl b/indra/newview/app_settings/shaders/class1/deferred/genbrdflutF.glsl
index 73a70d8dc53c69d0c95ca1609c15585fc6ee2b5b..9e6c8530155ff8753aa952a2f07a8343f60f6f4b 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/genbrdflutF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/genbrdflutF.glsl
@@ -53,7 +53,7 @@ VARYING vec2 vary_uv;
 
 out vec4 outColor;
 
-#define NUM_SAMPLES 1024
+#define NUM_SAMPLES 1024u
 
 const float PI = 3.1415926536;
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbralphaF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbralphaF.glsl
index 04be496292b96b4c3d1357318b59865aaa9c7d79..3afa9fb4355b868f5afaa0e523a6adb713dba888 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pbralphaF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbralphaF.glsl
@@ -99,8 +99,8 @@ vec3 scaleSoftClipFrag(vec3 l);
 void calcHalfVectors(vec3 lv, vec3 n, vec3 v, out vec3 h, out vec3 l, out float nh, out float nl, out float nv, out float vh, out float lightDist);
 float calcLegacyDistanceAttenuation(float distance, float falloff);
 float sampleDirectionalShadow(vec3 pos, vec3 norm, vec2 pos_screen);
-void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyEnv, 
-        vec3 pos, vec3 norm, float glossiness, float envIntensity);
+void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, 
+        vec3 pos, vec3 norm, float glossiness);
 
 // PBR interface
 vec3 pbrIbl(vec3 diffuseColor,
@@ -212,8 +212,7 @@ void main()
     float gloss      = 1.0 - perceptualRoughness;
     vec3  irradiance = vec3(0);
     vec3  radiance  = vec3(0);
-    vec3 legacyenv = vec3(0);
-    sampleReflectionProbes(irradiance, radiance, legacyenv, pos.xyz, norm.xyz, gloss, 0.0);
+    sampleReflectionProbes(irradiance, radiance, pos.xyz, norm.xyz, gloss);
     irradiance       = max(srgb_to_linear(amblit),irradiance) * ambocc*4.0;
 
     vec3 f0 = vec3(0.04);
diff --git a/indra/newview/app_settings/shaders/class1/deferred/reflectionProbeF.glsl b/indra/newview/app_settings/shaders/class1/deferred/reflectionProbeF.glsl
index 8f3e38b08bfd8ee9756a70b5ea49458dcc7920d8..4b79297aefbac1df6386d51cd90b4aace41bf17f 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/reflectionProbeF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/reflectionProbeF.glsl
@@ -24,7 +24,14 @@
  */
 
 // fallback stub -- will be used if actual reflection probe shader failed to load (output pink so it's obvious)
-void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv, 
+void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv,
+        vec3 pos, vec3 norm, float glossiness)
+{
+    ambenv = vec3(1,0,1);
+    glossenv = vec3(1,0,1);
+}
+
+void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv, 
         vec3 pos, vec3 norm, float glossiness, float envIntensity)
 {
     ambenv = vec3(1,0,1);
@@ -34,11 +41,11 @@ void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, inout vec3 l
 
 void applyGlossEnv(inout vec3 color, vec3 glossenv, vec4 spec, vec3 pos, vec3 norm)
 {
-    color = vec3(1,0,1);
+    
 }
 
 void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity)
 {
-    color = vec3(1,0,1);
+
 }
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl
index 918e119c319a185b9caf993b252948d2b4289925..4b34e55efd38d380afe2a124bdc9062b51ea4fb7 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl
@@ -156,12 +156,6 @@ void main()
 
     }
 
-// linear debuggables
-//color.rgb = vec3(final_da);
-//color.rgb = vec3(ambient);
-//color.rgb = vec3(scol);
-//color.rgb = diffuse_srgb.rgb;
-
     // convert to linear as fullscreen lights need to sum in linear colorspace
     // and will be gamma (re)corrected downstream...
     
diff --git a/indra/newview/app_settings/shaders/class2/deferred/reflectionProbeF.glsl b/indra/newview/app_settings/shaders/class2/deferred/reflectionProbeF.glsl
index 3d96fe25bea22fa25fdb6ed67cea2fe821ab3d50..15d6b5a05d8333032a79b86c0b8edcaa55bd9860 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/reflectionProbeF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/reflectionProbeF.glsl
@@ -23,455 +23,46 @@
  * $/LicenseInfo$
  */
 
-#extension GL_ARB_shader_texture_lod : enable
+// Implementation for when reflection probes are disabled
 
-#define FLT_MAX 3.402823466e+38
+uniform float minimumReflectionAmbiance;
 
-#define REFMAP_COUNT 256
-#define REF_SAMPLE_COUNT 64 //maximum number of samples to consider
+uniform samplerCube environmentMap;
 
-uniform samplerCubeArray   reflectionProbes;
-
-layout (std140) uniform ReflectionProbes
-{
-    // list of OBBs for user override probes
-    // box is a set of 3 planes outward facing planes and the depth of the box along that plane
-    // for each box refBox[i]...
-    /// box[0..2] - plane 0 .. 2 in [A,B,C,D] notation
-    //  box[3][0..2] - plane thickness
-    mat4 refBox[REFMAP_COUNT];
-    // list of bounding spheres for reflection probes sorted by distance to camera (closest first)
-    vec4 refSphere[REFMAP_COUNT];
-    // index  of cube map in reflectionProbes for a corresponding reflection probe
-    // e.g. cube map channel of refSphere[2] is stored in refIndex[2]
-    // refIndex.x - cubemap channel in reflectionProbes
-    // refIndex.y - index in refNeighbor of neighbor list (index is ivec4 index, not int index)
-    // refIndex.z - number of neighbors
-    // refIndex.w - priority, if negative, this probe has a box influence
-    ivec4 refIndex[REFMAP_COUNT];
-
-    // neighbor list data (refSphere indices, not cubemap array layer)
-    ivec4 refNeighbor[1024];
-
-    // number of reflection probes present in refSphere
-    int refmapCount;
-
-    // intensity of ambient light from reflection probes
-    float reflectionAmbiance;
-};
-
-// Inputs
 uniform mat3 env_mat;
 
-// list of probeIndexes shader will actually use after "getRefIndex" is called
-// (stores refIndex/refSphere indices, NOT rerflectionProbes layer)
-int probeIndex[REF_SAMPLE_COUNT];
-
-// number of probes stored in probeIndex
-int probeInfluences = 0;
-
-bool isAbove(vec3 pos, vec4 plane)
-{
-    return (dot(plane.xyz, pos) + plane.w) > 0;
-}
-
-// return true if probe at index i influences position pos
-bool shouldSampleProbe(int i, vec3 pos)
-{
-    if (refIndex[i].w < 0)
-    {
-        vec4 v = refBox[i] * vec4(pos, 1.0);
-        if (abs(v.x) > 1 || 
-            abs(v.y) > 1 ||
-            abs(v.z) > 1)
-        {
-            return false;
-        }
-    }
-    else
-    {
-        vec3 delta = pos.xyz - refSphere[i].xyz;
-        float d = dot(delta, delta);
-        float r2 = refSphere[i].w;
-        r2 *= r2;
-
-        if (d > r2)
-        { //outside bounding sphere
-            return false;
-        }
-    }
-
-    return true;
-}
-
-// call before sampleRef
-// populate "probeIndex" with N probe indices that influence pos where N is REF_SAMPLE_COUNT
-// overall algorithm -- 
-void preProbeSample(vec3 pos)
-{
-    // TODO: make some sort of structure that reduces the number of distance checks
-
-    for (int i = 0; i < refmapCount; ++i)
-    {
-        // found an influencing probe
-        if (shouldSampleProbe(i, pos))
-        {
-            probeIndex[probeInfluences] = i;
-            ++probeInfluences;
-
-            int neighborIdx = refIndex[i].y;
-            if (neighborIdx != -1)
-            {
-                int neighborCount = min(refIndex[i].z, REF_SAMPLE_COUNT-1);
-
-                int count = 0;
-                while (count < neighborCount)
-                {
-                    // check up to REF_SAMPLE_COUNT-1 neighbors (neighborIdx is ivec4 index)
-
-                    int idx = refNeighbor[neighborIdx].x;
-                    if (shouldSampleProbe(idx, pos))
-                    {
-                        probeIndex[probeInfluences++] = idx;
-                        if (probeInfluences == REF_SAMPLE_COUNT)
-                        {
-                            return;
-                        }
-                    }
-                    count++;
-                    if (count == neighborCount)
-                    {
-                        return;
-                    }
-
-                    idx = refNeighbor[neighborIdx].y;
-                    if (shouldSampleProbe(idx, pos))
-                    {
-                        probeIndex[probeInfluences++] = idx;
-                        if (probeInfluences == REF_SAMPLE_COUNT)
-                        {
-                            return;
-                        }
-                    }
-                    count++;
-                    if (count == neighborCount)
-                    {
-                        return;
-                    }
-
-                    idx = refNeighbor[neighborIdx].z;
-                    if (shouldSampleProbe(idx, pos))
-                    {
-                        probeIndex[probeInfluences++] = idx;
-                        if (probeInfluences == REF_SAMPLE_COUNT)
-                        {
-                            return;
-                        }
-                    }
-                    count++;
-                    if (count == neighborCount)
-                    {
-                        return;
-                    }
-
-                    idx = refNeighbor[neighborIdx].w;
-                    if (shouldSampleProbe(idx, pos))
-                    {
-                        probeIndex[probeInfluences++] = idx;
-                        if (probeInfluences == REF_SAMPLE_COUNT)
-                        {
-                            return;
-                        }
-                    }
-                    count++;
-                    if (count == neighborCount)
-                    {
-                        return;
-                    }
-
-                    ++neighborIdx;
-                }
-
-                return;
-            }
-        }
-    }
-}
-
-// from https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-sphere-intersection
-
-// original reference implementation:
-/*
-bool intersect(const Ray &ray) const 
-{ 
-        float t0, t1; // solutions for t if the ray intersects 
-#if 0 
-        // geometric solution
-        Vec3f L = center - orig; 
-        float tca = L.dotProduct(dir); 
-        // if (tca < 0) return false;
-        float d2 = L.dotProduct(L) - tca * tca; 
-        if (d2 > radius2) return false; 
-        float thc = sqrt(radius2 - d2); 
-        t0 = tca - thc; 
-        t1 = tca + thc; 
-#else 
-        // analytic solution
-        Vec3f L = orig - center; 
-        float a = dir.dotProduct(dir); 
-        float b = 2 * dir.dotProduct(L); 
-        float c = L.dotProduct(L) - radius2; 
-        if (!solveQuadratic(a, b, c, t0, t1)) return false; 
-#endif 
-        if (t0 > t1) std::swap(t0, t1); 
- 
-        if (t0 < 0) { 
-            t0 = t1; // if t0 is negative, let's use t1 instead 
-            if (t0 < 0) return false; // both t0 and t1 are negative 
-        } 
- 
-        t = t0; 
- 
-        return true; 
-} */
-
-// adapted -- assume that origin is inside sphere, return distance from origin to edge of sphere
-vec3 sphereIntersect(vec3 origin, vec3 dir, vec3 center, float radius2)
-{ 
-        float t0, t1; // solutions for t if the ray intersects 
-
-        vec3 L = center - origin; 
-        float tca = dot(L,dir);
-
-        float d2 = dot(L,L) - tca * tca; 
-
-        float thc = sqrt(radius2 - d2); 
-        t0 = tca - thc; 
-        t1 = tca + thc; 
- 
-        vec3 v = origin + dir * t1;
-        return v; 
-} 
-
-// from https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
-/*
-vec3 DirectionWS = normalize(PositionWS - CameraWS);
-vec3 ReflDirectionWS = reflect(DirectionWS, NormalWS);
-
-// Intersection with OBB convertto unit box space
-// Transform in local unit parallax cube space (scaled and rotated)
-vec3 RayLS = MulMatrix( float(3x3)WorldToLocal, ReflDirectionWS);
-vec3 PositionLS = MulMatrix( WorldToLocal, PositionWS);
-
-vec3 Unitary = vec3(1.0f, 1.0f, 1.0f);
-vec3 FirstPlaneIntersect  = (Unitary - PositionLS) / RayLS;
-vec3 SecondPlaneIntersect = (-Unitary - PositionLS) / RayLS;
-vec3 FurthestPlane = max(FirstPlaneIntersect, SecondPlaneIntersect);
-float Distance = min(FurthestPlane.x, min(FurthestPlane.y, FurthestPlane.z));
-
-// Use Distance in WS directly to recover intersection
-vec3 IntersectPositionWS = PositionWS + ReflDirectionWS * Distance;
-vec3 ReflDirectionWS = IntersectPositionWS - CubemapPositionWS;
+vec3 srgb_to_linear(vec3 c);
 
-return texCUBE(envMap, ReflDirectionWS);
-*/
-
-// get point of intersection with given probe's box influence volume
-// origin - ray origin in clip space
-// dir - ray direction in clip space
-// i - probe index in refBox/refSphere
-vec3 boxIntersect(vec3 origin, vec3 dir, int i)
-{
-    // Intersection with OBB convertto unit box space
-    // Transform in local unit parallax cube space (scaled and rotated)
-    mat4 clipToLocal = refBox[i];
-
-    vec3 RayLS = mat3(clipToLocal) * dir;
-    vec3 PositionLS = (clipToLocal * vec4(origin, 1.0)).xyz;
-
-    vec3 Unitary = vec3(1.0f, 1.0f, 1.0f);
-    vec3 FirstPlaneIntersect  = (Unitary - PositionLS) / RayLS;
-    vec3 SecondPlaneIntersect = (-Unitary - PositionLS) / RayLS;
-    vec3 FurthestPlane = max(FirstPlaneIntersect, SecondPlaneIntersect);
-    float Distance = min(FurthestPlane.x, min(FurthestPlane.y, FurthestPlane.z));
-
-    // Use Distance in CS directly to recover intersection
-    vec3 IntersectPositionCS = origin + dir * Distance;
-
-    return IntersectPositionCS;
-}
-
-
-
-// Tap a sphere based reflection probe
-// pos - position of pixel
-// dir - pixel normal
-// lod - which mip to bias towards (lower is higher res, sharper reflections)
-// c - center of probe
-// r2 - radius of probe squared
-// i - index of probe 
-// vi - point at which reflection vector struck the influence volume, in clip space
-vec3 tapRefMap(vec3 pos, vec3 dir, float lod, vec3 c, float r2, int i)
+void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv,
+        vec3 pos, vec3 norm, float glossiness)
 {
-    //lod = max(lod, 1);
-    // parallax adjustment
-
-    vec3 v;
-    if (refIndex[i].w < 0)
-    {
-        v = boxIntersect(pos, dir, i);
-    }
-    else
-    {
-        v = sphereIntersect(pos, dir, c, r2);
-    }
-
-    v -= c;
-    v = env_mat * v;
-    {
-        float min_lod = textureQueryLod(reflectionProbes,v).y; // lower is higher res
-        return textureLod(reflectionProbes, vec4(v.xyz, refIndex[i].x), max(min_lod, lod)).rgb;
-        //return texture(reflectionProbes, vec4(v.xyz, refIndex[i].x)).rgb;
-    }
-}
-
-vec3 sampleProbes(vec3 pos, vec3 dir, float lod)
-{
-    float wsum = 0.0;
-    vec3 col = vec3(0,0,0);
-    float vd2 = dot(pos,pos); // view distance squared
-
-    for (int idx = 0; idx < probeInfluences; ++idx)
-    {
-        int i = probeIndex[idx];
-        float r = refSphere[i].w; // radius of sphere volume
-        float p = float(abs(refIndex[i].w)); // priority
-        float rr = r*r; // radius squred
-        float r1 = r * 0.1; // 75% of radius (outer sphere to start interpolating down)
-        vec3 delta = pos.xyz-refSphere[i].xyz;
-        float d2 = dot(delta,delta);
-        float r2 = r1*r1; 
-        
-        {
-            vec3 refcol = tapRefMap(pos, dir, lod, refSphere[i].xyz, rr, i);
-            
-            float w = 1.0/d2;
-
-            float atten = 1.0-max(d2-r2, 0.0)/(rr-r2);
-            w *= atten;
-            w *= p; // boost weight based on priority
-            col += refcol*w;
-            
-            wsum += w;
-        }
-    }
-
-    if (probeInfluences <= 1)
-    { //edge-of-scene probe or no probe influence, mix in with embiggened version of probes closest to camera 
-        for (int idx = 0; idx < 8; ++idx)
-        {
-            if (refIndex[idx].w < 0)
-            { // don't fallback to box probes, they are *very* specific
-                continue;
-            }
-            int i = idx;
-            vec3 delta = pos.xyz-refSphere[i].xyz;
-            float d2 = dot(delta,delta);
-            
-            {
-                vec3 refcol = tapRefMap(pos, dir, lod, refSphere[i].xyz, d2, i);
-                
-                float w = 1.0/d2;
-                w *= w;
-                col += refcol*w;
-                wsum += w;
-            }
-        }
-    }
-
-    if (wsum > 0.0)
-    {
-        col *= 1.0/wsum;
-    }
+    ambenv = vec3(0,0,0);
     
-    return col;
+    vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz));
+    vec3 env_vec = env_mat * refnormpersp;
+    glossenv = srgb_to_linear(textureCube(environmentMap, env_vec).rgb);
 }
 
-vec3 sampleProbeAmbient(vec3 pos, vec3 dir, float lod)
+void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv, 
+        vec3 pos, vec3 norm, float glossiness, float envIntensity)
 {
-    vec3 col = sampleProbes(pos, dir, lod);
-
-    //desaturate
-    vec3 hcol = col *0.5;
-    
-    col *= 2.0;
-    col = vec3(
-        col.r + hcol.g + hcol.b,
-        col.g + hcol.r + hcol.b,
-        col.b + hcol.r + hcol.g
-    );
+    ambenv = vec3(0,0,0);
     
-    col *= 0.333333;
+    vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz));
+    vec3 env_vec = env_mat * refnormpersp;
 
-    return col*reflectionAmbiance;
+    legacyenv = textureCube(environmentMap, env_vec).rgb;
 
+    glossenv = legacyenv;
 }
 
-// brighten a color so that at least one component is 1
-vec3 brighten(vec3 c)
-{
-    float m = max(max(c.r, c.g), c.b);
-
-    if (m == 0)
-    {
-        return vec3(1,1,1);
-    }
-
-    return c * 1.0/m;
-}
-
-
-void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv, 
-        vec3 pos, vec3 norm, float glossiness, float envIntensity)
+void applyGlossEnv(inout vec3 color, vec3 glossenv, vec4 spec, vec3 pos, vec3 norm)
 {
-    // TODO - don't hard code lods
-    float reflection_lods = 8;
-    preProbeSample(pos);
-
-    vec3 refnormpersp = reflect(pos.xyz, norm.xyz);
-
-    ambenv = sampleProbeAmbient(pos, norm, reflection_lods-1);
-
-    if (glossiness > 0.0)
-    {
-        float lod = (1.0-glossiness)*reflection_lods;
-        glossenv = sampleProbes(pos, normalize(refnormpersp), lod);
-    }
-
-    if (envIntensity > 0.0)
-    {
-        legacyenv = sampleProbes(pos, normalize(refnormpersp), 0.0);
-    }
+    
 }
 
-void applyGlossEnv(inout vec3 color, vec3 glossenv, vec4 spec, vec3 pos, vec3 norm)
+void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity)
 {
-    glossenv *= 0.35; // fudge darker
-    float fresnel = 1.0+dot(normalize(pos.xyz), norm.xyz);
-    float minf = spec.a * 0.1;
-    fresnel = fresnel * (1.0-minf) + minf;
-    glossenv *= spec.rgb*min(fresnel, 1.0);
-    color.rgb += glossenv;
+    color = mix(color.rgb, legacyenv, envIntensity);
 }
 
- void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity)
- {
-    vec3 reflected_color = legacyenv; //*0.5; //fudge darker
-    vec3 lookAt = normalize(pos);
-    float fresnel = 1.0+dot(lookAt, norm.xyz);
-    fresnel *= fresnel;
-    fresnel = min(fresnel+envIntensity, 1.0);
-    reflected_color *= (envIntensity*fresnel)*brighten(spec.rgb);
-    color = mix(color.rgb, reflected_color, envIntensity);
- }
-
diff --git a/indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl b/indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl
index 67f1fc4c18fd0a76dfddd25aae765d1cfcb4d723..c0a1491446041634be003b0b63016ba4461034b1 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl
@@ -55,13 +55,11 @@ void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, ou
 vec3 linear_to_srgb(vec3 c);
 vec3 srgb_to_linear(vec3 c);
 
-#ifdef HAS_REFLECTION_PROBES
 // reflection probe interface
-void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyEnv, 
+void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyEnv, 
         vec3 pos, vec3 norm, float glossiness, float envIntensity);
 void applyGlossEnv(inout vec3 color, vec3 glossenv, vec4 spec, vec3 pos, vec3 norm);
 void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity);
-#endif
 
 // See:
 //   class1\deferred\fullbrightShinyF.glsl
@@ -87,35 +85,21 @@ void main()
         calcAtmosphericVars(pos.xyz, vec3(0), 1.0, sunlit, amblit, additive, atten, false);
 
         float env_intensity = vertex_color.a;
-#ifndef HAS_REFLECTION_PROBES
-        vec3 envColor = textureCube(environmentMap, vary_texcoord1.xyz).rgb;	
-        color.rgb = mix(color.rgb, envColor.rgb, env_intensity);
-#else
+
         vec3 ambenv;
         vec3 glossenv;
         vec3 legacyenv;
         vec3 norm = normalize(vary_texcoord1.xyz);
         vec4 spec = vec4(0,0,0,0);
-        sampleReflectionProbes(ambenv, glossenv, legacyenv, pos.xyz, norm.xyz, spec.a, env_intensity);
-        legacyenv *= 1.5; // fudge brighter
+        sampleReflectionProbesLegacy(ambenv, glossenv, legacyenv, pos.xyz, norm.xyz, spec.a, env_intensity);
         applyLegacyEnv(color.rgb, legacyenv, spec, pos, norm, env_intensity);
-#endif
+
         color.rgb = fullbrightAtmosTransportFrag(color.rgb, additive, atten);
         color.rgb = fullbrightScaleSoftClip(color.rgb);
 	}
 
-/*
-	// NOTE: HUD objects will be full bright. Uncomment if you want "some" environment lighting effecting these HUD objects.
-	else
-	{
-		vec3  envColor = textureCube(environmentMap, vary_texcoord1.xyz).rgb;
-		float env_intensity = vertex_color.a;
-		color.rgb = mix(color.rgb, envColor.rgb, env_intensity);
-	}
-*/
-
 	color.a = 1.0;
-
+    
     color.rgb = srgb_to_linear(color.rgb);
 	frag_color = color;
 }
diff --git a/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl b/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl
index 07f50af7e3d21c628079918c0c2f5bccd5712b37..27eb0b988857121c09a62bb0cd6d10a4fa986c1b 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl
@@ -64,12 +64,10 @@ out vec4 frag_color;
 float sampleDirectionalShadow(vec3 pos, vec3 norm, vec2 pos_screen);
 #endif
 
-#ifdef HAS_REFLECTION_PROBES
-void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv, 
+void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv, 
         vec3 pos, vec3 norm, float glossiness, float envIntensity);
 void applyGlossEnv(inout vec3 color, vec3 glossenv, vec4 spec, vec3 pos, vec3 norm);
 void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity);
-#endif
 
 uniform samplerCube environmentMap;
 uniform sampler2D     lightFunc;
@@ -326,27 +324,12 @@ void main()
     da = pow(da, 1.0 / 1.3);
     vec3 sun_contrib = min(da, shadow) * sunlit;
 
-#ifdef HAS_REFLECTION_PROBES
     vec3 ambenv;
     vec3 glossenv;
     vec3 legacyenv;
-    sampleReflectionProbes(ambenv, glossenv, legacyenv, pos.xyz, norm.xyz, spec.a, envIntensity);
+    sampleReflectionProbesLegacy(ambenv, glossenv, legacyenv, pos.xyz, norm.xyz, spec.a, envIntensity);
     amblit = max(ambenv, amblit);
     color.rgb = amblit;
-#else
-
-    color = amblit;
-
-    //darken ambient for normals perpendicular to light vector so surfaces in shadow 
-    // and facing away from light still have some definition to them.
-    // do NOT gamma correct this dot product so ambient lighting stays soft
-    float ambient = min(abs(dot(norm.xyz, sun_dir.xyz)), 1.0);
-    ambient *= 0.5;
-    ambient *= ambient;
-    ambient = (1.0 - ambient);
-
-    color *= ambient;
-#endif
 
     color += sun_contrib;
 
@@ -368,37 +351,14 @@ void main()
 
         color += spec_contrib;
 
-#ifdef HAS_REFLECTION_PROBES
         applyGlossEnv(color, glossenv, spec, pos.xyz, norm.xyz);
-#endif
     }
 
-#ifdef HAS_REFLECTION_PROBES
+    color = atmosFragLighting(color, additive, atten);
     if (envIntensity > 0.0)
     {  // add environmentmap
-        //fudge darker
-        legacyenv *= 0.5*diffuse.a+0.5;
-        
         applyLegacyEnv(color, legacyenv, spec, pos.xyz, norm.xyz, envIntensity);
     }
-#else
-    if (envIntensity > 0.0)
-    {
-        //add environmentmap
-        vec3 env_vec = env_mat * refnormpersp;
-
-        vec3 reflected_color = textureCube(environmentMap, env_vec).rgb;
-
-        color = mix(color, reflected_color, envIntensity);
-
-        float cur_glare = max(reflected_color.r, reflected_color.g);
-        cur_glare = max(cur_glare, reflected_color.b);
-        cur_glare *= envIntensity*4.0;
-        glare += cur_glare;
-    }
-#endif
-
-    color = atmosFragLighting(color, additive, atten);
     color = scaleSoftClipFrag(color);
 
     //convert to linear before adding local lights
@@ -410,6 +370,8 @@ void main()
     
     final_specular.rgb = srgb_to_linear(final_specular.rgb); // SL-14035
 
+    color = mix(color.rgb, srgb_to_linear(diffcol.rgb), diffuse.a);
+
 #define LIGHT_LOOP(i) light.rgb += calcPointLightOrSpotLight(light_diffuse[i].rgb, npos, diffuse.rgb, final_specular, pos.xyz, norm.xyz, light_position[i], light_direction[i].xyz, light_attenuation[i].x, light_attenuation[i].y, light_attenuation[i].z, glare, light_attenuation[i].w );
 
     LIGHT_LOOP(1)
@@ -431,7 +393,6 @@ void main()
     al = temp.a;
 #endif
 
-    color = mix(color.rgb, srgb_to_linear(diffcol.rgb), diffuse.a);
     frag_color = vec4(color, al);
 
 #else // mode is not DIFFUSE_ALPHA_MODE_BLEND, encode to gbuffer 
diff --git a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl
index 463709de9e264c81f1c8f9fdad9d106e89ec2067..beb0f6f2a677216f8967e354ce2ca72f12d7939e 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl
@@ -23,8 +23,6 @@
  * $/LicenseInfo$
  */
 
-#extension GL_ARB_shader_texture_lod : enable
-
 #define FLT_MAX 3.402823466e+38
 
 #define REFMAP_COUNT 256
@@ -33,6 +31,8 @@
 uniform samplerCubeArray   reflectionProbes;
 uniform samplerCubeArray   irradianceProbes;
 
+vec3 linear_to_srgb(vec3 col);
+
 layout (std140) uniform ReflectionProbes
 {
     // list of OBBs for user override probes
@@ -520,7 +520,22 @@ vec3 brighten(vec3 c)
 }
 
 
-void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv, 
+void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv,
+        vec3 pos, vec3 norm, float glossiness)
+{
+    // TODO - don't hard code lods
+    float reflection_lods = 7;
+    preProbeSample(pos);
+
+    vec3 refnormpersp = reflect(pos.xyz, norm.xyz);
+
+    ambenv = sampleProbeAmbient(pos, norm);
+
+    float lod = (1.0-glossiness)*reflection_lods;
+    glossenv = sampleProbes(pos, normalize(refnormpersp), lod, 1.f);
+}
+
+void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv,
         vec3 pos, vec3 norm, float glossiness, float envIntensity)
 {
     // TODO - don't hard code lods
@@ -531,16 +546,22 @@ void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, inout vec3 l
 
     ambenv = sampleProbeAmbient(pos, norm);
 
-    //if (glossiness > 0.0)
+    if (glossiness > 0.0)
     {
         float lod = (1.0-glossiness)*reflection_lods;
         glossenv = sampleProbes(pos, normalize(refnormpersp), lod, 1.f);
+        glossenv = linear_to_srgb(glossenv);
     }
 
     if (envIntensity > 0.0)
     {
         legacyenv = sampleProbes(pos, normalize(refnormpersp), 0.0, 1.f);
+        legacyenv = linear_to_srgb(legacyenv);
     }
+
+    // legacy expects values in sRGB space for now
+    ambenv = linear_to_srgb(ambenv);
+
 }
 
 void applyGlossEnv(inout vec3 color, vec3 glossenv, vec4 spec, vec3 pos, vec3 norm)
@@ -555,7 +576,7 @@ void applyGlossEnv(inout vec3 color, vec3 glossenv, vec4 spec, vec3 pos, vec3 no
 
  void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity)
  {
-    vec3 reflected_color = legacyenv; //*0.5; //fudge darker
+    vec3 reflected_color = legacyenv;
     vec3 lookAt = normalize(pos);
     float fresnel = 1.0+dot(lookAt, norm.xyz);
     fresnel *= fresnel;
diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl
index 5c049b6bd636c0a4be6def23b4d38beebc7a8d6e..cfdbbdfe2cae031946257c1d52fefa17e429980a 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl
@@ -76,7 +76,9 @@ vec3  fullbrightAtmosTransportFrag(vec3 light, vec3 additive, vec3 atten);
 vec3  fullbrightScaleSoftClip(vec3 light);
 
 // reflection probe interface
-void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyEnv, 
+void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv,
+        vec3 pos, vec3 norm, float glossiness);
+void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyEnv, 
         vec3 pos, vec3 norm, float glossiness, float envIntensity);
 void applyGlossEnv(inout vec3 color, vec3 glossenv, vec4 spec, vec3 pos, vec3 norm);
 void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity);
@@ -156,7 +158,7 @@ void main()
         float gloss      = 1.0 - perceptualRoughness;
         vec3  irradiance = vec3(0);
         vec3  radiance  = vec3(0);
-        sampleReflectionProbes(irradiance, radiance, legacyenv, pos.xyz, norm.xyz, gloss, 0.0);
+        sampleReflectionProbes(irradiance, radiance, pos.xyz, norm.xyz, gloss);
         irradiance       = max(srgb_to_linear(amblit),irradiance) * ambocc*4.0;
 
         vec3 f0 = vec3(0.04);
@@ -196,10 +198,8 @@ void main()
 
         //diffuse.rgb = linear_to_srgb(diffuse.rgb); // SL-14035
 
-        sampleReflectionProbes(ambenv, glossenv, legacyenv, pos.xyz, norm.xyz, spec.a, envIntensity);
-        ambenv.rgb = linear_to_srgb(ambenv.rgb); 
-        glossenv.rgb = linear_to_srgb(glossenv.rgb);
-        legacyenv.rgb = linear_to_srgb(legacyenv.rgb);
+        sampleReflectionProbesLegacy(ambenv, glossenv, legacyenv, pos.xyz, norm.xyz, spec.a, envIntensity);
+        vec3 debug = legacyenv;
 
         amblit = max(ambenv, amblit);
         color.rgb = amblit*ambocc;
@@ -227,16 +227,13 @@ void main()
 
         color.rgb = mix(color.rgb, diffuse.rgb, diffuse.a);
 
-        if (envIntensity > 0.0)
-        {  // add environmentmap
-            //fudge darker
-            legacyenv *= 0.5*diffuse.a+0.5;
-            applyLegacyEnv(color, legacyenv, spec, pos.xyz, norm.xyz, envIntensity);
-        }
-
         if (GET_GBUFFER_FLAG(GBUFFER_FLAG_HAS_ATMOS))
         {
             color = mix(atmosFragLighting(color, additive, atten), fullbrightAtmosTransportFrag(color, additive, atten), diffuse.a);
+            if (envIntensity > 0.0)
+            {  // add environmentmap
+                applyLegacyEnv(color, legacyenv, spec, pos.xyz, norm.xyz, envIntensity);
+            }
             color = mix(scaleSoftClipFrag(color), fullbrightScaleSoftClip(color), diffuse.a);
         }
 
@@ -248,8 +245,6 @@ void main()
 
         // convert to linear as fullscreen lights need to sum in linear colorspace
         // and will be gamma (re)corrected downstream...
-        //color = ambenv;
-        //color.b = diffuse.a;
         frag_color.rgb = srgb_to_linear(color.rgb);
     }
 
diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt
index cfee0ba5297b794241dc4beb7c0542604f79a476..cfcb623caf8c310b81d78466a875f63005d93e5b 100644
--- a/indra/newview/featuretable.txt
+++ b/indra/newview/featuretable.txt
@@ -1,4 +1,4 @@
-version 37
+version 39
 // The version number above should be incremented IF AND ONLY IF some
 // change has been made that is sufficiently important to justify
 // resetting the graphics preferences of all users to the recommended
@@ -47,6 +47,7 @@ RenderMaxPartCount			1	8192
 RenderObjectBump			1	1
 RenderLocalLights			1	1
 RenderReflectionDetail		1	4
+RenderReflectionProbeDetail		1	2
 RenderTerrainDetail			1	1
 RenderTerrainLODFactor		1	2.0
 RenderTransparentWater		1	1
@@ -90,12 +91,12 @@ RenderGlowResolutionPow		1	8
 RenderLocalLights			1	0
 RenderMaxPartCount			1	0
 RenderReflectionDetail		1	0
+RenderReflectionProbeDetail	1	-1
 RenderTerrainDetail			1	0
 RenderTerrainLODFactor		1	1
 RenderTransparentWater		1	0
 RenderTreeLODFactor			1	0
 RenderVolumeLODFactor		1	1.125
-RenderPBR                   1   0
 RenderDeferredSSAO			1	0
 RenderUseAdvancedAtmospherics 1 0
 RenderShadowDetail			1	0
@@ -116,12 +117,12 @@ RenderGlowResolutionPow		1	8
 RenderMaxPartCount			1	2048
 RenderLocalLights			1	1
 RenderReflectionDetail		1	0
+RenderReflectionProbeDetail	1	-1
 RenderTerrainDetail			1	1
 RenderTerrainLODFactor		1	1.0
 RenderTransparentWater		1	1
 RenderTreeLODFactor			1	0.5
 RenderVolumeLODFactor		1	1.125
-RenderPBR                   1   0
 RenderDeferredSSAO			1	0
 RenderUseAdvancedAtmospherics 1 0
 RenderShadowDetail			1	0
@@ -152,6 +153,7 @@ RenderUseAdvancedAtmospherics 1 0
 RenderShadowDetail			1	0
 WLSkyDetail					1	48
 RenderFSAASamples			1	2
+RenderReflectionProbeDetail	1	0
 
 //
 // Medium High Graphics Settings (deferred enabled)
@@ -177,6 +179,7 @@ RenderUseAdvancedAtmospherics 1 0
 RenderShadowDetail			1	0
 WLSkyDetail					1	48
 RenderFSAASamples			1	2
+RenderReflectionProbeDetail	1	1
 
 //
 // High Graphics Settings (deferred + SSAO)
@@ -202,6 +205,7 @@ RenderUseAdvancedAtmospherics 1 0
 RenderShadowDetail			1	0
 WLSkyDetail					1	48
 RenderFSAASamples			1	2
+RenderReflectionProbeDetail	1	1
 
 //
 // High Ultra Graphics Settings (deferred + SSAO + shadows)
@@ -227,6 +231,7 @@ RenderUseAdvancedAtmospherics 1 0
 RenderShadowDetail			1	2
 WLSkyDetail					1	48
 RenderFSAASamples			1	2
+RenderReflectionProbeDetail	1	1
 
 //
 // Ultra graphics (REALLY PURTY!)
@@ -252,6 +257,7 @@ RenderDeferredSSAO			1	1
 RenderUseAdvancedAtmospherics 1 0
 RenderShadowDetail			1	2
 RenderFSAASamples			1	2
+RenderReflectionProbeDetail	1	1
 
 //
 // Class Unknown Hardware (unknown)
@@ -259,7 +265,6 @@ RenderFSAASamples			1	2
 list Unknown
 RenderShadowDetail			1	0
 RenderDeferredSSAO			1	0
-RenderPBR                   1   0
 RenderUseAdvancedAtmospherics 1 0
 
 //
@@ -281,7 +286,7 @@ RenderTerrainDetail 		1	0
 RenderReflectionDetail		0	0
 RenderDeferredSSAO			0	0
 RenderShadowDetail			0	0
-RenderPBR                   1   0
+RenderReflectionProbeDetail	0	-1
 
 list Intel
 RenderAnisotropic			1	0
@@ -293,5 +298,6 @@ RenderGLContextCoreProfile  1   0
 list AMD
 RenderReflectionProbeCount  1   16
 
-
-
+list GL3
+RenderFSAASamples           0   0
+RenderReflectionProbeDetail	0	-1
diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt
index c224fd2a6c82c9cbba069a76ec626760419f8bac..79ce057c300d98face9e1945ab4c7f2c0e2b6a3a 100644
--- a/indra/newview/featuretable_mac.txt
+++ b/indra/newview/featuretable_mac.txt
@@ -72,6 +72,7 @@ RenderFSAASamples			1	16
 RenderMaxTextureIndex		1	16
 RenderGLContextCoreProfile         1   1
 RenderGLMultiThreaded       1   0
+RenderReflectionProbeDetail	1	2
 
 //
 // Low Graphics Settings
@@ -93,12 +94,12 @@ RenderTerrainLODFactor		1	1
 RenderTransparentWater		1	0
 RenderTreeLODFactor			1	0
 RenderVolumeLODFactor		1	0.5
-RenderPBR                   1   0
 RenderDeferredSSAO			1	0
 RenderUseAdvancedAtmospherics 1 0
 RenderShadowDetail			1	0
 WLSkyDetail					1	48
 RenderFSAASamples			1	0
+RenderReflectionProbeDetail	1	-1
 
 //
 // Medium Low Graphics Settings
@@ -119,12 +120,12 @@ RenderTerrainLODFactor		1	1.0
 RenderTransparentWater		1	1
 RenderTreeLODFactor			1	0.5
 RenderVolumeLODFactor		1	1.125
-RenderPBR                   1   0
 RenderDeferredSSAO			1	0
 RenderUseAdvancedAtmospherics 1 0
 RenderShadowDetail			1	0
 WLSkyDetail					1	48
 RenderFSAASamples			1	0
+RenderReflectionProbeDetail	1	-1
 
 //
 // Medium Graphics Settings (standard)
@@ -150,6 +151,7 @@ RenderUseAdvancedAtmospherics 1 0
 RenderShadowDetail			1	0
 WLSkyDetail					1	48
 RenderFSAASamples			1	2
+RenderReflectionProbeDetail	1	0
 
 //
 // Medium High Graphics Settings (deferred enabled)
@@ -175,6 +177,7 @@ RenderUseAdvancedAtmospherics 1 0
 RenderShadowDetail			1	0
 WLSkyDetail					1	48
 RenderFSAASamples			1	2
+RenderReflectionProbeDetail	1	0
 
 //
 // High Graphics Settings (deferred + SSAO)
@@ -200,6 +203,7 @@ RenderUseAdvancedAtmospherics 1 0
 RenderShadowDetail			1	0
 WLSkyDetail					1	48
 RenderFSAASamples			1	2
+RenderReflectionProbeDetail	1	1
 
 //
 // High Ultra Graphics Settings (deferred + SSAO + shadows)
@@ -225,6 +229,7 @@ RenderShadowDetail			1	2
 RenderUseAdvancedAtmospherics 1 0
 WLSkyDetail					1	48
 RenderFSAASamples			1	2
+RenderReflectionProbeDetail	1	1
 
 //
 // Ultra graphics (REALLY PURTY!)
@@ -250,13 +255,13 @@ RenderDeferredSSAO			1	1
 RenderUseAdvancedAtmospherics 1 0
 RenderShadowDetail			1	2
 RenderFSAASamples			1	2
+RenderReflectionProbeDetail	1	1
 
 //
 // Class Unknown Hardware (unknown)
 //
 list Unknown
 RenderShadowDetail			1	0
-RenderPBR                   1   0
 RenderDeferredSSAO			1	0
 RenderUseAdvancedAtmospherics 1 0
 
@@ -278,7 +283,6 @@ RenderLocalLights			1	0
 RenderMaxPartCount			1	1024
 RenderTerrainDetail 		1	0
 RenderReflectionDetail		0	0
-RenderPBR                   1   0
 RenderDeferredSSAO			0	0
 RenderUseAdvancedAtmospherics 0 0
 RenderShadowDetail			0	0
@@ -294,6 +298,6 @@ RenderAnisotropic			1	0
 RenderLocalLights			1	0
 RenderFSAASamples			1	0
 
-list OSX_10_6_8
-RenderDeferred 0 0
-
+list GL3
+RenderFSAASamples           0   0
+RenderReflectionProbeDetail	0	-1
diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp
index 33ac91f88bd3405eadfa1ef916a03693aaba4126..235672b07ae5cd513c55c9b7530311250960cdc8 100644
--- a/indra/newview/lldrawpoolbump.cpp
+++ b/indra/newview/lldrawpoolbump.cpp
@@ -465,10 +465,14 @@ void LLDrawPoolBump::beginFullbrightShiny()
 		shader->uniform4fv(LLViewerShaderMgr::SHINY_ORIGIN, 1, vec4.mV);
 
         cube_map->setMatrix(1);
-        if (shader->mFeatures.hasReflectionProbes)
+        if (LLPipeline::sReflectionProbesEnabled)
         {
             gPipeline.bindReflectionProbes(*shader);
         }
+        else
+        {
+            gPipeline.setEnvMat(*shader);
+        }
 
 		// Make sure that texture coord generation happens for tex unit 1, as that's the one we use for 
 		// the cube map in the one pass shiny shaders
diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp
index 2c770f6ab82813cd9036069850ba1892316b37d9..5817fdbfa0240e7f4c1ca832c9550661983a24c1 100644
--- a/indra/newview/llfeaturemanager.cpp
+++ b/indra/newview/llfeaturemanager.cpp
@@ -651,14 +651,10 @@ void LLFeatureManager::applyBaseMasks()
 	{
 		maskFeatures("VRAMGT512");
 	}
-
-#if LL_DARWIN
-	const LLOSInfo& osInfo = LLOSInfo::instance();
-	if (osInfo.mMajorVer == 10 && osInfo.mMinorVer < 7)
-	{
-		maskFeatures("OSX_10_6_8");
-	}
-#endif
+    if (gGLManager.mGLVersion < 3.99f)
+    {
+        maskFeatures("GL3");
+    }
 
 	// now mask by gpu string
 	// Replaces ' ' with '_' in mGPUString to deal with inability for parser to handle spaces
diff --git a/indra/newview/llreflectionmapmanager.cpp b/indra/newview/llreflectionmapmanager.cpp
index 2aa1f06eaf809c4560b448999bd970e6d4c473c2..6108fe84d34db668fcda1aeae1ea00efb1ef3711 100644
--- a/indra/newview/llreflectionmapmanager.cpp
+++ b/indra/newview/llreflectionmapmanager.cpp
@@ -63,7 +63,7 @@ struct CompareProbeDistance
 // helper class to seed octree with probes
 void LLReflectionMapManager::update()
 {
-    if (!LLPipeline::sRenderPBR || gTeleportDisplay)
+    if (!LLPipeline::sReflectionProbesEnabled || gTeleportDisplay)
     {
         return;
     }
@@ -130,8 +130,9 @@ void LLReflectionMapManager::update()
     }
 
     bool did_update = false;
-
-    bool realtime = gSavedSettings.getS32("RenderReflectionProbeDetail") >= (S32)LLReflectionMapManager::DetailLevel::REALTIME;
+    
+    static LLCachedControl<S32> sDetail(gSavedSettings, "RenderReflectionProbeDetail", -1);
+    bool realtime = sDetail >= (S32)LLReflectionMapManager::DetailLevel::REALTIME;
     
     LLReflectionMap* closestDynamic = nullptr;
 
@@ -613,6 +614,11 @@ void LLReflectionMapManager::updateNeighbors(LLReflectionMap* probe)
 
 void LLReflectionMapManager::updateUniforms()
 {
+    if (!LLPipeline::sReflectionProbesEnabled)
+    {
+        return;
+    }
+
     LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
 
     // structure for packing uniform buffer object
@@ -740,7 +746,11 @@ void LLReflectionMapManager::updateUniforms()
 
 void LLReflectionMapManager::setUniforms()
 {
-    llassert(LLPipeline::sRenderPBR);
+    if (!LLPipeline::sReflectionProbesEnabled)
+    {
+        return;
+    }
+
     if (mUBO == 0)
     { 
         updateUniforms();
diff --git a/indra/newview/llsettingsvo.cpp b/indra/newview/llsettingsvo.cpp
index 634fc394602c5a94b1ed9c08ae8c40853938064e..93e0f33177db48f90bcf18d688ddd0579abe394b 100644
--- a/indra/newview/llsettingsvo.cpp
+++ b/indra/newview/llsettingsvo.cpp
@@ -708,6 +708,9 @@ void LLSettingsVOSky::applySpecial(void *ptarget, bool force)
     shader->uniform1f(LLShaderMgr::SCENE_LIGHT_STRENGTH, mSceneLightStrength);
 
     LLColor4 ambient(getTotalAmbient());
+
+    gPipeline.adjustAmbient(this, ambient);
+    
     shader->uniform4fv(LLShaderMgr::AMBIENT, LLVector4(ambient.mV));
 
     shader->uniform1i(LLShaderMgr::SUN_UP_FACTOR, getIsSunUp() ? 1 : 0);
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 2413d7705ae7fa9fa9933e26e3ae16a3e2e73c24..8638dba1b02077f975cf65429612e8eee156da18 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -434,7 +434,7 @@ static bool handleRenderLocalLightsChanged(const LLSD& newvalue)
 	return true;
 }
 
-static bool handleRenderPBRChanged(const LLSD& newvalue)
+static bool handleReflectionProbeDetailChanged(const LLSD& newvalue)
 {
     if (gPipeline.isInit())
     {
@@ -696,7 +696,7 @@ void settings_setup_listeners()
 	gSavedSettings.getControl("RenderDebugPipeline")->getSignal()->connect(boost::bind(&handleRenderDebugPipelineChanged, _2));
 	gSavedSettings.getControl("RenderResolutionDivisor")->getSignal()->connect(boost::bind(&handleRenderResolutionDivisorChanged, _2));
 	// DEPRECATED - gSavedSettings.getControl("RenderDeferred")->getSignal()->connect(boost::bind(&handleRenderDeferredChanged, _2));
-    gSavedSettings.getControl("RenderPBR")->getSignal()->connect(boost::bind(&handleRenderPBRChanged, _2));
+    gSavedSettings.getControl("RenderReflectionProbeDetail")->getSignal()->connect(boost::bind(&handleReflectionProbeDetailChanged, _2));
 	gSavedSettings.getControl("RenderShadowDetail")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
 	gSavedSettings.getControl("RenderDeferredSSAO")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
 	gSavedSettings.getControl("RenderPerformanceTest")->getSignal()->connect(boost::bind(&handleRenderPerfTestChanged, _2));
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index 63d9a03abac4ee80a3e34d0899f394246903abc9..d1e5c8bd28ba159cfad6c9483ccc679138ac46fc 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -902,6 +902,8 @@ std::string LLViewerShaderMgr::loadBasicShaders()
 		ch = llmax(LLGLSLShader::sIndexedTextureChannels-1, 1);
 	}
 
+    bool has_reflection_probes = gSavedSettings.getS32("RenderReflectionProbeDetail") >= 0 && gGLManager.mGLVersion > 3.99f;
+
 	std::vector<S32> index_channels;    
 	index_channels.push_back(-1);    shaders.push_back( make_pair( "windlight/atmosphericsVarsF.glsl",      mShaderLevel[SHADER_WINDLIGHT] ) );
 	index_channels.push_back(-1);    shaders.push_back( make_pair( "windlight/atmosphericsVarsWaterF.glsl",     mShaderLevel[SHADER_WINDLIGHT] ) );
@@ -916,7 +918,7 @@ std::string LLViewerShaderMgr::loadBasicShaders()
 	index_channels.push_back(-1);    shaders.push_back( make_pair( "deferred/deferredUtil.glsl",                    1) );
 	index_channels.push_back(-1);    shaders.push_back( make_pair( "deferred/shadowUtil.glsl",                      1) );
 	index_channels.push_back(-1);    shaders.push_back( make_pair( "deferred/aoUtil.glsl",                          1) );
-    index_channels.push_back(-1);    shaders.push_back( make_pair( "deferred/reflectionProbeF.glsl",                llmax(mShaderLevel[SHADER_DEFERRED], 1)) );
+    index_channels.push_back(-1);    shaders.push_back( make_pair( "deferred/reflectionProbeF.glsl",                has_reflection_probes ? 3 : 2) );
 	index_channels.push_back(-1);    shaders.push_back( make_pair( "lighting/lightNonIndexedF.glsl",                    mShaderLevel[SHADER_LIGHTING] ) );
 	index_channels.push_back(-1);    shaders.push_back( make_pair( "lighting/lightAlphaMaskNonIndexedF.glsl",                   mShaderLevel[SHADER_LIGHTING] ) );
 	index_channels.push_back(-1);    shaders.push_back( make_pair( "lighting/lightFullbrightNonIndexedF.glsl",          mShaderLevel[SHADER_LIGHTING] ) );
@@ -1448,12 +1450,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
             gDeferredMaterialProgram[i].mFeatures.hasAtmospherics = true;
             gDeferredMaterialProgram[i].mFeatures.hasGamma = true;
             gDeferredMaterialProgram[i].mFeatures.hasShadows = use_sun_shadow;
-            
-            if (mShaderLevel[SHADER_DEFERRED] > 2)
-            {
-                gDeferredMaterialProgram[i].mFeatures.hasReflectionProbes = true;
-                gDeferredMaterialProgram[i].addPermutation("HAS_REFLECTION_PROBES", "1");
-            }
+            gDeferredMaterialProgram[i].mFeatures.hasReflectionProbes = true;
 
             if (has_skin)
             {
@@ -1530,6 +1527,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
                 gDeferredMaterialWaterProgram[i].addPermutation("LOCAL_LIGHT_KILL", "1");
             }
 
+            gDeferredMaterialWaterProgram[i].mFeatures.hasReflectionProbes = true;
             gDeferredMaterialWaterProgram[i].mFeatures.hasWaterFog = true;
             gDeferredMaterialWaterProgram[i].mFeatures.hasSrgb = true;
             gDeferredMaterialWaterProgram[i].mFeatures.encodesNormal = true;
@@ -2229,11 +2227,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		gDeferredFullbrightShinyProgram.mShaderFiles.push_back(make_pair("deferred/fullbrightShinyV.glsl", GL_VERTEX_SHADER));
 		gDeferredFullbrightShinyProgram.mShaderFiles.push_back(make_pair("deferred/fullbrightShinyF.glsl", GL_FRAGMENT_SHADER));
 		gDeferredFullbrightShinyProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
-        if (gDeferredFullbrightShinyProgram.mShaderLevel > 2)
-        {
-            gDeferredFullbrightShinyProgram.addPermutation("HAS_REFLECTION_PROBES", "1");
-            gDeferredFullbrightShinyProgram.mFeatures.hasReflectionProbes = true;
-        }
+        gDeferredFullbrightShinyProgram.mFeatures.hasReflectionProbes = true;
         success = make_rigged_variant(gDeferredFullbrightShinyProgram, gDeferredSkinnedFullbrightShinyProgram);
 		success = success && gDeferredFullbrightShinyProgram.createShader(NULL, NULL);
 		llassert(success);
@@ -2701,7 +2695,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		llassert(success);
 	}
 
-	if (success)
+	if (success && gGLManager.mGLVersion > 3.9f)
 	{
 		gFXAAProgram.mName = "FXAA Shader";
 		gFXAAProgram.mFeatures.isDeferred = true;
@@ -2858,7 +2852,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
 		success = gNormalMapGenProgram.createShader(NULL, NULL);
 	}
 
-    if (success && gGLManager.mHasCubeMapArray)
+    if (success)
     {
         gDeferredGenBrdfLutProgram.mName = "Brdf Gen Shader";
         gDeferredGenBrdfLutProgram.mShaderFiles.clear();
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 177d712f4b8384773f5dc8c1ed05fbb0144b1cfc..b14ceedac8348f9b499c502e3c24767ef106ec4d 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -355,6 +355,7 @@ bool	LLPipeline::sRenderFrameTest = false;
 bool	LLPipeline::sRenderAttachedLights = true;
 bool	LLPipeline::sRenderAttachedParticles = true;
 bool	LLPipeline::sRenderDeferred = false;
+bool	LLPipeline::sReflectionProbesEnabled = false;
 bool    LLPipeline::sRenderPBR = false;
 S32		LLPipeline::sVisibleLightCount = 0;
 bool	LLPipeline::sRenderingHUDs;
@@ -371,11 +372,10 @@ void validate_framebuffer_object();
 // target -- RenderTarget to add attachments to
 bool addDeferredAttachments(LLRenderTarget& target, bool for_impostor = false)
 {
-    bool pbr = gSavedSettings.getBOOL("RenderPBR");
     bool valid = true
         && target.addColorAttachment(GL_RGBA) // frag-data[1] specular OR PBR ORM
         && target.addColorAttachment(GL_RGB10_A2)                              // frag_data[2] normal+z+fogmask, See: class1\deferred\materialF.glsl & softenlight
-        && (pbr ? target.addColorAttachment(GL_RGBA) : true);                  // frag_data[3] PBR emissive
+        && target.addColorAttachment(GL_RGBA);                  // frag_data[3] PBR emissive
     return valid;
 }
 
@@ -553,7 +553,6 @@ void LLPipeline::init()
 	connectRefreshCachedSettingsSafe("UseOcclusion");
 	// DEPRECATED -- connectRefreshCachedSettingsSafe("WindLightUseAtmosShaders");
 	// DEPRECATED -- connectRefreshCachedSettingsSafe("RenderDeferred");
-    connectRefreshCachedSettingsSafe("RenderPBR");
 	connectRefreshCachedSettingsSafe("RenderDeferredSunWash");
 	connectRefreshCachedSettingsSafe("RenderFSAASamples");
 	connectRefreshCachedSettingsSafe("RenderResolutionDivisor");
@@ -1033,12 +1032,10 @@ void LLPipeline::updateRenderBump()
 // static
 void LLPipeline::updateRenderDeferred()
 {
-    sRenderDeferred = !gUseWireframe &&
-                      RenderDeferred &&
-                      LLRenderTarget::sUseFBO &&
-                      LLPipeline::sRenderBump &&
-                      WindLightUseAtmosShaders;
-    sRenderPBR = sRenderDeferred && gSavedSettings.getBOOL("RenderPBR");
+    sRenderDeferred = !gUseWireframe;
+    sRenderPBR = sRenderDeferred;
+    static LLCachedControl<S32> sProbeDetail(gSavedSettings, "RenderReflectionProbeDetail", -1);
+    sReflectionProbesEnabled = sProbeDetail >= 0 && gGLManager.mGLVersion > 3.99f;
 }
 
 // static
@@ -6238,6 +6235,22 @@ void LLPipeline::calcNearbyLights(LLCamera& camera)
 	}
 }
 
+void LLPipeline::adjustAmbient(const LLSettingsSky* sky, LLColor4& ambient)
+{
+    //bump ambient based on reflection probe ambiance of probes are disabled 
+    // so sky settings that rely on probes for ambiance don't go completely dark
+    // on low end hardware
+    if (!LLPipeline::sReflectionProbesEnabled)
+    {
+        F32 ambiance = linearTosRGB(sky->getReflectionProbeAmbiance());
+
+        for (int i = 0; i < 3; ++i)
+        {
+            ambient.mV[i] = llmax(ambient.mV[i], ambiance);
+        }
+    }
+}
+
 void LLPipeline::setupHWLights(LLDrawPool* pool)
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL;
@@ -6248,7 +6261,9 @@ void LLPipeline::setupHWLights(LLDrawPool* pool)
 
     // Ambient
     LLColor4 ambient = psky->getTotalAmbient();
-		gGL.setAmbientLightColor(ambient);
+    adjustAmbient(psky.get(), ambient);
+    
+	gGL.setAmbientLightColor(ambient);
 
     bool sun_up  = environment.getIsSunUp();
     bool moon_up = environment.getIsMoonUp();
@@ -9281,8 +9296,24 @@ void LLPipeline::unbindDeferredShader(LLGLSLShader &shader)
 	shader.unbind();
 }
 
+void LLPipeline::setEnvMat(LLGLSLShader& shader)
+{
+    F32* m = gGLModelView;
+
+    F32 mat[] = { m[0], m[1], m[2],
+                    m[4], m[5], m[6],
+                    m[8], m[9], m[10] };
+
+    shader.uniformMatrix3fv(LLShaderMgr::DEFERRED_ENV_MAT, 1, TRUE, mat);
+}
+
 void LLPipeline::bindReflectionProbes(LLGLSLShader& shader)
 {
+    if (!sReflectionProbesEnabled)
+    {
+        return;
+    }
+
     S32 channel = shader.enableTexture(LLShaderMgr::REFLECTION_PROBES, LLTexUnit::TT_CUBE_MAP_ARRAY);
     bool bound = false;
     if (channel > -1 && mReflectionMapManager.mTexture.notNull())
@@ -9302,13 +9333,7 @@ void LLPipeline::bindReflectionProbes(LLGLSLShader& shader)
     {
         mReflectionMapManager.setUniforms();
 
-        F32* m = gGLModelView;
-
-        F32 mat[] = { m[0], m[1], m[2],
-                      m[4], m[5], m[6],
-                      m[8], m[9], m[10] };
-
-        shader.uniformMatrix3fv(LLShaderMgr::DEFERRED_ENV_MAT, 1, TRUE, mat);
+        setEnvMat(shader);
     }
 }
 
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index 5665469c1a70b0086904570fbd2bb3741f2a9e91..58857e417604f7b0dc485bc4ef6efa315af49775 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -51,6 +51,7 @@ class LLVOAvatar;
 class LLVOPartGroup;
 class LLGLSLShader;
 class LLDrawPoolAlpha;
+class LLSettingsSky;
 
 typedef enum e_avatar_skinning_method
 {
@@ -297,9 +298,14 @@ class LLPipeline
 
 	void unbindDeferredShader(LLGLSLShader& shader);
 
+    // set env_mat parameter in given shader
+    void setEnvMat(LLGLSLShader& shader);
+
     void bindReflectionProbes(LLGLSLShader& shader);
     void unbindReflectionProbes(LLGLSLShader& shader);
 
+    
+
 	void renderDeferredLighting(LLRenderTarget* light_target);
 	void postDeferredGammaCorrect(LLRenderTarget* screen_target);
 
@@ -326,6 +332,7 @@ class LLPipeline
 	S32  getLightCount() const { return mLights.size(); }
 
 	void calcNearbyLights(LLCamera& camera);
+    void adjustAmbient(const LLSettingsSky* sky, LLColor4& ambient);
 	void setupHWLights(LLDrawPool* pool);
 	void setupAvatarLights(bool for_edit = false);
 	void enableLights(U32 mask);
@@ -640,6 +647,7 @@ class LLPipeline
 	static bool				sRenderAttachedLights;
 	static bool				sRenderAttachedParticles;
 	static bool				sRenderDeferred;
+    static bool				sReflectionProbesEnabled;
     static bool				sRenderPBR;
 	static S32				sVisibleLightCount;
 	static bool				sRenderingHUDs;
diff --git a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml
index 97a87d56f4b31c4c9e2c88b185fd19482b8ac6b5..6ff27ad50b5a1794ced49c45f26b75ef4c4717e5 100644
--- a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml
+++ b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml
@@ -719,6 +719,10 @@
    top_delta="0"
    name="ReflectionDetial"
    width="150">
+    <combo_box.item
+      label="Disabled"
+      name="0"
+      value="-1"/>
     <combo_box.item
       label="Static Only"
       name="0"
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml
index 908ddd32b55bd890678eab6475ca72d4fd43046b..38d364cf9a612646772acf64042a96e34e45ba7b 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml
@@ -231,20 +231,6 @@
      m
   </text>
 
-  <check_box
-    control_name="RenderPBR"
-    height="16"
-    initial_value="true"
-    label="Physically Based Rendering"
-    layout="topleft"
-    left="30"
-    name="UsePBRShaders"
-    top_delta="24"
-    width="256">
-    <check_box.commit_callback
-      function="Pref.RenderOptionUpdate" />
-  </check_box>
-
   <slider
     control_name="IndirectMaxComplexity"
     tool_tip="Controls at what point a visually complex avatar is drawn as a JellyDoll"