diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index 75d6ef6c4613409293ae3736d9bfbd8e8ff7bb48..22940dc703fecb4e54f28a480209578a8057434f 100644
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -1300,8 +1300,9 @@ void LLShaderMgr::initAttribsAndUniforms()
 	mReservedUniforms.push_back("warmthAmount");
 	mReservedUniforms.push_back("glowStrength");
 	mReservedUniforms.push_back("glowDelta");
+	mReservedUniforms.push_back("glowNoiseMap");
 
-	llassert(mReservedUniforms.size() == LLShaderMgr::GLOW_DELTA+1);
+	llassert(mReservedUniforms.size() == LLShaderMgr::GLOW_NOISE_MAP+1);
 
 
 	mReservedUniforms.push_back("minimum_alpha");
diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h
index 46f352aa58c29fa69c0d51b7405d9aab15f8b296..ac4b393fb7d76da7f174c2e2c9752e93a691d9e0 100644
--- a/indra/llrender/llshadermgr.h
+++ b/indra/llrender/llshadermgr.h
@@ -131,6 +131,7 @@ class LLShaderMgr
         GLOW_WARMTH_AMOUNT,                 //  "warmthAmount"
         GLOW_STRENGTH,                      //  "glowStrength"
         GLOW_DELTA,                         //  "glowDelta"
+        GLOW_NOISE_MAP,                     //  "glowNoiseMap"
 
         MINIMUM_ALPHA,                      //  "minimum_alpha"
         EMISSIVE_BRIGHTNESS,                //  "emissive_brightness"
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 9f3a5bd5b9eee6cd88423e32c4d7eeffa1f2416e..8e5e092250eaa660201774be7069a4a79e04576d 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -10225,6 +10225,17 @@
       <key>Value</key>
       <real>1.3</real>
     </map>
+    <key>RenderGlowNoise</key>
+    <map>
+      <key>Comment</key>
+      <string>Enables glow noise (dithering). Reduces banding from glow in certain cases.</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <real>1</real>
+    </map>
     <key>DisableAllRenderTypes</key>
     <map>
       <key>Comment</key>
diff --git a/indra/newview/app_settings/shaders/class1/effects/glowExtractF.glsl b/indra/newview/app_settings/shaders/class1/effects/glowExtractF.glsl
index 7a5e14566bb7d09d6fdb89526d42ee2c747cf452..b5437d43d2869789e8bf23d469cc0b9a04580573 100644
--- a/indra/newview/app_settings/shaders/class1/effects/glowExtractF.glsl
+++ b/indra/newview/app_settings/shaders/class1/effects/glowExtractF.glsl
@@ -28,6 +28,10 @@
 out vec4 frag_color;
 
 uniform sampler2D diffuseMap;
+#if HAS_NOISE
+uniform sampler2D glowNoiseMap;
+uniform vec2 screen_res;
+#endif
 uniform float minLuminance;
 uniform float maxExtractAlpha;
 uniform vec3 lumWeights;
@@ -44,7 +48,16 @@ void main()
 	float lum = smoothstep(minLuminance, minLuminance+1.0, dot(col.rgb, lumWeights ) );
 	float warmth = smoothstep(minLuminance, minLuminance+1.0, max(col.r * warmthWeights.r, max(col.g * warmthWeights.g, col.b * warmthWeights.b)) ); 
 	
-	frag_color.rgb = col.rgb; 
+#if HAS_NOISE
+    float TRUE_NOISE_RES = 128; // See mTrueNoiseMap
+    // *NOTE: Usually this is vary_fragcoord not vary_texcoord0, but glow extraction is in screen space
+    vec3 glow_noise = texture(glowNoiseMap, vary_texcoord0.xy * (screen_res / TRUE_NOISE_RES)).xyz;
+    // Dithering. Reduces banding effects in the reduced precision glow buffer.
+    float NOISE_DEPTH = 64.0;
+    col.rgb += glow_noise / NOISE_DEPTH;
+    col.rgb = max(col.rgb, vec3(0));
+#endif
+	frag_color.rgb = col.rgb;
 	frag_color.a = max(col.a, mix(lum, warmth, warmthAmount) * maxExtractAlpha);
 	
 }
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 9707a2a7e655decac2729cb4000baa453b0399ed..9f4287c23d2f46a286019b542b8c88e3b9434129 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -710,6 +710,7 @@ void settings_setup_listeners()
     setting_setup_signal_listener(gSavedSettings, "RenderGlow", handleSetShaderChanged);
     setting_setup_signal_listener(gSavedSettings, "RenderGlowResolutionPow", handleReleaseGLBufferChanged);
     setting_setup_signal_listener(gSavedSettings, "RenderGlowHDR", handleReleaseGLBufferChanged);
+    setting_setup_signal_listener(gSavedSettings, "RenderGlowNoise", handleSetShaderChanged);
     setting_setup_signal_listener(gSavedSettings, "RenderGammaFull", handleSetShaderChanged);
     setting_setup_signal_listener(gSavedSettings, "RenderVolumeLODFactor", handleVolumeLODChanged);
     setting_setup_signal_listener(gSavedSettings, "RenderAvatarLODFactor", handleAvatarLODChanged);
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index 4559d71d6d0fc9037efa55494f2fbdd07e313cdf..82b16d67bd62d1be2d00b79002f292b78f80799c 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -898,11 +898,20 @@ BOOL LLViewerShaderMgr::loadShadersEffects()
 	
 	if (success)
 	{
-		gGlowExtractProgram.mName = "Glow Extract Shader (Post)";
+        const bool use_glow_noise = gSavedSettings.getBOOL("RenderGlowNoise");
+        const std::string glow_noise_label = use_glow_noise ? " (+Noise)" : "";
+
+		gGlowExtractProgram.mName = llformat("Glow Extract Shader (Post)%s", glow_noise_label.c_str());
 		gGlowExtractProgram.mShaderFiles.clear();
 		gGlowExtractProgram.mShaderFiles.push_back(make_pair("effects/glowExtractV.glsl", GL_VERTEX_SHADER));
 		gGlowExtractProgram.mShaderFiles.push_back(make_pair("effects/glowExtractF.glsl", GL_FRAGMENT_SHADER));
 		gGlowExtractProgram.mShaderLevel = mShaderLevel[SHADER_EFFECT];
+
+        if (use_glow_noise)
+        {
+            gGlowExtractProgram.addPermutation("HAS_NOISE", "1");
+        }
+
 		success = gGlowExtractProgram.createShader(NULL, NULL);
 		if (!success)
 		{
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 418399026190d76adb50fc47c68f7962bb9f41ab..d50e671e059533dc48bbdbd4b2fae0aad13d4140 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -157,6 +157,7 @@ S32 LLPipeline::RenderGlowResolutionPow;
 S32 LLPipeline::RenderGlowIterations;
 F32 LLPipeline::RenderGlowWidth;
 F32 LLPipeline::RenderGlowStrength;
+bool LLPipeline::RenderGlowNoise;
 bool LLPipeline::RenderDepthOfField;
 bool LLPipeline::RenderDepthOfFieldInEditMode;
 F32 LLPipeline::CameraFocusTransitionTime;
@@ -517,6 +518,7 @@ void LLPipeline::init()
 	connectRefreshCachedSettingsSafe("RenderGlowIterations");
 	connectRefreshCachedSettingsSafe("RenderGlowWidth");
 	connectRefreshCachedSettingsSafe("RenderGlowStrength");
+	connectRefreshCachedSettingsSafe("RenderGlowNoise");
 	connectRefreshCachedSettingsSafe("RenderDepthOfField");
 	connectRefreshCachedSettingsSafe("RenderDepthOfFieldInEditMode");
 	connectRefreshCachedSettingsSafe("CameraFocusTransitionTime");
@@ -1007,6 +1009,7 @@ void LLPipeline::refreshCachedSettings()
 	RenderGlowIterations = gSavedSettings.getS32("RenderGlowIterations");
 	RenderGlowWidth = gSavedSettings.getF32("RenderGlowWidth");
 	RenderGlowStrength = gSavedSettings.getF32("RenderGlowStrength");
+	RenderGlowNoise = gSavedSettings.getBOOL("RenderGlowNoise");
 	RenderDepthOfField = gSavedSettings.getBOOL("RenderDepthOfField");
 	RenderDepthOfFieldInEditMode = gSavedSettings.getBOOL("RenderDepthOfFieldInEditMode");
 	CameraFocusTransitionTime = gSavedSettings.getF32("CameraFocusTransitionTime");
@@ -6886,6 +6889,19 @@ void LLPipeline::generateGlow(LLRenderTarget* src)
 			warmthWeights.mV[2]);
 		gGlowExtractProgram.uniform1f(LLShaderMgr::GLOW_WARMTH_AMOUNT, warmthAmount);
 
+        if (RenderGlowNoise)
+        {
+            S32 channel = gGlowExtractProgram.enableTexture(LLShaderMgr::GLOW_NOISE_MAP);
+            if (channel > -1)
+            {
+                gGL.getTexUnit(channel)->bindManual(LLTexUnit::TT_TEXTURE, mTrueNoiseMap);
+                gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
+            }
+            gGlowExtractProgram.uniform2f(LLShaderMgr::DEFERRED_SCREEN_RES,
+                                          mGlow[2].getWidth(),
+                                          mGlow[2].getHeight());
+        }
+
 		{
 			LLGLEnable blend_on(GL_BLEND);
 
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index 961a55330a25a712fe410abe293d9c9f2a96d322..c0559ce83bc5b641be347704011babbe2881c9da 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -1001,6 +1001,7 @@ class LLPipeline
 	static S32 RenderGlowIterations;
 	static F32 RenderGlowWidth;
 	static F32 RenderGlowStrength;
+	static bool RenderGlowNoise;
 	static bool RenderDepthOfField;
 	static bool RenderDepthOfFieldInEditMode;
 	static F32 CameraFocusTransitionTime;