mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2025-01-19 05:30:49 +00:00
8b6e09ca09
This was done to clean up the license and to ensure that any commercial fork of the engine has to obey the far stricter requirements concerning source distribution. The old license was compatible with GPLv2 whereas combining GPLv2 and LGPLv3 force a license upgrade to GPLv3. The license of code that originates from ZDoomGL has not been changed.
248 lines
8.3 KiB
C++
248 lines
8.3 KiB
C++
//
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// 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/
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
/*
|
|
** gl_blurshader.cpp
|
|
** Gaussian blur shader
|
|
**
|
|
*/
|
|
|
|
#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_blurshader.h"
|
|
#include "gl/data/gl_vertexbuffer.h"
|
|
#include "gl/renderer/gl_renderer.h"
|
|
|
|
//==========================================================================
|
|
//
|
|
// Performs a vertical gaussian blur pass
|
|
//
|
|
//==========================================================================
|
|
|
|
void FBlurShader::BlurVertical(FGLRenderer *renderer, float blurAmount, int sampleCount, GLuint inputTexture, GLuint outputFrameBuffer, int width, int height)
|
|
{
|
|
Blur(renderer, blurAmount, sampleCount, inputTexture, outputFrameBuffer, width, height, true);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// Performs a horizontal gaussian blur pass
|
|
//
|
|
//==========================================================================
|
|
|
|
void FBlurShader::BlurHorizontal(FGLRenderer *renderer, float blurAmount, int sampleCount, GLuint inputTexture, GLuint outputFrameBuffer, int width, int height)
|
|
{
|
|
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, GLuint inputTexture, GLuint outputFrameBuffer, int width, int height, bool vertical)
|
|
{
|
|
BlurSetup *setup = GetSetup(blurAmount, sampleCount);
|
|
if (vertical)
|
|
setup->VerticalShader->Bind();
|
|
else
|
|
setup->HorizontalShader->Bind();
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, inputTexture);
|
|
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_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, outputFrameBuffer);
|
|
glViewport(0, 0, width, height);
|
|
glDisable(GL_BLEND);
|
|
|
|
renderer->RenderScreenQuad();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// 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;
|
|
}
|
|
}
|