diff --git a/src/f_wipe.c b/src/f_wipe.c
index 43b7180b754408faf39387ef259d54aae590bf9f..b9a09e89fb8ddb5e4251f7bbe75549f0217d1ebb 100644
--- a/src/f_wipe.c
+++ b/src/f_wipe.c
@@ -565,7 +565,7 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu)
 			if (rendermode == render_opengl)
 			{
 				// send in the wipe type and wipe frame because we need to cache the graphic
-				HWR_DoTintedWipe(wipetype, wipeframe-1);
+				HWR_DoWipe(wipetype, wipeframe-1);
 			}
 			else
 #endif
diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h
index bf1bed9ee06cf75980dbc481a4732b7339f74d4c..c325e9d3f2bf64d6385636ec33d0525d0bd95c37 100644
--- a/src/hardware/hw_defs.h
+++ b/src/hardware/hw_defs.h
@@ -154,6 +154,7 @@ enum
 	SHADER_SKY,
 	SHADER_PALETTE_POSTPROCESS,
 	SHADER_UI_COLORMAP_FADE,
+	SHADER_UI_TINTED_WIPE,
 
 	NUMSHADERTARGETS
 };
diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h
index d4fe88d474cc71ac202c368ab320ca690aeeaeb6..f4d9a09f8e10cd4a73996ccffdb5452e27cd97fb 100644
--- a/src/hardware/hw_drv.h
+++ b/src/hardware/hw_drv.h
@@ -55,7 +55,7 @@ EXPORT void HWRAPI(SetTransform) (FTransform *ptransform);
 EXPORT INT32 HWRAPI(GetTextureUsed) (void);
 
 EXPORT void HWRAPI(FlushScreenTextures) (void);
-EXPORT void HWRAPI(DoScreenWipe) (int wipeStart, int wipeEnd);
+EXPORT void HWRAPI(DoScreenWipe) (int wipeStart, int wipeEnd, FSurfaceInfo *surf, FBITFIELD polyFlags);
 EXPORT void HWRAPI(DrawScreenTexture) (int tex, FSurfaceInfo *surf, FBITFIELD polyflags);
 EXPORT void HWRAPI(MakeScreenTexture) (int tex);
 EXPORT void HWRAPI(DrawScreenFinalTexture) (int tex, int width, int height);
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 7dfe1bff9e2c93dbd5ba3c8fc35fe1353e097e8c..4c14a4cfe6c4a4819e432351a2405102685dee15 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -6858,13 +6858,31 @@ void HWR_DoWipe(UINT8 wipenum, UINT8 scrnnum)
 		return;
 
 	HWR_GetFadeMask(wipelumpnum);
-	HWD.pfnDoScreenWipe(HWD_SCREENTEXTURE_WIPE_START, HWD_SCREENTEXTURE_WIPE_END);
-}
+	if (wipestyle == WIPESTYLE_COLORMAP && HWR_UseShader())
+	{
+		FSurfaceInfo surf = {0};
+		FBITFIELD polyflags = PF_Modulated|PF_NoDepthTest;
 
-void HWR_DoTintedWipe(UINT8 wipenum, UINT8 scrnnum)
-{
-	// It does the same thing
-	HWR_DoWipe(wipenum, scrnnum);
+		polyflags |= (wipestyleflags & WSF_TOWHITE) ? PF_Additive : PF_ReverseSubtract;
+		surf.PolyColor.s.red = FADEREDFACTOR;
+		surf.PolyColor.s.green = FADEGREENFACTOR;
+		surf.PolyColor.s.blue = FADEBLUEFACTOR;
+		// polycolor alpha communicates fadein / fadeout to the shader and the backend
+		surf.PolyColor.s.alpha = (wipestyleflags & WSF_FADEIN) ? 255 : 0;
+
+		// backend shader may not have been enabled yet so do it here
+		HWR_SetShaderState();
+
+		HWD.pfnSetShader(HWR_GetShaderFromTarget(SHADER_UI_TINTED_WIPE));
+		HWD.pfnDoScreenWipe(HWD_SCREENTEXTURE_WIPE_START, HWD_SCREENTEXTURE_WIPE_END,
+			&surf, polyflags);
+		HWD.pfnUnSetShader();
+	}
+	else
+	{
+		HWD.pfnDoScreenWipe(HWD_SCREENTEXTURE_WIPE_START, HWD_SCREENTEXTURE_WIPE_END,
+			NULL, 0);
+	}
 }
 
 void HWR_MakeScreenFinalTexture(void)
diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h
index 8af1fc0418c15a4b2940437b8bb2c751b1b292f1..36d1e94f9bf1973cb1bf33269354253d52580f7f 100644
--- a/src/hardware/hw_main.h
+++ b/src/hardware/hw_main.h
@@ -62,7 +62,6 @@ void HWR_StartScreenWipe(void);
 void HWR_EndScreenWipe(void);
 void HWR_DrawIntermissionBG(void);
 void HWR_DoWipe(UINT8 wipenum, UINT8 scrnnum);
-void HWR_DoTintedWipe(UINT8 wipenum, UINT8 scrnnum);
 void HWR_MakeScreenFinalTexture(void);
 void HWR_DrawScreenFinalTexture(int width, int height);
 
diff --git a/src/hardware/hw_shaders.c b/src/hardware/hw_shaders.c
index 6f81a55ef2b2f6887066344d996f32eb29a642a5..4e2ec14a1faca1c55bd1ad7f9edf674924dc2d2e 100644
--- a/src/hardware/hw_shaders.c
+++ b/src/hardware/hw_shaders.c
@@ -315,6 +315,7 @@
 		"gl_FragColor = final_color;\n" \
 	"}\0"
 
+// Applies a palettized colormap fade to tex
 #define GLSL_UI_COLORMAP_FADE_SHADER \
 	"uniform sampler2D tex;\n" \
 	"uniform float lighting;\n" \
@@ -325,7 +326,29 @@
 		"float tex_pal_idx = texture3D(palette_lookup_tex, vec3((texel * 63.0 + 0.5) / 64.0))[0] * 255.0;\n" \
 		"vec2 lighttable_coord = vec2((tex_pal_idx + 0.5) / 256.0, (lighting + 0.5) / 32.0);\n" \
 		"gl_FragColor = texture2D(lighttable_tex, lighttable_coord);\n" \
-	"}\0" \
+	"}\0"
+
+// For wipes that use additive and subtractive blending.
+// alpha_factor = 31 * 8 / 10 = 24.8
+// Calculated based on the use of the "fade" variable from the GETCOLOR macro
+// in r_data.c:R_CreateFadeColormaps.
+// However this value created some ugliness in fades to white (special stage entry)
+// while palette rendering is enabled, so I raised the value just a bit.
+#define GLSL_UI_TINTED_WIPE_SHADER \
+	"uniform sampler2D tex;\n" \
+	"uniform vec4 poly_color;\n" \
+	"const float alpha_factor = 24.875;\n" \
+	"void main(void) {\n" \
+		"vec4 texel = texture2D(tex, gl_TexCoord[0].st);\n" \
+		"vec4 final_color = poly_color;\n" \
+		"float alpha = texel.a;\n" \
+		"if (final_color.a >= 0.5)\n" \
+			"alpha = 1.0 - alpha;\n" \
+		"alpha *= alpha_factor;\n" \
+		"final_color *= alpha;\n" \
+		"final_color.a = 1.0;\n" \
+		"gl_FragColor = final_color;\n" \
+	"}\0"
 
 // ================
 //  Shader sources
@@ -362,6 +385,9 @@ static struct {
 	// UI colormap fade shader
 	{GLSL_DEFAULT_VERTEX_SHADER, GLSL_UI_COLORMAP_FADE_SHADER},
 
+	// UI tinted wipe shader
+	{GLSL_DEFAULT_VERTEX_SHADER, GLSL_UI_TINTED_WIPE_SHADER},
+
 	{NULL, NULL},
 };
 
diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index 6152b2c433871a7031378a71ecb2f2c003f22193..e9027eba55ffd27e1f6ba58db294e9cfd9675cef 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -3030,7 +3030,8 @@ EXPORT void HWRAPI(DrawScreenTexture)(int tex, FSurfaceInfo *surf, FBITFIELD pol
 }
 
 // Do screen fades!
-EXPORT void HWRAPI(DoScreenWipe)(int wipeStart, int wipeEnd)
+EXPORT void HWRAPI(DoScreenWipe)(int wipeStart, int wipeEnd, FSurfaceInfo *surf,
+		FBITFIELD polyFlags)
 {
 	INT32 texsize = 512;
 	float xfix, yfix;
@@ -3055,6 +3056,12 @@ EXPORT void HWRAPI(DoScreenWipe)(int wipeStart, int wipeEnd)
 		1.0f, 1.0f
 	};
 
+	int firstScreen;
+	if (surf && surf->PolyColor.s.alpha == 255)
+		firstScreen = wipeEnd; // it's a tinted fade-in, we need wipeEnd
+	else
+		firstScreen = wipeStart;
+
 	// look for power of two that is large enough for the screen
 	while (texsize < screen_width || texsize < screen_height)
 		texsize <<= 1;
@@ -3079,43 +3086,55 @@ EXPORT void HWRAPI(DoScreenWipe)(int wipeStart, int wipeEnd)
 	SetBlend(PF_Modulated|PF_NoDepthTest);
 	pglEnable(GL_TEXTURE_2D);
 
-	// Draw the original screen
-	pglBindTexture(GL_TEXTURE_2D, screenTextures[wipeStart]);
+	pglBindTexture(GL_TEXTURE_2D, screenTextures[firstScreen]);
 	pglColor4ubv(white);
 	pglTexCoordPointer(2, GL_FLOAT, 0, fix);
 	pglVertexPointer(3, GL_FLOAT, 0, screenVerts);
 	pglDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 
-	SetBlend(PF_Modulated|PF_Translucent|PF_NoDepthTest);
+	if (surf)
+	{
+		// Draw fade mask to screen using surf and polyFlags
+		// Used for colormap/tinted wipes.
+		pglBindTexture(GL_TEXTURE_2D, fademaskdownloaded);
+		pglTexCoordPointer(2, GL_FLOAT, 0, defaultST);
+		pglVertexPointer(3, GL_FLOAT, 0, screenVerts);
+		PreparePolygon(surf, NULL, polyFlags);
+		pglDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+	}
+	else // Blend wipeEnd into screen with the fade mask
+	{
+		SetBlend(PF_Modulated|PF_Translucent|PF_NoDepthTest);
 
-	// Draw the end screen that fades in
-	pglActiveTexture(GL_TEXTURE0);
-	pglEnable(GL_TEXTURE_2D);
-	pglBindTexture(GL_TEXTURE_2D, screenTextures[wipeEnd]);
-	pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+		// Draw the end screen that fades in
+		pglActiveTexture(GL_TEXTURE0);
+		pglEnable(GL_TEXTURE_2D);
+		pglBindTexture(GL_TEXTURE_2D, screenTextures[wipeEnd]);
+		pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
 
-	pglActiveTexture(GL_TEXTURE1);
-	pglEnable(GL_TEXTURE_2D);
-	pglBindTexture(GL_TEXTURE_2D, fademaskdownloaded);
+		pglActiveTexture(GL_TEXTURE1);
+		pglEnable(GL_TEXTURE_2D);
+		pglBindTexture(GL_TEXTURE_2D, fademaskdownloaded);
 
-	pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+		pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
 
-	// const float defaultST[8]
+		// const float defaultST[8]
 
-	pglClientActiveTexture(GL_TEXTURE0);
-	pglTexCoordPointer(2, GL_FLOAT, 0, fix);
-	pglVertexPointer(3, GL_FLOAT, 0, screenVerts);
-	pglClientActiveTexture(GL_TEXTURE1);
-	pglEnableClientState(GL_TEXTURE_COORD_ARRAY);
-	pglTexCoordPointer(2, GL_FLOAT, 0, defaultST);
-	pglDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+		pglClientActiveTexture(GL_TEXTURE0);
+		pglTexCoordPointer(2, GL_FLOAT, 0, fix);
+		pglVertexPointer(3, GL_FLOAT, 0, screenVerts);
+		pglClientActiveTexture(GL_TEXTURE1);
+		pglEnableClientState(GL_TEXTURE_COORD_ARRAY);
+		pglTexCoordPointer(2, GL_FLOAT, 0, defaultST);
+		pglDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 
-	pglDisable(GL_TEXTURE_2D); // disable the texture in the 2nd texture unit
-	pglDisableClientState(GL_TEXTURE_COORD_ARRAY);
+		pglDisable(GL_TEXTURE_2D); // disable the texture in the 2nd texture unit
+		pglDisableClientState(GL_TEXTURE_COORD_ARRAY);
 
-	pglActiveTexture(GL_TEXTURE0);
-	pglClientActiveTexture(GL_TEXTURE0);
-	tex_downloaded = screenTextures[wipeEnd];
+		pglActiveTexture(GL_TEXTURE0);
+		pglClientActiveTexture(GL_TEXTURE0);
+		tex_downloaded = screenTextures[wipeEnd];
+	}
 }
 
 // Create a texture from the screen.