- rewrote the blur shader so that it works the same way as the rest

This commit is contained in:
Magnus Norddahl 2018-06-12 22:14:44 +02:00
parent cb5caa757b
commit 832e7818c8
5 changed files with 120 additions and 238 deletions

View file

@ -275,6 +275,48 @@ void FGLRenderer::UpdateCameraExposure()
//
//-----------------------------------------------------------------------------
static float ComputeBlurGaussian(float n, float theta) // theta = Blur Amount
{
return (float)((1.0f / sqrtf(2 * (float)M_PI * theta)) * expf(-(n * n) / (2.0f * theta * theta)));
}
static void ComputeBlurSamples(int sampleCount, float blurAmount, float *sampleWeights)
{
sampleWeights[0] = ComputeBlurGaussian(0, blurAmount);
float totalWeights = sampleWeights[0];
for (int i = 0; i < sampleCount / 2; i++)
{
float weight = ComputeBlurGaussian(i + 1.0f, blurAmount);
sampleWeights[i * 2 + 1] = weight;
sampleWeights[i * 2 + 2] = weight;
totalWeights += weight * 2;
}
for (int i = 0; i < sampleCount; i++)
{
sampleWeights[i] /= totalWeights;
}
}
static void RenderBlur(FGLRenderer *renderer, float blurAmount, PPTexture input, PPFrameBuffer output, int width, int height, bool vertical)
{
ComputeBlurSamples(7, blurAmount, renderer->mBlurShader->Uniforms[vertical]->SampleWeights);
renderer->mBlurShader->Bind(vertical);
renderer->mBlurShader->SourceTexture[vertical].Set(0);
renderer->mBlurShader->Uniforms[vertical].Set(POSTPROCESS_BINDINGPOINT);
input.Bind(0);
output.Bind();
glViewport(0, 0, width, height);
glDisable(GL_BLEND);
renderer->RenderScreenQuad();
}
void FGLRenderer::BloomScene(int fixedcm)
{
// Only bloom things if enabled and no special fixed light mode is active
@ -287,7 +329,6 @@ void FGLRenderer::BloomScene(int fixedcm)
savedState.SaveTextureBindings(2);
const float blurAmount = gl_bloom_amount;
int sampleCount = gl_bloom_kernel_size;
auto &level0 = mBuffers->BloomLevels[0];
@ -312,8 +353,8 @@ void FGLRenderer::BloomScene(int fixedcm)
{
auto &level = mBuffers->BloomLevels[i];
auto &next = mBuffers->BloomLevels[i + 1];
mBlurShader->BlurHorizontal(this, blurAmount, sampleCount, level.VTexture, level.HFramebuffer, level.Width, level.Height);
mBlurShader->BlurVertical(this, blurAmount, sampleCount, level.HTexture, next.VFramebuffer, next.Width, next.Height);
RenderBlur(this, blurAmount, level.VTexture, level.HFramebuffer, level.Width, level.Height, false);
RenderBlur(this, blurAmount, level.HTexture, next.VFramebuffer, next.Width, next.Height, true);
}
// Blur and upscale:
@ -322,8 +363,8 @@ void FGLRenderer::BloomScene(int fixedcm)
auto &level = mBuffers->BloomLevels[i];
auto &next = mBuffers->BloomLevels[i - 1];
mBlurShader->BlurHorizontal(this, blurAmount, sampleCount, level.VTexture, level.HFramebuffer, level.Width, level.Height);
mBlurShader->BlurVertical(this, blurAmount, sampleCount, level.HTexture, level.VFramebuffer, level.Width, level.Height);
RenderBlur(this, blurAmount, level.VTexture, level.HFramebuffer, level.Width, level.Height, false);
RenderBlur(this, blurAmount, level.HTexture, level.VFramebuffer, level.Width, level.Height, true);
// Linear upscale:
next.VFramebuffer.Bind();
@ -334,8 +375,8 @@ void FGLRenderer::BloomScene(int fixedcm)
RenderScreenQuad();
}
mBlurShader->BlurHorizontal(this, blurAmount, sampleCount, level0.VTexture, level0.HFramebuffer, level0.Width, level0.Height);
mBlurShader->BlurVertical(this, blurAmount, sampleCount, level0.HTexture, level0.VFramebuffer, level0.Width, level0.Height);
RenderBlur(this, blurAmount, level0.VTexture, level0.HFramebuffer, level0.Width, level0.Height, false);
RenderBlur(this, blurAmount, level0.HTexture, level0.VFramebuffer, level0.Width, level0.Height, true);
// Add bloom back to scene texture:
mBuffers->BindCurrentFB();
@ -376,7 +417,6 @@ void FGLRenderer::BlurScene(float gameinfobluramount)
FGLPostProcessState savedState;
savedState.SaveTextureBindings(2);
int sampleCount = 9;
int numLevels = 3; // Must be 4 or less (since FGLRenderBuffers::NumBloomLevels is 4 and we are using its buffers).
assert(numLevels <= FGLRenderBuffers::NumBloomLevels);
@ -392,8 +432,8 @@ void FGLRenderer::BlurScene(float gameinfobluramount)
{
auto &level = mBuffers->BloomLevels[i];
auto &next = mBuffers->BloomLevels[i + 1];
mBlurShader->BlurHorizontal(this, blurAmount, sampleCount, level.VTexture, level.HFramebuffer, level.Width, level.Height);
mBlurShader->BlurVertical(this, blurAmount, sampleCount, level.HTexture, next.VFramebuffer, next.Width, next.Height);
RenderBlur(this, blurAmount, level.VTexture, level.HFramebuffer, level.Width, level.Height, false);
RenderBlur(this, blurAmount, level.HTexture, next.VFramebuffer, next.Width, next.Height, true);
}
// Blur and upscale:
@ -402,8 +442,8 @@ void FGLRenderer::BlurScene(float gameinfobluramount)
auto &level = mBuffers->BloomLevels[i];
auto &next = mBuffers->BloomLevels[i - 1];
mBlurShader->BlurHorizontal(this, blurAmount, sampleCount, level.VTexture, level.HFramebuffer, level.Width, level.Height);
mBlurShader->BlurVertical(this, blurAmount, sampleCount, level.HTexture, level.VFramebuffer, level.Width, level.Height);
RenderBlur(this, blurAmount, level.VTexture, level.HFramebuffer, level.Width, level.Height, false);
RenderBlur(this, blurAmount, level.HTexture, level.VFramebuffer, level.Width, level.Height, true);
// Linear upscale:
next.VFramebuffer.Bind();
@ -414,8 +454,8 @@ void FGLRenderer::BlurScene(float gameinfobluramount)
RenderScreenQuad();
}
mBlurShader->BlurHorizontal(this, blurAmount, sampleCount, level0.VTexture, level0.HFramebuffer, level0.Width, level0.Height);
mBlurShader->BlurVertical(this, blurAmount, sampleCount, level0.HTexture, level0.VFramebuffer, level0.Width, level0.Height);
RenderBlur(this, blurAmount, level0.VTexture, level0.HFramebuffer, level0.Width, level0.Height, false);
RenderBlur(this, blurAmount, level0.HTexture, level0.VFramebuffer, level0.Width, level0.Height, true);
// Copy blur back to scene texture:
mBuffers->BlitLinear(level0.VFramebuffer, mBuffers->GetCurrentFB(), 0, 0, level0.Width, level0.Height, viewport.left, viewport.top, viewport.width, viewport.height);

View file

@ -32,206 +32,25 @@
#include "gl/renderer/gl_renderer.h"
#include "gl/renderer/gl_renderbuffers.h"
//==========================================================================
//
// Performs a vertical gaussian blur pass
//
//==========================================================================
void FBlurShader::BlurVertical(FGLRenderer *renderer, float blurAmount, int sampleCount, PPTexture inputTexture, PPFrameBuffer outputFrameBuffer, int width, int height)
void FBlurShader::Bind(bool vertical)
{
Blur(renderer, blurAmount, sampleCount, inputTexture, outputFrameBuffer, width, height, true);
}
//==========================================================================
//
// Performs a horizontal gaussian blur pass
//
//==========================================================================
void FBlurShader::BlurHorizontal(FGLRenderer *renderer, float blurAmount, int sampleCount, PPTexture inputTexture, PPFrameBuffer outputFrameBuffer, int width, int height)
if (!mShader[vertical])
{
Blur(renderer, blurAmount, sampleCount, inputTexture, outputFrameBuffer, width, height, false);
}
//==========================================================================
//
// Helper for BlurVertical and BlurHorizontal. Executes the actual pass
//
//==========================================================================
void FBlurShader::Blur(FGLRenderer *renderer, float blurAmount, int sampleCount, PPTexture inputTexture, PPFrameBuffer outputFrameBuffer, int width, int height, bool vertical)
{
BlurSetup *setup = GetSetup(blurAmount, sampleCount);
FString prolog = Uniforms[vertical].CreateDeclaration("Uniforms", UniformBlock::Desc());
if (vertical)
setup->VerticalShader->Bind();
prolog += "#define BLUR_VERTICAL\n";
else
setup->HorizontalShader->Bind();
prolog += "#define BLUR_HORIZONTAL\n";
inputTexture.Bind(0);
outputFrameBuffer.Bind();
glViewport(0, 0, width, height);
glDisable(GL_BLEND);
renderer->RenderScreenQuad();
mShader[vertical].Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", 330);
mShader[vertical].Compile(FShaderProgram::Fragment, "shaders/glsl/blur.fp", prolog, 330);
mShader[vertical].SetFragDataLocation(0, "FragColor");
mShader[vertical].Link("shaders/glsl/blur");
mShader[vertical].SetAttribLocation(0, "PositionInProjection");
mShader[vertical].SetUniformBufferLocation(POSTPROCESS_BINDINGPOINT, "Uniforms");
SourceTexture[vertical].Init(mShader[vertical], "SourceTexture");
Uniforms[vertical].Init();
}
//==========================================================================
//
// Compiles the blur shaders needed for the specified blur amount and
// kernel size
//
//==========================================================================
FBlurShader::BlurSetup *FBlurShader::GetSetup(float blurAmount, int sampleCount)
{
for (size_t mBlurSetupIndex = 0; mBlurSetupIndex < mBlurSetups.Size(); mBlurSetupIndex++)
{
if (mBlurSetups[mBlurSetupIndex].blurAmount == blurAmount && mBlurSetups[mBlurSetupIndex].sampleCount == sampleCount)
{
return &mBlurSetups[mBlurSetupIndex];
}
}
BlurSetup blurSetup(blurAmount, sampleCount);
FString vertexCode = VertexShaderCode();
FString horizontalCode = FragmentShaderCode(blurAmount, sampleCount, false);
FString verticalCode = FragmentShaderCode(blurAmount, sampleCount, true);
blurSetup.VerticalShader = std::make_shared<FShaderProgram>();
blurSetup.VerticalShader->Compile(FShaderProgram::Vertex, "vertical blur vertex shader", vertexCode, "", 330);
blurSetup.VerticalShader->Compile(FShaderProgram::Fragment, "vertical blur fragment shader", verticalCode, "", 330);
blurSetup.VerticalShader->SetFragDataLocation(0, "FragColor");
blurSetup.VerticalShader->SetAttribLocation(0, "PositionInProjection");
blurSetup.VerticalShader->Link("vertical blur");
blurSetup.VerticalShader->Bind();
glUniform1i(glGetUniformLocation(*blurSetup.VerticalShader.get(), "SourceTexture"), 0);
blurSetup.HorizontalShader = std::make_shared<FShaderProgram>();
blurSetup.HorizontalShader->Compile(FShaderProgram::Vertex, "horizontal blur vertex shader", vertexCode, "", 330);
blurSetup.HorizontalShader->Compile(FShaderProgram::Fragment, "horizontal blur fragment shader", horizontalCode, "", 330);
blurSetup.HorizontalShader->SetFragDataLocation(0, "FragColor");
blurSetup.HorizontalShader->SetAttribLocation(0, "PositionInProjection");
blurSetup.HorizontalShader->Link("horizontal blur");
blurSetup.HorizontalShader->Bind();
glUniform1i(glGetUniformLocation(*blurSetup.HorizontalShader.get(), "SourceTexture"), 0);
mBlurSetups.Push(blurSetup);
return &mBlurSetups[mBlurSetups.Size() - 1];
}
//==========================================================================
//
// The vertex shader GLSL code
//
//==========================================================================
FString FBlurShader::VertexShaderCode()
{
return R"(
in vec4 PositionInProjection;
out vec2 TexCoord;
void main()
{
gl_Position = PositionInProjection;
TexCoord = (gl_Position.xy + 1.0) * 0.5;
}
)";
}
//==========================================================================
//
// Generates the fragment shader GLSL code for a specific blur setup
//
//==========================================================================
FString FBlurShader::FragmentShaderCode(float blurAmount, int sampleCount, bool vertical)
{
TArray<float> sampleWeights;
TArray<int> sampleOffsets;
ComputeBlurSamples(sampleCount, blurAmount, sampleWeights, sampleOffsets);
const char *fragmentShader =
R"(
in vec2 TexCoord;
uniform sampler2D SourceTexture;
out vec4 FragColor;
#if __VERSION__ < 130
uniform float ScaleX;
uniform float ScaleY;
vec4 textureOffset(sampler2D s, vec2 texCoord, ivec2 offset)
{
return texture2D(s, texCoord + vec2(ScaleX * float(offset.x), ScaleY * float(offset.y)));
}
#endif
void main()
{
FragColor = %s;
}
)";
FString loopCode;
for (int i = 0; i < sampleCount; i++)
{
if (i > 0)
loopCode += " + ";
if (vertical)
loopCode.AppendFormat("\r\n\t\t\ttextureOffset(SourceTexture, TexCoord, ivec2(0, %d)) * %f", sampleOffsets[i], (double)sampleWeights[i]);
else
loopCode.AppendFormat("\r\n\t\t\ttextureOffset(SourceTexture, TexCoord, ivec2(%d, 0)) * %f", sampleOffsets[i], (double)sampleWeights[i]);
}
FString code;
code.Format(fragmentShader, loopCode.GetChars());
return code;
}
//==========================================================================
//
// Calculates the sample weight for a specific offset in the kernel
//
//==========================================================================
float FBlurShader::ComputeGaussian(float n, float theta) // theta = Blur Amount
{
return (float)((1.0f / sqrtf(2 * (float)M_PI * theta)) * expf(-(n * n) / (2.0f * theta * theta)));
}
//==========================================================================
//
// Calculates the sample weights and offsets
//
//==========================================================================
void FBlurShader::ComputeBlurSamples(int sampleCount, float blurAmount, TArray<float> &sampleWeights, TArray<int> &sampleOffsets)
{
sampleWeights.Resize(sampleCount);
sampleOffsets.Resize(sampleCount);
sampleWeights[0] = ComputeGaussian(0, blurAmount);
sampleOffsets[0] = 0;
float totalWeights = sampleWeights[0];
for (int i = 0; i < sampleCount / 2; i++)
{
float weight = ComputeGaussian(i + 1.0f, blurAmount);
sampleWeights[i * 2 + 1] = weight;
sampleWeights[i * 2 + 2] = weight;
sampleOffsets[i * 2 + 1] = i + 1;
sampleOffsets[i * 2 + 2] = -i - 1;
totalWeights += weight * 2;
}
for (int i = 0; i < sampleCount; i++)
{
sampleWeights[i] /= totalWeights;
}
mShader[vertical].Bind();
}

View file

@ -1,5 +1,5 @@
#ifndef __GL_BLURSHADER_H
#define __GL_BLURSHADER_H
#pragma once
#include "gl_shaderprogram.h"
#include <memory>
@ -11,31 +11,32 @@ class PPTexture;
class FBlurShader
{
public:
void BlurVertical(FGLRenderer *renderer, float blurAmount, int sampleCount, PPTexture inputTexture, PPFrameBuffer outputFrameBuffer, int width, int height);
void BlurHorizontal(FGLRenderer *renderer, float blurAmount, int sampleCount, PPTexture inputTexture, PPFrameBuffer outputFrameBuffer, int width, int height);
void Bind(bool vertical);
FBufferedUniformSampler SourceTexture[2];
struct UniformBlock
{
float SampleWeights[8];
static std::vector<UniformFieldDesc> Desc()
{
return
{
{ "SampleWeights0", UniformType::Float, offsetof(UniformBlock, SampleWeights[0]) },
{ "SampleWeights1", UniformType::Float, offsetof(UniformBlock, SampleWeights[1]) },
{ "SampleWeights2", UniformType::Float, offsetof(UniformBlock, SampleWeights[2]) },
{ "SampleWeights3", UniformType::Float, offsetof(UniformBlock, SampleWeights[3]) },
{ "SampleWeights4", UniformType::Float, offsetof(UniformBlock, SampleWeights[4]) },
{ "SampleWeights5", UniformType::Float, offsetof(UniformBlock, SampleWeights[5]) },
{ "SampleWeights6", UniformType::Float, offsetof(UniformBlock, SampleWeights[6]) },
{ "SampleWeights7", UniformType::Float, offsetof(UniformBlock, SampleWeights[7]) },
};
}
};
ShaderUniforms<UniformBlock, POSTPROCESS_BINDINGPOINT> Uniforms[2];
private:
void Blur(FGLRenderer *renderer, float blurAmount, int sampleCount, PPTexture inputTexture, PPFrameBuffer outputFrameBuffer, int width, int height, bool vertical);
struct BlurSetup
{
BlurSetup(float blurAmount, int sampleCount) : blurAmount(blurAmount), sampleCount(sampleCount) { }
float blurAmount;
int sampleCount;
std::shared_ptr<FShaderProgram> VerticalShader;
std::shared_ptr<FShaderProgram> HorizontalShader;
FShaderProgram mShader[2];
};
BlurSetup *GetSetup(float blurAmount, int sampleCount);
FString VertexShaderCode();
FString FragmentShaderCode(float blurAmount, int sampleCount, bool vertical);
float ComputeGaussian(float n, float theta);
void ComputeBlurSamples(int sampleCount, float blurAmount, TArray<float> &sample_weights, TArray<int> &sample_offsets);
TArray<BlurSetup> mBlurSetups;
};
#endif

View file

@ -50,12 +50,6 @@ CUSTOM_CVAR(Int, gl_tonemap, 0, CVAR_ARCHIVE)
self = 0;
}
CUSTOM_CVAR(Int, gl_bloom_kernel_size, 7, CVAR_ARCHIVE)
{
if (self < 3 || self > 15 || self % 2 == 0)
self = 7;
}
CVAR(Bool, gl_lens, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Float, gl_lens_k, -0.12f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)

View file

@ -0,0 +1,28 @@
in vec2 TexCoord;
out vec4 FragColor;
uniform sampler2D SourceTexture;
void main()
{
#if defined(BLUR_HORIZONTAL)
FragColor =
textureOffset(SourceTexture, TexCoord, ivec2( 0, 0)) * SampleWeights0 +
textureOffset(SourceTexture, TexCoord, ivec2( 1, 0)) * SampleWeights1 +
textureOffset(SourceTexture, TexCoord, ivec2(-1, 0)) * SampleWeights2 +
textureOffset(SourceTexture, TexCoord, ivec2( 2, 0)) * SampleWeights3 +
textureOffset(SourceTexture, TexCoord, ivec2(-2, 0)) * SampleWeights4 +
textureOffset(SourceTexture, TexCoord, ivec2( 3, 0)) * SampleWeights5 +
textureOffset(SourceTexture, TexCoord, ivec2(-3, 0)) * SampleWeights6;
#else
FragColor =
textureOffset(SourceTexture, TexCoord, ivec2(0, 0)) * SampleWeights0 +
textureOffset(SourceTexture, TexCoord, ivec2(0, 1)) * SampleWeights1 +
textureOffset(SourceTexture, TexCoord, ivec2(0,-1)) * SampleWeights2 +
textureOffset(SourceTexture, TexCoord, ivec2(0, 2)) * SampleWeights3 +
textureOffset(SourceTexture, TexCoord, ivec2(0,-2)) * SampleWeights4 +
textureOffset(SourceTexture, TexCoord, ivec2(0, 3)) * SampleWeights5 +
textureOffset(SourceTexture, TexCoord, ivec2(0,-3)) * SampleWeights6;
#endif
}