This commit is contained in:
Christoph Oelckers 2016-09-25 10:30:04 +02:00
commit 1395a44a75
24 changed files with 1129 additions and 154 deletions

View file

@ -1125,6 +1125,7 @@ set( FASTMATH_SOURCES
gl/shaders/gl_shaderprogram.cpp
gl/shaders/gl_presentshader.cpp
gl/shaders/gl_bloomshader.cpp
gl/shaders/gl_ambientshader.cpp
gl/shaders/gl_blurshader.cpp
gl/shaders/gl_colormapshader.cpp
gl/shaders/gl_tonemapshader.cpp

View file

@ -55,6 +55,7 @@
#include "gl/renderer/gl_postprocessstate.h"
#include "gl/data/gl_data.h"
#include "gl/data/gl_vertexbuffer.h"
#include "gl/shaders/gl_ambientshader.h"
#include "gl/shaders/gl_bloomshader.h"
#include "gl/shaders/gl_blurshader.h"
#include "gl/shaders/gl_tonemapshader.h"
@ -98,6 +99,27 @@ CVAR(Float, gl_lens_k, -0.12f, 0)
CVAR(Float, gl_lens_kcube, 0.1f, 0)
CVAR(Float, gl_lens_chromatic, 1.12f, 0)
CUSTOM_CVAR(Int, gl_ssao, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (self < 0 || self > 3)
self = 0;
}
CUSTOM_CVAR(Int, gl_ssao_portals, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (self < 0)
self = 0;
}
CVAR(Float, gl_ssao_strength, 0.7, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Bool, gl_ssao_debug, false, 0)
CVAR(Float, gl_ssao_bias, 0.5f, 0)
CVAR(Float, gl_ssao_radius, 100.0f, 0)
CUSTOM_CVAR(Float, gl_ssao_blur_amount, 4.0f, 0)
{
if (self < 0.1f) self = 0.1f;
}
EXTERN_CVAR(Float, vid_brightness)
EXTERN_CVAR(Float, vid_contrast)
@ -109,6 +131,136 @@ void FGLRenderer::RenderScreenQuad()
GLRenderer->mVBO->RenderArray(GL_TRIANGLE_STRIP, FFlatVertexBuffer::PRESENT_INDEX, 4);
}
void FGLRenderer::PostProcessScene()
{
mBuffers->BlitSceneToTexture();
UpdateCameraExposure();
BloomScene();
TonemapScene();
ColormapScene();
LensDistortScene();
}
//-----------------------------------------------------------------------------
//
// Adds ambient occlusion to the scene
//
//-----------------------------------------------------------------------------
void FGLRenderer::AmbientOccludeScene()
{
FGLDebug::PushGroup("AmbientOccludeScene");
FGLPostProcessState savedState;
savedState.SaveTextureBinding1();
float bias = gl_ssao_bias;
float aoRadius = gl_ssao_radius;
const float blurAmount = gl_ssao_blur_amount;
float aoStrength = gl_ssao_strength;
bool multisample = gl_multisample > 1;
//float tanHalfFovy = tan(fovy * (M_PI / 360.0f));
float tanHalfFovy = 1.0f / gl_RenderState.mProjectionMatrix.get()[5];
float invFocalLenX = tanHalfFovy * (mBuffers->GetSceneWidth() / (float)mBuffers->GetSceneHeight());
float invFocalLenY = tanHalfFovy;
float nDotVBias = clamp(bias, 0.0f, 1.0f);
float r2 = aoRadius * aoRadius;
float blurSharpness = 1.0f / blurAmount;
// Calculate linear depth values
glBindFramebuffer(GL_FRAMEBUFFER, mBuffers->AmbientFB0);
glViewport(0, 0, mBuffers->AmbientWidth, mBuffers->AmbientHeight);
mBuffers->BindSceneDepthTexture(0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
mBuffers->BindSceneColorTexture(1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glActiveTexture(GL_TEXTURE0);
mLinearDepthShader->Bind(multisample);
mLinearDepthShader->DepthTexture[multisample].Set(0);
mLinearDepthShader->ColorTexture[multisample].Set(1);
if (multisample) mLinearDepthShader->SampleCount[multisample].Set(gl_multisample);
mLinearDepthShader->LinearizeDepthA[multisample].Set(1.0f / GetZFar() - 1.0f / GetZNear());
mLinearDepthShader->LinearizeDepthB[multisample].Set(MAX(1.0f / GetZNear(), 1.e-8f));
mLinearDepthShader->InverseDepthRangeA[multisample].Set(1.0f);
mLinearDepthShader->InverseDepthRangeB[multisample].Set(0.0f);
mLinearDepthShader->Scale[multisample].Set(mBuffers->AmbientWidth * 2.0f / (float)mScreenViewport.width, mBuffers->AmbientHeight * 2.0f / (float)mScreenViewport.height);
mLinearDepthShader->Offset[multisample].Set(mSceneViewport.left / (float)mScreenViewport.width, mSceneViewport.top / (float)mScreenViewport.height);
RenderScreenQuad();
// Apply ambient occlusion
glBindFramebuffer(GL_FRAMEBUFFER, mBuffers->AmbientFB1);
glBindTexture(GL_TEXTURE_2D, mBuffers->AmbientTexture0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, mBuffers->AmbientRandomTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glActiveTexture(GL_TEXTURE0);
mSSAOShader->Bind();
mSSAOShader->DepthTexture.Set(0);
mSSAOShader->RandomTexture.Set(1);
mSSAOShader->UVToViewA.Set(2.0f * invFocalLenX, -2.0f * invFocalLenY);
mSSAOShader->UVToViewB.Set(-invFocalLenX, invFocalLenY);
mSSAOShader->InvFullResolution.Set(1.0f / mBuffers->AmbientWidth, 1.0f / mBuffers->AmbientHeight);
mSSAOShader->NDotVBias.Set(nDotVBias);
mSSAOShader->NegInvR2.Set(-1.0f / r2);
mSSAOShader->RadiusToScreen.Set(aoRadius * 0.5 / tanHalfFovy * mBuffers->AmbientHeight);
mSSAOShader->AOMultiplier.Set(1.0f / (1.0f - nDotVBias));
mSSAOShader->AOStrength.Set(aoStrength);
RenderScreenQuad();
// Blur SSAO texture
glBindFramebuffer(GL_FRAMEBUFFER, mBuffers->AmbientFB0);
glBindTexture(GL_TEXTURE_2D, mBuffers->AmbientTexture1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
mDepthBlurShader->Bind(false);
mDepthBlurShader->BlurSharpness[false].Set(blurSharpness);
mDepthBlurShader->InvFullResolution[false].Set(1.0f / mBuffers->AmbientWidth, 1.0f / mBuffers->AmbientHeight);
RenderScreenQuad();
glBindFramebuffer(GL_FRAMEBUFFER, mBuffers->AmbientFB1);
glBindTexture(GL_TEXTURE_2D, mBuffers->AmbientTexture0);
mDepthBlurShader->Bind(true);
mDepthBlurShader->BlurSharpness[true].Set(blurSharpness);
mDepthBlurShader->InvFullResolution[true].Set(1.0f / mBuffers->AmbientWidth, 1.0f / mBuffers->AmbientHeight);
mDepthBlurShader->PowExponent[true].Set(1.8f);
RenderScreenQuad();
// Add SSAO back to scene texture:
mBuffers->BindSceneFB(false);
GLenum buffers[] = { GL_COLOR_ATTACHMENT0 };
glDrawBuffers(1, buffers);
glViewport(mSceneViewport.left, mSceneViewport.top, mSceneViewport.width, mSceneViewport.height);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
if (gl_ssao_debug)
glBlendFunc(GL_ONE, GL_ZERO);
else
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mBuffers->AmbientTexture1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
mBuffers->BindSceneDataTexture(1);
mSSAOCombineShader->Bind(multisample);
mSSAOCombineShader->AODepthTexture[multisample].Set(0);
mSSAOCombineShader->SceneDataTexture[multisample].Set(1);
if (multisample) mSSAOCombineShader->SampleCount[multisample].Set(gl_multisample);
mSSAOCombineShader->Scale[multisample].Set(mBuffers->AmbientWidth * 2.0f / (float)mScreenViewport.width, mBuffers->AmbientHeight * 2.0f / (float)mScreenViewport.height);
mSSAOCombineShader->Offset[multisample].Set(mSceneViewport.left / (float)mScreenViewport.width, mSceneViewport.top / (float)mScreenViewport.height);
RenderScreenQuad();
FGLDebug::PopGroup();
}
//-----------------------------------------------------------------------------
//
// Extracts light average from the scene and updates the camera exposure texture
@ -190,7 +342,7 @@ void FGLRenderer::UpdateCameraExposure()
void FGLRenderer::BloomScene()
{
// Only bloom things if enabled and no special fixed light mode is active
if (!gl_bloom || gl_fixedcolormap != CM_DEFAULT)
if (!gl_bloom || gl_fixedcolormap != CM_DEFAULT || gl_ssao_debug)
return;
FGLDebug::PushGroup("BloomScene");

View file

@ -40,6 +40,7 @@
#include "w_wad.h"
#include "i_system.h"
#include "doomerrors.h"
#include <random>
CVAR(Int, gl_multisample, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
CVAR(Bool, gl_renderbuffers, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
@ -75,15 +76,16 @@ FGLRenderBuffers::~FGLRenderBuffers()
ClearEyeBuffers();
ClearBloom();
ClearExposureLevels();
ClearAmbientOcclusion();
}
void FGLRenderBuffers::ClearScene()
{
DeleteFrameBuffer(mSceneFB);
DeleteRenderBuffer(mSceneMultisample);
DeleteRenderBuffer(mSceneDepthStencil);
DeleteRenderBuffer(mSceneDepth);
DeleteRenderBuffer(mSceneStencil);
DeleteFrameBuffer(mSceneDataFB);
DeleteTexture(mSceneMultisample);
DeleteTexture(mSceneData);
DeleteTexture(mSceneDepthStencil);
}
void FGLRenderBuffers::ClearPipeline()
@ -132,6 +134,15 @@ void FGLRenderBuffers::ClearEyeBuffers()
mEyeFBs.Clear();
}
void FGLRenderBuffers::ClearAmbientOcclusion()
{
DeleteFrameBuffer(AmbientFB0);
DeleteFrameBuffer(AmbientFB1);
DeleteTexture(AmbientTexture0);
DeleteTexture(AmbientTexture1);
DeleteTexture(AmbientRandomTexture);
}
void FGLRenderBuffers::DeleteTexture(GLuint &handle)
{
if (handle != 0)
@ -203,6 +214,7 @@ bool FGLRenderBuffers::Setup(int width, int height, int sceneWidth, int sceneHei
{
CreateBloom(sceneWidth, sceneHeight);
CreateExposureLevels(sceneWidth, sceneHeight);
CreateAmbientOcclusion(sceneWidth, sceneHeight);
mSceneWidth = sceneWidth;
mSceneHeight = sceneHeight;
}
@ -240,10 +252,19 @@ void FGLRenderBuffers::CreateScene(int width, int height, int samples)
ClearScene();
if (samples > 1)
mSceneMultisample = CreateRenderBuffer("SceneMultisample", GL_RGBA16F, samples, width, height);
{
mSceneMultisample = Create2DMultisampleTexture("SceneMultisample", GL_RGBA16F, width, height, samples, false);
mSceneDepthStencil = Create2DMultisampleTexture("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height, samples, false);
mSceneData = Create2DMultisampleTexture("SceneSSAOData", GL_RGBA8, width, height, samples, false);
}
else
{
mSceneDepthStencil = Create2DTexture("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height);
mSceneData = Create2DTexture("SceneSSAOData", GL_RGBA8, width, height);
}
mSceneDepthStencil = CreateRenderBuffer("SceneDepthStencil", GL_DEPTH24_STENCIL8, samples, width, height);
mSceneFB = CreateFrameBuffer("SceneFB", samples > 1 ? mSceneMultisample : mPipelineTexture[0], mSceneDepthStencil, samples > 1);
mSceneFB = CreateFrameBuffer("SceneFB", samples > 1 ? mSceneMultisample : mPipelineTexture[0], 0, mSceneDepthStencil, samples > 1);
mSceneDataFB = CreateFrameBuffer("SSAOSceneFB", samples > 1 ? mSceneMultisample : mPipelineTexture[0], mSceneData, mSceneDepthStencil, samples > 1);
}
//==========================================================================
@ -296,6 +317,47 @@ void FGLRenderBuffers::CreateBloom(int width, int height)
}
}
//==========================================================================
//
// Creates ambient occlusion working buffers
//
//==========================================================================
void FGLRenderBuffers::CreateAmbientOcclusion(int width, int height)
{
ClearAmbientOcclusion();
if (width <= 0 || height <= 0)
return;
AmbientWidth = width / 2;
AmbientHeight = height / 2;
AmbientTexture0 = Create2DTexture("AmbientTexture0", GL_RG32F, AmbientWidth, AmbientHeight);
AmbientTexture1 = Create2DTexture("AmbientTexture1", GL_RG32F, AmbientWidth, AmbientHeight);
AmbientFB0 = CreateFrameBuffer("AmbientFB0", AmbientTexture0);
AmbientFB1 = CreateFrameBuffer("AmbientFB1", AmbientTexture1);
int16_t randomValues[16 * 4];
std::mt19937 generator(1337);
std::uniform_real_distribution<double> distribution(-1.0, 1.0);
for (int i = 0; i < 16; i++)
{
double num_directions = 8.0; // Must be same as the define in ssao.fp
double angle = 2.0 * M_PI * distribution(generator) / num_directions;
double x = cos(angle);
double y = sin(angle);
double z = distribution(generator);
double w = distribution(generator);
randomValues[i * 4 + 0] = (int16_t)clamp(x * 32767.0, -32768.0, 32767.0);
randomValues[i * 4 + 1] = (int16_t)clamp(y * 32767.0, -32768.0, 32767.0);
randomValues[i * 4 + 2] = (int16_t)clamp(z * 32767.0, -32768.0, 32767.0);
randomValues[i * 4 + 3] = (int16_t)clamp(w * 32767.0, -32768.0, 32767.0);
}
AmbientRandomTexture = Create2DTexture("AmbientRandomTexture", GL_RGBA16_SNORM, 4, 4, randomValues);
}
//==========================================================================
//
// Creates camera exposure level buffers
@ -368,12 +430,28 @@ void FGLRenderBuffers::CreateEyeBuffers(int eye)
GLuint FGLRenderBuffers::Create2DTexture(const FString &name, GLuint format, int width, int height, const void *data)
{
GLuint type = (format == GL_RGBA16F || format == GL_R32F) ? GL_FLOAT : GL_UNSIGNED_BYTE;
GLuint handle = 0;
glGenTextures(1, &handle);
glBindTexture(GL_TEXTURE_2D, handle);
FGLDebug::LabelObject(GL_TEXTURE, handle, name);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format != GL_R32F ? GL_RGBA : GL_RED, type, data);
GLenum dataformat, datatype;
switch (format)
{
case GL_RGBA8: dataformat = GL_RGBA; datatype = GL_UNSIGNED_BYTE; break;
case GL_RGBA16: dataformat = GL_RGBA; datatype = GL_UNSIGNED_SHORT; break;
case GL_RGBA16F: dataformat = GL_RGBA; datatype = GL_FLOAT; break;
case GL_RGBA32F: dataformat = GL_RGBA; datatype = GL_FLOAT; break;
case GL_R32F: dataformat = GL_RED; datatype = GL_FLOAT; break;
case GL_RG32F: dataformat = GL_RG; datatype = GL_FLOAT; break;
case GL_DEPTH_COMPONENT24: dataformat = GL_DEPTH_COMPONENT; datatype = GL_FLOAT; break;
case GL_STENCIL_INDEX8: dataformat = GL_STENCIL_INDEX; datatype = GL_INT; break;
case GL_DEPTH24_STENCIL8: dataformat = GL_DEPTH_STENCIL; datatype = GL_UNSIGNED_INT_24_8; break;
case GL_RGBA16_SNORM: dataformat = GL_RGBA; datatype = GL_SHORT; break;
default: I_FatalError("Unknown format passed to FGLRenderBuffers.Create2DTexture");
}
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, dataformat, datatype, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
@ -381,6 +459,17 @@ GLuint FGLRenderBuffers::Create2DTexture(const FString &name, GLuint format, int
return handle;
}
GLuint FGLRenderBuffers::Create2DMultisampleTexture(const FString &name, GLuint format, int width, int height, int samples, bool fixedSampleLocations)
{
GLuint handle = 0;
glGenTextures(1, &handle);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, handle);
FGLDebug::LabelObject(GL_TEXTURE, handle, name);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, format, width, height, fixedSampleLocations);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
return handle;
}
//==========================================================================
//
// Creates a render buffer
@ -428,34 +517,26 @@ GLuint FGLRenderBuffers::CreateFrameBuffer(const FString &name, GLuint colorbuff
return handle;
}
GLuint FGLRenderBuffers::CreateFrameBuffer(const FString &name, GLuint colorbuffer, GLuint depthstencil, bool colorIsARenderBuffer)
GLuint FGLRenderBuffers::CreateFrameBuffer(const FString &name, GLuint colorbuffer0, GLuint colorbuffer1, GLuint depthstencil, bool multisample)
{
GLuint handle = 0;
glGenFramebuffers(1, &handle);
glBindFramebuffer(GL_FRAMEBUFFER, handle);
FGLDebug::LabelObject(GL_FRAMEBUFFER, handle, name);
if (colorIsARenderBuffer)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuffer);
if (multisample)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, colorbuffer0, 0);
if (colorbuffer1 != 0)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D_MULTISAMPLE, colorbuffer1, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, depthstencil, 0);
}
else
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthstencil);
if (CheckFrameBufferCompleteness())
ClearFrameBuffer(true, true);
return handle;
}
GLuint FGLRenderBuffers::CreateFrameBuffer(const FString &name, GLuint colorbuffer, GLuint depth, GLuint stencil, bool colorIsARenderBuffer)
{
GLuint handle = 0;
glGenFramebuffers(1, &handle);
glBindFramebuffer(GL_FRAMEBUFFER, handle);
FGLDebug::LabelObject(GL_FRAMEBUFFER, handle, name);
if (colorIsARenderBuffer)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuffer);
else
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, stencil);
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer0, 0);
if (colorbuffer1 != 0)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, colorbuffer1, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthstencil, 0);
}
if (CheckFrameBufferCompleteness())
ClearFrameBuffer(true, true);
return handle;
@ -475,22 +556,23 @@ bool FGLRenderBuffers::CheckFrameBufferCompleteness()
FailedCreate = true;
#if 0
FString error = "glCheckFramebufferStatus failed: ";
switch (result)
if (gl_debug_level > 0)
{
default: error.AppendFormat("error code %d", (int)result); break;
case GL_FRAMEBUFFER_UNDEFINED: error << "GL_FRAMEBUFFER_UNDEFINED"; break;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: error << "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: error << "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: error << "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"; break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: error << "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"; break;
case GL_FRAMEBUFFER_UNSUPPORTED: error << "GL_FRAMEBUFFER_UNSUPPORTED"; break;
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: error << "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; break;
case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: error << "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS"; break;
FString error = "glCheckFramebufferStatus failed: ";
switch (result)
{
default: error.AppendFormat("error code %d", (int)result); break;
case GL_FRAMEBUFFER_UNDEFINED: error << "GL_FRAMEBUFFER_UNDEFINED"; break;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: error << "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: error << "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: error << "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"; break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: error << "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"; break;
case GL_FRAMEBUFFER_UNSUPPORTED: error << "GL_FRAMEBUFFER_UNSUPPORTED"; break;
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: error << "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; break;
case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: error << "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS"; break;
}
Printf("%s\n", error.GetChars());
}
I_FatalError(error);
#endif
return false;
}
@ -595,9 +677,54 @@ void FGLRenderBuffers::BindEyeFB(int eye, bool readBuffer)
//
//==========================================================================
void FGLRenderBuffers::BindSceneFB()
void FGLRenderBuffers::BindSceneFB(bool sceneData)
{
glBindFramebuffer(GL_FRAMEBUFFER, mSceneFB);
glBindFramebuffer(GL_FRAMEBUFFER, sceneData ? mSceneDataFB : mSceneFB);
}
//==========================================================================
//
// Binds the scene color texture to the specified texture unit
//
//==========================================================================
void FGLRenderBuffers::BindSceneColorTexture(int index)
{
glActiveTexture(GL_TEXTURE0 + index);
if (mSamples > 1)
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mSceneMultisample);
else
glBindTexture(GL_TEXTURE_2D, mPipelineTexture[0]);
}
//==========================================================================
//
// Binds the scene data texture to the specified texture unit
//
//==========================================================================
void FGLRenderBuffers::BindSceneDataTexture(int index)
{
glActiveTexture(GL_TEXTURE0 + index);
if (mSamples > 1)
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mSceneData);
else
glBindTexture(GL_TEXTURE_2D, mSceneData);
}
//==========================================================================
//
// Binds the depth texture to the specified texture unit
//
//==========================================================================
void FGLRenderBuffers::BindSceneDepthTexture(int index)
{
glActiveTexture(GL_TEXTURE0 + index);
if (mSamples > 1)
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mSceneDepthStencil);
else
glBindTexture(GL_TEXTURE_2D, mSceneDepthStencil);
}
//==========================================================================

View file

@ -31,7 +31,10 @@ public:
bool Setup(int width, int height, int sceneWidth, int sceneHeight);
void BindSceneFB();
void BindSceneFB(bool sceneData);
void BindSceneColorTexture(int index);
void BindSceneDataTexture(int index);
void BindSceneDepthTexture(int index);
void BlitSceneToTexture();
void BindCurrentTexture(int index);
@ -53,28 +56,42 @@ public:
GLuint ExposureFB = 0;
bool FirstExposureFrame = true;
// Ambient occlusion buffers
GLuint AmbientTexture0 = 0;
GLuint AmbientTexture1 = 0;
GLuint AmbientFB0 = 0;
GLuint AmbientFB1 = 0;
int AmbientWidth = 0;
int AmbientHeight = 0;
GLuint AmbientRandomTexture = 0;
static bool IsEnabled();
int GetWidth() const { return mWidth; }
int GetHeight() const { return mHeight; }
int GetSceneWidth() const { return mSceneWidth; }
int GetSceneHeight() const { return mSceneHeight; }
private:
void ClearScene();
void ClearPipeline();
void ClearEyeBuffers();
void ClearBloom();
void ClearExposureLevels();
void ClearAmbientOcclusion();
void CreateScene(int width, int height, int samples);
void CreatePipeline(int width, int height);
void CreateBloom(int width, int height);
void CreateExposureLevels(int width, int height);
void CreateEyeBuffers(int eye);
void CreateAmbientOcclusion(int width, int height);
GLuint Create2DTexture(const FString &name, GLuint format, int width, int height, const void *data = nullptr);
GLuint Create2DMultisampleTexture(const FString &name, GLuint format, int width, int height, int samples, bool fixedSampleLocations);
GLuint CreateRenderBuffer(const FString &name, GLuint format, int width, int height);
GLuint CreateRenderBuffer(const FString &name, GLuint format, int samples, int width, int height);
GLuint CreateFrameBuffer(const FString &name, GLuint colorbuffer);
GLuint CreateFrameBuffer(const FString &name, GLuint colorbuffer, GLuint depthstencil, bool colorIsARenderBuffer);
GLuint CreateFrameBuffer(const FString &name, GLuint colorbuffer, GLuint depth, GLuint stencil, bool colorIsARenderBuffer);
GLuint CreateFrameBuffer(const FString &name, GLuint colorbuffer0, GLuint colorbuffer1, GLuint depthstencil, bool multisample);
bool CheckFrameBufferCompleteness();
void ClearFrameBuffer(bool stencil, bool depth);
void DeleteTexture(GLuint &handle);
@ -94,9 +111,9 @@ private:
// Buffers for the scene
GLuint mSceneMultisample = 0;
GLuint mSceneDepthStencil = 0;
GLuint mSceneDepth = 0;
GLuint mSceneStencil = 0;
GLuint mSceneData = 0;
GLuint mSceneFB = 0;
GLuint mSceneDataFB = 0;
// Effect/HUD buffers
GLuint mPipelineTexture[NumPipelineTextures];

View file

@ -51,6 +51,7 @@
#include "gl/data/gl_vertexbuffer.h"
#include "gl/scene/gl_drawinfo.h"
#include "gl/shaders/gl_shader.h"
#include "gl/shaders/gl_ambientshader.h"
#include "gl/shaders/gl_bloomshader.h"
#include "gl/shaders/gl_blurshader.h"
#include "gl/shaders/gl_tonemapshader.h"
@ -113,6 +114,10 @@ FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb)
mTonemapPalette = nullptr;
mColormapShader = nullptr;
mLensShader = nullptr;
mLinearDepthShader = nullptr;
mDepthBlurShader = nullptr;
mSSAOShader = nullptr;
mSSAOCombineShader = nullptr;
}
void gl_LoadModels();
@ -121,6 +126,10 @@ void gl_FlushModels();
void FGLRenderer::Initialize(int width, int height)
{
mBuffers = new FGLRenderBuffers();
mLinearDepthShader = new FLinearDepthShader();
mDepthBlurShader = new FDepthBlurShader();
mSSAOShader = new FSSAOShader();
mSSAOCombineShader = new FSSAOCombineShader();
mBloomExtractShader = new FBloomExtractShader();
mBloomCombineShader = new FBloomCombineShader();
mExposureExtractShader = new FExposureExtractShader();
@ -184,6 +193,10 @@ FGLRenderer::~FGLRenderer()
}
if (mBuffers) delete mBuffers;
if (mPresentShader) delete mPresentShader;
if (mLinearDepthShader) delete mLinearDepthShader;
if (mDepthBlurShader) delete mDepthBlurShader;
if (mSSAOShader) delete mSSAOShader;
if (mSSAOCombineShader) delete mSSAOCombineShader;
if (mBloomExtractShader) delete mBloomExtractShader;
if (mBloomCombineShader) delete mBloomCombineShader;
if (mExposureExtractShader) delete mExposureExtractShader;
@ -310,7 +323,7 @@ void FGLRenderer::Begin2D()
if (mBuffers->Setup(mScreenViewport.width, mScreenViewport.height, mSceneViewport.width, mSceneViewport.height))
{
if (mDrawingScene2D)
mBuffers->BindSceneFB();
mBuffers->BindSceneFB(false);
else
mBuffers->BindCurrentFB();
}

View file

@ -19,6 +19,10 @@ class FLightBuffer;
class FSamplerManager;
class DPSprite;
class FGLRenderBuffers;
class FLinearDepthShader;
class FDepthBlurShader;
class FSSAOShader;
class FSSAOCombineShader;
class FBloomExtractShader;
class FBloomCombineShader;
class FExposureExtractShader;
@ -93,6 +97,10 @@ public:
int mOldFBID;
FGLRenderBuffers *mBuffers;
FLinearDepthShader *mLinearDepthShader;
FSSAOShader *mSSAOShader;
FDepthBlurShader *mDepthBlurShader;
FSSAOCombineShader *mSSAOCombineShader;
FBloomExtractShader *mBloomExtractShader;
FBloomCombineShader *mBloomCombineShader;
FExposureExtractShader *mExposureExtractShader;
@ -172,6 +180,8 @@ public:
void WriteSavePic (player_t *player, FileWriter *file, int width, int height);
void EndDrawScene(sector_t * viewsector);
void UpdateCameraExposure();
void PostProcessScene();
void AmbientOccludeScene();
void BloomScene();
void TonemapScene();
void ColormapScene();
@ -198,6 +208,9 @@ public:
DAngle rotation, FDynamicColormap *colormap, int lightlevel);
int PTM_BestColor (const uint32 *pal_in, int r, int g, int b, int first, int num);
static float GetZNear() { return 5.f; }
static float GetZFar() { return 65536.f; }
};
// Global functions. Make them members of GLRenderer later?

View file

@ -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();
}
@ -342,7 +343,7 @@ void FRenderState::ApplyMatrices()
{
if (GLRenderer->mShaderManager != NULL)
{
GLRenderer->mShaderManager->ApplyMatrices(&mProjectionMatrix, &mViewMatrix);
GLRenderer->mShaderManager->ApplyMatrices(&mProjectionMatrix, &mViewMatrix, mPassType);
}
}

View file

@ -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();

View file

@ -423,14 +423,16 @@ void GLPortal::End(bool usestencil)
glDepthFunc(GL_LEQUAL);
glDepthRange(0, 1);
{
ScopedColorMask colorMask(0, 0, 0, 0);
// glColorMask(0,0,0,0); // no graphics
ScopedColorMask colorMask(0, 0, 0, 1); // mark portal in alpha channel but don't touch color
gl_RenderState.SetEffect(EFF_STENCIL);
gl_RenderState.EnableTexture(false);
gl_RenderState.BlendFunc(GL_ONE, GL_ZERO);
gl_RenderState.BlendEquation(GL_FUNC_ADD);
gl_RenderState.Apply();
DrawPortalStencil();
gl_RenderState.SetEffect(EFF_NONE);
gl_RenderState.EnableTexture(true);
} // glColorMask(1, 1, 1, 1);
}
glDepthFunc(GL_LESS);
}
PortalAll.Unclock();

View file

@ -158,7 +158,12 @@ void FGLRenderer::Set3DViewport(bool mainview)
{
if (mainview && mBuffers->Setup(mScreenViewport.width, mScreenViewport.height, mSceneViewport.width, mSceneViewport.height))
{
mBuffers->BindSceneFB();
bool useSSAO = (gl_ssao != 0);
mBuffers->BindSceneFB(useSSAO);
GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
glDrawBuffers(useSSAO ? 2 : 1, buffers);
gl_RenderState.SetPassType(useSSAO ? GBUFFER_PASS : NORMAL_PASS);
gl_RenderState.Apply();
}
// Always clear all buffers with scissor test disabled.
@ -209,7 +214,7 @@ void FGLRenderer::SetProjection(float fov, float ratio, float fovratio)
{
float fovy = 2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovratio));
gl_RenderState.mProjectionMatrix.perspective(fovy, ratio, 5.f, 65536.f);
gl_RenderState.mProjectionMatrix.perspective(fovy, ratio, GetZNear(), GetZFar());
}
// raw matrix input from stereo 3d modes
@ -472,6 +477,7 @@ void FGLRenderer::RenderTranslucent()
void FGLRenderer::DrawScene(int drawmode)
{
static int recursion=0;
static int ssao_portals_available = 0;
if (camera != nullptr)
{
@ -489,8 +495,39 @@ void FGLRenderer::DrawScene(int drawmode)
}
GLRenderer->mClipPortal = NULL; // this must be reset before any portal recursion takes place.
// Decide if we need to do ssao for this scene
bool applySSAO = gl_ssao != 0 && FGLRenderBuffers::IsEnabled();
switch (drawmode)
{
case DM_MAINVIEW: ssao_portals_available = gl_ssao_portals; break;
case DM_OFFSCREEN: ssao_portals_available = 0; applySSAO = false; break;
case DM_PORTAL: applySSAO = applySSAO && (ssao_portals_available > 0); ssao_portals_available--; break;
}
// If SSAO is active, switch to gbuffer shaders and use the framebuffer with gbuffers
if (applySSAO)
{
GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
glDrawBuffers(2, buffers);
gl_RenderState.SetPassType(GBUFFER_PASS);
gl_RenderState.Apply();
gl_RenderState.ApplyMatrices();
}
RenderScene(recursion);
// Apply ambient occlusion and switch back to shaders without gbuffer output
if (applySSAO)
{
GLenum buffers[] = { GL_COLOR_ATTACHMENT0 };
glDrawBuffers(1, buffers);
AmbientOccludeScene();
mBuffers->BindSceneFB(true);
gl_RenderState.SetPassType(NORMAL_PASS);
gl_RenderState.Apply();
gl_RenderState.ApplyMatrices();
}
// Handle all portals after rendering the opaque objects but before
// doing all translucent stuff
recursion++;
@ -826,12 +863,7 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo
if (mainview && toscreen) EndDrawScene(lviewsector); // do not call this for camera textures.
if (mainview && FGLRenderBuffers::IsEnabled())
{
mBuffers->BlitSceneToTexture();
UpdateCameraExposure();
BloomScene();
TonemapScene();
ColormapScene();
LensDistortScene();
PostProcessScene();
// This should be done after postprocessing, not before.
mBuffers->BindCurrentFB();

View file

@ -0,0 +1,137 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2016 Magnus Norddahl
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
#include "gl/system/gl_system.h"
#include "files.h"
#include "m_swap.h"
#include "v_video.h"
#include "gl/gl_functions.h"
#include "vectors.h"
#include "gl/system/gl_interface.h"
#include "gl/system/gl_framebuffer.h"
#include "gl/system/gl_cvars.h"
#include "gl/shaders/gl_ambientshader.h"
void FLinearDepthShader::Bind(bool multisample)
{
auto &shader = mShader[multisample];
if (!shader)
{
shader.Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", 330);
shader.Compile(FShaderProgram::Fragment, "shaders/glsl/lineardepth.fp", multisample ? "#define MULTISAMPLE\n" : "", 330);
shader.SetFragDataLocation(0, "FragColor");
shader.Link("shaders/glsl/lineardepth");
shader.SetAttribLocation(0, "PositionInProjection");
DepthTexture[multisample].Init(shader, "DepthTexture");
ColorTexture[multisample].Init(shader, "ColorTexture");
SampleCount[multisample].Init(shader, "SampleCount");
LinearizeDepthA[multisample].Init(shader, "LinearizeDepthA");
LinearizeDepthB[multisample].Init(shader, "LinearizeDepthB");
InverseDepthRangeA[multisample].Init(shader, "InverseDepthRangeA");
InverseDepthRangeB[multisample].Init(shader, "InverseDepthRangeB");
Scale[multisample].Init(shader, "Scale");
Offset[multisample].Init(shader, "Offset");
}
shader.Bind();
}
void FSSAOShader::Bind()
{
auto &shader = mShader[gl_ssao];
if (!shader)
{
shader.Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", 330);
shader.Compile(FShaderProgram::Fragment, "shaders/glsl/ssao.fp", GetDefines(gl_ssao), 330);
shader.SetFragDataLocation(0, "FragColor");
shader.Link("shaders/glsl/ssao");
shader.SetAttribLocation(0, "PositionInProjection");
DepthTexture.Init(shader, "DepthTexture");
RandomTexture.Init(shader, "RandomTexture");
UVToViewA.Init(shader, "UVToViewA");
UVToViewB.Init(shader, "UVToViewB");
InvFullResolution.Init(shader, "InvFullResolution");
NDotVBias.Init(shader, "NDotVBias");
NegInvR2.Init(shader, "NegInvR2");
RadiusToScreen.Init(shader, "RadiusToScreen");
AOMultiplier.Init(shader, "AOMultiplier");
AOStrength.Init(shader, "AOStrength");
}
shader.Bind();
}
FString FSSAOShader::GetDefines(int mode)
{
int numDirections, numSteps;
switch (gl_ssao)
{
default:
case LowQuality: numDirections = 2; numSteps = 4; break;
case MediumQuality: numDirections = 4; numSteps = 4; break;
case HighQuality: numDirections = 8; numSteps = 4; break;
}
FString defines;
defines.Format(R"(
#define USE_RANDOM_TEXTURE
#define RANDOM_TEXTURE_WIDTH 4.0
#define NUM_DIRECTIONS %d.0
#define NUM_STEPS %d.0
)", numDirections, numSteps);
return defines;
}
void FDepthBlurShader::Bind(bool vertical)
{
auto &shader = mShader[vertical];
if (!shader)
{
shader.Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", 330);
shader.Compile(FShaderProgram::Fragment, "shaders/glsl/depthblur.fp", vertical ? "#define BLUR_VERTICAL\n" : "#define BLUR_HORIZONTAL\n", 330);
shader.SetFragDataLocation(0, "FragColor");
shader.Link("shaders/glsl/depthblur");
shader.SetAttribLocation(0, "PositionInProjection");
AODepthTexture[vertical].Init(shader, "AODepthTexture");
BlurSharpness[vertical].Init(shader, "BlurSharpness");
InvFullResolution[vertical].Init(shader, "InvFullResolution");
PowExponent[vertical].Init(shader, "PowExponent");
}
shader.Bind();
}
void FSSAOCombineShader::Bind(bool multisample)
{
auto &shader = mShader[multisample];
if (!shader)
{
shader.Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", 330);
shader.Compile(FShaderProgram::Fragment, "shaders/glsl/ssaocombine.fp", multisample ? "#define MULTISAMPLE\n" : "", 330);
shader.SetFragDataLocation(0, "FragColor");
shader.Link("shaders/glsl/ssaocombine");
shader.SetAttribLocation(0, "PositionInProjection");
AODepthTexture[multisample].Init(shader, "AODepthTexture");
SceneDataTexture[multisample].Init(shader, "SceneDataTexture");
SampleCount[multisample].Init(shader, "SampleCount");
Scale[multisample].Init(shader, "Scale");
Offset[multisample].Init(shader, "Offset");
}
shader.Bind();
}

View file

@ -0,0 +1,85 @@
#ifndef __GL_AMBIENTSHADER_H
#define __GL_AMBIENTSHADER_H
#include "gl_shaderprogram.h"
class FLinearDepthShader
{
public:
void Bind(bool multisample);
FBufferedUniformSampler DepthTexture[2];
FBufferedUniformSampler ColorTexture[2];
FBufferedUniform1i SampleCount[2];
FBufferedUniform1f LinearizeDepthA[2];
FBufferedUniform1f LinearizeDepthB[2];
FBufferedUniform1f InverseDepthRangeA[2];
FBufferedUniform1f InverseDepthRangeB[2];
FBufferedUniform2f Scale[2];
FBufferedUniform2f Offset[2];
private:
FShaderProgram mShader[2];
};
class FSSAOShader
{
public:
void Bind();
FBufferedUniformSampler DepthTexture;
FBufferedUniformSampler RandomTexture;
FBufferedUniform2f UVToViewA;
FBufferedUniform2f UVToViewB;
FBufferedUniform2f InvFullResolution;
FBufferedUniform1f NDotVBias;
FBufferedUniform1f NegInvR2;
FBufferedUniform1f RadiusToScreen;
FBufferedUniform1f AOMultiplier;
FBufferedUniform1f AOStrength;
private:
enum Quality
{
Off,
LowQuality,
MediumQuality,
HighQuality,
NumQualityModes
};
FString GetDefines(int mode);
FShaderProgram mShader[NumQualityModes];
};
class FDepthBlurShader
{
public:
void Bind(bool vertical);
FBufferedUniformSampler AODepthTexture[2];
FBufferedUniform1f BlurSharpness[2];
FBufferedUniform2f InvFullResolution[2];
FBufferedUniform1f PowExponent[2];
private:
FShaderProgram mShader[2];
};
class FSSAOCombineShader
{
public:
void Bind(bool multisample);
FBufferedUniformSampler AODepthTexture[2];
FBufferedUniformSampler SceneDataTexture[2];
FBufferedUniform1i SampleCount[2];
FBufferedUniform2f Scale[2];
FBufferedUniform2f Offset[2];
private:
FShaderProgram mShader[2];
};
#endif

View file

@ -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);
@ -297,12 +300,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
@ -385,27 +389,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();
}
//==========================================================================
@ -414,10 +466,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++)
@ -427,11 +499,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);
}
}
@ -441,7 +513,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);
}
@ -463,11 +535,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];
@ -491,7 +560,7 @@ void FShaderManager::Clean()
//
//==========================================================================
int FShaderManager::Find(const char * shn)
int FShaderCollection::Find(const char * shn)
{
FName sfn = shn;
@ -505,21 +574,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;
}
}
//==========================================================================
//
@ -527,7 +581,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)
{
@ -545,36 +599,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);
}
}

View file

@ -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;
@ -322,7 +323,6 @@ public:
};
//==========================================================================
//
// The global shader manager
@ -330,26 +330,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()
{

View file

@ -41,7 +41,7 @@ VSMatrix EyePose::GetProjection(float fov, float aspectRatio, float fovRatio) co
// Lifted from gl_scene.cpp FGLRenderer::SetProjection()
float fovy = (float)(2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovRatio)));
result.perspective(fovy, aspectRatio, 5.f, 65536.f);
result.perspective(fovy, aspectRatio, FGLRenderer::GetZNear(), FGLRenderer::GetZFar());
return result;
}

View file

@ -50,6 +50,13 @@ EXTERN_CVAR(Bool, gl_lens)
EXTERN_CVAR(Float, gl_lens_k)
EXTERN_CVAR(Float, gl_lens_kcube)
EXTERN_CVAR(Float, gl_lens_chromatic)
EXTERN_CVAR(Int, gl_ssao)
EXTERN_CVAR(Int, gl_ssao_portals)
EXTERN_CVAR(Float, gl_ssao_strength)
EXTERN_CVAR(Bool, gl_ssao_debug)
EXTERN_CVAR(Float, gl_ssao_bias)
EXTERN_CVAR(Float, gl_ssao_radius)
EXTERN_CVAR(Float, gl_ssao_blur_amount)
EXTERN_CVAR(Int, gl_debug_level)
EXTERN_CVAR(Bool, gl_debug_breakpoint)

View file

@ -2167,6 +2167,9 @@ NETMNU_TICBALANCE = "Latency balancing";
// Option Values
OPTVAL_OFF = "Off";
OPTVAL_ON = "On";
OPTVAL_LOW = "Low";
OPTVAL_MEDIUM = "Medium";
OPTVAL_HIGH = "High";
OPTVAL_MALE = "Male";
OPTVAL_FEMALE = "Female";
OPTVAL_OTHER = "Other";
@ -2633,6 +2636,8 @@ GLPREFMNU_MULTISAMPLE = "Multisample";
GLPREFMNU_TONEMAP = "Tonemap Mode";
GLPREFMNU_BLOOM = "Bloom effect";
GLPREFMNU_LENS = "Lens distortion effect";
GLPREFMNU_SSAO = "Ambient occlusion quality";
GLPREFMNU_SSAO_PORTALS = "Portals with AO";
// Option Values
OPTVAL_SMART = "Smart";

View file

@ -42,6 +42,14 @@ OptionValue "TonemapModes"
5, "$OPTVAL_PALETTE"
}
OptionValue "SSAOModes"
{
0, "$OPTVAL_OFF"
1, "$OPTVAL_LOW"
2, "$OPTVAL_MEDIUM"
3, "$OPTVAL_HIGH"
}
OptionValue "TextureFormats"
{
0, "$OPTVAL_RGBA8"
@ -226,4 +234,6 @@ OptionMenu "GLPrefOptions"
Option "$GLPREFMNU_TONEMAP", gl_tonemap, "TonemapModes"
Option "$GLPREFMNU_BLOOM", gl_bloom, "OnOff"
Option "$GLPREFMNU_LENS", gl_lens, "OnOff"
Option "$GLPREFMNU_SSAO", gl_ssao, "SSAOModes"
Slider "$GLPREFMNU_SSAO_PORTALS", gl_ssao_portals, 0.0, 4.0, 1.0, 0
}

View file

@ -0,0 +1,69 @@
in vec2 TexCoord;
out vec4 FragColor;
uniform sampler2D AODepthTexture;
uniform float BlurSharpness;
uniform vec2 InvFullResolution;
uniform float PowExponent;
#define KERNEL_RADIUS 7.0
float CrossBilateralWeight(float r, float sampleDepth, float centerDepth)
{
const float blurSigma = KERNEL_RADIUS * 0.5;
const float blurFalloff = 1.0 / (2.0 * blurSigma * blurSigma);
float deltaZ = (sampleDepth - centerDepth) * BlurSharpness;
return exp2(-r * r * blurFalloff - deltaZ * deltaZ);
}
void ProcessSample(float ao, float z, float r, float centerDepth, inout float totalAO, inout float totalW)
{
float w = CrossBilateralWeight(r, z, centerDepth);
totalAO += w * ao;
totalW += w;
}
void ProcessRadius(vec2 deltaUV, float centerDepth, inout float totalAO, inout float totalW)
{
for (float r = 1; r <= KERNEL_RADIUS; r += 1.0)
{
vec2 uv = r * deltaUV + TexCoord;
vec2 aoZ = texture(AODepthTexture, uv).xy;
ProcessSample(aoZ.x, aoZ.y, r, centerDepth, totalAO, totalW);
}
}
vec2 ComputeBlur(vec2 deltaUV)
{
vec2 aoZ = texture(AODepthTexture, TexCoord).xy;
float totalAO = aoZ.x;
float totalW = 1.0;
ProcessRadius(deltaUV, aoZ.y, totalAO, totalW);
ProcessRadius(-deltaUV, aoZ.y, totalAO, totalW);
return vec2(totalAO / totalW, aoZ.y);
}
vec2 BlurX()
{
return ComputeBlur(vec2(InvFullResolution.x, 0.0));
}
float BlurY()
{
return pow(clamp(ComputeBlur(vec2(0.0, InvFullResolution.y)).x, 0.0, 1.0), PowExponent);
}
void main()
{
#if defined(BLUR_HORIZONTAL)
FragColor = vec4(BlurX(), 0.0, 1.0);
#else
FragColor = vec4(BlurY(), 0.0, 0.0, 1.0);
#endif
}

View file

@ -0,0 +1,46 @@
in vec2 TexCoord;
out vec4 FragColor;
#if defined(MULTISAMPLE)
uniform sampler2DMS DepthTexture;
uniform sampler2DMS ColorTexture;
uniform int SampleCount;
#else
uniform sampler2D DepthTexture;
uniform sampler2D ColorTexture;
#endif
uniform float LinearizeDepthA;
uniform float LinearizeDepthB;
uniform float InverseDepthRangeA;
uniform float InverseDepthRangeB;
uniform vec2 Scale;
uniform vec2 Offset;
void main()
{
vec2 uv = Offset + TexCoord * Scale;
#if defined(MULTISAMPLE)
ivec2 texSize = textureSize(DepthTexture);
#else
ivec2 texSize = textureSize(DepthTexture, 0);
#endif
// Use floor here because as we downscale the sampling error has to remain uniform to prevent
// noise in the depth values.
ivec2 ipos = ivec2(max(floor(uv * vec2(texSize) - 0.75), vec2(0.0)));
#if defined(MULTISAMPLE)
float depth = 0.0;
for (int i = 0; i < SampleCount; i++)
depth += texelFetch(ColorTexture, ipos, i).a != 0.0 ? texelFetch(DepthTexture, ipos, i).x : 1.0;
depth /= float(SampleCount);
#else
float depth = texelFetch(ColorTexture, ipos, 0).a != 0.0 ? texelFetch(DepthTexture, ipos, 0).x : 1.0;
#endif
float normalizedDepth = clamp(InverseDepthRangeA * depth + InverseDepthRangeB, 0.0, 1.0);
FragColor = vec4(1.0 / (normalizedDepth * LinearizeDepthA + LinearizeDepthB), 0.0, 0.0, 1.0);
}

View file

@ -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
@ -230,6 +233,32 @@ vec4 applyFog(vec4 frag, float fogfactor)
return vec4(mix(uFogColor.rgb, frag.rgb, fogfactor), frag.a);
}
//===========================================================================
//
// The color of the fragment if it is fully occluded by ambient lighting
//
//===========================================================================
vec3 AmbientOcclusionColor()
{
float fogdist;
float fogfactor;
//
// calculate fog factor
//
if (uFogEnabled == -1)
{
fogdist = pixelpos.w;
}
else
{
fogdist = max(16.0, length(pixelpos.xyz));
}
fogfactor = exp2 (uFogDensity * fogdist);
return mix(uFogColor.rgb, vec3(0.0), fogfactor);
}
//===========================================================================
//
@ -345,5 +374,8 @@ void main()
}
}
FragColor = frag;
#ifdef GBUFFER_PASS
FragData = vec4(AmbientOcclusionColor(), 1.0);
#endif
}

View file

@ -0,0 +1,112 @@
in vec2 TexCoord;
out vec4 FragColor;
uniform vec2 UVToViewA;
uniform vec2 UVToViewB;
uniform vec2 InvFullResolution;
uniform float NDotVBias;
uniform float NegInvR2;
uniform float RadiusToScreen;
uniform float AOMultiplier;
uniform float AOStrength;
uniform sampler2D DepthTexture;
#if defined(USE_RANDOM_TEXTURE)
uniform sampler2D RandomTexture;
#endif
#define PI 3.14159265358979323846
// Calculate eye space position for the specified texture coordinate
vec3 FetchViewPos(vec2 uv)
{
float z = texture(DepthTexture, uv).x;
return vec3((UVToViewA * uv + UVToViewB) * z, z);
}
vec3 MinDiff(vec3 p, vec3 pr, vec3 pl)
{
vec3 v1 = pr - p;
vec3 v2 = p - pl;
return (dot(v1, v1) < dot(v2, v2)) ? v1 : v2;
}
// Reconstruct eye space normal from nearest neighbors
vec3 ReconstructNormal(vec3 p)
{
vec3 pr = FetchViewPos(TexCoord + vec2(InvFullResolution.x, 0));
vec3 pl = FetchViewPos(TexCoord + vec2(-InvFullResolution.x, 0));
vec3 pt = FetchViewPos(TexCoord + vec2(0, InvFullResolution.y));
vec3 pb = FetchViewPos(TexCoord + vec2(0, -InvFullResolution.y));
return normalize(cross(MinDiff(p, pr, pl), MinDiff(p, pt, pb)));
}
// Compute normalized 2D direction
vec2 RotateDirection(vec2 dir, vec2 cossin)
{
return vec2(dir.x * cossin.x - dir.y * cossin.y, dir.x * cossin.y + dir.y * cossin.x);
}
vec4 GetJitter()
{
#if !defined(USE_RANDOM_TEXTURE)
return vec4(1,0,1,1);
//vec3 rand = noise3(TexCoord.x + TexCoord.y);
//float angle = 2.0 * PI * rand.x / NUM_DIRECTIONS;
//return vec4(cos(angle), sin(angle), rand.y, rand.z);
#else
return texture(RandomTexture, gl_FragCoord.xy / RANDOM_TEXTURE_WIDTH);
#endif
}
// Calculates the ambient occlusion of a sample
float ComputeSampleAO(vec3 kernelPos, vec3 normal, vec3 samplePos)
{
vec3 v = samplePos - kernelPos;
float distanceSquare = dot(v, v);
float nDotV = dot(normal, v) * inversesqrt(distanceSquare);
return clamp(nDotV - NDotVBias, 0.0, 1.0) * clamp(distanceSquare * NegInvR2 + 1.0, 0.0, 1.0);
}
// Calculates the total ambient occlusion for the entire fragment
float ComputeAO(vec3 viewPosition, vec3 viewNormal)
{
vec4 rand = GetJitter();
float radiusPixels = RadiusToScreen / viewPosition.z;
float stepSizePixels = radiusPixels / (NUM_STEPS + 1.0);
const float directionAngleStep = 2.0 * PI / NUM_DIRECTIONS;
float ao = 0.0;
for (float directionIndex = 0.0; directionIndex < NUM_DIRECTIONS; ++directionIndex)
{
float angle = directionAngleStep * directionIndex;
vec2 direction = RotateDirection(vec2(cos(angle), sin(angle)), rand.xy);
float rayPixels = (rand.z * stepSizePixels + 1.0);
for (float StepIndex = 0.0; StepIndex < NUM_STEPS; ++StepIndex)
{
vec2 sampleUV = round(rayPixels * direction) * InvFullResolution + TexCoord;
vec3 samplePos = FetchViewPos(sampleUV);
ao += ComputeSampleAO(viewPosition, viewNormal, samplePos);
rayPixels += stepSizePixels;
}
}
ao *= AOMultiplier / (NUM_DIRECTIONS * NUM_STEPS);
return clamp(1.0 - ao * 2.0, 0.0, 1.0);
}
void main()
{
vec3 viewPosition = FetchViewPos(TexCoord);
vec3 viewNormal = ReconstructNormal(viewPosition);
float occlusion = ComputeAO(viewPosition, viewNormal) * AOStrength + (1.0 - AOStrength);
FragColor = vec4(occlusion, viewPosition.z, 0.0, 1.0);
}

View file

@ -0,0 +1,38 @@
in vec2 TexCoord;
out vec4 FragColor;
uniform sampler2D AODepthTexture;
#if defined(MULTISAMPLE)
uniform sampler2DMS SceneDataTexture;
uniform int SampleCount;
#else
uniform sampler2D SceneDataTexture;
#endif
uniform vec2 Scale;
uniform vec2 Offset;
void main()
{
vec2 uv = Offset + TexCoord * Scale;
#if defined(MULTISAMPLE)
ivec2 texSize = textureSize(SceneDataTexture);
#else
ivec2 texSize = textureSize(SceneDataTexture, 0);
#endif
ivec2 ipos = ivec2(max(floor(uv * vec2(texSize) - 0.75), vec2(0.0)));
#if defined(MULTISAMPLE)
vec3 fogColor = vec3(0.0);
for (int i = 0; i < SampleCount; i++)
fogColor += texelFetch(SceneDataTexture, ipos, i).rgb;
fogColor /= float(SampleCount);
#else
vec3 fogColor = texelFetch(SceneDataTexture, ipos, 0).rgb;
#endif
float attenutation = texture(AODepthTexture, TexCoord).x;
FragColor = vec4(fogColor, 1.0 - attenutation);
}

View file

@ -3,6 +3,6 @@ out vec4 FragColor;
void main()
{
FragColor = vec4(1.0);
FragColor = vec4(1.0, 1.0, 1.0, 0.0);
}