diff --git a/src/gl/renderer/gl_renderstate.cpp b/src/gl/renderer/gl_renderstate.cpp
index cd51e540a..2cfd739e9 100644
--- a/src/gl/renderer/gl_renderstate.cpp
+++ b/src/gl/renderer/gl_renderstate.cpp
@@ -105,6 +105,7 @@ void FRenderState::Reset()
 	mViewMatrix.loadIdentity();
 	mModelMatrix.loadIdentity();
 	mTextureMatrix.loadIdentity();
+	mPassType = NORMAL_PASS;
 }
 
 //==========================================================================
@@ -118,11 +119,11 @@ bool FRenderState::ApplyShader()
 	static const float nulvec[] = { 0.f, 0.f, 0.f, 0.f };
 	if (mSpecialEffect > EFF_NONE)
 	{
-		activeShader = GLRenderer->mShaderManager->BindEffect(mSpecialEffect);
+		activeShader = GLRenderer->mShaderManager->BindEffect(mSpecialEffect, mPassType);
 	}
 	else
 	{
-		activeShader = GLRenderer->mShaderManager->Get(mTextureEnabled ? mEffectState : 4, mAlphaThreshold >= 0.f);
+		activeShader = GLRenderer->mShaderManager->Get(mTextureEnabled ? mEffectState : 4, mAlphaThreshold >= 0.f, mPassType);
 		activeShader->Bind();
 	}
 
@@ -343,7 +344,7 @@ void FRenderState::ApplyMatrices()
 {
 	if (GLRenderer->mShaderManager != NULL)
 	{
-		GLRenderer->mShaderManager->ApplyMatrices(&mProjectionMatrix, &mViewMatrix);
+		GLRenderer->mShaderManager->ApplyMatrices(&mProjectionMatrix, &mViewMatrix, mPassType);
 	}
 }
 
diff --git a/src/gl/renderer/gl_renderstate.h b/src/gl/renderer/gl_renderstate.h
index 1c0a20348..7bec51be0 100644
--- a/src/gl/renderer/gl_renderstate.h
+++ b/src/gl/renderer/gl_renderstate.h
@@ -63,6 +63,13 @@ enum EEffect
 	MAX_EFFECTS
 };
 
+enum EPassType
+{
+	NORMAL_PASS,
+	GBUFFER_PASS,
+	MAX_PASS_TYPES
+};
+
 class FRenderState
 {
 	bool mTextureEnabled;
@@ -111,6 +118,8 @@ class FRenderState
 
 	FShader *activeShader;
 
+	EPassType mPassType = NORMAL_PASS;
+
 	bool ApplyShader();
 
 public:
@@ -459,6 +468,16 @@ public:
 		return mInterpolationFactor;
 	}
 
+	void SetPassType(EPassType passType)
+	{
+		mPassType = passType;
+	}
+
+	EPassType GetPassType()
+	{
+		return mPassType;
+	}
+
 	// Backwards compatibility crap follows
 	void ApplyFixedFunction();
 	void DrawColormapOverlay();
diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp
index 158163a48..26a449bf2 100644
--- a/src/gl/shaders/gl_shader.cpp
+++ b/src/gl/shaders/gl_shader.cpp
@@ -180,6 +180,9 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
 	glBindAttribLocation(hShader, VATTR_COLOR, "aColor");
 	glBindAttribLocation(hShader, VATTR_VERTEX2, "aVertex2");
 
+	glBindFragDataLocation(hShader, 0, "FragColor");
+	glBindFragDataLocation(hShader, 1, "FragData");
+
 	glLinkProgram(hShader);
 
 	glGetShaderInfoLog(hVertProg, 10000, NULL, buffer);
@@ -298,12 +301,13 @@ bool FShader::Bind()
 //
 //==========================================================================
 
-FShader *FShaderManager::Compile (const char *ShaderName, const char *ShaderPath, bool usediscard)
+FShader *FShaderCollection::Compile (const char *ShaderName, const char *ShaderPath, bool usediscard, EPassType passType)
 {
 	FString defines;
 	// this can't be in the shader code due to ATI strangeness.
 	if (gl.MaxLights() == 128) defines += "#define MAXLIGHTS128\n";
 	if (!usediscard) defines += "#define NO_ALPHATEST\n";
+	if (passType == GBUFFER_PASS) defines += "#define GBUFFER_PASS\n";
 
 	FShader *shader = NULL;
 	try
@@ -386,27 +390,75 @@ static const FEffectShader effectshaders[]=
 	{ "stencil", "shaders/glsl/main.vp", "shaders/glsl/stencil.fp", NULL, "#define SIMPLE\n#define NO_ALPHATEST\n" },
 };
 
-
-//==========================================================================
-//
-//
-//
-//==========================================================================
-
 FShaderManager::FShaderManager()
 {
-	if (!gl.legacyMode) CompileShaders();
+	if (!gl.legacyMode)
+	{
+		for (int passType = 0; passType < MAX_PASS_TYPES; passType++)
+			mPassShaders.Push(new FShaderCollection((EPassType)passType));
+	}
 }
 
-//==========================================================================
-//
-//
-//
-//==========================================================================
-
 FShaderManager::~FShaderManager()
 {
-	if (!gl.legacyMode) Clean();
+	if (!gl.legacyMode)
+	{
+		glUseProgram(0);
+		mActiveShader = NULL;
+
+		for (auto collection : mPassShaders)
+			delete collection;
+	}
+}
+
+void FShaderManager::SetActiveShader(FShader *sh)
+{
+	if (mActiveShader != sh)
+	{
+		glUseProgram(sh!= NULL? sh->GetHandle() : 0);
+		mActiveShader = sh;
+	}
+}
+
+FShader *FShaderManager::BindEffect(int effect, EPassType passType)
+{
+	if (passType < mPassShaders.Size())
+		return mPassShaders[passType]->BindEffect(effect);
+	else
+		return nullptr;
+}
+
+FShader *FShaderManager::Get(unsigned int eff, bool alphateston, EPassType passType)
+{
+	if (passType < mPassShaders.Size())
+		return mPassShaders[passType]->Get(eff, alphateston);
+	else
+		return nullptr;
+}
+
+void FShaderManager::ApplyMatrices(VSMatrix *proj, VSMatrix *view, EPassType passType)
+{
+	if (gl.legacyMode)
+	{
+		glMatrixMode(GL_PROJECTION);
+		glLoadMatrixf(proj->get());
+		glMatrixMode(GL_MODELVIEW);
+		glLoadMatrixf(view->get());
+	}
+	else
+	{
+		if (passType < mPassShaders.Size())
+			mPassShaders[passType]->ApplyMatrices(proj, view);
+
+		if (mActiveShader)
+			mActiveShader->Bind();
+	}
+}
+
+void FShaderManager::ResetFixedColormap()
+{
+	for (auto &collection : mPassShaders)
+		collection->ResetFixedColormap();
 }
 
 //==========================================================================
@@ -415,10 +467,30 @@ FShaderManager::~FShaderManager()
 //
 //==========================================================================
 
-void FShaderManager::CompileShaders()
+FShaderCollection::FShaderCollection(EPassType passType)
 {
-	mActiveShader = NULL;
+	CompileShaders(passType);
+}
 
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+FShaderCollection::~FShaderCollection()
+{
+	Clean();
+}
+
+//==========================================================================
+//
+//
+//
+//==========================================================================
+
+void FShaderCollection::CompileShaders(EPassType passType)
+{
 	mTextureEffects.Clear();
 	mTextureEffectsNAT.Clear();
 	for (int i = 0; i < MAX_EFFECTS; i++)
@@ -428,11 +500,11 @@ void FShaderManager::CompileShaders()
 
 	for(int i=0;defaultshaders[i].ShaderName != NULL;i++)
 	{
-		FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, true);
+		FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, true, passType);
 		mTextureEffects.Push(shc);
 		if (i <= 3)
 		{
-			FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, false);
+			FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, false, passType);
 			mTextureEffectsNAT.Push(shc);
 		}
 	}
@@ -442,7 +514,7 @@ void FShaderManager::CompileShaders()
 		FString name = ExtractFileBase(usershaders[i]);
 		FName sfn = name;
 
-		FShader *shc = Compile(sfn, usershaders[i], true);
+		FShader *shc = Compile(sfn, usershaders[i], true, passType);
 		mTextureEffects.Push(shc);
 	}
 
@@ -464,11 +536,8 @@ void FShaderManager::CompileShaders()
 //
 //==========================================================================
 
-void FShaderManager::Clean()
+void FShaderCollection::Clean()
 {
-	glUseProgram(0);
-	mActiveShader = NULL;
-
 	for (unsigned int i = 0; i < mTextureEffectsNAT.Size(); i++)
 	{
 		if (mTextureEffectsNAT[i] != NULL) delete mTextureEffectsNAT[i];
@@ -492,7 +561,7 @@ void FShaderManager::Clean()
 //
 //==========================================================================
 
-int FShaderManager::Find(const char * shn)
+int FShaderCollection::Find(const char * shn)
 {
 	FName sfn = shn;
 
@@ -506,21 +575,6 @@ int FShaderManager::Find(const char * shn)
 	return -1;
 }
 
-//==========================================================================
-//
-//
-//
-//==========================================================================
-
-void FShaderManager::SetActiveShader(FShader *sh)
-{
-	if (mActiveShader != sh)
-	{
-		glUseProgram(sh!= NULL? sh->GetHandle() : 0);
-		mActiveShader = sh;
-	}
-}
-
 
 //==========================================================================
 //
@@ -528,7 +582,7 @@ void FShaderManager::SetActiveShader(FShader *sh)
 //
 //==========================================================================
 
-FShader *FShaderManager::BindEffect(int effect)
+FShader *FShaderCollection::BindEffect(int effect)
 {
 	if (effect >= 0 && effect < MAX_EFFECTS && mEffectShaders[effect] != NULL)
 	{
@@ -546,36 +600,25 @@ FShader *FShaderManager::BindEffect(int effect)
 //==========================================================================
 EXTERN_CVAR(Int, gl_fuzztype)
 
-void FShaderManager::ApplyMatrices(VSMatrix *proj, VSMatrix *view)
+void FShaderCollection::ApplyMatrices(VSMatrix *proj, VSMatrix *view)
 {
-	if (gl.legacyMode)
+	for (int i = 0; i < 4; i++)
 	{
-		glMatrixMode(GL_PROJECTION);
-		glLoadMatrixf(proj->get());
-		glMatrixMode(GL_MODELVIEW);
-		glLoadMatrixf(view->get());
+		mTextureEffects[i]->ApplyMatrices(proj, view);
+		mTextureEffectsNAT[i]->ApplyMatrices(proj, view);
 	}
-	else
+	mTextureEffects[4]->ApplyMatrices(proj, view);
+	if (gl_fuzztype != 0)
 	{
-		for (int i = 0; i < 4; i++)
-		{
-			mTextureEffects[i]->ApplyMatrices(proj, view);
-			mTextureEffectsNAT[i]->ApplyMatrices(proj, view);
-		}
-		mTextureEffects[4]->ApplyMatrices(proj, view);
-		if (gl_fuzztype != 0)
-		{
-			mTextureEffects[4 + gl_fuzztype]->ApplyMatrices(proj, view);
-		}
-		for (unsigned i = 12; i < mTextureEffects.Size(); i++)
-		{
-			mTextureEffects[i]->ApplyMatrices(proj, view);
-		}
-		for (int i = 0; i < MAX_EFFECTS; i++)
-		{
-			mEffectShaders[i]->ApplyMatrices(proj, view);
-		}
-		if (mActiveShader != NULL) mActiveShader->Bind();
+		mTextureEffects[4 + gl_fuzztype]->ApplyMatrices(proj, view);
+	}
+	for (unsigned i = 12; i < mTextureEffects.Size(); i++)
+	{
+		mTextureEffects[i]->ApplyMatrices(proj, view);
+	}
+	for (int i = 0; i < MAX_EFFECTS; i++)
+	{
+		mEffectShaders[i]->ApplyMatrices(proj, view);
 	}
 }
 
diff --git a/src/gl/shaders/gl_shader.h b/src/gl/shaders/gl_shader.h
index 5af927e6d..793fa63dd 100644
--- a/src/gl/shaders/gl_shader.h
+++ b/src/gl/shaders/gl_shader.h
@@ -37,6 +37,7 @@ enum
 	VATTR_NORMAL = 4
 };
 
+class FShaderCollection;
 
 //==========================================================================
 //
@@ -248,7 +249,7 @@ public:
 
 class FShader
 {
-	friend class FShaderManager;
+	friend class FShaderCollection;
 	friend class FRenderState;
 
 	unsigned int hShader;
@@ -323,7 +324,6 @@ public:
 
 };
 
-
 //==========================================================================
 //
 // The global shader manager
@@ -331,26 +331,40 @@ public:
 //==========================================================================
 class FShaderManager
 {
-	TArray<FShader*> mTextureEffects;
-	TArray<FShader*> mTextureEffectsNAT;
-	FShader *mActiveShader;
-	FShader *mEffectShaders[MAX_EFFECTS];
-
-	void Clean();
-	void CompileShaders();
-	
 public:
 	FShaderManager();
 	~FShaderManager();
-	FShader *Compile(const char *ShaderName, const char *ShaderPath, bool usediscard);
+
+	void SetActiveShader(FShader *sh);
+	FShader *GetActiveShader() const { return mActiveShader; }
+
+	FShader *BindEffect(int effect, EPassType passType);
+	FShader *Get(unsigned int eff, bool alphateston, EPassType passType);
+	void ApplyMatrices(VSMatrix *proj, VSMatrix *view, EPassType passType);
+
+	void ResetFixedColormap();
+
+private:
+	FShader *mActiveShader = nullptr;
+	TArray<FShaderCollection*> mPassShaders;
+};
+
+class FShaderCollection
+{
+	TArray<FShader*> mTextureEffects;
+	TArray<FShader*> mTextureEffectsNAT;
+	FShader *mEffectShaders[MAX_EFFECTS];
+
+	void Clean();
+	void CompileShaders(EPassType passType);
+	
+public:
+	FShaderCollection(EPassType passType);
+	~FShaderCollection();
+	FShader *Compile(const char *ShaderName, const char *ShaderPath, bool usediscard, EPassType passType);
 	int Find(const char *mame);
 	FShader *BindEffect(int effect);
-	void SetActiveShader(FShader *sh);
 	void ApplyMatrices(VSMatrix *proj, VSMatrix *view);
-	FShader *GetActiveShader() const
-	{
-		return mActiveShader;
-	}
 
 	void ResetFixedColormap()
 	{
diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp
index 84b7f5742..0b66f0569 100644
--- a/wadsrc/static/shaders/glsl/main.fp
+++ b/wadsrc/static/shaders/glsl/main.fp
@@ -5,6 +5,9 @@ in vec4 vTexCoord;
 in vec4 vColor;
 
 out vec4 FragColor;
+#ifdef GBUFFER_PASS
+out vec4 FragData;
+#endif
 
 #ifdef SHADER_STORAGE_LIGHTS
 	layout(std430, binding = 1) buffer LightBufferSSO
@@ -433,5 +436,8 @@ void main()
 		}
 	}
 	FragColor = frag;
+#ifdef GBUFFER_PASS
+	FragData = vec4(uFogColor.rgb, 1.0);
+#endif
 }