diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl
index c83a6be85dee3f3980b28638de57b897a3648a2f..57c0a6024fcca96739e852a59e13cdd1001af097 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl
@@ -133,6 +133,7 @@ uniform vec4 minimum_alphas; // PBR alphaMode: MASK, See: mAlphaCutoff, setAlpha
 #if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
 in vec4[2] vary_coords;
 #endif
+in vec3 vary_position;
 in vec3 vary_normal;
 in vec3 vary_tangent;
 flat in float vary_sign;
@@ -140,11 +141,14 @@ in vec4 vary_texcoord0;
 in vec4 vary_texcoord1;
 
 vec2 encode_normal(vec3 n);
+void mirrorClip(vec3 position);
 
 float terrain_mix(TerrainMix tm, vec4 tms4);
 
 void main()
 {
+    // Make sure we clip the terrain if we're in a mirror.
+    mirrorClip(vary_position);
 
 #if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
     TerrainCoord terrain_texcoord = vary_coords;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl
index dbb940421937754a404d402df9fdcb64e3d65218..489fc26e3f52084d4fdbb4e6e06674a2fa256a7d 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl
@@ -25,6 +25,7 @@
 
 uniform mat3 normal_matrix;
 uniform mat4 texture_matrix0;
+uniform mat4 modelview_matrix;
 uniform mat4 modelview_projection_matrix;
 
 in vec3 position;
@@ -42,6 +43,7 @@ out vec3 vary_tangent;
 flat out float vary_sign;
 out vec4 vary_texcoord0;
 out vec4 vary_texcoord1;
+out vec3 vary_position;
 
 // *HACK: tangent_space_transform should use texture_normal_transform, or maybe
 // we shouldn't use tangent_space_transform at all. See the call to
@@ -55,6 +57,7 @@ void main()
 {
     //transform vertex
 	gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0); 
+    vary_position = (modelview_matrix*vec4(position.xyz, 1.0)).xyz;
 
 	vec3 n = normal_matrix * normal;
     vary_vertex_normal = normal;
diff --git a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl
index 257299258e7a27a76785281f773d9ee971e3ab1f..4f2475b1018339c84aa6ca7271c478d1f82cbdcf 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl
@@ -48,6 +48,7 @@ layout (std140) uniform ReflectionProbes
     /// box[0..2] - plane 0 .. 2 in [A,B,C,D] notation
     //  box[3][0..2] - plane thickness
     mat4 refBox[MAX_REFMAP_COUNT];
+    mat4 heroBox;
     // list of bounding spheres for reflection probes sorted by distance to camera (closest first)
     vec4 refSphere[MAX_REFMAP_COUNT];
     // extra parameters 
@@ -56,6 +57,7 @@ layout (std140) uniform ReflectionProbes
     //  z - fade in
     //  w - znear
     vec4 refParams[MAX_REFMAP_COUNT];
+    vec4 heroSphere;
     // 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
@@ -71,6 +73,10 @@ layout (std140) uniform ReflectionProbes
 
     // number of reflection probes present in refSphere
     int refmapCount;
+
+    int heroShape;
+    int heroMipCount;
+    int heroProbeCount;
 };
 
 // Inputs
@@ -682,48 +688,37 @@ vec3 sampleProbeAmbient(vec3 pos, vec3 dir, vec3 amblit)
     return col[1]+col[0];
 }
 
-
 #if defined(HERO_PROBES)
 
 uniform vec4 clipPlane;
-
 uniform samplerCubeArray   heroProbes;
 
-layout (std140) uniform HeroProbeData
-{
-    mat4 heroBox[1];
-    vec4 heroSphere[1];
-    uint heroShape[1];
-    int heroMipCount;
-    int heroProbeCount;
-};
-
 void tapHeroProbe(inout vec3 glossenv, vec3 pos, vec3 norm, float glossiness)
 {
     float clipDist = dot(pos.xyz, clipPlane.xyz) + clipPlane.w;
     float w = 0;
     float dw = 0;
-    if (heroShape[0] < 1)
+    float falloffMult = 10;
+    vec3 refnormpersp = reflect(pos.xyz, norm.xyz);
+    if (heroShape < 1)
     {
         float d = 0;
-        boxIntersect(pos, norm, heroBox[0], d, 1.0);
+        boxIntersect(pos, norm, heroBox, d, 1.0);
         
-        w = max(d, 0.001);
+        w = max(d, 0);
     }
     else
     {
-        float r = heroSphere[0].w;
+        float r = heroSphere.w;
         
-        w = sphereWeight(pos, norm, heroSphere[0].xyz, r, vec4(1), dw);
-    }
-    
-    {
-        vec3 refnormpersp = reflect(pos.xyz, norm.xyz);
-        if (dot(refnormpersp.xyz, clipPlane.xyz) > 0.0)
-        {
-            glossenv = mix(glossenv, textureLod(heroProbes, vec4(env_mat * refnormpersp, 0), (1.0-glossiness)*heroMipCount).xyz, w);
-        }
+        w = sphereWeight(pos, refnormpersp, heroSphere.xyz, r, vec4(1), dw);
     }
+
+    clipDist = clipDist * 0.95 + 0.05;
+    clipDist = clamp(clipDist * falloffMult, 0, 1);
+    w = clamp(w * falloffMult * clipDist, 0, 1);
+
+    glossenv = mix(glossenv, textureLod(heroProbes, vec4(env_mat * refnormpersp, 0), (1.0-glossiness)*heroMipCount).xyz, w);
 }
 
 #else
diff --git a/indra/newview/llheroprobemanager.cpp b/indra/newview/llheroprobemanager.cpp
index ee993c6ba1ad76b0b669328ac4ec4f9648b21654..8f1c1848cce8d4ff6bc8a2f80965e56edc2d4216 100644
--- a/indra/newview/llheroprobemanager.cpp
+++ b/indra/newview/llheroprobemanager.cpp
@@ -151,6 +151,7 @@ void LLHeroProbeManager::update()
 
             // Collect the list of faces that need updating based upon the camera's rotation.
             LLVector3 cam_direction = LLVector3(0, 0, 1) * LLViewerCamera::instance().getQuaternion();
+            cam_direction.normalize();
 
             static LLVector3 cubeFaces[6] = { 
                 LLVector3(1, 0, 0), 
@@ -163,7 +164,7 @@ void LLHeroProbeManager::update()
 
             for (int i = 0; i < 6; i++)
             {
-                float shouldUpdate = cam_direction * cubeFaces[i] * 0.5 + 0.5;
+                float shouldUpdate = fminf(1, (fmaxf(-1, cam_direction * cubeFaces[i]) * 0.5 + 0.5));
                 
                 int updateRate = ceilf((1 - shouldUpdate) * gPipeline.RenderHeroProbeConservativeUpdateMultiplier);
                 
@@ -215,6 +216,9 @@ void LLHeroProbeManager::update()
         mRenderingMirror = false;
         
         gPipeline.mReflectionMapManager.mRadiancePass = radiance_pass;
+
+        mProbes[0]->mViewerObject = mNearestHero;
+        mProbes[0]->autoAdjustOrigin();
     }
     
     mCurrentProbeUpdateFrame++;
@@ -424,26 +428,14 @@ void LLHeroProbeManager::updateUniforms()
 
     LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
     
-    struct HeroProbeData
-    {
-        LLMatrix4 heroBox[1];
-        LLVector4 heroSphere[1];
-        GLint heroShape[1];
-        GLint heroMipCount;
-        GLint heroProbeCount;
-    };
-    
-    HeroProbeData hpd;
-    
     LLMatrix4a modelview;
     modelview.loadu(gGLModelView);
     LLVector4a oa; // scratch space for transformed origin
     oa.set(0, 0, 0, 0);
-    hpd.heroProbeCount = 1;
+    mHeroData.heroProbeCount = 1;
     
     if (mNearestHero != nullptr)
     {
-        mProbes[0]->mViewerObject = mNearestHero;
         if (mNearestHero->getReflectionProbeIsBox())
         {
             LLVector3 s = mNearestHero->getScale().scaledVec(LLVector3(0.5f, 0.5f, 0.5f));
@@ -455,44 +447,17 @@ void LLHeroProbeManager::updateUniforms()
         }
         
         modelview.affineTransform(mProbes[0]->mOrigin, oa);
-        hpd.heroShape[0] = 0;
-        if (!mProbes[0]->getBox(hpd.heroBox[0]))
+        mHeroData.heroShape = 0;
+        if (!mProbes[0]->getBox(mHeroData.heroBox))
         {
-            hpd.heroShape[0] = 1;
+            mHeroData.heroShape = 1;
         }
         
-        hpd.heroSphere[0].set(oa.getF32ptr());
-        hpd.heroSphere[0].mV[3] = mProbes[0]->mRadius;
+        mHeroData.heroSphere.set(oa.getF32ptr());
+        mHeroData.heroSphere.mV[3] = mProbes[0]->mRadius;
     }
     
-    hpd.heroMipCount = mMipChain.size();
-
-    //copy rpd into uniform buffer object
-    if (mUBO == 0)
-    {
-        glGenBuffers(1, &mUBO);
-    }
-
-    {
-        LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("rmmsu - update buffer");
-        glBindBuffer(GL_UNIFORM_BUFFER, mUBO);
-        glBufferData(GL_UNIFORM_BUFFER, sizeof(HeroProbeData), &hpd, GL_STREAM_DRAW);
-        glBindBuffer(GL_UNIFORM_BUFFER, 0);
-    }
-}
-
-void LLHeroProbeManager::setUniforms()
-{
-    if (!LLPipeline::sReflectionProbesEnabled)
-    {
-        return;
-    }
-
-    if (mUBO == 0)
-    { 
-        updateUniforms();
-    }
-    glBindBufferBase(GL_UNIFORM_BUFFER, 1, mUBO);
+    mHeroData.heroMipCount = mMipChain.size();
 }
 
 void LLHeroProbeManager::renderDebug()
@@ -581,9 +546,6 @@ void LLHeroProbeManager::cleanup()
     
     mDefaultProbe = nullptr;
     mUpdatingProbe = nullptr;
-
-    glDeleteBuffers(1, &mUBO);
-    mUBO = 0;
     
     mHeroVOList.clear();
     mNearestHero = nullptr;
diff --git a/indra/newview/llheroprobemanager.h b/indra/newview/llheroprobemanager.h
index e430cae2032b64a23df18b09d7b8a05d66d3b48c..04027cd57e838ccb538e7b951e817ba75cc15a5e 100644
--- a/indra/newview/llheroprobemanager.h
+++ b/indra/newview/llheroprobemanager.h
@@ -38,6 +38,15 @@ class LLViewerObject;
 // number of reflection probes to keep in vram
 #define LL_MAX_HERO_PROBE_COUNT 2
 
+struct HeroProbeData
+{
+    LLMatrix4 heroBox;
+    LLVector4 heroSphere;
+    GLint     heroShape;
+    GLint     heroMipCount;
+    GLint     heroProbeCount;
+};
+
 class alignas(16) LLHeroProbeManager
 {
     LL_ALIGN_NEW
@@ -74,16 +83,17 @@ class alignas(16) LLHeroProbeManager
     bool isMirrorPass() const { return mRenderingMirror; }
 
     LLVector3 mMirrorPosition;
-    LLVector3 mMirrorNormal;
+    LLVector3     mMirrorNormal;
+    HeroProbeData mHeroData;
     
 private:
     friend class LLPipeline;
+    friend class LLReflectionMapManager;
     
     // update UBO used for rendering (call only once per render pipe flush)
     void updateUniforms();
 
     // bind UBO used for rendering
-    void setUniforms();
 
     // render target for cube snapshots
     // used to generate mipmaps without doing a copy-to-texture
@@ -109,9 +119,6 @@ class alignas(16) LLHeroProbeManager
     // list of active reflection maps
     std::vector<LLPointer<LLReflectionMap>> mProbes;
 
-    // handle to UBO
-    U32 mUBO = 0;
-
     // list of maps being used for rendering
     std::vector<LLReflectionMap*> mReflectionMaps;
 
@@ -141,5 +148,6 @@ class alignas(16) LLHeroProbeManager
     
     std::vector<LLVOVolume*>                       mHeroVOList;
     LLVOVolume*                                 mNearestHero;
+
 };
 
diff --git a/indra/newview/llreflectionmapmanager.cpp b/indra/newview/llreflectionmapmanager.cpp
index 314426090585aadc38dc168fefe58ba84a534d29..ce389a5cad13394831b4297339673b9cbb6daa52 100644
--- a/indra/newview/llreflectionmapmanager.cpp
+++ b/indra/newview/llreflectionmapmanager.cpp
@@ -906,6 +906,8 @@ void LLReflectionMapManager::updateUniforms()
         // the box probe
         LLMatrix4 refBox[LL_MAX_REFLECTION_PROBE_COUNT];
 
+        LLMatrix4 heroBox;
+
         // for sphere probes, origin (xyz) and radius (w) of refmaps in clip space
         LLVector4 refSphere[LL_MAX_REFLECTION_PROBE_COUNT];
 
@@ -916,6 +918,8 @@ void LLReflectionMapManager::updateUniforms()
         //  w - znear
         LLVector4 refParams[LL_MAX_REFLECTION_PROBE_COUNT];
 
+        LLVector4 heroSphere;
+
         // indices used by probe:
         //  [i][0] - cubemap array index for this probe
         //  [i][1] - index into "refNeighbor" for probes that intersect this probe
@@ -929,6 +933,10 @@ void LLReflectionMapManager::updateUniforms()
         GLint refBucket[256][4]; //lookup table for which index to start with for the given Z depth
         // numbrer of active refmaps
         GLint refmapCount;
+
+        GLint     heroShape;
+        GLint     heroMipCount;
+        GLint     heroProbeCount;
     };
 
     mReflectionMaps.resize(mReflectionProbeCount);
@@ -1118,6 +1126,16 @@ void LLReflectionMapManager::updateUniforms()
 
     rpd.refmapCount = count;
 
+    gPipeline.mHeroProbeManager.updateUniforms();
+
+    // Get the hero data.
+
+    rpd.heroBox = gPipeline.mHeroProbeManager.mHeroData.heroBox;
+    rpd.heroSphere = gPipeline.mHeroProbeManager.mHeroData.heroSphere;
+    rpd.heroShape  = gPipeline.mHeroProbeManager.mHeroData.heroShape;
+    rpd.heroMipCount = gPipeline.mHeroProbeManager.mHeroData.heroMipCount;
+    rpd.heroProbeCount = gPipeline.mHeroProbeManager.mHeroData.heroProbeCount;
+
     //copy rpd into uniform buffer object
     if (mUBO == 0)
     {