mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-24 10:40:46 +00:00
- added the needed parts of GZDoom's render backend to have the postprocessor working.
Not hooked up yet.
This commit is contained in:
parent
c4219f40be
commit
a021b96119
37 changed files with 4929 additions and 24 deletions
|
@ -613,6 +613,11 @@ file( GLOB HEADER_FILES
|
||||||
common/rendering/*.h
|
common/rendering/*.h
|
||||||
common/rendering/gl_load/*.h
|
common/rendering/gl_load/*.h
|
||||||
common/rendering/gl/system/*.h
|
common/rendering/gl/system/*.h
|
||||||
|
common/rendering/gl/renderer/*.h
|
||||||
|
common/rendering/gl/shaders/*.h
|
||||||
|
common/rendering/hwrenderer/data/*.h
|
||||||
|
common/rendering/hwrenderer/postprocessing/*.h
|
||||||
|
common/rendering/hwrenderer/utility/*.h
|
||||||
|
|
||||||
build/src/*.h
|
build/src/*.h
|
||||||
platform/win32/*.h
|
platform/win32/*.h
|
||||||
|
@ -660,7 +665,6 @@ set (PCH_SOURCES
|
||||||
glbackend/glbackend.cpp
|
glbackend/glbackend.cpp
|
||||||
glbackend/gl_palmanager.cpp
|
glbackend/gl_palmanager.cpp
|
||||||
glbackend/gl_texture.cpp
|
glbackend/gl_texture.cpp
|
||||||
glbackend/gl_buffers.cpp
|
|
||||||
glbackend/hw_draw2d.cpp
|
glbackend/hw_draw2d.cpp
|
||||||
|
|
||||||
thirdparty/src/base64.cpp
|
thirdparty/src/base64.cpp
|
||||||
|
@ -811,13 +815,21 @@ set (PCH_SOURCES
|
||||||
#common/input/i_input.cpp
|
#common/input/i_input.cpp
|
||||||
common/input/m_joy.cpp
|
common/input/m_joy.cpp
|
||||||
|
|
||||||
common/rendering/v_video.cpp
|
|
||||||
common/rendering/v_framebuffer.cpp
|
|
||||||
common/rendering/r_videoscale.cpp
|
common/rendering/r_videoscale.cpp
|
||||||
common/rendering/gl_load/gl_interface.cpp
|
common/rendering/v_framebuffer.cpp
|
||||||
|
common/rendering/v_video.cpp
|
||||||
|
common/rendering/gl/renderer/gl_renderer.cpp
|
||||||
|
common/rendering/gl/renderer/gl_postprocess.cpp
|
||||||
|
common/rendering/gl/renderer/gl_postprocessstate.cpp
|
||||||
|
common/rendering/gl/renderer/gl_renderbuffers.cpp
|
||||||
|
common/rendering/gl/shaders/gl_shaderprogram.cpp
|
||||||
|
common/rendering/gl/system/gl_buffers.cpp
|
||||||
common/rendering/gl/system/gl_debug.cpp
|
common/rendering/gl/system/gl_debug.cpp
|
||||||
common/rendering/gl/system/gl_framebuffer.cpp
|
common/rendering/gl/system/gl_framebuffer.cpp
|
||||||
|
common/rendering/gl_load/gl_interface.cpp
|
||||||
|
common/rendering/hwrenderer/postprocessing/hw_postprocess.cpp
|
||||||
|
common/rendering/hwrenderer/postprocessing/hw_postprocess_cvars.cpp
|
||||||
|
common/rendering/hwrenderer/utility/hw_shaderpatcher.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if( MSVC )
|
if( MSVC )
|
||||||
|
@ -882,9 +894,13 @@ include_directories(
|
||||||
common/dobject
|
common/dobject
|
||||||
common/menu
|
common/menu
|
||||||
common/input
|
common/input
|
||||||
common/rendering/gl/system
|
|
||||||
common/rendering/gl_load
|
common/rendering/gl_load
|
||||||
common/rendering/gl
|
common/rendering/gl/system
|
||||||
|
common/rendering/gl/renderer
|
||||||
|
common/rendering/gl/shaders
|
||||||
|
common/rendering/hwrenderer/data
|
||||||
|
common/rendering/hwrenderer/postprocessing
|
||||||
|
common/rendering/hwrenderer/utility
|
||||||
common/rendering
|
common/rendering
|
||||||
platform
|
platform
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,14 @@ include_directories(
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/music
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/music
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/input
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/input
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../platform
|
${CMAKE_CURRENT_SOURCE_DIR}/../platform
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl_load
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/system
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/renderer
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/shaders
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/data
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/postprocessing
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/utility
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -503,6 +503,7 @@ CUSTOM_CVARD(Float, vid_saturation, 0.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "adju
|
||||||
}
|
}
|
||||||
|
|
||||||
CVAR(Int, gl_satformula, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
|
CVAR(Int, gl_satformula, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
|
||||||
|
CVAR(Int, gl_multisample, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
|
||||||
|
|
||||||
CCMD (bumpgamma)
|
CCMD (bumpgamma)
|
||||||
{
|
{
|
||||||
|
|
|
@ -82,7 +82,7 @@ EXTERN_CVAR(Int, r_upscalefactor)
|
||||||
EXTERN_CVAR(Float, vid_gamma)
|
EXTERN_CVAR(Float, vid_gamma)
|
||||||
EXTERN_CVAR(Float, vid_contrast)
|
EXTERN_CVAR(Float, vid_contrast)
|
||||||
EXTERN_CVAR(Float, vid_brightness)
|
EXTERN_CVAR(Float, vid_brightness)
|
||||||
|
EXTERN_CVAR(Int, gl_multisample)
|
||||||
|
|
||||||
EXTERN_CVAR(Bool, in_joystick)
|
EXTERN_CVAR(Bool, in_joystick)
|
||||||
EXTERN_CVAR(Int, in_mouse)
|
EXTERN_CVAR(Int, in_mouse)
|
||||||
|
|
255
source/common/rendering/gl/renderer/gl_postprocess.cpp
Normal file
255
source/common/rendering/gl/renderer/gl_postprocess.cpp
Normal file
|
@ -0,0 +1,255 @@
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// 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_postprocess.cpp
|
||||||
|
** Post processing effects in the render pipeline
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gl_load/gl_system.h"
|
||||||
|
#include "m_png.h"
|
||||||
|
#include "gl/system/gl_buffers.h"
|
||||||
|
#include "gl/system/gl_framebuffer.h"
|
||||||
|
#include "gamecvars.h"
|
||||||
|
#include "gl/system/gl_debug.h"
|
||||||
|
//#include "gl/renderer/gl_renderstate.h"
|
||||||
|
#include "gl/renderer/gl_renderbuffers.h"
|
||||||
|
#include "gl/renderer/gl_renderer.h"
|
||||||
|
#include "gl/renderer/gl_postprocessstate.h"
|
||||||
|
#include "gl/shaders/gl_shaderprogram.h"
|
||||||
|
#include "hwrenderer/postprocessing/hw_postprocess.h"
|
||||||
|
#include "hwrenderer/postprocessing/hw_postprocess_cvars.h"
|
||||||
|
//#include "hwrenderer/data/flatvertices.h"
|
||||||
|
#include "r_videoscale.h"
|
||||||
|
|
||||||
|
extern bool vid_hdr_active;
|
||||||
|
|
||||||
|
CVAR(Int, gl_dither_bpc, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
|
||||||
|
|
||||||
|
namespace OpenGLRenderer
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
void FGLRenderer::RenderScreenQuad()
|
||||||
|
{
|
||||||
|
// The buffer here needs to be enabled later again, of course.
|
||||||
|
#if 0
|
||||||
|
auto buffer = static_cast<GLVertexBuffer *>(screen->mVertexData->GetBufferObjects().first);
|
||||||
|
buffer->Bind(nullptr);
|
||||||
|
glDrawArrays(GL_TRIANGLE_STRIP, FFlatVertexBuffer::PRESENT_INDEX, 4);
|
||||||
|
#else
|
||||||
|
glBegin(GL_TRIANGLE_STRIP);
|
||||||
|
glTexCoord2f(0, 0);
|
||||||
|
glVertex2f(-1, -1);
|
||||||
|
glTexCoord2f(0, 1);
|
||||||
|
glVertex2f(-1, 1);
|
||||||
|
glTexCoord2f(1, 0);
|
||||||
|
glVertex2f(1, -1);
|
||||||
|
glTexCoord2f(1, 1);
|
||||||
|
glVertex2f(1,1);
|
||||||
|
glEnd();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLRenderer::PostProcessScene(int fixedcm, const std::function<void()> &afterBloomDrawEndScene2D)
|
||||||
|
{
|
||||||
|
int sceneWidth = mBuffers->GetSceneWidth();
|
||||||
|
int sceneHeight = mBuffers->GetSceneHeight();
|
||||||
|
|
||||||
|
GLPPRenderState renderstate(mBuffers);
|
||||||
|
|
||||||
|
hw_postprocess.Pass1(&renderstate, fixedcm, sceneWidth, sceneHeight);
|
||||||
|
mBuffers->BindCurrentFB();
|
||||||
|
afterBloomDrawEndScene2D();
|
||||||
|
hw_postprocess.Pass2(&renderstate, fixedcm, sceneWidth, sceneHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Adds ambient occlusion to the scene
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void FGLRenderer::AmbientOccludeScene(float m5)
|
||||||
|
{
|
||||||
|
int sceneWidth = mBuffers->GetSceneWidth();
|
||||||
|
int sceneHeight = mBuffers->GetSceneHeight();
|
||||||
|
|
||||||
|
GLPPRenderState renderstate(mBuffers);
|
||||||
|
hw_postprocess.ssao.Render(&renderstate, m5, sceneWidth, sceneHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLRenderer::BlurScene(float gameinfobluramount)
|
||||||
|
{
|
||||||
|
int sceneWidth = mBuffers->GetSceneWidth();
|
||||||
|
int sceneHeight = mBuffers->GetSceneHeight();
|
||||||
|
|
||||||
|
GLPPRenderState renderstate(mBuffers);
|
||||||
|
|
||||||
|
hw_postprocess.bloom.RenderBlur(&renderstate, sceneWidth, sceneHeight, gameinfobluramount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLRenderer::ClearTonemapPalette()
|
||||||
|
{
|
||||||
|
hw_postprocess.tonemap.ClearTonemapPalette();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Copies the rendered screen to its final destination
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void FGLRenderer::Flush()
|
||||||
|
{
|
||||||
|
CopyToBackbuffer(nullptr, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Gamma correct while copying to frame buffer
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void FGLRenderer::CopyToBackbuffer(const IntRect *bounds, bool applyGamma)
|
||||||
|
{
|
||||||
|
//screen->Draw2D(); // draw all pending 2D stuff before copying the buffer
|
||||||
|
//screen->Clear2D();
|
||||||
|
|
||||||
|
GLPPRenderState renderstate(mBuffers);
|
||||||
|
|
||||||
|
FGLDebug::PushGroup("CopyToBackbuffer");
|
||||||
|
FGLPostProcessState savedState;
|
||||||
|
savedState.SaveTextureBindings(2);
|
||||||
|
mBuffers->BindOutputFB();
|
||||||
|
|
||||||
|
IntRect box;
|
||||||
|
if (bounds)
|
||||||
|
{
|
||||||
|
box = *bounds;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ClearBorders();
|
||||||
|
box = screen->mOutputLetterbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
mBuffers->BindCurrentTexture(0);
|
||||||
|
DrawPresentTexture(box, applyGamma);
|
||||||
|
FGLDebug::PopGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLRenderer::DrawPresentTexture(const IntRect &box, bool applyGamma)
|
||||||
|
{
|
||||||
|
glViewport(box.left, box.top, box.width, box.height);
|
||||||
|
|
||||||
|
mBuffers->BindDitherTexture(1);
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
if (ViewportLinearScale())
|
||||||
|
{
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
}
|
||||||
|
|
||||||
|
mPresentShader->Bind();
|
||||||
|
if (!applyGamma || framebuffer->IsHWGammaActive())
|
||||||
|
{
|
||||||
|
mPresentShader->Uniforms->InvGamma = 1.0f;
|
||||||
|
mPresentShader->Uniforms->Contrast = 1.0f;
|
||||||
|
mPresentShader->Uniforms->Brightness = 0.0f;
|
||||||
|
mPresentShader->Uniforms->Saturation = 1.0f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mPresentShader->Uniforms->InvGamma = 1.0f / clamp<float>(vid_gamma, 0.1f, 4.f);
|
||||||
|
mPresentShader->Uniforms->Contrast = clamp<float>(vid_contrast, 0.1f, 3.f);
|
||||||
|
mPresentShader->Uniforms->Brightness = clamp<float>(vid_brightness, -0.8f, 0.8f);
|
||||||
|
mPresentShader->Uniforms->Saturation = clamp<float>(vid_saturation, -15.0f, 15.f);
|
||||||
|
mPresentShader->Uniforms->GrayFormula = static_cast<int>(gl_satformula);
|
||||||
|
}
|
||||||
|
if (vid_hdr_active && framebuffer->IsFullscreen())
|
||||||
|
{
|
||||||
|
// Full screen exclusive mode treats a rgba16f frame buffer as linear.
|
||||||
|
// It probably will eventually in desktop mode too, but the DWM doesn't seem to support that.
|
||||||
|
mPresentShader->Uniforms->HdrMode = 1;
|
||||||
|
mPresentShader->Uniforms->ColorScale = (gl_dither_bpc == -1) ? 1023.0f : (float)((1 << gl_dither_bpc) - 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mPresentShader->Uniforms->HdrMode = 0;
|
||||||
|
mPresentShader->Uniforms->ColorScale = (gl_dither_bpc == -1) ? 255.0f : (float)((1 << gl_dither_bpc) - 1);
|
||||||
|
}
|
||||||
|
mPresentShader->Uniforms->Scale = { screen->mScreenViewport.width / (float)mBuffers->GetWidth(), screen->mScreenViewport.height / (float)mBuffers->GetHeight() };
|
||||||
|
mPresentShader->Uniforms->Offset = { 0.0f, 0.0f };
|
||||||
|
mPresentShader->Uniforms.SetData();
|
||||||
|
static_cast<GLDataBuffer*>(mPresentShader->Uniforms.GetBuffer())->BindBase();
|
||||||
|
RenderScreenQuad();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Fills the black bars around the screen letterbox
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void FGLRenderer::ClearBorders()
|
||||||
|
{
|
||||||
|
const auto &box = screen->mOutputLetterbox;
|
||||||
|
|
||||||
|
int clientWidth = framebuffer->GetClientWidth();
|
||||||
|
int clientHeight = framebuffer->GetClientHeight();
|
||||||
|
if (clientWidth == 0 || clientHeight == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
glViewport(0, 0, clientWidth, clientHeight);
|
||||||
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
glEnable(GL_SCISSOR_TEST);
|
||||||
|
if (box.top > 0)
|
||||||
|
{
|
||||||
|
glScissor(0, 0, clientWidth, box.top);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
}
|
||||||
|
if (clientHeight - box.top - box.height > 0)
|
||||||
|
{
|
||||||
|
glScissor(0, box.top + box.height, clientWidth, clientHeight - box.top - box.height);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
}
|
||||||
|
if (box.left > 0)
|
||||||
|
{
|
||||||
|
glScissor(0, box.top, box.left, box.height);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
}
|
||||||
|
if (clientWidth - box.left - box.width > 0)
|
||||||
|
{
|
||||||
|
glScissor(box.left + box.width, box.top, clientWidth - box.left - box.width, box.height);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
}
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
140
source/common/rendering/gl/renderer/gl_postprocessstate.cpp
Normal file
140
source/common/rendering/gl/renderer/gl_postprocessstate.cpp
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// 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_postprocessstate.cpp
|
||||||
|
** Render state maintenance
|
||||||
|
**
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include "templates.h"
|
||||||
|
#include "gl_load/gl_system.h"
|
||||||
|
#include "gl_load/gl_interface.h"
|
||||||
|
#include "gl/renderer/gl_postprocessstate.h"
|
||||||
|
|
||||||
|
namespace OpenGLRenderer
|
||||||
|
{
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Saves state modified by post processing shaders
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
FGLPostProcessState::FGLPostProcessState()
|
||||||
|
{
|
||||||
|
glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTex);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
SaveTextureBindings(1);
|
||||||
|
|
||||||
|
glGetBooleanv(GL_BLEND, &blendEnabled);
|
||||||
|
glGetBooleanv(GL_SCISSOR_TEST, &scissorEnabled);
|
||||||
|
glGetBooleanv(GL_DEPTH_TEST, &depthEnabled);
|
||||||
|
glGetBooleanv(GL_MULTISAMPLE, &multisampleEnabled);
|
||||||
|
glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProgram);
|
||||||
|
glGetIntegerv(GL_BLEND_EQUATION_RGB, &blendEquationRgb);
|
||||||
|
glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &blendEquationAlpha);
|
||||||
|
glGetIntegerv(GL_BLEND_SRC_RGB, &blendSrcRgb);
|
||||||
|
glGetIntegerv(GL_BLEND_SRC_ALPHA, &blendSrcAlpha);
|
||||||
|
glGetIntegerv(GL_BLEND_DST_RGB, &blendDestRgb);
|
||||||
|
glGetIntegerv(GL_BLEND_DST_ALPHA, &blendDestAlpha);
|
||||||
|
|
||||||
|
glDisable(GL_MULTISAMPLE);
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLPostProcessState::SaveTextureBindings(unsigned int numUnits)
|
||||||
|
{
|
||||||
|
while (textureBinding.Size() < numUnits)
|
||||||
|
{
|
||||||
|
unsigned int i = textureBinding.Size();
|
||||||
|
|
||||||
|
GLint texture;
|
||||||
|
glActiveTexture(GL_TEXTURE0 + i);
|
||||||
|
glGetIntegerv(GL_TEXTURE_BINDING_2D, &texture);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
textureBinding.Push(texture);
|
||||||
|
|
||||||
|
GLint sampler;
|
||||||
|
glGetIntegerv(GL_SAMPLER_BINDING, &sampler);
|
||||||
|
glBindSampler(i, 0);
|
||||||
|
samplerBinding.Push(sampler);
|
||||||
|
}
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Restores state at the end of post processing
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
FGLPostProcessState::~FGLPostProcessState()
|
||||||
|
{
|
||||||
|
if (blendEnabled)
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
else
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
|
||||||
|
if (scissorEnabled)
|
||||||
|
glEnable(GL_SCISSOR_TEST);
|
||||||
|
else
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
|
||||||
|
if (depthEnabled)
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
else
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
if (multisampleEnabled)
|
||||||
|
glEnable(GL_MULTISAMPLE);
|
||||||
|
else
|
||||||
|
glDisable(GL_MULTISAMPLE);
|
||||||
|
|
||||||
|
glBlendEquationSeparate(blendEquationRgb, blendEquationAlpha);
|
||||||
|
glBlendFuncSeparate(blendSrcRgb, blendDestRgb, blendSrcAlpha, blendDestAlpha);
|
||||||
|
|
||||||
|
glUseProgram(currentProgram);
|
||||||
|
|
||||||
|
// Fully unbind to avoid incomplete texture warnings from Nvidia's driver when gl_debug_level 4 is active
|
||||||
|
for (unsigned int i = 0; i < textureBinding.Size(); i++)
|
||||||
|
{
|
||||||
|
glActiveTexture(GL_TEXTURE0 + i);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < samplerBinding.Size(); i++)
|
||||||
|
{
|
||||||
|
glBindSampler(i, samplerBinding[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < textureBinding.Size(); i++)
|
||||||
|
{
|
||||||
|
glActiveTexture(GL_TEXTURE0 + i);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, textureBinding[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
glActiveTexture(activeTex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
41
source/common/rendering/gl/renderer/gl_postprocessstate.h
Normal file
41
source/common/rendering/gl/renderer/gl_postprocessstate.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#ifndef __GL_POSTPROCESSSTATE_H
|
||||||
|
#define __GL_POSTPROCESSSTATE_H
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "gl_load/gl_interface.h"
|
||||||
|
#include "matrix.h"
|
||||||
|
#include "c_cvars.h"
|
||||||
|
|
||||||
|
namespace OpenGLRenderer
|
||||||
|
{
|
||||||
|
|
||||||
|
class FGLPostProcessState
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FGLPostProcessState();
|
||||||
|
~FGLPostProcessState();
|
||||||
|
|
||||||
|
void SaveTextureBindings(unsigned int numUnits);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FGLPostProcessState(const FGLPostProcessState &) = delete;
|
||||||
|
FGLPostProcessState &operator=(const FGLPostProcessState &) = delete;
|
||||||
|
|
||||||
|
GLint activeTex;
|
||||||
|
TArray<GLint> textureBinding;
|
||||||
|
TArray<GLint> samplerBinding;
|
||||||
|
GLboolean blendEnabled;
|
||||||
|
GLboolean scissorEnabled;
|
||||||
|
GLboolean depthEnabled;
|
||||||
|
GLboolean multisampleEnabled;
|
||||||
|
GLint currentProgram;
|
||||||
|
GLint blendEquationRgb;
|
||||||
|
GLint blendEquationAlpha;
|
||||||
|
GLint blendSrcRgb;
|
||||||
|
GLint blendSrcAlpha;
|
||||||
|
GLint blendDestRgb;
|
||||||
|
GLint blendDestAlpha;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
933
source/common/rendering/gl/renderer/gl_renderbuffers.cpp
Normal file
933
source/common/rendering/gl/renderer/gl_renderbuffers.cpp
Normal file
|
@ -0,0 +1,933 @@
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// 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_renderbuffers.cpp
|
||||||
|
** Render buffers used during rendering
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gl_load/gl_system.h"
|
||||||
|
#include "v_video.h"
|
||||||
|
#include "printf.h"
|
||||||
|
#include "gl_load/gl_interface.h"
|
||||||
|
#include "gamecvars.h"
|
||||||
|
#include "gl/system/gl_debug.h"
|
||||||
|
#include "gl/renderer/gl_renderer.h"
|
||||||
|
#include "gl/renderer/gl_renderbuffers.h"
|
||||||
|
#include "gl/renderer/gl_postprocessstate.h"
|
||||||
|
#include "gl/shaders/gl_shaderprogram.h"
|
||||||
|
#include "gl/system/gl_buffers.h"
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
EXTERN_CVAR(Int, gl_debug_level)
|
||||||
|
|
||||||
|
|
||||||
|
namespace OpenGLRenderer
|
||||||
|
{
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Initialize render buffers and textures used in rendering passes
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
FGLRenderBuffers::FGLRenderBuffers()
|
||||||
|
{
|
||||||
|
glGetIntegerv(GL_MAX_SAMPLES, &mMaxSamples);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Free render buffer resources
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
FGLRenderBuffers::~FGLRenderBuffers()
|
||||||
|
{
|
||||||
|
ClearScene();
|
||||||
|
ClearPipeline();
|
||||||
|
ClearEyeBuffers();
|
||||||
|
DeleteTexture(mDitherTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLRenderBuffers::ClearScene()
|
||||||
|
{
|
||||||
|
DeleteFrameBuffer(mSceneFB);
|
||||||
|
DeleteFrameBuffer(mSceneDataFB);
|
||||||
|
DeleteRenderBuffer(mSceneMultisampleBuf);
|
||||||
|
DeleteRenderBuffer(mSceneFogBuf);
|
||||||
|
DeleteRenderBuffer(mSceneNormalBuf);
|
||||||
|
DeleteRenderBuffer(mSceneDepthStencilBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLRenderBuffers::ClearPipeline()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < NumPipelineTextures; i++)
|
||||||
|
{
|
||||||
|
DeleteFrameBuffer(mPipelineFB[i]);
|
||||||
|
DeleteTexture(mPipelineTexture[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLRenderBuffers::ClearEyeBuffers()
|
||||||
|
{
|
||||||
|
for (auto handle : mEyeFBs)
|
||||||
|
DeleteFrameBuffer(handle);
|
||||||
|
|
||||||
|
for (auto handle : mEyeTextures)
|
||||||
|
DeleteTexture(handle);
|
||||||
|
|
||||||
|
mEyeTextures.Clear();
|
||||||
|
mEyeFBs.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLRenderBuffers::DeleteTexture(PPGLTexture &tex)
|
||||||
|
{
|
||||||
|
if (tex.handle != 0)
|
||||||
|
glDeleteTextures(1, &tex.handle);
|
||||||
|
tex.handle = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLRenderBuffers::DeleteRenderBuffer(PPGLRenderBuffer &buf)
|
||||||
|
{
|
||||||
|
if (buf.handle != 0)
|
||||||
|
glDeleteRenderbuffers(1, &buf.handle);
|
||||||
|
buf.handle = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLRenderBuffers::DeleteFrameBuffer(PPGLFrameBuffer &fb)
|
||||||
|
{
|
||||||
|
if (fb.handle != 0)
|
||||||
|
glDeleteFramebuffers(1, &fb.handle);
|
||||||
|
fb.handle = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Makes sure all render buffers have sizes suitable for rending at the
|
||||||
|
// specified resolution
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FGLRenderBuffers::Setup(int width, int height, int sceneWidth, int sceneHeight)
|
||||||
|
{
|
||||||
|
if (width <= 0 || height <= 0)
|
||||||
|
I_FatalError("Requested invalid render buffer sizes: screen = %dx%d", width, height);
|
||||||
|
|
||||||
|
int samples = clamp((int)gl_multisample, 0, mMaxSamples);
|
||||||
|
|
||||||
|
GLint activeTex;
|
||||||
|
GLint textureBinding;
|
||||||
|
glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTex);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding);
|
||||||
|
|
||||||
|
if (width != mWidth || height != mHeight)
|
||||||
|
CreatePipeline(width, height);
|
||||||
|
|
||||||
|
if (width != mWidth || height != mHeight || mSamples != samples)
|
||||||
|
CreateScene(width, height, samples, false);
|
||||||
|
|
||||||
|
mWidth = width;
|
||||||
|
mHeight = height;
|
||||||
|
mSamples = samples;
|
||||||
|
mSceneWidth = sceneWidth;
|
||||||
|
mSceneHeight = sceneHeight;
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, textureBinding);
|
||||||
|
glActiveTexture(activeTex);
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
if (FailedCreate)
|
||||||
|
{
|
||||||
|
ClearScene();
|
||||||
|
ClearPipeline();
|
||||||
|
ClearEyeBuffers();
|
||||||
|
mWidth = 0;
|
||||||
|
mHeight = 0;
|
||||||
|
mSamples = 0;
|
||||||
|
mSceneWidth = 0;
|
||||||
|
mSceneHeight = 0;
|
||||||
|
I_FatalError("Unable to create render buffers.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Creates the scene buffers
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FGLRenderBuffers::CreateScene(int width, int height, int samples, bool needsSceneTextures)
|
||||||
|
{
|
||||||
|
ClearScene();
|
||||||
|
|
||||||
|
if (samples > 1)
|
||||||
|
{
|
||||||
|
if (needsSceneTextures)
|
||||||
|
{
|
||||||
|
mSceneMultisampleTex = Create2DMultisampleTexture("SceneMultisample", GL_RGBA16F, width, height, samples, false);
|
||||||
|
mSceneDepthStencilTex = Create2DMultisampleTexture("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height, samples, false);
|
||||||
|
mSceneFogTex = Create2DMultisampleTexture("SceneFog", GL_RGBA8, width, height, samples, false);
|
||||||
|
mSceneNormalTex = Create2DMultisampleTexture("SceneNormal", GL_RGB10_A2, width, height, samples, false);
|
||||||
|
mSceneFB = CreateFrameBuffer("SceneFB", mSceneMultisampleTex, {}, {}, mSceneDepthStencilTex, true);
|
||||||
|
mSceneDataFB = CreateFrameBuffer("SceneGBufferFB", mSceneMultisampleTex, mSceneFogTex, mSceneNormalTex, mSceneDepthStencilTex, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mSceneMultisampleBuf = CreateRenderBuffer("SceneMultisample", GL_RGBA16F, width, height, samples);
|
||||||
|
mSceneDepthStencilBuf = CreateRenderBuffer("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height, samples);
|
||||||
|
mSceneFB = CreateFrameBuffer("SceneFB", mSceneMultisampleBuf, mSceneDepthStencilBuf);
|
||||||
|
mSceneDataFB = CreateFrameBuffer("SceneGBufferFB", mSceneMultisampleBuf, mSceneDepthStencilBuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (needsSceneTextures)
|
||||||
|
{
|
||||||
|
mSceneDepthStencilTex = Create2DTexture("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height);
|
||||||
|
mSceneFogTex = Create2DTexture("SceneFog", GL_RGBA8, width, height);
|
||||||
|
mSceneNormalTex = Create2DTexture("SceneNormal", GL_RGB10_A2, width, height);
|
||||||
|
mSceneFB = CreateFrameBuffer("SceneFB", mPipelineTexture[0], {}, {}, mSceneDepthStencilTex, false);
|
||||||
|
mSceneDataFB = CreateFrameBuffer("SceneGBufferFB", mPipelineTexture[0], mSceneFogTex, mSceneNormalTex, mSceneDepthStencilTex, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mSceneDepthStencilBuf = CreateRenderBuffer("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height);
|
||||||
|
mSceneFB = CreateFrameBuffer("SceneFB", mPipelineTexture[0], mSceneDepthStencilBuf);
|
||||||
|
mSceneDataFB = CreateFrameBuffer("SceneGBufferFB", mPipelineTexture[0], mSceneDepthStencilBuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Creates the buffers needed for post processing steps
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FGLRenderBuffers::CreatePipeline(int width, int height)
|
||||||
|
{
|
||||||
|
ClearPipeline();
|
||||||
|
ClearEyeBuffers();
|
||||||
|
|
||||||
|
for (int i = 0; i < NumPipelineTextures; i++)
|
||||||
|
{
|
||||||
|
mPipelineTexture[i] = Create2DTexture("PipelineTexture", GL_RGBA16F, width, height);
|
||||||
|
mPipelineFB[i] = CreateFrameBuffer("PipelineFB", mPipelineTexture[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Creates eye buffers if needed
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FGLRenderBuffers::CreateEyeBuffers(int eye)
|
||||||
|
{
|
||||||
|
if (mEyeFBs.Size() > unsigned(eye))
|
||||||
|
return;
|
||||||
|
|
||||||
|
GLint activeTex, textureBinding, frameBufferBinding;
|
||||||
|
glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTex);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding);
|
||||||
|
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &frameBufferBinding);
|
||||||
|
|
||||||
|
while (mEyeFBs.Size() <= unsigned(eye))
|
||||||
|
{
|
||||||
|
PPGLTexture texture = Create2DTexture("EyeTexture", GL_RGBA16F, mWidth, mHeight);
|
||||||
|
mEyeTextures.Push(texture);
|
||||||
|
mEyeFBs.Push(CreateFrameBuffer("EyeFB", texture));
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, textureBinding);
|
||||||
|
glActiveTexture(activeTex);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferBinding);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Creates a 2D texture defaulting to linear filtering and clamp to edge
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
PPGLTexture FGLRenderBuffers::Create2DTexture(const char *name, GLuint format, int width, int height, const void *data)
|
||||||
|
{
|
||||||
|
PPGLTexture tex;
|
||||||
|
tex.Width = width;
|
||||||
|
tex.Height = height;
|
||||||
|
glGenTextures(1, &tex.handle);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, tex.handle);
|
||||||
|
FGLDebug::LabelObject(GL_TEXTURE, tex.handle, name);
|
||||||
|
|
||||||
|
GLenum dataformat = 0, datatype = 0;
|
||||||
|
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_RGBA16_SNORM: dataformat = GL_RGBA; datatype = GL_SHORT; break;
|
||||||
|
case GL_R32F: dataformat = GL_RED; datatype = GL_FLOAT; break;
|
||||||
|
case GL_R16F: dataformat = GL_RED; datatype = GL_FLOAT; break;
|
||||||
|
case GL_RG32F: dataformat = GL_RG; datatype = GL_FLOAT; break;
|
||||||
|
case GL_RG16F: dataformat = GL_RG; datatype = GL_FLOAT; break;
|
||||||
|
case GL_RGB10_A2: dataformat = GL_RGBA; datatype = GL_UNSIGNED_INT_10_10_10_2; 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;
|
||||||
|
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);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
return tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
PPGLTexture FGLRenderBuffers::Create2DMultisampleTexture(const char *name, GLuint format, int width, int height, int samples, bool fixedSampleLocations)
|
||||||
|
{
|
||||||
|
PPGLTexture tex;
|
||||||
|
tex.Width = width;
|
||||||
|
tex.Height = height;
|
||||||
|
glGenTextures(1, &tex.handle);
|
||||||
|
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, tex.handle);
|
||||||
|
FGLDebug::LabelObject(GL_TEXTURE, tex.handle, name);
|
||||||
|
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, format, width, height, fixedSampleLocations);
|
||||||
|
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
|
||||||
|
return tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Creates a render buffer
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
PPGLRenderBuffer FGLRenderBuffers::CreateRenderBuffer(const char *name, GLuint format, int width, int height)
|
||||||
|
{
|
||||||
|
PPGLRenderBuffer buf;
|
||||||
|
glGenRenderbuffers(1, &buf.handle);
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, buf.handle);
|
||||||
|
FGLDebug::LabelObject(GL_RENDERBUFFER, buf.handle, name);
|
||||||
|
glRenderbufferStorage(GL_RENDERBUFFER, format, width, height);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
PPGLRenderBuffer FGLRenderBuffers::CreateRenderBuffer(const char *name, GLuint format, int width, int height, int samples)
|
||||||
|
{
|
||||||
|
if (samples <= 1)
|
||||||
|
return CreateRenderBuffer(name, format, width, height);
|
||||||
|
|
||||||
|
PPGLRenderBuffer buf;
|
||||||
|
glGenRenderbuffers(1, &buf.handle);
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, buf.handle);
|
||||||
|
FGLDebug::LabelObject(GL_RENDERBUFFER, buf.handle, name);
|
||||||
|
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, format, width, height);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Creates a frame buffer
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
PPGLFrameBuffer FGLRenderBuffers::CreateFrameBuffer(const char *name, PPGLTexture colorbuffer)
|
||||||
|
{
|
||||||
|
PPGLFrameBuffer fb;
|
||||||
|
glGenFramebuffers(1, &fb.handle);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fb.handle);
|
||||||
|
FGLDebug::LabelObject(GL_FRAMEBUFFER, fb.handle, name);
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer.handle, 0);
|
||||||
|
if (CheckFrameBufferCompleteness())
|
||||||
|
ClearFrameBuffer(false, false);
|
||||||
|
return fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
PPGLFrameBuffer FGLRenderBuffers::CreateFrameBuffer(const char *name, PPGLTexture colorbuffer, PPGLRenderBuffer depthstencil)
|
||||||
|
{
|
||||||
|
PPGLFrameBuffer fb;
|
||||||
|
glGenFramebuffers(1, &fb.handle);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fb.handle);
|
||||||
|
FGLDebug::LabelObject(GL_FRAMEBUFFER, fb.handle, name);
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer.handle, 0);
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthstencil.handle);
|
||||||
|
if (CheckFrameBufferCompleteness())
|
||||||
|
ClearFrameBuffer(true, true);
|
||||||
|
return fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
PPGLFrameBuffer FGLRenderBuffers::CreateFrameBuffer(const char *name, PPGLRenderBuffer colorbuffer, PPGLRenderBuffer depthstencil)
|
||||||
|
{
|
||||||
|
PPGLFrameBuffer fb;
|
||||||
|
glGenFramebuffers(1, &fb.handle);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fb.handle);
|
||||||
|
FGLDebug::LabelObject(GL_FRAMEBUFFER, fb.handle, name);
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuffer.handle);
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthstencil.handle);
|
||||||
|
if (CheckFrameBufferCompleteness())
|
||||||
|
ClearFrameBuffer(true, true);
|
||||||
|
return fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
PPGLFrameBuffer FGLRenderBuffers::CreateFrameBuffer(const char *name, PPGLTexture colorbuffer0, PPGLTexture colorbuffer1, PPGLTexture colorbuffer2, PPGLTexture depthstencil, bool multisample)
|
||||||
|
{
|
||||||
|
PPGLFrameBuffer fb;
|
||||||
|
glGenFramebuffers(1, &fb.handle);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fb.handle);
|
||||||
|
FGLDebug::LabelObject(GL_FRAMEBUFFER, fb.handle, name);
|
||||||
|
if (multisample)
|
||||||
|
{
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, colorbuffer0.handle, 0);
|
||||||
|
if (colorbuffer1.handle != 0)
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D_MULTISAMPLE, colorbuffer1.handle, 0);
|
||||||
|
if (colorbuffer2.handle != 0)
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D_MULTISAMPLE, colorbuffer2.handle, 0);
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, depthstencil.handle, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer0.handle, 0);
|
||||||
|
if (colorbuffer1.handle != 0)
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, colorbuffer1.handle, 0);
|
||||||
|
if (colorbuffer2.handle != 0)
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, colorbuffer2.handle, 0);
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthstencil.handle, 0);
|
||||||
|
}
|
||||||
|
if (CheckFrameBufferCompleteness())
|
||||||
|
ClearFrameBuffer(true, true);
|
||||||
|
return fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Verifies that the frame buffer setup is valid
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
bool FGLRenderBuffers::CheckFrameBufferCompleteness()
|
||||||
|
{
|
||||||
|
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||||
|
if (result == GL_FRAMEBUFFER_COMPLETE)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
bool FailedCreate = true;
|
||||||
|
|
||||||
|
if (gl_debug_level > 0)
|
||||||
|
{
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Clear frame buffer to make sure it never contains uninitialized data
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FGLRenderBuffers::ClearFrameBuffer(bool stencil, bool depth)
|
||||||
|
{
|
||||||
|
GLboolean scissorEnabled;
|
||||||
|
GLint stencilValue;
|
||||||
|
GLdouble depthValue;
|
||||||
|
glGetBooleanv(GL_SCISSOR_TEST, &scissorEnabled);
|
||||||
|
glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &stencilValue);
|
||||||
|
glGetDoublev(GL_DEPTH_CLEAR_VALUE, &depthValue);
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
glClearColor(0.0, 0.0, 0.0, 0.0);
|
||||||
|
glClearDepth(0.0);
|
||||||
|
glClearStencil(0);
|
||||||
|
GLenum flags = GL_COLOR_BUFFER_BIT;
|
||||||
|
if (stencil)
|
||||||
|
flags |= GL_STENCIL_BUFFER_BIT;
|
||||||
|
if (depth)
|
||||||
|
flags |= GL_DEPTH_BUFFER_BIT;
|
||||||
|
glClear(flags);
|
||||||
|
glClearStencil(stencilValue);
|
||||||
|
glClearDepth(depthValue);
|
||||||
|
if (scissorEnabled)
|
||||||
|
glEnable(GL_SCISSOR_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Resolves the multisample frame buffer by copying it to the first pipeline texture
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FGLRenderBuffers::BlitSceneToTexture()
|
||||||
|
{
|
||||||
|
mCurrentPipelineTexture = 0;
|
||||||
|
|
||||||
|
if (mSamples <= 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, mSceneFB.handle);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mPipelineFB[mCurrentPipelineTexture].handle);
|
||||||
|
glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||||
|
|
||||||
|
if ((gl.flags & RFL_INVALIDATE_BUFFER) != 0)
|
||||||
|
{
|
||||||
|
GLenum attachments[2] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_STENCIL_ATTACHMENT };
|
||||||
|
glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 2, attachments);
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Eye textures and their frame buffers
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FGLRenderBuffers::BlitToEyeTexture(int eye, bool allowInvalidate)
|
||||||
|
{
|
||||||
|
CreateEyeBuffers(eye);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, mPipelineFB[mCurrentPipelineTexture].handle);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mEyeFBs[eye].handle);
|
||||||
|
glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||||
|
|
||||||
|
if ((gl.flags & RFL_INVALIDATE_BUFFER) != 0 && allowInvalidate)
|
||||||
|
{
|
||||||
|
GLenum attachments[2] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_STENCIL_ATTACHMENT };
|
||||||
|
glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 2, attachments);
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLRenderBuffers::BlitFromEyeTexture(int eye)
|
||||||
|
{
|
||||||
|
if (mEyeFBs.Size() <= unsigned(eye)) return;
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mPipelineFB[mCurrentPipelineTexture].handle);
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, mEyeFBs[eye].handle);
|
||||||
|
glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||||
|
|
||||||
|
if ((gl.flags & RFL_INVALIDATE_BUFFER) != 0)
|
||||||
|
{
|
||||||
|
GLenum attachments[2] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_STENCIL_ATTACHMENT };
|
||||||
|
glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 2, attachments);
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLRenderBuffers::BindEyeTexture(int eye, int texunit)
|
||||||
|
{
|
||||||
|
CreateEyeBuffers(eye);
|
||||||
|
glActiveTexture(GL_TEXTURE0 + texunit);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mEyeTextures[eye].handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLRenderBuffers::BindDitherTexture(int texunit)
|
||||||
|
{
|
||||||
|
if (!mDitherTexture)
|
||||||
|
{
|
||||||
|
static const float data[64] =
|
||||||
|
{
|
||||||
|
.0078125, .2578125, .1328125, .3828125, .0234375, .2734375, .1484375, .3984375,
|
||||||
|
.7578125, .5078125, .8828125, .6328125, .7734375, .5234375, .8984375, .6484375,
|
||||||
|
.0703125, .3203125, .1953125, .4453125, .0859375, .3359375, .2109375, .4609375,
|
||||||
|
.8203125, .5703125, .9453125, .6953125, .8359375, .5859375, .9609375, .7109375,
|
||||||
|
.0390625, .2890625, .1640625, .4140625, .0546875, .3046875, .1796875, .4296875,
|
||||||
|
.7890625, .5390625, .9140625, .6640625, .8046875, .5546875, .9296875, .6796875,
|
||||||
|
.1015625, .3515625, .2265625, .4765625, .1171875, .3671875, .2421875, .4921875,
|
||||||
|
.8515625, .6015625, .9765625, .7265625, .8671875, .6171875, .9921875, .7421875,
|
||||||
|
};
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE0 + texunit);
|
||||||
|
mDitherTexture = Create2DTexture("DitherTexture", GL_R32F, 8, 8, data);
|
||||||
|
}
|
||||||
|
mDitherTexture.Bind(1, GL_NEAREST, GL_REPEAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Makes the scene frame buffer active (multisample, depth, stecil, etc.)
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FGLRenderBuffers::BindSceneFB(bool sceneData)
|
||||||
|
{
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, sceneData ? mSceneDataFB.handle : mSceneFB.handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// 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, mSceneMultisampleTex.handle);
|
||||||
|
else
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mPipelineTexture[0].handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Binds the scene fog data texture to the specified texture unit
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FGLRenderBuffers::BindSceneFogTexture(int index)
|
||||||
|
{
|
||||||
|
glActiveTexture(GL_TEXTURE0 + index);
|
||||||
|
if (mSamples > 1)
|
||||||
|
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mSceneFogTex.handle);
|
||||||
|
else
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mSceneFogTex.handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Binds the scene normal data texture to the specified texture unit
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FGLRenderBuffers::BindSceneNormalTexture(int index)
|
||||||
|
{
|
||||||
|
glActiveTexture(GL_TEXTURE0 + index);
|
||||||
|
if (mSamples > 1)
|
||||||
|
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mSceneNormalTex.handle);
|
||||||
|
else
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mSceneNormalTex.handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// 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, mSceneDepthStencilTex.handle);
|
||||||
|
else
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mSceneDepthStencilTex.handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Binds the current scene/effect/hud texture to the specified texture unit
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FGLRenderBuffers::BindCurrentTexture(int index, int filter, int wrap)
|
||||||
|
{
|
||||||
|
mPipelineTexture[mCurrentPipelineTexture].Bind(index, filter, wrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Makes the frame buffer for the current texture active
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FGLRenderBuffers::BindCurrentFB()
|
||||||
|
{
|
||||||
|
mPipelineFB[mCurrentPipelineTexture].Bind();
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Makes the frame buffer for the next texture active
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FGLRenderBuffers::BindNextFB()
|
||||||
|
{
|
||||||
|
int out = (mCurrentPipelineTexture + 1) % NumPipelineTextures;
|
||||||
|
mPipelineFB[out].Bind();
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Next pipeline texture now contains the output
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FGLRenderBuffers::NextTexture()
|
||||||
|
{
|
||||||
|
mCurrentPipelineTexture = (mCurrentPipelineTexture + 1) % NumPipelineTextures;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Makes the screen frame buffer active
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FGLRenderBuffers::BindOutputFB()
|
||||||
|
{
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Returns true if render buffers are supported and should be used
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
bool FGLRenderBuffers::FailedCreate = false;
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Creates or updates textures used by postprocess effects
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
PPGLTextureBackend *GLPPRenderState::GetGLTexture(PPTexture *texture)
|
||||||
|
{
|
||||||
|
if (!texture->Backend)
|
||||||
|
{
|
||||||
|
FGLPostProcessState savedState;
|
||||||
|
|
||||||
|
auto backend = std::make_unique<PPGLTextureBackend>();
|
||||||
|
|
||||||
|
int glformat;
|
||||||
|
switch (texture->Format)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case PixelFormat::Rgba8: glformat = GL_RGBA8; break;
|
||||||
|
case PixelFormat::Rgba16f: glformat = GL_RGBA16F; break;
|
||||||
|
case PixelFormat::R32f: glformat = GL_R32F; break;
|
||||||
|
case PixelFormat::Rg16f: glformat = GL_RG16F; break;
|
||||||
|
case PixelFormat::Rgba16_snorm: glformat = GL_RGBA16_SNORM; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (texture->Data)
|
||||||
|
backend->Tex = buffers->Create2DTexture("PPTexture", glformat, texture->Width, texture->Height, texture->Data.get());
|
||||||
|
else
|
||||||
|
backend->Tex = buffers->Create2DTexture("PPTexture", glformat, texture->Width, texture->Height);
|
||||||
|
|
||||||
|
texture->Backend = std::move(backend);
|
||||||
|
}
|
||||||
|
return static_cast<PPGLTextureBackend*>(texture->Backend.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Compile the shaders declared by post process effects
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
FShaderProgram *GLPPRenderState::GetGLShader(PPShader *shader)
|
||||||
|
{
|
||||||
|
if (!shader->Backend)
|
||||||
|
{
|
||||||
|
auto glshader = std::make_unique<FShaderProgram>();
|
||||||
|
|
||||||
|
FString prolog;
|
||||||
|
if (!shader->Uniforms.empty())
|
||||||
|
prolog = UniformBlockDecl::Create("Uniforms", shader->Uniforms, POSTPROCESS_BINDINGPOINT);
|
||||||
|
prolog += shader->Defines;
|
||||||
|
|
||||||
|
glshader->Compile(FShaderProgram::Vertex, shader->VertexShader, "", shader->Version);
|
||||||
|
glshader->Compile(FShaderProgram::Fragment, shader->FragmentShader, prolog, shader->Version);
|
||||||
|
glshader->Link(shader->FragmentShader.GetChars());
|
||||||
|
if (!shader->Uniforms.empty())
|
||||||
|
glshader->SetUniformBufferLocation(POSTPROCESS_BINDINGPOINT, "Uniforms");
|
||||||
|
|
||||||
|
shader->Backend = std::move(glshader);
|
||||||
|
}
|
||||||
|
return static_cast<FShaderProgram*>(shader->Backend.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Renders one post process effect
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void GLPPRenderState::Draw()
|
||||||
|
{
|
||||||
|
FGLPostProcessState savedState;
|
||||||
|
|
||||||
|
// Bind input textures
|
||||||
|
for (unsigned int index = 0; index < Textures.Size(); index++)
|
||||||
|
{
|
||||||
|
savedState.SaveTextureBindings(index + 1);
|
||||||
|
|
||||||
|
const PPTextureInput &input = Textures[index];
|
||||||
|
int filter = (input.Filter == PPFilterMode::Nearest) ? GL_NEAREST : GL_LINEAR;
|
||||||
|
int wrap = (input.Wrap == PPWrapMode::Clamp) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
|
||||||
|
|
||||||
|
switch (input.Type)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case PPTextureType::CurrentPipelineTexture:
|
||||||
|
buffers->BindCurrentTexture(index, filter, wrap);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PPTextureType::NextPipelineTexture:
|
||||||
|
I_FatalError("PPTextureType::NextPipelineTexture not allowed as input\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PPTextureType::PPTexture:
|
||||||
|
GetGLTexture(input.Texture)->Tex.Bind(index, filter, wrap);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PPTextureType::SceneColor:
|
||||||
|
buffers->BindSceneColorTexture(index);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PPTextureType::SceneFog:
|
||||||
|
buffers->BindSceneFogTexture(index);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PPTextureType::SceneNormal:
|
||||||
|
buffers->BindSceneNormalTexture(index);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PPTextureType::SceneDepth:
|
||||||
|
buffers->BindSceneDepthTexture(index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set render target
|
||||||
|
switch (Output.Type)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
I_FatalError("Unsupported postprocess output type\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PPTextureType::CurrentPipelineTexture:
|
||||||
|
buffers->BindCurrentFB();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PPTextureType::NextPipelineTexture:
|
||||||
|
buffers->BindNextFB();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PPTextureType::PPTexture:
|
||||||
|
if (GetGLTexture(Output.Texture)->FB)
|
||||||
|
GetGLTexture(Output.Texture)->FB.Bind();
|
||||||
|
else
|
||||||
|
GetGLTexture(Output.Texture)->FB = buffers->CreateFrameBuffer("PPTextureFB"/*Output.Texture.GetChars()*/, GetGLTexture(Output.Texture)->Tex);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PPTextureType::SceneColor:
|
||||||
|
buffers->BindSceneFB(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set blend mode
|
||||||
|
if (BlendMode.BlendOp == STYLEOP_Add && BlendMode.SrcAlpha == STYLEALPHA_One && BlendMode.DestAlpha == STYLEALPHA_Zero && BlendMode.Flags == 0)
|
||||||
|
{
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// To do: support all the modes
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendEquation(GL_FUNC_ADD);
|
||||||
|
if (BlendMode.SrcAlpha == STYLEALPHA_One && BlendMode.DestAlpha == STYLEALPHA_One)
|
||||||
|
glBlendFunc(GL_ONE, GL_ONE);
|
||||||
|
else
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup viewport
|
||||||
|
glViewport(Viewport.left, Viewport.top, Viewport.width, Viewport.height);
|
||||||
|
|
||||||
|
auto shader = GetGLShader(Shader);
|
||||||
|
|
||||||
|
// Set uniforms
|
||||||
|
if (Uniforms.Data.Size() > 0)
|
||||||
|
{
|
||||||
|
if (!shader->Uniforms)
|
||||||
|
shader->Uniforms.reset(screen->CreateDataBuffer(POSTPROCESS_BINDINGPOINT, false, false));
|
||||||
|
shader->Uniforms->SetData(Uniforms.Data.Size(), Uniforms.Data.Data());
|
||||||
|
static_cast<GLDataBuffer*>(shader->Uniforms.get())->BindBase();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set shader
|
||||||
|
shader->Bind();
|
||||||
|
|
||||||
|
// Draw the screen quad
|
||||||
|
GLRenderer->RenderScreenQuad();
|
||||||
|
|
||||||
|
// Advance to next PP texture if our output was sent there
|
||||||
|
if (Output.Type == PPTextureType::NextPipelineTexture)
|
||||||
|
buffers->NextTexture();
|
||||||
|
|
||||||
|
glViewport(screen->mScreenViewport.left, screen->mScreenViewport.top, screen->mScreenViewport.width, screen->mScreenViewport.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLPPRenderState::PushGroup(const FString &name)
|
||||||
|
{
|
||||||
|
FGLDebug::PushGroup(name.GetChars());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLPPRenderState::PopGroup()
|
||||||
|
{
|
||||||
|
FGLDebug::PopGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Store the current stereo 3D eye buffer, and Load the next one
|
||||||
|
|
||||||
|
int FGLRenderBuffers::NextEye(int eyeCount)
|
||||||
|
{
|
||||||
|
int nextEye = (mCurrentEye + 1) % eyeCount;
|
||||||
|
if (nextEye == mCurrentEye) return mCurrentEye;
|
||||||
|
BlitToEyeTexture(mCurrentEye);
|
||||||
|
mCurrentEye = nextEye;
|
||||||
|
BlitFromEyeTexture(mCurrentEye);
|
||||||
|
return mCurrentEye;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace OpenGLRenderer
|
205
source/common/rendering/gl/renderer/gl_renderbuffers.h
Normal file
205
source/common/rendering/gl/renderer/gl_renderbuffers.h
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "hwrenderer/postprocessing/hw_postprocess.h"
|
||||||
|
|
||||||
|
namespace OpenGLRenderer
|
||||||
|
{
|
||||||
|
|
||||||
|
class FGLRenderBuffers;
|
||||||
|
|
||||||
|
class PPGLTexture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Bind(int index, int filter = GL_NEAREST, int wrap = GL_CLAMP_TO_EDGE)
|
||||||
|
{
|
||||||
|
glActiveTexture(GL_TEXTURE0 + index);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, handle);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Width = -1;
|
||||||
|
int Height = -1;
|
||||||
|
|
||||||
|
explicit operator bool() const { return handle != 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLuint handle = 0;
|
||||||
|
|
||||||
|
friend class FGLRenderBuffers;
|
||||||
|
friend class PPGLTextureBackend;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PPGLFrameBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Bind()
|
||||||
|
{
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const { return handle != 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLuint handle = 0;
|
||||||
|
|
||||||
|
friend class FGLRenderBuffers;
|
||||||
|
friend class PPGLTextureBackend;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PPGLRenderBuffer
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
GLuint handle = 0;
|
||||||
|
|
||||||
|
explicit operator bool() const { return handle != 0; }
|
||||||
|
|
||||||
|
friend class FGLRenderBuffers;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PPGLTextureBackend : public PPTextureBackend
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~PPGLTextureBackend()
|
||||||
|
{
|
||||||
|
if (Tex.handle != 0)
|
||||||
|
{
|
||||||
|
glDeleteTextures(1, &Tex.handle);
|
||||||
|
Tex.handle = 0;
|
||||||
|
}
|
||||||
|
if (FB.handle != 0)
|
||||||
|
{
|
||||||
|
glDeleteFramebuffers(1, &FB.handle);
|
||||||
|
FB.handle = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PPGLTexture Tex;
|
||||||
|
PPGLFrameBuffer FB;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FShaderProgram;
|
||||||
|
|
||||||
|
class GLPPRenderState : public PPRenderState
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GLPPRenderState(FGLRenderBuffers *buffers) : buffers(buffers) { }
|
||||||
|
|
||||||
|
void PushGroup(const FString &name) override;
|
||||||
|
void PopGroup() override;
|
||||||
|
void Draw() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
PPGLTextureBackend *GetGLTexture(PPTexture *texture);
|
||||||
|
FShaderProgram *GetGLShader(PPShader *shader);
|
||||||
|
|
||||||
|
FGLRenderBuffers *buffers;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FGLRenderBuffers
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FGLRenderBuffers();
|
||||||
|
~FGLRenderBuffers();
|
||||||
|
|
||||||
|
void Setup(int width, int height, int sceneWidth, int sceneHeight);
|
||||||
|
|
||||||
|
void BindSceneFB(bool sceneData);
|
||||||
|
void BindSceneColorTexture(int index);
|
||||||
|
void BindSceneFogTexture(int index);
|
||||||
|
void BindSceneNormalTexture(int index);
|
||||||
|
void BindSceneDepthTexture(int index);
|
||||||
|
void BlitSceneToTexture();
|
||||||
|
|
||||||
|
void BindCurrentTexture(int index, int filter = GL_NEAREST, int wrap = GL_CLAMP_TO_EDGE);
|
||||||
|
void BindCurrentFB();
|
||||||
|
void BindNextFB();
|
||||||
|
void NextTexture();
|
||||||
|
|
||||||
|
PPGLFrameBuffer GetCurrentFB() const { return mPipelineFB[mCurrentPipelineTexture]; }
|
||||||
|
|
||||||
|
void BindOutputFB();
|
||||||
|
|
||||||
|
void BlitToEyeTexture(int eye, bool allowInvalidate=true);
|
||||||
|
void BlitFromEyeTexture(int eye);
|
||||||
|
void BindEyeTexture(int eye, int texunit);
|
||||||
|
int NextEye(int eyeCount);
|
||||||
|
int & CurrentEye() { return mCurrentEye; }
|
||||||
|
|
||||||
|
void BindDitherTexture(int texunit);
|
||||||
|
|
||||||
|
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 CreateScene(int width, int height, int samples, bool needsSceneTextures);
|
||||||
|
void CreatePipeline(int width, int height);
|
||||||
|
void CreateEyeBuffers(int eye);
|
||||||
|
|
||||||
|
PPGLTexture Create2DTexture(const char *name, GLuint format, int width, int height, const void *data = nullptr);
|
||||||
|
PPGLTexture Create2DMultisampleTexture(const char *name, GLuint format, int width, int height, int samples, bool fixedSampleLocations);
|
||||||
|
PPGLRenderBuffer CreateRenderBuffer(const char *name, GLuint format, int width, int height);
|
||||||
|
PPGLRenderBuffer CreateRenderBuffer(const char *name, GLuint format, int width, int height, int samples);
|
||||||
|
PPGLFrameBuffer CreateFrameBuffer(const char *name, PPGLTexture colorbuffer);
|
||||||
|
PPGLFrameBuffer CreateFrameBuffer(const char *name, PPGLTexture colorbuffer, PPGLRenderBuffer depthstencil);
|
||||||
|
PPGLFrameBuffer CreateFrameBuffer(const char *name, PPGLRenderBuffer colorbuffer, PPGLRenderBuffer depthstencil);
|
||||||
|
PPGLFrameBuffer CreateFrameBuffer(const char *name, PPGLTexture colorbuffer0, PPGLTexture colorbuffer1, PPGLTexture colorbuffer2, PPGLTexture depthstencil, bool multisample);
|
||||||
|
bool CheckFrameBufferCompleteness();
|
||||||
|
void ClearFrameBuffer(bool stencil, bool depth);
|
||||||
|
void DeleteTexture(PPGLTexture &handle);
|
||||||
|
void DeleteRenderBuffer(PPGLRenderBuffer &handle);
|
||||||
|
void DeleteFrameBuffer(PPGLFrameBuffer &handle);
|
||||||
|
|
||||||
|
int mWidth = 0;
|
||||||
|
int mHeight = 0;
|
||||||
|
int mSamples = 0;
|
||||||
|
int mMaxSamples = 0;
|
||||||
|
int mSceneWidth = 0;
|
||||||
|
int mSceneHeight = 0;
|
||||||
|
|
||||||
|
static const int NumPipelineTextures = 2;
|
||||||
|
int mCurrentPipelineTexture = 0;
|
||||||
|
|
||||||
|
// Buffers for the scene
|
||||||
|
PPGLTexture mSceneMultisampleTex;
|
||||||
|
PPGLTexture mSceneDepthStencilTex;
|
||||||
|
PPGLTexture mSceneFogTex;
|
||||||
|
PPGLTexture mSceneNormalTex;
|
||||||
|
PPGLRenderBuffer mSceneMultisampleBuf;
|
||||||
|
PPGLRenderBuffer mSceneDepthStencilBuf;
|
||||||
|
PPGLRenderBuffer mSceneFogBuf;
|
||||||
|
PPGLRenderBuffer mSceneNormalBuf;
|
||||||
|
PPGLFrameBuffer mSceneFB;
|
||||||
|
PPGLFrameBuffer mSceneDataFB;
|
||||||
|
|
||||||
|
// Effect/HUD buffers
|
||||||
|
PPGLTexture mPipelineTexture[NumPipelineTextures];
|
||||||
|
PPGLFrameBuffer mPipelineFB[NumPipelineTextures];
|
||||||
|
|
||||||
|
// Eye buffers
|
||||||
|
TArray<PPGLTexture> mEyeTextures;
|
||||||
|
TArray<PPGLFrameBuffer> mEyeFBs;
|
||||||
|
int mCurrentEye = 0;
|
||||||
|
|
||||||
|
// Shadow map texture
|
||||||
|
PPGLTexture mShadowMapTexture;
|
||||||
|
PPGLFrameBuffer mShadowMapFB;
|
||||||
|
int mCurrentShadowMapSize = 0;
|
||||||
|
|
||||||
|
PPGLTexture mDitherTexture;
|
||||||
|
|
||||||
|
static bool FailedCreate;
|
||||||
|
|
||||||
|
friend class GLPPRenderState;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
243
source/common/rendering/gl/renderer/gl_renderer.cpp
Normal file
243
source/common/rendering/gl/renderer/gl_renderer.cpp
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Copyright(C) 2005-2016 Christoph Oelckers
|
||||||
|
// 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/
|
||||||
|
//
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
/*
|
||||||
|
** gl1_renderer.cpp
|
||||||
|
** Renderer interface
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gl_load/gl_system.h"
|
||||||
|
#include "files.h"
|
||||||
|
#include "v_video.h"
|
||||||
|
#include "m_png.h"
|
||||||
|
#include "filesystem.h"
|
||||||
|
#include "i_time.h"
|
||||||
|
#include "cmdlib.h"
|
||||||
|
//#include "swrenderer/r_swscene.h"
|
||||||
|
//#include "hwrenderer/utility/hw_clock.h"
|
||||||
|
|
||||||
|
#include "gl_load/gl_interface.h"
|
||||||
|
#include "gl/system/gl_framebuffer.h"
|
||||||
|
#include "gamecvars.h"
|
||||||
|
#include "gl/system/gl_debug.h"
|
||||||
|
#include "gl/renderer/gl_renderer.h"
|
||||||
|
//#include "gl/renderer/gl_renderstate.h"
|
||||||
|
#include "gl/renderer/gl_renderbuffers.h"
|
||||||
|
#include "gl/shaders/gl_shaderprogram.h"
|
||||||
|
//#include "hwrenderer/data/flatvertices.h"
|
||||||
|
//#include "gl/textures/gl_samplers.h"
|
||||||
|
//#include "hwrenderer/dynlights/hw_lightbuffer.h"
|
||||||
|
//#include "hwrenderer/data/hw_viewpointbuffer.h"
|
||||||
|
#include "r_videoscale.h"
|
||||||
|
//#include "r_data/models/models.h"
|
||||||
|
#include "gl/renderer/gl_postprocessstate.h"
|
||||||
|
#include "gl/system/gl_buffers.h"
|
||||||
|
|
||||||
|
EXTERN_CVAR(Int, screenblocks)
|
||||||
|
EXTERN_CVAR(Bool, cl_capfps)
|
||||||
|
|
||||||
|
extern bool NoInterpolateView;
|
||||||
|
|
||||||
|
void DoWriteSavePic(FileWriter *file, ESSType ssformat, uint8_t *scr, int width, int height, sector_t *viewsector, bool upsidedown);
|
||||||
|
|
||||||
|
namespace OpenGLRenderer
|
||||||
|
{
|
||||||
|
|
||||||
|
FGLRenderer* GLRenderer;
|
||||||
|
//===========================================================================
|
||||||
|
//
|
||||||
|
// Renderer interface
|
||||||
|
//
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Initialize
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb)
|
||||||
|
{
|
||||||
|
framebuffer = fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLRenderer::Initialize(int width, int height)
|
||||||
|
{
|
||||||
|
mScreenBuffers = new FGLRenderBuffers();
|
||||||
|
mSaveBuffers = new FGLRenderBuffers();
|
||||||
|
mBuffers = mScreenBuffers;
|
||||||
|
mPresentShader = new FPresentShader();
|
||||||
|
mPresent3dCheckerShader = new FPresent3DCheckerShader();
|
||||||
|
mPresent3dColumnShader = new FPresent3DColumnShader();
|
||||||
|
mPresent3dRowShader = new FPresent3DRowShader();
|
||||||
|
|
||||||
|
//glGenQueries(1, &PortalQueryObject);
|
||||||
|
|
||||||
|
// needed for the core profile, because someone decided it was a good idea to remove the default VAO.
|
||||||
|
glGenVertexArrays(1, &mVAOID);
|
||||||
|
glBindVertexArray(mVAOID);
|
||||||
|
FGLDebug::LabelObject(GL_VERTEX_ARRAY, mVAOID, "FGLRenderer.mVAOID");
|
||||||
|
|
||||||
|
mFBID = 0;
|
||||||
|
mOldFBID = 0;
|
||||||
|
|
||||||
|
//mShaderManager = new FShaderManager;
|
||||||
|
//mSamplerManager = new FSamplerManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
FGLRenderer::~FGLRenderer()
|
||||||
|
{
|
||||||
|
//FlushModels();
|
||||||
|
//TexMan.FlushAll();
|
||||||
|
//if (mShaderManager != nullptr) delete mShaderManager;
|
||||||
|
//if (mSamplerManager != nullptr) delete mSamplerManager;
|
||||||
|
if (mFBID != 0) glDeleteFramebuffers(1, &mFBID);
|
||||||
|
if (mVAOID != 0)
|
||||||
|
{
|
||||||
|
glBindVertexArray(0);
|
||||||
|
glDeleteVertexArrays(1, &mVAOID);
|
||||||
|
}
|
||||||
|
//if (PortalQueryObject != 0) glDeleteQueries(1, &PortalQueryObject);
|
||||||
|
|
||||||
|
//if (swdrawer) delete swdrawer;
|
||||||
|
if (mBuffers) delete mBuffers;
|
||||||
|
if (mSaveBuffers) delete mSaveBuffers;
|
||||||
|
if (mPresentShader) delete mPresentShader;
|
||||||
|
if (mPresent3dCheckerShader) delete mPresent3dCheckerShader;
|
||||||
|
if (mPresent3dColumnShader) delete mPresent3dColumnShader;
|
||||||
|
if (mPresent3dRowShader) delete mPresent3dRowShader;
|
||||||
|
}
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
bool FGLRenderer::StartOffscreen()
|
||||||
|
{
|
||||||
|
bool firstBind = (mFBID == 0);
|
||||||
|
if (mFBID == 0)
|
||||||
|
glGenFramebuffers(1, &mFBID);
|
||||||
|
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mOldFBID);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, mFBID);
|
||||||
|
if (firstBind)
|
||||||
|
FGLDebug::LabelObject(GL_FRAMEBUFFER, mFBID, "OffscreenFB");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
void FGLRenderer::EndOffscreen()
|
||||||
|
{
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, mOldFBID);
|
||||||
|
}
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
void FGLRenderer::BindToFrameBuffer(FMaterial *mat)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
auto BaseLayer = static_cast<FHardwareTexture*>(mat->GetLayer(0, 0));
|
||||||
|
|
||||||
|
if (BaseLayer == nullptr)
|
||||||
|
{
|
||||||
|
// must create the hardware texture first
|
||||||
|
BaseLayer->BindOrCreate(mat->sourcetex, 0, 0, 0, 0);
|
||||||
|
FHardwareTexture::Unbind(0);
|
||||||
|
gl_RenderState.ClearLastMaterial();
|
||||||
|
}
|
||||||
|
BaseLayer->BindToFrameBuffer(mat->GetWidth(), mat->GetHeight());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
//
|
||||||
|
// Render the view to a savegame picture
|
||||||
|
//
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
void FGLRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, int height)
|
||||||
|
{
|
||||||
|
IntRect bounds;
|
||||||
|
bounds.left = 0;
|
||||||
|
bounds.top = 0;
|
||||||
|
bounds.width = width;
|
||||||
|
bounds.height = height;
|
||||||
|
|
||||||
|
// we must be sure the GPU finished reading from the buffer before we fill it with new data.
|
||||||
|
glFinish();
|
||||||
|
|
||||||
|
// Switch to render buffers dimensioned for the savepic
|
||||||
|
mBuffers = mSaveBuffers;
|
||||||
|
|
||||||
|
hw_ClearFakeFlat();
|
||||||
|
gl_RenderState.SetVertexBuffer(screen->mVertexData);
|
||||||
|
screen->mVertexData->Reset();
|
||||||
|
screen->mLights->Clear();
|
||||||
|
screen->mViewpoints->Clear();
|
||||||
|
|
||||||
|
// This shouldn't overwrite the global viewpoint even for a short time.
|
||||||
|
FRenderViewpoint savevp;
|
||||||
|
sector_t *viewsector = RenderViewpoint(savevp, players[consoleplayer].camera, &bounds, r_viewpoint.FieldOfView.Degrees, 1.6f, 1.6f, true, false);
|
||||||
|
glDisable(GL_STENCIL_TEST);
|
||||||
|
gl_RenderState.SetNoSoftLightLevel();
|
||||||
|
CopyToBackbuffer(&bounds, false);
|
||||||
|
|
||||||
|
// strictly speaking not needed as the glReadPixels should block until the scene is rendered, but this is to safeguard against shitty drivers
|
||||||
|
glFinish();
|
||||||
|
|
||||||
|
int numpixels = width * height;
|
||||||
|
uint8_t * scr = (uint8_t *)M_Malloc(numpixels * 3);
|
||||||
|
glReadPixels(0,0,width, height,GL_RGB,GL_UNSIGNED_BYTE,scr);
|
||||||
|
|
||||||
|
DoWriteSavePic(file, SS_RGB, scr, width, height, viewsector, true);
|
||||||
|
M_Free(scr);
|
||||||
|
|
||||||
|
// Switch back the screen render buffers
|
||||||
|
screen->SetViewportRects(nullptr);
|
||||||
|
mBuffers = mScreenBuffers;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
void FGLRenderer::BeginFrame()
|
||||||
|
{
|
||||||
|
mScreenBuffers->Setup(screen->mScreenViewport.width, screen->mScreenViewport.height, screen->mSceneViewport.width, screen->mSceneViewport.height);
|
||||||
|
//mSaveBuffers->Setup(SAVEPICWIDTH, SAVEPICHEIGHT, SAVEPICWIDTH, SAVEPICHEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
121
source/common/rendering/gl/renderer/gl_renderer.h
Normal file
121
source/common/rendering/gl/renderer/gl_renderer.h
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
#ifndef __GL_RENDERER_H
|
||||||
|
#define __GL_RENDERER_H
|
||||||
|
|
||||||
|
#include "v_video.h"
|
||||||
|
#include "vectors.h"
|
||||||
|
#include "matrix.h"
|
||||||
|
#include "gl/renderer/gl_renderbuffers.h"
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(disable:4244)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct particle_t;
|
||||||
|
class FCanvasTexture;
|
||||||
|
class FFlatVertexBuffer;
|
||||||
|
class FSkyVertexBuffer;
|
||||||
|
class FShaderManager;
|
||||||
|
class HWPortal;
|
||||||
|
class FLightBuffer;
|
||||||
|
class DPSprite;
|
||||||
|
class FGLRenderBuffers;
|
||||||
|
class FGL2DDrawer;
|
||||||
|
class FHardwareTexture;
|
||||||
|
class SWSceneDrawer;
|
||||||
|
class HWViewpointBuffer;
|
||||||
|
struct FRenderViewpoint;
|
||||||
|
|
||||||
|
namespace OpenGLRenderer
|
||||||
|
{
|
||||||
|
class FSamplerManager;
|
||||||
|
class OpenGLFrameBuffer;
|
||||||
|
class FPresentShaderBase;
|
||||||
|
class FPresentShader;
|
||||||
|
class FPresent3DCheckerShader;
|
||||||
|
class FPresent3DColumnShader;
|
||||||
|
class FPresent3DRowShader;
|
||||||
|
|
||||||
|
class FGLRenderer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
OpenGLFrameBuffer *framebuffer;
|
||||||
|
int mMirrorCount = 0;
|
||||||
|
int mPlaneMirrorCount = 0;
|
||||||
|
//FShaderManager *mShaderManager = nullptr;
|
||||||
|
//FSamplerManager *mSamplerManager = nullptr;
|
||||||
|
unsigned int mFBID;
|
||||||
|
unsigned int mVAOID;
|
||||||
|
//unsigned int PortalQueryObject;
|
||||||
|
unsigned int mStencilValue = 0;
|
||||||
|
|
||||||
|
int mOldFBID;
|
||||||
|
|
||||||
|
FGLRenderBuffers *mBuffers = nullptr;
|
||||||
|
FGLRenderBuffers *mScreenBuffers = nullptr;
|
||||||
|
FGLRenderBuffers *mSaveBuffers = nullptr;
|
||||||
|
FPresentShader *mPresentShader = nullptr;
|
||||||
|
FPresent3DCheckerShader *mPresent3dCheckerShader = nullptr;
|
||||||
|
FPresent3DColumnShader *mPresent3dColumnShader = nullptr;
|
||||||
|
FPresent3DRowShader *mPresent3dRowShader = nullptr;
|
||||||
|
|
||||||
|
//FRotator mAngles;
|
||||||
|
|
||||||
|
//SWSceneDrawer *swdrawer = nullptr;
|
||||||
|
|
||||||
|
FGLRenderer(OpenGLFrameBuffer *fb);
|
||||||
|
~FGLRenderer() ;
|
||||||
|
|
||||||
|
void Initialize(int width, int height);
|
||||||
|
|
||||||
|
void ClearBorders();
|
||||||
|
|
||||||
|
void PresentStereo();
|
||||||
|
void RenderScreenQuad();
|
||||||
|
void PostProcessScene(int fixedcm, const std::function<void()> &afterBloomDrawEndScene2D);
|
||||||
|
void AmbientOccludeScene(float m5);
|
||||||
|
void ClearTonemapPalette();
|
||||||
|
void BlurScene(float gameinfobluramount);
|
||||||
|
void CopyToBackbuffer(const IntRect *bounds, bool applyGamma);
|
||||||
|
void DrawPresentTexture(const IntRect &box, bool applyGamma);
|
||||||
|
void Flush();
|
||||||
|
//void Draw2D(F2DDrawer *data);
|
||||||
|
#if 0
|
||||||
|
void WriteSavePic(player_t *player, FileWriter *file, int width, int height);
|
||||||
|
#endif
|
||||||
|
void BeginFrame();
|
||||||
|
|
||||||
|
|
||||||
|
bool StartOffscreen();
|
||||||
|
void EndOffscreen();
|
||||||
|
|
||||||
|
void BindToFrameBuffer(FMaterial *mat);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void DrawScene(HWDrawInfo *di, int drawmode);
|
||||||
|
bool QuadStereoCheckInitialRenderContextState();
|
||||||
|
void PresentAnaglyph(bool r, bool g, bool b);
|
||||||
|
void PresentSideBySide();
|
||||||
|
void PresentTopBottom();
|
||||||
|
void prepareInterleavedPresent(FPresentShaderBase& shader);
|
||||||
|
void PresentColumnInterleaved();
|
||||||
|
void PresentRowInterleaved();
|
||||||
|
void PresentCheckerInterleaved();
|
||||||
|
void PresentQuadStereo();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TexFilter_s
|
||||||
|
{
|
||||||
|
int minfilter;
|
||||||
|
int magfilter;
|
||||||
|
bool mipmapping;
|
||||||
|
} ;
|
||||||
|
|
||||||
|
|
||||||
|
extern FGLRenderer *GLRenderer;
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
355
source/common/rendering/gl/shaders/gl_shaderprogram.cpp
Normal file
355
source/common/rendering/gl/shaders/gl_shaderprogram.cpp
Normal file
|
@ -0,0 +1,355 @@
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// 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_shaderprogram.cpp
|
||||||
|
** GLSL shader program compile and link
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gl_load/gl_system.h"
|
||||||
|
#include "v_video.h"
|
||||||
|
#include "gl_load/gl_interface.h"
|
||||||
|
#include "gamecvars.h"
|
||||||
|
#include "gl/system/gl_debug.h"
|
||||||
|
#include "gl/shaders/gl_shaderprogram.h"
|
||||||
|
#include "hwrenderer/utility/hw_shaderpatcher.h"
|
||||||
|
#include "filesystem.h"
|
||||||
|
#include "printf.h"
|
||||||
|
|
||||||
|
namespace OpenGLRenderer
|
||||||
|
{
|
||||||
|
|
||||||
|
bool IsShaderCacheActive();
|
||||||
|
TArray<uint8_t> LoadCachedProgramBinary(const FString &vertex, const FString &fragment, uint32_t &binaryFormat);
|
||||||
|
void SaveCachedProgramBinary(const FString &vertex, const FString &fragment, const TArray<uint8_t> &binary, uint32_t binaryFormat);
|
||||||
|
|
||||||
|
FShaderProgram::FShaderProgram()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < NumShaderTypes; i++)
|
||||||
|
mShaders[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Free shader program resources
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
FShaderProgram::~FShaderProgram()
|
||||||
|
{
|
||||||
|
if (mProgram != 0)
|
||||||
|
glDeleteProgram(mProgram);
|
||||||
|
|
||||||
|
for (int i = 0; i < NumShaderTypes; i++)
|
||||||
|
{
|
||||||
|
if (mShaders[i] != 0)
|
||||||
|
glDeleteShader(mShaders[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Creates an OpenGL shader object for the specified type of shader
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FShaderProgram::CreateShader(ShaderType type)
|
||||||
|
{
|
||||||
|
GLenum gltype = 0;
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case Vertex: gltype = GL_VERTEX_SHADER; break;
|
||||||
|
case Fragment: gltype = GL_FRAGMENT_SHADER; break;
|
||||||
|
}
|
||||||
|
mShaders[type] = glCreateShader(gltype);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Compiles a shader and attaches it the program object
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FShaderProgram::Compile(ShaderType type, const char *lumpName, const char *defines, int maxGlslVersion)
|
||||||
|
{
|
||||||
|
int lump = fileSystem.FindFile(lumpName);
|
||||||
|
if (lump == -1) I_FatalError("Unable to load '%s'", lumpName);
|
||||||
|
FString code = fileSystem.ReadFile(lump).GetString().GetChars();
|
||||||
|
Compile(type, lumpName, code, defines, maxGlslVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FShaderProgram::Compile(ShaderType type, const char *name, const FString &code, const char *defines, int maxGlslVersion)
|
||||||
|
{
|
||||||
|
mShaderNames[type] = name;
|
||||||
|
mShaderSources[type] = PatchShader(type, code, defines, maxGlslVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FShaderProgram::CompileShader(ShaderType type)
|
||||||
|
{
|
||||||
|
CreateShader(type);
|
||||||
|
|
||||||
|
const auto &handle = mShaders[type];
|
||||||
|
|
||||||
|
FGLDebug::LabelObject(GL_SHADER, handle, mShaderNames[type]);
|
||||||
|
|
||||||
|
const FString &patchedCode = mShaderSources[type];
|
||||||
|
int lengths[1] = { (int)patchedCode.Len() };
|
||||||
|
const char *sources[1] = { patchedCode.GetChars() };
|
||||||
|
glShaderSource(handle, 1, sources, lengths);
|
||||||
|
|
||||||
|
glCompileShader(handle);
|
||||||
|
|
||||||
|
GLint status = 0;
|
||||||
|
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
|
||||||
|
if (status == GL_FALSE)
|
||||||
|
{
|
||||||
|
I_FatalError("Compile Shader '%s':\n%s\n", mShaderNames[type].GetChars(), GetShaderInfoLog(handle).GetChars());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (mProgram == 0)
|
||||||
|
mProgram = glCreateProgram();
|
||||||
|
glAttachShader(mProgram, handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Links a program with the compiled shaders
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FShaderProgram::Link(const char *name)
|
||||||
|
{
|
||||||
|
FGLDebug::LabelObject(GL_PROGRAM, mProgram, name);
|
||||||
|
|
||||||
|
uint32_t binaryFormat = 0;
|
||||||
|
TArray<uint8_t> binary;
|
||||||
|
#if 0
|
||||||
|
if (IsShaderCacheActive())
|
||||||
|
binary = LoadCachedProgramBinary(mShaderSources[Vertex], mShaderSources[Fragment], binaryFormat);
|
||||||
|
#endif
|
||||||
|
bool loadedFromBinary = false;
|
||||||
|
if (binary.Size() > 0 && glProgramBinary)
|
||||||
|
{
|
||||||
|
if (mProgram == 0)
|
||||||
|
mProgram = glCreateProgram();
|
||||||
|
glProgramBinary(mProgram, binaryFormat, binary.Data(), binary.Size());
|
||||||
|
GLint status = 0;
|
||||||
|
glGetProgramiv(mProgram, GL_LINK_STATUS, &status);
|
||||||
|
loadedFromBinary = (status == GL_TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loadedFromBinary)
|
||||||
|
{
|
||||||
|
CompileShader(Vertex);
|
||||||
|
CompileShader(Fragment);
|
||||||
|
|
||||||
|
glLinkProgram(mProgram);
|
||||||
|
|
||||||
|
GLint status = 0;
|
||||||
|
glGetProgramiv(mProgram, GL_LINK_STATUS, &status);
|
||||||
|
if (status == GL_FALSE)
|
||||||
|
{
|
||||||
|
I_FatalError("Link Shader '%s':\n%s\n", name, GetProgramInfoLog(mProgram).GetChars());
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
else if (glProgramBinary && IsShaderCacheActive())
|
||||||
|
{
|
||||||
|
int binaryLength = 0;
|
||||||
|
glGetProgramiv(mProgram, GL_PROGRAM_BINARY_LENGTH, &binaryLength);
|
||||||
|
binary.Resize(binaryLength);
|
||||||
|
glGetProgramBinary(mProgram, binary.Size(), &binaryLength, &binaryFormat, binary.Data());
|
||||||
|
binary.Resize(binaryLength);
|
||||||
|
SaveCachedProgramBinary(mShaderSources[Vertex], mShaderSources[Fragment], binary, binaryFormat);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is only for old OpenGL which didn't allow to set the binding from within the shader.
|
||||||
|
if (screen->glslversion < 4.20)
|
||||||
|
{
|
||||||
|
glUseProgram(mProgram);
|
||||||
|
for (auto &uni : samplerstobind)
|
||||||
|
{
|
||||||
|
auto index = glGetUniformLocation(mProgram, uni.first);
|
||||||
|
if (index >= 0)
|
||||||
|
{
|
||||||
|
glUniform1i(index, uni.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
samplerstobind.Clear();
|
||||||
|
samplerstobind.ShrinkToFit();
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Set uniform buffer location (only useful for GL 3.3)
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FShaderProgram::SetUniformBufferLocation(int index, const char *name)
|
||||||
|
{
|
||||||
|
if (screen->glslversion < 4.20)
|
||||||
|
{
|
||||||
|
GLuint uniformBlockIndex = glGetUniformBlockIndex(mProgram, name);
|
||||||
|
if (uniformBlockIndex != GL_INVALID_INDEX)
|
||||||
|
glUniformBlockBinding(mProgram, uniformBlockIndex, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Makes the shader the active program
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FShaderProgram::Bind()
|
||||||
|
{
|
||||||
|
glUseProgram(mProgram);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Returns the shader info log (warnings and compile errors)
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
FString FShaderProgram::GetShaderInfoLog(GLuint handle)
|
||||||
|
{
|
||||||
|
static char buffer[10000];
|
||||||
|
GLsizei length = 0;
|
||||||
|
buffer[0] = 0;
|
||||||
|
glGetShaderInfoLog(handle, 10000, &length, buffer);
|
||||||
|
return FString(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Returns the program info log (warnings and compile errors)
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
FString FShaderProgram::GetProgramInfoLog(GLuint handle)
|
||||||
|
{
|
||||||
|
static char buffer[10000];
|
||||||
|
GLsizei length = 0;
|
||||||
|
buffer[0] = 0;
|
||||||
|
glGetProgramInfoLog(handle, 10000, &length, buffer);
|
||||||
|
return FString(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Patches a shader to be compatible with the version of OpenGL in use
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
FString FShaderProgram::PatchShader(ShaderType type, const FString &code, const char *defines, int maxGlslVersion)
|
||||||
|
{
|
||||||
|
FString patchedCode;
|
||||||
|
|
||||||
|
// If we have 4.2, always use it because it adds important new syntax.
|
||||||
|
if (maxGlslVersion < 420 && gl.glslversion >= 4.2f) maxGlslVersion = 420;
|
||||||
|
int shaderVersion = std::min((int)round(gl.glslversion * 10) * 10, maxGlslVersion);
|
||||||
|
patchedCode.AppendFormat("#version %d\n", shaderVersion);
|
||||||
|
|
||||||
|
// TODO: Find some way to add extension requirements to the patching
|
||||||
|
//
|
||||||
|
// #extension GL_ARB_uniform_buffer_object : require
|
||||||
|
// #extension GL_ARB_shader_storage_buffer_object : require
|
||||||
|
|
||||||
|
if (defines)
|
||||||
|
patchedCode << defines;
|
||||||
|
|
||||||
|
// these settings are actually pointless but there seem to be some old ATI drivers that fail to compile the shader without setting the precision here.
|
||||||
|
patchedCode << "precision highp int;\n";
|
||||||
|
patchedCode << "precision highp float;\n";
|
||||||
|
|
||||||
|
patchedCode << "#line 1\n";
|
||||||
|
patchedCode << RemoveLayoutLocationDecl(code, type == Vertex ? "out" : "in");
|
||||||
|
|
||||||
|
if (maxGlslVersion < 420)
|
||||||
|
{
|
||||||
|
// Here we must strip out all layout(binding) declarations for sampler uniforms and store them in 'samplerstobind' which can then be processed by the link function.
|
||||||
|
patchedCode = RemoveSamplerBindings(patchedCode, samplerstobind);
|
||||||
|
}
|
||||||
|
|
||||||
|
return patchedCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void FPresentShaderBase::Init(const char * vtx_shader_name, const char * program_name)
|
||||||
|
{
|
||||||
|
FString prolog = Uniforms.CreateDeclaration("Uniforms", PresentUniforms::Desc());
|
||||||
|
|
||||||
|
mShader.reset(new FShaderProgram());
|
||||||
|
mShader->Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", prolog, 330);
|
||||||
|
mShader->Compile(FShaderProgram::Fragment, vtx_shader_name, prolog, 330);
|
||||||
|
mShader->Link(program_name);
|
||||||
|
mShader->SetUniformBufferLocation(Uniforms.BindingPoint(), "Uniforms");
|
||||||
|
Uniforms.Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FPresentShader::Bind()
|
||||||
|
{
|
||||||
|
if (!mShader)
|
||||||
|
{
|
||||||
|
Init("shaders/glsl/present.fp", "shaders/glsl/present");
|
||||||
|
}
|
||||||
|
mShader->Bind();
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void FPresent3DCheckerShader::Bind()
|
||||||
|
{
|
||||||
|
if (!mShader)
|
||||||
|
{
|
||||||
|
Init("shaders/glsl/present_checker3d.fp", "shaders/glsl/presentChecker3d");
|
||||||
|
}
|
||||||
|
mShader->Bind();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FPresent3DColumnShader::Bind()
|
||||||
|
{
|
||||||
|
if (!mShader)
|
||||||
|
{
|
||||||
|
Init("shaders/glsl/present_column3d.fp", "shaders/glsl/presentColumn3d");
|
||||||
|
}
|
||||||
|
mShader->Bind();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FPresent3DRowShader::Bind()
|
||||||
|
{
|
||||||
|
if (!mShader)
|
||||||
|
{
|
||||||
|
Init("shaders/glsl/present_row3d.fp", "shaders/glsl/presentRow3d");
|
||||||
|
}
|
||||||
|
mShader->Bind();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
93
source/common/rendering/gl/shaders/gl_shaderprogram.h
Normal file
93
source/common/rendering/gl/shaders/gl_shaderprogram.h
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "gl_load/gl_system.h"
|
||||||
|
//#include "gl_shader.h"
|
||||||
|
#include "hwrenderer/postprocessing/hw_postprocess.h"
|
||||||
|
|
||||||
|
namespace OpenGLRenderer
|
||||||
|
{
|
||||||
|
|
||||||
|
class FShaderProgram : public PPShaderBackend
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FShaderProgram();
|
||||||
|
~FShaderProgram();
|
||||||
|
|
||||||
|
enum ShaderType
|
||||||
|
{
|
||||||
|
Vertex,
|
||||||
|
Fragment,
|
||||||
|
NumShaderTypes
|
||||||
|
};
|
||||||
|
|
||||||
|
void Compile(ShaderType type, const char *lumpName, const char *defines, int maxGlslVersion);
|
||||||
|
void Compile(ShaderType type, const char *name, const FString &code, const char *defines, int maxGlslVersion);
|
||||||
|
void Link(const char *name);
|
||||||
|
void SetUniformBufferLocation(int index, const char *name);
|
||||||
|
|
||||||
|
void Bind();
|
||||||
|
|
||||||
|
GLuint Handle() { return mProgram; }
|
||||||
|
//explicit operator bool() const { return mProgram != 0; }
|
||||||
|
|
||||||
|
std::unique_ptr<IDataBuffer> Uniforms;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FShaderProgram(const FShaderProgram &) = delete;
|
||||||
|
FShaderProgram &operator=(const FShaderProgram &) = delete;
|
||||||
|
|
||||||
|
void CompileShader(ShaderType type);
|
||||||
|
FString PatchShader(ShaderType type, const FString &code, const char *defines, int maxGlslVersion);
|
||||||
|
|
||||||
|
void CreateShader(ShaderType type);
|
||||||
|
FString GetShaderInfoLog(GLuint handle);
|
||||||
|
FString GetProgramInfoLog(GLuint handle);
|
||||||
|
|
||||||
|
GLuint mProgram = 0;
|
||||||
|
GLuint mShaders[NumShaderTypes];
|
||||||
|
FString mShaderSources[NumShaderTypes];
|
||||||
|
FString mShaderNames[NumShaderTypes];
|
||||||
|
TArray<std::pair<FString, int>> samplerstobind;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FPresentShaderBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~FPresentShaderBase() {}
|
||||||
|
virtual void Bind() = 0;
|
||||||
|
|
||||||
|
ShaderUniforms<PresentUniforms, POSTPROCESS_BINDINGPOINT> Uniforms;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void Init(const char * vtx_shader_name, const char * program_name);
|
||||||
|
std::unique_ptr<FShaderProgram> mShader;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FPresentShader : public FPresentShaderBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Bind() override;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class FPresent3DCheckerShader : public FPresentShaderBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Bind() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FPresent3DColumnShader : public FPresentShaderBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Bind() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FPresent3DRowShader : public FPresentShaderBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Bind() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "gl_load.h"
|
#include "gl_load.h"
|
||||||
#include "glbackend.h"
|
//#include "glbackend.h"
|
||||||
#include "gl_buffers.h"
|
#include "gl_buffers.h"
|
||||||
|
|
||||||
namespace OpenGLRenderer
|
namespace OpenGLRenderer
|
|
@ -44,9 +44,9 @@ CUSTOM_CVAR(Int, gl_debug_level, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOIN
|
||||||
|
|
||||||
CVAR(Bool, gl_debug_breakpoint, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
|
CVAR(Bool, gl_debug_breakpoint, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
|
||||||
|
|
||||||
bool gpuStatActive;
|
extern bool gpuStatActive;
|
||||||
bool keepGpuStatActive;
|
extern bool keepGpuStatActive;
|
||||||
FString gpuStatOutput;
|
extern FString gpuStatOutput;
|
||||||
|
|
||||||
namespace OpenGLRenderer
|
namespace OpenGLRenderer
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
#include "v_video.h"
|
#include "v_video.h"
|
||||||
#include "printf.h"
|
#include "printf.h"
|
||||||
#include "gl_load/gl_interface.h"
|
#include "gl_load/gl_interface.h"
|
||||||
//#include "hwrenderer/utility/hw_cvars.h"
|
#include "gamecvars.h"
|
||||||
|
|
||||||
static TArray<FString> m_Extensions;
|
static TArray<FString> m_Extensions;
|
||||||
RenderContext gl;
|
RenderContext gl;
|
||||||
|
|
33
source/common/rendering/hwrenderer/data/renderqueue.h
Normal file
33
source/common/rendering/hwrenderer/data/renderqueue.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "tflags.h"
|
||||||
|
|
||||||
|
// A render queue is what contains all render commands.
|
||||||
|
// On Vulkan there can be several of them so this interface is needed to allow for the needed parallelism.
|
||||||
|
// On OpenGL the render state is global so all this will do is to translate the system independent calls into OpenGL API calls.
|
||||||
|
|
||||||
|
enum class ColormaskBits
|
||||||
|
{
|
||||||
|
RED = 1,
|
||||||
|
GREEN = 2,
|
||||||
|
BLUE = 4,
|
||||||
|
ALPHA = 8
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef TFlags<ColormaskBits, uint8_t> Colormask;
|
||||||
|
|
||||||
|
class IRenderQueue
|
||||||
|
{
|
||||||
|
Colormask mColorMask;
|
||||||
|
|
||||||
|
|
||||||
|
Colormask GetColorMask() const
|
||||||
|
{
|
||||||
|
return mColorMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void SetColorMask(Colormask mask) = 0;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
154
source/common/rendering/hwrenderer/data/shaderuniforms.h
Normal file
154
source/common/rendering/hwrenderer/data/shaderuniforms.h
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "hwrenderer/data/buffers.h"
|
||||||
|
#include "v_video.h"
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
LIGHTBUF_BINDINGPOINT = 1,
|
||||||
|
POSTPROCESS_BINDINGPOINT = 2,
|
||||||
|
VIEWPOINT_BINDINGPOINT = 3,
|
||||||
|
LIGHTNODES_BINDINGPOINT = 4,
|
||||||
|
LIGHTLINES_BINDINGPOINT = 5,
|
||||||
|
LIGHTLIST_BINDINGPOINT = 6
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class UniformType
|
||||||
|
{
|
||||||
|
Int,
|
||||||
|
UInt,
|
||||||
|
Float,
|
||||||
|
Vec2,
|
||||||
|
Vec3,
|
||||||
|
Vec4,
|
||||||
|
IVec2,
|
||||||
|
IVec3,
|
||||||
|
IVec4,
|
||||||
|
UVec2,
|
||||||
|
UVec3,
|
||||||
|
UVec4,
|
||||||
|
Mat4
|
||||||
|
};
|
||||||
|
|
||||||
|
class UniformFieldDesc
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UniformFieldDesc() { }
|
||||||
|
UniformFieldDesc(const char *name, UniformType type, std::size_t offset) : Name(name), Type(type), Offset(offset) { }
|
||||||
|
|
||||||
|
const char *Name;
|
||||||
|
UniformType Type;
|
||||||
|
std::size_t Offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UniformBlockDecl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static FString Create(const char *name, const std::vector<UniformFieldDesc> &fields, int bindingpoint)
|
||||||
|
{
|
||||||
|
FString decl;
|
||||||
|
FString layout;
|
||||||
|
if (bindingpoint == -1)
|
||||||
|
{
|
||||||
|
layout = "push_constant";
|
||||||
|
}
|
||||||
|
else if (screen->glslversion < 4.20)
|
||||||
|
{
|
||||||
|
layout = "std140";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
layout.Format("std140, binding = %d", bindingpoint);
|
||||||
|
}
|
||||||
|
decl.Format("layout(%s) uniform %s\n{\n", layout.GetChars(), name);
|
||||||
|
for (size_t i = 0; i < fields.size(); i++)
|
||||||
|
{
|
||||||
|
decl.AppendFormat("\t%s %s;\n", GetTypeStr(fields[i].Type), fields[i].Name);
|
||||||
|
}
|
||||||
|
decl += "};\n";
|
||||||
|
|
||||||
|
return decl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const char *GetTypeStr(UniformType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case UniformType::Int: return "int";
|
||||||
|
case UniformType::UInt: return "uint";
|
||||||
|
case UniformType::Float: return "float";
|
||||||
|
case UniformType::Vec2: return "vec2";
|
||||||
|
case UniformType::Vec3: return "vec3";
|
||||||
|
case UniformType::Vec4: return "vec4";
|
||||||
|
case UniformType::IVec2: return "ivec2";
|
||||||
|
case UniformType::IVec3: return "ivec3";
|
||||||
|
case UniformType::IVec4: return "ivec4";
|
||||||
|
case UniformType::UVec2: return "uvec2";
|
||||||
|
case UniformType::UVec3: return "uvec3";
|
||||||
|
case UniformType::UVec4: return "uvec4";
|
||||||
|
case UniformType::Mat4: return "mat4";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, int bindingpoint>
|
||||||
|
class ShaderUniforms
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShaderUniforms()
|
||||||
|
{
|
||||||
|
memset(&Values, 0, sizeof(Values));
|
||||||
|
}
|
||||||
|
|
||||||
|
~ShaderUniforms()
|
||||||
|
{
|
||||||
|
if (mBuffer != nullptr)
|
||||||
|
delete mBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BindingPoint() const
|
||||||
|
{
|
||||||
|
return bindingpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
FString CreateDeclaration(const char *name, const std::vector<UniformFieldDesc> &fields)
|
||||||
|
{
|
||||||
|
mFields = fields;
|
||||||
|
return UniformBlockDecl::Create(name, fields, bindingpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init()
|
||||||
|
{
|
||||||
|
if (mBuffer == nullptr)
|
||||||
|
mBuffer = screen->CreateDataBuffer(bindingpoint, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetData()
|
||||||
|
{
|
||||||
|
if (mBuffer != nullptr)
|
||||||
|
mBuffer->SetData(sizeof(T), &Values);
|
||||||
|
}
|
||||||
|
|
||||||
|
IDataBuffer* GetBuffer() const
|
||||||
|
{
|
||||||
|
// OpenGL needs to mess around with this in ways that should not be part of the interface.
|
||||||
|
return mBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *operator->() { return &Values; }
|
||||||
|
const T *operator->() const { return &Values; }
|
||||||
|
|
||||||
|
T Values;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ShaderUniforms(const ShaderUniforms &) = delete;
|
||||||
|
ShaderUniforms &operator=(const ShaderUniforms &) = delete;
|
||||||
|
|
||||||
|
IDataBuffer *mBuffer = nullptr;
|
||||||
|
std::vector<UniformFieldDesc> mFields;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,826 @@
|
||||||
|
|
||||||
|
#include "v_video.h"
|
||||||
|
#include "hw_postprocess.h"
|
||||||
|
#include "gamecvars.h"
|
||||||
|
#include "stats.h"
|
||||||
|
#include "imagehelpers.h"
|
||||||
|
#include "hwrenderer/utility/hw_cvars.h"
|
||||||
|
#include "hwrenderer/postprocessing/hw_postprocess_cvars.h"
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
Postprocess hw_postprocess;
|
||||||
|
|
||||||
|
PPResource *PPResource::First = nullptr;
|
||||||
|
|
||||||
|
bool gpuStatActive = false;
|
||||||
|
bool keepGpuStatActive = false;
|
||||||
|
FString gpuStatOutput;
|
||||||
|
|
||||||
|
ADD_STAT(gpu)
|
||||||
|
{
|
||||||
|
keepGpuStatActive = true;
|
||||||
|
return gpuStatOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void PPBloom::UpdateTextures(int width, int height)
|
||||||
|
{
|
||||||
|
if (width == lastWidth && height == lastHeight)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int bloomWidth = (width + 1) / 2;
|
||||||
|
int bloomHeight = (height + 1) / 2;
|
||||||
|
|
||||||
|
for (int i = 0; i < NumBloomLevels; i++)
|
||||||
|
{
|
||||||
|
auto &blevel = levels[i];
|
||||||
|
blevel.Viewport.left = 0;
|
||||||
|
blevel.Viewport.top = 0;
|
||||||
|
blevel.Viewport.width = (bloomWidth + 1) / 2;
|
||||||
|
blevel.Viewport.height = (bloomHeight + 1) / 2;
|
||||||
|
blevel.VTexture = { blevel.Viewport.width, blevel.Viewport.height, PixelFormat::Rgba16f };
|
||||||
|
blevel.HTexture = { blevel.Viewport.width, blevel.Viewport.height, PixelFormat::Rgba16f };
|
||||||
|
|
||||||
|
bloomWidth = blevel.Viewport.width;
|
||||||
|
bloomHeight = blevel.Viewport.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastWidth = width;
|
||||||
|
lastHeight = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PPBloom::RenderBloom(PPRenderState *renderstate, int sceneWidth, int sceneHeight, int fixedcm)
|
||||||
|
{
|
||||||
|
// Only bloom things if enabled and no special fixed light mode is active
|
||||||
|
if (!gl_bloom || sceneWidth <= 0 || sceneHeight <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderstate->PushGroup("bloom");
|
||||||
|
|
||||||
|
UpdateTextures(sceneWidth, sceneHeight);
|
||||||
|
|
||||||
|
ExtractUniforms extractUniforms;
|
||||||
|
extractUniforms.Scale = screen->SceneScale();
|
||||||
|
extractUniforms.Offset = screen->SceneOffset();
|
||||||
|
|
||||||
|
auto &level0 = levels[0];
|
||||||
|
|
||||||
|
// Extract blooming pixels from scene texture:
|
||||||
|
renderstate->Clear();
|
||||||
|
renderstate->Shader = &BloomExtract;
|
||||||
|
renderstate->Uniforms.Set(extractUniforms);
|
||||||
|
renderstate->Viewport = level0.Viewport;
|
||||||
|
renderstate->SetInputCurrent(0, PPFilterMode::Linear);
|
||||||
|
renderstate->SetInputTexture(1, &hw_postprocess.exposure.CameraTexture);
|
||||||
|
renderstate->SetOutputTexture(&level0.VTexture);
|
||||||
|
renderstate->SetNoBlend();
|
||||||
|
renderstate->Draw();
|
||||||
|
|
||||||
|
const float blurAmount = gl_bloom_amount;
|
||||||
|
BlurUniforms blurUniforms;
|
||||||
|
ComputeBlurSamples(7, blurAmount, blurUniforms.SampleWeights);
|
||||||
|
|
||||||
|
// Blur and downscale:
|
||||||
|
for (int i = 0; i < NumBloomLevels - 1; i++)
|
||||||
|
{
|
||||||
|
auto &blevel = levels[i];
|
||||||
|
auto &next = levels[i + 1];
|
||||||
|
|
||||||
|
BlurStep(renderstate, blurUniforms, blevel.VTexture, blevel.HTexture, blevel.Viewport, false);
|
||||||
|
BlurStep(renderstate, blurUniforms, blevel.HTexture, blevel.VTexture, blevel.Viewport, true);
|
||||||
|
|
||||||
|
// Linear downscale:
|
||||||
|
renderstate->Clear();
|
||||||
|
renderstate->Shader = &BloomCombine;
|
||||||
|
renderstate->Uniforms.Clear();
|
||||||
|
renderstate->Viewport = next.Viewport;
|
||||||
|
renderstate->SetInputTexture(0, &blevel.VTexture, PPFilterMode::Linear);
|
||||||
|
renderstate->SetOutputTexture(&next.VTexture);
|
||||||
|
renderstate->SetNoBlend();
|
||||||
|
renderstate->Draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blur and upscale:
|
||||||
|
for (int i = NumBloomLevels - 1; i > 0; i--)
|
||||||
|
{
|
||||||
|
auto &blevel = levels[i];
|
||||||
|
auto &next = levels[i - 1];
|
||||||
|
|
||||||
|
BlurStep(renderstate, blurUniforms, blevel.VTexture, blevel.HTexture, blevel.Viewport, false);
|
||||||
|
BlurStep(renderstate, blurUniforms, blevel.HTexture, blevel.VTexture, blevel.Viewport, true);
|
||||||
|
|
||||||
|
// Linear upscale:
|
||||||
|
renderstate->Clear();
|
||||||
|
renderstate->Shader = &BloomCombine;
|
||||||
|
renderstate->Uniforms.Clear();
|
||||||
|
renderstate->Viewport = next.Viewport;
|
||||||
|
renderstate->SetInputTexture(0, &blevel.VTexture, PPFilterMode::Linear);
|
||||||
|
renderstate->SetOutputTexture(&next.VTexture);
|
||||||
|
renderstate->SetNoBlend();
|
||||||
|
renderstate->Draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
BlurStep(renderstate, blurUniforms, level0.VTexture, level0.HTexture, level0.Viewport, false);
|
||||||
|
BlurStep(renderstate, blurUniforms, level0.HTexture, level0.VTexture, level0.Viewport, true);
|
||||||
|
|
||||||
|
// Add bloom back to scene texture:
|
||||||
|
renderstate->Clear();
|
||||||
|
renderstate->Shader = &BloomCombine;
|
||||||
|
renderstate->Uniforms.Clear();
|
||||||
|
renderstate->Viewport = screen->mSceneViewport;
|
||||||
|
renderstate->SetInputTexture(0, &level0.VTexture, PPFilterMode::Linear);
|
||||||
|
renderstate->SetOutputCurrent();
|
||||||
|
renderstate->SetAdditiveBlend();
|
||||||
|
renderstate->Draw();
|
||||||
|
|
||||||
|
renderstate->PopGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PPBloom::RenderBlur(PPRenderState *renderstate, int sceneWidth, int sceneHeight, float gameinfobluramount)
|
||||||
|
{
|
||||||
|
// No scene, no blur!
|
||||||
|
if (sceneWidth <= 0 || sceneHeight <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
UpdateTextures(sceneWidth, sceneHeight);
|
||||||
|
|
||||||
|
// first, respect the CVar
|
||||||
|
float blurAmount = gl_menu_blur;
|
||||||
|
|
||||||
|
// if CVar is negative, use the gameinfo entry
|
||||||
|
if (gl_menu_blur < 0)
|
||||||
|
blurAmount = gameinfobluramount;
|
||||||
|
|
||||||
|
// if blurAmount == 0 or somehow still returns negative, exit to prevent a crash, clearly we don't want this
|
||||||
|
if (blurAmount <= 0.0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderstate->PushGroup("blur");
|
||||||
|
|
||||||
|
int numLevels = 3;
|
||||||
|
assert(numLevels <= NumBloomLevels);
|
||||||
|
|
||||||
|
auto &level0 = levels[0];
|
||||||
|
|
||||||
|
// Grab the area we want to bloom:
|
||||||
|
renderstate->Clear();
|
||||||
|
renderstate->Shader = &BloomCombine;
|
||||||
|
renderstate->Uniforms.Clear();
|
||||||
|
renderstate->Viewport = level0.Viewport;
|
||||||
|
renderstate->SetInputCurrent(0, PPFilterMode::Linear);
|
||||||
|
renderstate->SetOutputTexture(&level0.VTexture);
|
||||||
|
renderstate->SetNoBlend();
|
||||||
|
renderstate->Draw();
|
||||||
|
|
||||||
|
BlurUniforms blurUniforms;
|
||||||
|
ComputeBlurSamples(7, blurAmount, blurUniforms.SampleWeights);
|
||||||
|
|
||||||
|
// Blur and downscale:
|
||||||
|
for (int i = 0; i < numLevels - 1; i++)
|
||||||
|
{
|
||||||
|
auto &blevel = levels[i];
|
||||||
|
auto &next = levels[i + 1];
|
||||||
|
|
||||||
|
BlurStep(renderstate, blurUniforms, blevel.VTexture, blevel.HTexture, blevel.Viewport, false);
|
||||||
|
BlurStep(renderstate, blurUniforms, blevel.HTexture, blevel.VTexture, blevel.Viewport, true);
|
||||||
|
|
||||||
|
// Linear downscale:
|
||||||
|
renderstate->Clear();
|
||||||
|
renderstate->Shader = &BloomCombine;
|
||||||
|
renderstate->Uniforms.Clear();
|
||||||
|
renderstate->Viewport = next.Viewport;
|
||||||
|
renderstate->SetInputTexture(0, &blevel.VTexture, PPFilterMode::Linear);
|
||||||
|
renderstate->SetOutputTexture(&next.VTexture);
|
||||||
|
renderstate->SetNoBlend();
|
||||||
|
renderstate->Draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blur and upscale:
|
||||||
|
for (int i = numLevels - 1; i > 0; i--)
|
||||||
|
{
|
||||||
|
auto &blevel = levels[i];
|
||||||
|
auto &next = levels[i - 1];
|
||||||
|
|
||||||
|
BlurStep(renderstate, blurUniforms, blevel.VTexture, blevel.HTexture, blevel.Viewport, false);
|
||||||
|
BlurStep(renderstate, blurUniforms, blevel.HTexture, blevel.VTexture, blevel.Viewport, true);
|
||||||
|
|
||||||
|
// Linear upscale:
|
||||||
|
renderstate->Clear();
|
||||||
|
renderstate->Shader = &BloomCombine;
|
||||||
|
renderstate->Uniforms.Clear();
|
||||||
|
renderstate->Viewport = next.Viewport;
|
||||||
|
renderstate->SetInputTexture(0, &blevel.VTexture, PPFilterMode::Linear);
|
||||||
|
renderstate->SetOutputTexture(&next.VTexture);
|
||||||
|
renderstate->SetNoBlend();
|
||||||
|
renderstate->Draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
BlurStep(renderstate, blurUniforms, level0.VTexture, level0.HTexture, level0.Viewport, false);
|
||||||
|
BlurStep(renderstate, blurUniforms, level0.HTexture, level0.VTexture, level0.Viewport, true);
|
||||||
|
|
||||||
|
// Copy blur back to scene texture:
|
||||||
|
renderstate->Clear();
|
||||||
|
renderstate->Shader = &BloomCombine;
|
||||||
|
renderstate->Uniforms.Clear();
|
||||||
|
renderstate->Viewport = screen->mScreenViewport;
|
||||||
|
renderstate->SetInputTexture(0, &level0.VTexture, PPFilterMode::Linear);
|
||||||
|
renderstate->SetOutputCurrent();
|
||||||
|
renderstate->SetNoBlend();
|
||||||
|
renderstate->Draw();
|
||||||
|
|
||||||
|
renderstate->PopGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PPBloom::BlurStep(PPRenderState *renderstate, const BlurUniforms &blurUniforms, PPTexture &input, PPTexture &output, PPViewport viewport, bool vertical)
|
||||||
|
{
|
||||||
|
renderstate->Clear();
|
||||||
|
renderstate->Shader = vertical ? &BlurVertical : &BlurHorizontal;
|
||||||
|
renderstate->Uniforms.Set(blurUniforms);
|
||||||
|
renderstate->Viewport = viewport;
|
||||||
|
renderstate->SetInputTexture(0, &input);
|
||||||
|
renderstate->SetOutputTexture(&output);
|
||||||
|
renderstate->SetNoBlend();
|
||||||
|
renderstate->Draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
float PPBloom::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)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PPBloom::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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void PPLensDistort::Render(PPRenderState *renderstate)
|
||||||
|
{
|
||||||
|
if (gl_lens == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float k[4] =
|
||||||
|
{
|
||||||
|
gl_lens_k,
|
||||||
|
gl_lens_k * gl_lens_chromatic,
|
||||||
|
gl_lens_k * gl_lens_chromatic * gl_lens_chromatic,
|
||||||
|
0.0f
|
||||||
|
};
|
||||||
|
float kcube[4] =
|
||||||
|
{
|
||||||
|
gl_lens_kcube,
|
||||||
|
gl_lens_kcube * gl_lens_chromatic,
|
||||||
|
gl_lens_kcube * gl_lens_chromatic * gl_lens_chromatic,
|
||||||
|
0.0f
|
||||||
|
};
|
||||||
|
|
||||||
|
float aspect = screen->mSceneViewport.width / (float)screen->mSceneViewport.height;
|
||||||
|
|
||||||
|
// Scale factor to keep sampling within the input texture
|
||||||
|
float r2 = aspect * aspect * 0.25f + 0.25f;
|
||||||
|
float sqrt_r2 = sqrt(r2);
|
||||||
|
float f0 = 1.0f + std::max(r2 * (k[0] + kcube[0] * sqrt_r2), 0.0f);
|
||||||
|
float f2 = 1.0f + std::max(r2 * (k[2] + kcube[2] * sqrt_r2), 0.0f);
|
||||||
|
float f = std::max(f0, f2);
|
||||||
|
float scale = 1.0f / f;
|
||||||
|
|
||||||
|
LensUniforms uniforms;
|
||||||
|
uniforms.AspectRatio = aspect;
|
||||||
|
uniforms.Scale = scale;
|
||||||
|
uniforms.LensDistortionCoefficient = k;
|
||||||
|
uniforms.CubicDistortionValue = kcube;
|
||||||
|
|
||||||
|
renderstate->PushGroup("lens");
|
||||||
|
|
||||||
|
renderstate->Clear();
|
||||||
|
renderstate->Shader = &Lens;
|
||||||
|
renderstate->Uniforms.Set(uniforms);
|
||||||
|
renderstate->Viewport = screen->mScreenViewport;
|
||||||
|
renderstate->SetInputCurrent(0, PPFilterMode::Linear);
|
||||||
|
renderstate->SetOutputNext();
|
||||||
|
renderstate->SetNoBlend();
|
||||||
|
renderstate->Draw();
|
||||||
|
|
||||||
|
renderstate->PopGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void PPFXAA::Render(PPRenderState *renderstate)
|
||||||
|
{
|
||||||
|
if (0 == gl_fxaa)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateShaders();
|
||||||
|
|
||||||
|
FXAAUniforms uniforms;
|
||||||
|
uniforms.ReciprocalResolution = { 1.0f / screen->mScreenViewport.width, 1.0f / screen->mScreenViewport.height };
|
||||||
|
|
||||||
|
renderstate->PushGroup("fxaa");
|
||||||
|
|
||||||
|
renderstate->Clear();
|
||||||
|
renderstate->Shader = &FXAALuma;
|
||||||
|
renderstate->Uniforms.Clear();
|
||||||
|
renderstate->Viewport = screen->mScreenViewport;
|
||||||
|
renderstate->SetInputCurrent(0, PPFilterMode::Nearest);
|
||||||
|
renderstate->SetOutputNext();
|
||||||
|
renderstate->SetNoBlend();
|
||||||
|
renderstate->Draw();
|
||||||
|
|
||||||
|
renderstate->Shader = &FXAA;
|
||||||
|
renderstate->Uniforms.Set(uniforms);
|
||||||
|
renderstate->SetInputCurrent(0, PPFilterMode::Linear);
|
||||||
|
renderstate->Draw();
|
||||||
|
|
||||||
|
renderstate->PopGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
int PPFXAA::GetMaxVersion()
|
||||||
|
{
|
||||||
|
return screen->glslversion >= 4.f ? 400 : 330;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PPFXAA::CreateShaders()
|
||||||
|
{
|
||||||
|
if (LastQuality == gl_fxaa)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FXAALuma = { "shaders/glsl/fxaa.fp", "#define FXAA_LUMA_PASS\n", {} };
|
||||||
|
FXAA = { "shaders/glsl/fxaa.fp", GetDefines(), FXAAUniforms::Desc(), GetMaxVersion() };
|
||||||
|
LastQuality = gl_fxaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
FString PPFXAA::GetDefines()
|
||||||
|
{
|
||||||
|
int quality;
|
||||||
|
|
||||||
|
switch (gl_fxaa)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case IFXAAShader::Low: quality = 10; break;
|
||||||
|
case IFXAAShader::Medium: quality = 12; break;
|
||||||
|
case IFXAAShader::High: quality = 29; break;
|
||||||
|
case IFXAAShader::Extreme: quality = 39; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int gatherAlpha = GetMaxVersion() >= 400 ? 1 : 0;
|
||||||
|
|
||||||
|
// TODO: enable FXAA_GATHER4_ALPHA on OpenGL earlier than 4.0
|
||||||
|
// when GL_ARB_gpu_shader5/GL_NV_gpu_shader5 extensions are supported
|
||||||
|
|
||||||
|
FString result;
|
||||||
|
result.Format(
|
||||||
|
"#define FXAA_QUALITY__PRESET %i\n"
|
||||||
|
"#define FXAA_GATHER4_ALPHA %i\n",
|
||||||
|
quality, gatherAlpha);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void PPCameraExposure::Render(PPRenderState *renderstate, int sceneWidth, int sceneHeight)
|
||||||
|
{
|
||||||
|
if (!gl_bloom)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderstate->PushGroup("exposure");
|
||||||
|
|
||||||
|
UpdateTextures(sceneWidth, sceneHeight);
|
||||||
|
|
||||||
|
ExposureExtractUniforms extractUniforms;
|
||||||
|
extractUniforms.Scale = screen->SceneScale();
|
||||||
|
extractUniforms.Offset = screen->SceneOffset();
|
||||||
|
|
||||||
|
ExposureCombineUniforms combineUniforms;
|
||||||
|
combineUniforms.ExposureBase = gl_exposure_base;
|
||||||
|
combineUniforms.ExposureMin = gl_exposure_min;
|
||||||
|
combineUniforms.ExposureScale = gl_exposure_scale;
|
||||||
|
combineUniforms.ExposureSpeed = gl_exposure_speed;
|
||||||
|
|
||||||
|
auto &level0 = ExposureLevels[0];
|
||||||
|
|
||||||
|
// Extract light blevel from scene texture:
|
||||||
|
renderstate->Clear();
|
||||||
|
renderstate->Shader = &ExposureExtract;
|
||||||
|
renderstate->Uniforms.Set(extractUniforms);
|
||||||
|
renderstate->Viewport = level0.Viewport;
|
||||||
|
renderstate->SetInputCurrent(0, PPFilterMode::Linear);
|
||||||
|
renderstate->SetOutputTexture(&level0.Texture);
|
||||||
|
renderstate->SetNoBlend();
|
||||||
|
renderstate->Draw();
|
||||||
|
|
||||||
|
// Find the average value:
|
||||||
|
for (size_t i = 0; i + 1 < ExposureLevels.size(); i++)
|
||||||
|
{
|
||||||
|
auto &blevel = ExposureLevels[i];
|
||||||
|
auto &next = ExposureLevels[i + 1];
|
||||||
|
|
||||||
|
renderstate->Shader = &ExposureAverage;
|
||||||
|
renderstate->Uniforms.Clear();
|
||||||
|
renderstate->Viewport = next.Viewport;
|
||||||
|
renderstate->SetInputTexture(0, &blevel.Texture, PPFilterMode::Linear);
|
||||||
|
renderstate->SetOutputTexture(&next.Texture);
|
||||||
|
renderstate->SetNoBlend();
|
||||||
|
renderstate->Draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine average value with current camera exposure:
|
||||||
|
renderstate->Shader = &ExposureCombine;
|
||||||
|
renderstate->Uniforms.Set(combineUniforms);
|
||||||
|
renderstate->Viewport.left = 0;
|
||||||
|
renderstate->Viewport.top = 0;
|
||||||
|
renderstate->Viewport.width = 1;
|
||||||
|
renderstate->Viewport.height = 1;
|
||||||
|
renderstate->SetInputTexture(0, &ExposureLevels.back().Texture, PPFilterMode::Linear);
|
||||||
|
renderstate->SetOutputTexture(&CameraTexture);
|
||||||
|
if (!FirstExposureFrame)
|
||||||
|
renderstate->SetAlphaBlend();
|
||||||
|
else
|
||||||
|
renderstate->SetNoBlend();
|
||||||
|
renderstate->Draw();
|
||||||
|
|
||||||
|
renderstate->PopGroup();
|
||||||
|
|
||||||
|
FirstExposureFrame = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PPCameraExposure::UpdateTextures(int width, int height)
|
||||||
|
{
|
||||||
|
int firstwidth = std::max(width / 2, 1);
|
||||||
|
int firstheight = std::max(height / 2, 1);
|
||||||
|
|
||||||
|
if (ExposureLevels.size() > 0 && ExposureLevels[0].Viewport.width == firstwidth && ExposureLevels[0].Viewport.height == firstheight)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExposureLevels.clear();
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
width = std::max(width / 2, 1);
|
||||||
|
height = std::max(height / 2, 1);
|
||||||
|
|
||||||
|
PPExposureLevel blevel;
|
||||||
|
blevel.Viewport.left = 0;
|
||||||
|
blevel.Viewport.top = 0;
|
||||||
|
blevel.Viewport.width = width;
|
||||||
|
blevel.Viewport.height = height;
|
||||||
|
blevel.Texture = { blevel.Viewport.width, blevel.Viewport.height, PixelFormat::R32f };
|
||||||
|
ExposureLevels.push_back(std::move(blevel));
|
||||||
|
|
||||||
|
i++;
|
||||||
|
|
||||||
|
} while (width > 1 || height > 1);
|
||||||
|
|
||||||
|
FirstExposureFrame = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void PPTonemap::UpdateTextures()
|
||||||
|
{
|
||||||
|
if (gl_tonemap == Palette && !PaletteTexture.Data)
|
||||||
|
{
|
||||||
|
std::shared_ptr<void> data(new uint32_t[512 * 512], [](void *p) { delete[](uint32_t*)p; });
|
||||||
|
|
||||||
|
uint8_t *lut = (uint8_t *)data.get();
|
||||||
|
for (int r = 0; r < 64; r++)
|
||||||
|
{
|
||||||
|
for (int g = 0; g < 64; g++)
|
||||||
|
{
|
||||||
|
for (int b = 0; b < 64; b++)
|
||||||
|
{
|
||||||
|
PalEntry color = ImageHelpers::BaseColors[(uint8_t)ImageHelpers::PTM_BestColor((r << 2) | (r >> 4), (g << 2) | (g >> 4), (b << 2) | (b >> 4),
|
||||||
|
gl_paltonemap_reverselookup, gl_paltonemap_powtable, 0, 256)];
|
||||||
|
int index = ((r * 64 + g) * 64 + b) * 4;
|
||||||
|
lut[index] = color.r;
|
||||||
|
lut[index + 1] = color.g;
|
||||||
|
lut[index + 2] = color.b;
|
||||||
|
lut[index + 3] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PaletteTexture = { 512, 512, PixelFormat::Rgba8, data };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PPTonemap::Render(PPRenderState *renderstate)
|
||||||
|
{
|
||||||
|
if (gl_tonemap == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateTextures();
|
||||||
|
|
||||||
|
PPShader *shader = nullptr;
|
||||||
|
switch (gl_tonemap)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case Linear: shader = &LinearShader; break;
|
||||||
|
case Reinhard: shader = &ReinhardShader; break;
|
||||||
|
case HejlDawson: shader = &HejlDawsonShader; break;
|
||||||
|
case Uncharted2: shader = &Uncharted2Shader; break;
|
||||||
|
case Palette: shader = &PaletteShader; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderstate->PushGroup("tonemap");
|
||||||
|
|
||||||
|
renderstate->Clear();
|
||||||
|
renderstate->Shader = shader;
|
||||||
|
renderstate->Viewport = screen->mScreenViewport;
|
||||||
|
renderstate->SetInputCurrent(0);
|
||||||
|
if (gl_tonemap == Palette)
|
||||||
|
renderstate->SetInputTexture(1, &PaletteTexture);
|
||||||
|
renderstate->SetOutputNext();
|
||||||
|
renderstate->SetNoBlend();
|
||||||
|
renderstate->Draw();
|
||||||
|
|
||||||
|
renderstate->PopGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
PPAmbientOcclusion::PPAmbientOcclusion()
|
||||||
|
{
|
||||||
|
// Must match quality enum in PPAmbientOcclusion::DeclareShaders
|
||||||
|
double numDirections[NumAmbientRandomTextures] = { 2.0, 4.0, 8.0 };
|
||||||
|
|
||||||
|
std::mt19937 generator(1337);
|
||||||
|
std::uniform_real_distribution<double> distribution(0.0, 1.0);
|
||||||
|
for (int quality = 0; quality < NumAmbientRandomTextures; quality++)
|
||||||
|
{
|
||||||
|
std::shared_ptr<void> data(new int16_t[16 * 4], [](void *p) { delete[](int16_t*)p; });
|
||||||
|
int16_t *randomValues = (int16_t *)data.get();
|
||||||
|
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
{
|
||||||
|
double angle = 2.0 * M_PI * distribution(generator) / numDirections[quality];
|
||||||
|
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[quality] = { 4, 4, PixelFormat::Rgba16_snorm, data };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PPAmbientOcclusion::CreateShaders()
|
||||||
|
{
|
||||||
|
if (gl_ssao == LastQuality)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Must match quality values in PPAmbientOcclusion::UpdateTextures
|
||||||
|
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);
|
||||||
|
|
||||||
|
LinearDepth = { "shaders/glsl/lineardepth.fp", "", LinearDepthUniforms::Desc() };
|
||||||
|
LinearDepthMS = { "shaders/glsl/lineardepth.fp", "#define MULTISAMPLE\n", LinearDepthUniforms::Desc() };
|
||||||
|
AmbientOcclude = { "shaders/glsl/ssao.fp", defines, SSAOUniforms::Desc() };
|
||||||
|
AmbientOccludeMS = { "shaders/glsl/ssao.fp", defines + "\n#define MULTISAMPLE\n", SSAOUniforms::Desc() };
|
||||||
|
BlurVertical = { "shaders/glsl/depthblur.fp", "#define BLUR_VERTICAL\n", DepthBlurUniforms::Desc() };
|
||||||
|
BlurHorizontal = { "shaders/glsl/depthblur.fp", "#define BLUR_HORIZONTAL\n", DepthBlurUniforms::Desc() };
|
||||||
|
Combine = { "shaders/glsl/ssaocombine.fp", "", AmbientCombineUniforms::Desc() };
|
||||||
|
CombineMS = { "shaders/glsl/ssaocombine.fp", "#define MULTISAMPLE\n", AmbientCombineUniforms::Desc() };
|
||||||
|
|
||||||
|
LastQuality = gl_ssao;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PPAmbientOcclusion::UpdateTextures(int width, int height)
|
||||||
|
{
|
||||||
|
if ((width <= 0 || height <= 0) || (width == LastWidth && height == LastHeight))
|
||||||
|
return;
|
||||||
|
|
||||||
|
AmbientWidth = (width + 1) / 2;
|
||||||
|
AmbientHeight = (height + 1) / 2;
|
||||||
|
|
||||||
|
LinearDepthTexture = { AmbientWidth, AmbientHeight, PixelFormat::R32f };
|
||||||
|
Ambient0 = { AmbientWidth, AmbientHeight, PixelFormat::Rg16f };
|
||||||
|
Ambient1 = { AmbientWidth, AmbientHeight, PixelFormat::Rg16f };
|
||||||
|
|
||||||
|
LastWidth = width;
|
||||||
|
LastHeight = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PPAmbientOcclusion::Render(PPRenderState *renderstate, float m5, int sceneWidth, int sceneHeight)
|
||||||
|
{
|
||||||
|
if (gl_ssao == 0 || sceneWidth == 0 || sceneHeight == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateShaders();
|
||||||
|
UpdateTextures(sceneWidth, sceneHeight);
|
||||||
|
|
||||||
|
float bias = gl_ssao_bias;
|
||||||
|
float aoRadius = gl_ssao_radius;
|
||||||
|
const float blurAmount = gl_ssao_blur;
|
||||||
|
float aoStrength = gl_ssao_strength;
|
||||||
|
|
||||||
|
//float tanHalfFovy = tan(fovy * (M_PI / 360.0f));
|
||||||
|
float tanHalfFovy = 1.0f / m5;
|
||||||
|
float invFocalLenX = tanHalfFovy * (sceneWidth / (float)sceneHeight);
|
||||||
|
float invFocalLenY = tanHalfFovy;
|
||||||
|
float nDotVBias = clamp(bias, 0.0f, 1.0f);
|
||||||
|
float r2 = aoRadius * aoRadius;
|
||||||
|
|
||||||
|
float blurSharpness = 1.0f / blurAmount;
|
||||||
|
|
||||||
|
auto sceneScale = screen->SceneScale();
|
||||||
|
auto sceneOffset = screen->SceneOffset();
|
||||||
|
|
||||||
|
int randomTexture = clamp(gl_ssao - 1, 0, NumAmbientRandomTextures - 1);
|
||||||
|
|
||||||
|
LinearDepthUniforms linearUniforms;
|
||||||
|
linearUniforms.SampleIndex = 0;
|
||||||
|
linearUniforms.LinearizeDepthA = 1.0f / screen->GetZFar() - 1.0f / screen->GetZNear();
|
||||||
|
linearUniforms.LinearizeDepthB = std::max(1.0f / screen->GetZNear(), 1.e-8f);
|
||||||
|
linearUniforms.InverseDepthRangeA = 1.0f;
|
||||||
|
linearUniforms.InverseDepthRangeB = 0.0f;
|
||||||
|
linearUniforms.Scale = sceneScale;
|
||||||
|
linearUniforms.Offset = sceneOffset;
|
||||||
|
|
||||||
|
SSAOUniforms ssaoUniforms;
|
||||||
|
ssaoUniforms.SampleIndex = 0;
|
||||||
|
ssaoUniforms.UVToViewA = { 2.0f * invFocalLenX, 2.0f * invFocalLenY };
|
||||||
|
ssaoUniforms.UVToViewB = { -invFocalLenX, -invFocalLenY };
|
||||||
|
ssaoUniforms.InvFullResolution = { 1.0f / AmbientWidth, 1.0f / AmbientHeight };
|
||||||
|
ssaoUniforms.NDotVBias = nDotVBias;
|
||||||
|
ssaoUniforms.NegInvR2 = -1.0f / r2;
|
||||||
|
ssaoUniforms.RadiusToScreen = aoRadius * 0.5f / tanHalfFovy * AmbientHeight;
|
||||||
|
ssaoUniforms.AOMultiplier = 1.0f / (1.0f - nDotVBias);
|
||||||
|
ssaoUniforms.AOStrength = aoStrength;
|
||||||
|
ssaoUniforms.Scale = sceneScale;
|
||||||
|
ssaoUniforms.Offset = sceneOffset;
|
||||||
|
|
||||||
|
DepthBlurUniforms blurUniforms;
|
||||||
|
blurUniforms.BlurSharpness = blurSharpness;
|
||||||
|
blurUniforms.PowExponent = gl_ssao_exponent;
|
||||||
|
|
||||||
|
AmbientCombineUniforms combineUniforms;
|
||||||
|
combineUniforms.SampleCount = gl_multisample;
|
||||||
|
combineUniforms.Scale = screen->SceneScale();
|
||||||
|
combineUniforms.Offset = screen->SceneOffset();
|
||||||
|
combineUniforms.DebugMode = gl_ssao_debug;
|
||||||
|
|
||||||
|
IntRect ambientViewport;
|
||||||
|
ambientViewport.left = 0;
|
||||||
|
ambientViewport.top = 0;
|
||||||
|
ambientViewport.width = AmbientWidth;
|
||||||
|
ambientViewport.height = AmbientHeight;
|
||||||
|
|
||||||
|
renderstate->PushGroup("ssao");
|
||||||
|
|
||||||
|
// Calculate linear depth values
|
||||||
|
renderstate->Clear();
|
||||||
|
renderstate->Shader = gl_multisample > 1 ? &LinearDepthMS : &LinearDepth;
|
||||||
|
renderstate->Uniforms.Set(linearUniforms);
|
||||||
|
renderstate->Viewport = ambientViewport;
|
||||||
|
renderstate->SetInputSceneDepth(0);
|
||||||
|
renderstate->SetInputSceneColor(1);
|
||||||
|
renderstate->SetOutputTexture(&LinearDepthTexture);
|
||||||
|
renderstate->SetNoBlend();
|
||||||
|
renderstate->Draw();
|
||||||
|
|
||||||
|
// Apply ambient occlusion
|
||||||
|
renderstate->Clear();
|
||||||
|
renderstate->Shader = gl_multisample > 1 ? &AmbientOccludeMS : &AmbientOcclude;
|
||||||
|
renderstate->Uniforms.Set(ssaoUniforms);
|
||||||
|
renderstate->Viewport = ambientViewport;
|
||||||
|
renderstate->SetInputTexture(0, &LinearDepthTexture);
|
||||||
|
renderstate->SetInputSceneNormal(1);
|
||||||
|
renderstate->SetInputTexture(2, &AmbientRandomTexture[randomTexture], PPFilterMode::Nearest, PPWrapMode::Repeat);
|
||||||
|
renderstate->SetOutputTexture(&Ambient0);
|
||||||
|
renderstate->SetNoBlend();
|
||||||
|
renderstate->Draw();
|
||||||
|
|
||||||
|
// Blur SSAO texture
|
||||||
|
if (gl_ssao_debug < 2)
|
||||||
|
{
|
||||||
|
renderstate->Clear();
|
||||||
|
renderstate->Shader = &BlurHorizontal;
|
||||||
|
renderstate->Uniforms.Set(blurUniforms);
|
||||||
|
renderstate->Viewport = ambientViewport;
|
||||||
|
renderstate->SetInputTexture(0, &Ambient0);
|
||||||
|
renderstate->SetOutputTexture(&Ambient1);
|
||||||
|
renderstate->SetNoBlend();
|
||||||
|
renderstate->Draw();
|
||||||
|
|
||||||
|
renderstate->Clear();
|
||||||
|
renderstate->Shader = &BlurVertical;
|
||||||
|
renderstate->Uniforms.Set(blurUniforms);
|
||||||
|
renderstate->Viewport = ambientViewport;
|
||||||
|
renderstate->SetInputTexture(0, &Ambient1);
|
||||||
|
renderstate->SetOutputTexture(&Ambient0);
|
||||||
|
renderstate->SetNoBlend();
|
||||||
|
renderstate->Draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add SSAO back to scene texture:
|
||||||
|
renderstate->Clear();
|
||||||
|
renderstate->Shader = gl_multisample > 1 ? &CombineMS : &Combine;
|
||||||
|
renderstate->Uniforms.Set(combineUniforms);
|
||||||
|
renderstate->Viewport = screen->mSceneViewport;
|
||||||
|
if (gl_ssao_debug < 4)
|
||||||
|
renderstate->SetInputTexture(0, &Ambient0, PPFilterMode::Linear);
|
||||||
|
else
|
||||||
|
renderstate->SetInputSceneNormal(0, PPFilterMode::Linear);
|
||||||
|
renderstate->SetInputSceneFog(1);
|
||||||
|
renderstate->SetOutputSceneColor();
|
||||||
|
if (gl_ssao_debug != 0)
|
||||||
|
renderstate->SetNoBlend();
|
||||||
|
else
|
||||||
|
renderstate->SetAlphaBlend();
|
||||||
|
renderstate->Draw();
|
||||||
|
|
||||||
|
renderstate->PopGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
PPPresent::PPPresent()
|
||||||
|
{
|
||||||
|
static const float data[64] =
|
||||||
|
{
|
||||||
|
.0078125, .2578125, .1328125, .3828125, .0234375, .2734375, .1484375, .3984375,
|
||||||
|
.7578125, .5078125, .8828125, .6328125, .7734375, .5234375, .8984375, .6484375,
|
||||||
|
.0703125, .3203125, .1953125, .4453125, .0859375, .3359375, .2109375, .4609375,
|
||||||
|
.8203125, .5703125, .9453125, .6953125, .8359375, .5859375, .9609375, .7109375,
|
||||||
|
.0390625, .2890625, .1640625, .4140625, .0546875, .3046875, .1796875, .4296875,
|
||||||
|
.7890625, .5390625, .9140625, .6640625, .8046875, .5546875, .9296875, .6796875,
|
||||||
|
.1015625, .3515625, .2265625, .4765625, .1171875, .3671875, .2421875, .4921875,
|
||||||
|
.8515625, .6015625, .9765625, .7265625, .8671875, .6171875, .9921875, .7421875,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<void> pixels(new float[64], [](void *p) { delete[](float*)p; });
|
||||||
|
memcpy(pixels.get(), data, 64 * sizeof(float));
|
||||||
|
Dither = { 8, 8, PixelFormat::R32f, pixels };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Postprocess::Pass1(PPRenderState* state, int fixedcm, int sceneWidth, int sceneHeight)
|
||||||
|
{
|
||||||
|
exposure.Render(state, sceneWidth, sceneHeight);
|
||||||
|
bloom.RenderBloom(state, sceneWidth, sceneHeight, fixedcm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Postprocess::Pass2(PPRenderState* state, int fixedcm, int sceneWidth, int sceneHeight)
|
||||||
|
{
|
||||||
|
tonemap.Render(state);
|
||||||
|
lens.Render(state);
|
||||||
|
fxaa.Render(state);
|
||||||
|
}
|
|
@ -0,0 +1,771 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "hwrenderer/data/shaderuniforms.h"
|
||||||
|
#include <memory>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
struct PostProcessShader;
|
||||||
|
|
||||||
|
typedef FRenderStyle PPBlendMode;
|
||||||
|
typedef IntRect PPViewport;
|
||||||
|
|
||||||
|
class PPTexture;
|
||||||
|
class PPShader;
|
||||||
|
|
||||||
|
enum class PPFilterMode { Nearest, Linear };
|
||||||
|
enum class PPWrapMode { Clamp, Repeat };
|
||||||
|
enum class PPTextureType { CurrentPipelineTexture, NextPipelineTexture, PPTexture, SceneColor, SceneFog, SceneNormal, SceneDepth, SwapChain, ShadowMap };
|
||||||
|
|
||||||
|
class PPTextureInput
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PPFilterMode Filter = PPFilterMode::Nearest;
|
||||||
|
PPWrapMode Wrap = PPWrapMode::Clamp;
|
||||||
|
PPTextureType Type = PPTextureType::CurrentPipelineTexture;
|
||||||
|
PPTexture *Texture = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PPOutput
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PPTextureType Type = PPTextureType::NextPipelineTexture;
|
||||||
|
PPTexture *Texture = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PPUniforms
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PPUniforms()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PPUniforms(const PPUniforms &src)
|
||||||
|
{
|
||||||
|
Data = src.Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
~PPUniforms()
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
PPUniforms &operator=(const PPUniforms &src)
|
||||||
|
{
|
||||||
|
Data = src.Data;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clear()
|
||||||
|
{
|
||||||
|
Data.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void Set(const T &v)
|
||||||
|
{
|
||||||
|
if (Data.Size() != (int)sizeof(T))
|
||||||
|
{
|
||||||
|
Data.Resize(sizeof(T));
|
||||||
|
memcpy(Data.Data(), &v, Data.Size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<uint8_t> Data;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PPRenderState
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~PPRenderState() = default;
|
||||||
|
|
||||||
|
virtual void PushGroup(const FString &name) = 0;
|
||||||
|
virtual void PopGroup() = 0;
|
||||||
|
|
||||||
|
virtual void Draw() = 0;
|
||||||
|
|
||||||
|
void Clear()
|
||||||
|
{
|
||||||
|
Shader = nullptr;
|
||||||
|
Textures = TArray<PPTextureInput>();
|
||||||
|
Uniforms = PPUniforms();
|
||||||
|
Viewport = PPViewport();
|
||||||
|
BlendMode = PPBlendMode();
|
||||||
|
Output = PPOutput();
|
||||||
|
ShadowMapBuffers = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetInputTexture(int index, PPTexture *texture, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||||
|
{
|
||||||
|
if ((int)Textures.Size() < index + 1)
|
||||||
|
Textures.Resize(index + 1);
|
||||||
|
auto &tex = Textures[index];
|
||||||
|
tex.Filter = filter;
|
||||||
|
tex.Wrap = wrap;
|
||||||
|
tex.Type = PPTextureType::PPTexture;
|
||||||
|
tex.Texture = texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetInputCurrent(int index, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||||
|
{
|
||||||
|
SetInputSpecialType(index, PPTextureType::CurrentPipelineTexture, filter, wrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetInputSceneColor(int index, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||||
|
{
|
||||||
|
SetInputSpecialType(index, PPTextureType::SceneColor, filter, wrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetInputSceneFog(int index, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||||
|
{
|
||||||
|
SetInputSpecialType(index, PPTextureType::SceneFog, filter, wrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetInputSceneNormal(int index, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||||
|
{
|
||||||
|
SetInputSpecialType(index, PPTextureType::SceneNormal, filter, wrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetInputSceneDepth(int index, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||||
|
{
|
||||||
|
SetInputSpecialType(index, PPTextureType::SceneDepth, filter, wrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetInputSpecialType(int index, PPTextureType type, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||||
|
{
|
||||||
|
if ((int)Textures.Size() < index + 1)
|
||||||
|
Textures.Resize(index + 1);
|
||||||
|
auto &tex = Textures[index];
|
||||||
|
tex.Filter = filter;
|
||||||
|
tex.Wrap = wrap;
|
||||||
|
tex.Type = type;
|
||||||
|
tex.Texture = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetShadowMapBuffers(bool enable)
|
||||||
|
{
|
||||||
|
ShadowMapBuffers = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetOutputTexture(PPTexture *texture)
|
||||||
|
{
|
||||||
|
Output.Type = PPTextureType::PPTexture;
|
||||||
|
Output.Texture = texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetOutputCurrent()
|
||||||
|
{
|
||||||
|
Output.Type = PPTextureType::CurrentPipelineTexture;
|
||||||
|
Output.Texture = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetOutputNext()
|
||||||
|
{
|
||||||
|
Output.Type = PPTextureType::NextPipelineTexture;
|
||||||
|
Output.Texture = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetOutputSceneColor()
|
||||||
|
{
|
||||||
|
Output.Type = PPTextureType::SceneColor;
|
||||||
|
Output.Texture = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetOutputSwapChain()
|
||||||
|
{
|
||||||
|
Output.Type = PPTextureType::SwapChain;
|
||||||
|
Output.Texture = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetOutputShadowMap()
|
||||||
|
{
|
||||||
|
Output.Type = PPTextureType::ShadowMap;
|
||||||
|
Output.Texture = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNoBlend()
|
||||||
|
{
|
||||||
|
BlendMode.BlendOp = STYLEOP_Add;
|
||||||
|
BlendMode.SrcAlpha = STYLEALPHA_One;
|
||||||
|
BlendMode.DestAlpha = STYLEALPHA_Zero;
|
||||||
|
BlendMode.Flags = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetAdditiveBlend()
|
||||||
|
{
|
||||||
|
BlendMode.BlendOp = STYLEOP_Add;
|
||||||
|
BlendMode.SrcAlpha = STYLEALPHA_One;
|
||||||
|
BlendMode.DestAlpha = STYLEALPHA_One;
|
||||||
|
BlendMode.Flags = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetAlphaBlend()
|
||||||
|
{
|
||||||
|
BlendMode.BlendOp = STYLEOP_Add;
|
||||||
|
BlendMode.SrcAlpha = STYLEALPHA_Src;
|
||||||
|
BlendMode.DestAlpha = STYLEALPHA_InvSrc;
|
||||||
|
BlendMode.Flags = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PPShader *Shader;
|
||||||
|
TArray<PPTextureInput> Textures;
|
||||||
|
PPUniforms Uniforms;
|
||||||
|
PPViewport Viewport;
|
||||||
|
PPBlendMode BlendMode;
|
||||||
|
PPOutput Output;
|
||||||
|
bool ShadowMapBuffers = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class PixelFormat
|
||||||
|
{
|
||||||
|
Rgba8,
|
||||||
|
Rgba16f,
|
||||||
|
R32f,
|
||||||
|
Rg16f,
|
||||||
|
Rgba16_snorm
|
||||||
|
};
|
||||||
|
|
||||||
|
class PPResource
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PPResource()
|
||||||
|
{
|
||||||
|
Next = First;
|
||||||
|
First = this;
|
||||||
|
if (Next) Next->Prev = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
PPResource(const PPResource &)
|
||||||
|
{
|
||||||
|
Next = First;
|
||||||
|
First = this;
|
||||||
|
if (Next) Next->Prev = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~PPResource()
|
||||||
|
{
|
||||||
|
if (Next) Next->Prev = Prev;
|
||||||
|
if (Prev) Prev->Next = Next;
|
||||||
|
else First = Next;
|
||||||
|
}
|
||||||
|
|
||||||
|
PPResource &operator=(const PPResource &other)
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ResetAll()
|
||||||
|
{
|
||||||
|
for (PPResource *cur = First; cur; cur = cur->Next)
|
||||||
|
cur->ResetBackend();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void ResetBackend() = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static PPResource *First;
|
||||||
|
PPResource *Prev = nullptr;
|
||||||
|
PPResource *Next = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PPTextureBackend
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~PPTextureBackend() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PPTexture : public PPResource
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PPTexture() = default;
|
||||||
|
PPTexture(int width, int height, PixelFormat format, std::shared_ptr<void> data = {}) : Width(width), Height(height), Format(format), Data(data) { }
|
||||||
|
|
||||||
|
void ResetBackend() override { Backend.reset(); }
|
||||||
|
|
||||||
|
int Width;
|
||||||
|
int Height;
|
||||||
|
PixelFormat Format;
|
||||||
|
std::shared_ptr<void> Data;
|
||||||
|
|
||||||
|
std::unique_ptr<PPTextureBackend> Backend;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PPShaderBackend
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~PPShaderBackend() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PPShader : public PPResource
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PPShader() = default;
|
||||||
|
PPShader(const FString &fragment, const FString &defines, const std::vector<UniformFieldDesc> &uniforms, int version = 330) : FragmentShader(fragment), Defines(defines), Uniforms(uniforms), Version(version) { }
|
||||||
|
|
||||||
|
void ResetBackend() override { Backend.reset(); }
|
||||||
|
|
||||||
|
FString VertexShader = "shaders/glsl/screenquad.vp";
|
||||||
|
FString FragmentShader;
|
||||||
|
FString Defines;
|
||||||
|
std::vector<UniformFieldDesc> Uniforms;
|
||||||
|
int Version = 330;
|
||||||
|
|
||||||
|
std::unique_ptr<PPShaderBackend> Backend;
|
||||||
|
};
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct ExtractUniforms
|
||||||
|
{
|
||||||
|
FVector2 Scale;
|
||||||
|
FVector2 Offset;
|
||||||
|
|
||||||
|
static std::vector<UniformFieldDesc> Desc()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
{
|
||||||
|
{ "Scale", UniformType::Vec2, offsetof(ExtractUniforms, Scale) },
|
||||||
|
{ "Offset", UniformType::Vec2, offsetof(ExtractUniforms, Offset) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BlurUniforms
|
||||||
|
{
|
||||||
|
float SampleWeights[8];
|
||||||
|
|
||||||
|
static std::vector<UniformFieldDesc> Desc()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
{
|
||||||
|
{ "SampleWeights0", UniformType::Float, offsetof(BlurUniforms, SampleWeights[0]) },
|
||||||
|
{ "SampleWeights1", UniformType::Float, offsetof(BlurUniforms, SampleWeights[1]) },
|
||||||
|
{ "SampleWeights2", UniformType::Float, offsetof(BlurUniforms, SampleWeights[2]) },
|
||||||
|
{ "SampleWeights3", UniformType::Float, offsetof(BlurUniforms, SampleWeights[3]) },
|
||||||
|
{ "SampleWeights4", UniformType::Float, offsetof(BlurUniforms, SampleWeights[4]) },
|
||||||
|
{ "SampleWeights5", UniformType::Float, offsetof(BlurUniforms, SampleWeights[5]) },
|
||||||
|
{ "SampleWeights6", UniformType::Float, offsetof(BlurUniforms, SampleWeights[6]) },
|
||||||
|
{ "SampleWeights7", UniformType::Float, offsetof(BlurUniforms, SampleWeights[7]) },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum { NumBloomLevels = 4 };
|
||||||
|
|
||||||
|
class PPBlurLevel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PPViewport Viewport;
|
||||||
|
PPTexture VTexture;
|
||||||
|
PPTexture HTexture;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PPBloom
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void RenderBloom(PPRenderState *renderstate, int sceneWidth, int sceneHeight, int fixedcm);
|
||||||
|
void RenderBlur(PPRenderState *renderstate, int sceneWidth, int sceneHeight, float gameinfobluramount);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void BlurStep(PPRenderState *renderstate, const BlurUniforms &blurUniforms, PPTexture &input, PPTexture &output, PPViewport viewport, bool vertical);
|
||||||
|
void UpdateTextures(int width, int height);
|
||||||
|
|
||||||
|
static float ComputeBlurGaussian(float n, float theta);
|
||||||
|
static void ComputeBlurSamples(int sampleCount, float blurAmount, float *sampleWeights);
|
||||||
|
|
||||||
|
PPBlurLevel levels[NumBloomLevels];
|
||||||
|
int lastWidth = 0;
|
||||||
|
int lastHeight = 0;
|
||||||
|
|
||||||
|
PPShader BloomCombine = { "shaders/glsl/bloomcombine.fp", "", {} };
|
||||||
|
PPShader BloomExtract = { "shaders/glsl/bloomextract.fp", "", ExtractUniforms::Desc() };
|
||||||
|
PPShader BlurVertical = { "shaders/glsl/blur.fp", "#define BLUR_VERTICAL\n", BlurUniforms::Desc() };
|
||||||
|
PPShader BlurHorizontal = { "shaders/glsl/blur.fp", "#define BLUR_HORIZONTAL\n", BlurUniforms::Desc() };
|
||||||
|
};
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct LensUniforms
|
||||||
|
{
|
||||||
|
float AspectRatio;
|
||||||
|
float Scale;
|
||||||
|
float Padding0, Padding1;
|
||||||
|
FVector4 LensDistortionCoefficient;
|
||||||
|
FVector4 CubicDistortionValue;
|
||||||
|
|
||||||
|
static std::vector<UniformFieldDesc> Desc()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
{
|
||||||
|
{ "Aspect", UniformType::Float, offsetof(LensUniforms, AspectRatio) },
|
||||||
|
{ "Scale", UniformType::Float, offsetof(LensUniforms, Scale) },
|
||||||
|
{ "Padding0", UniformType::Float, offsetof(LensUniforms, Padding0) },
|
||||||
|
{ "Padding1", UniformType::Float, offsetof(LensUniforms, Padding1) },
|
||||||
|
{ "k", UniformType::Vec4, offsetof(LensUniforms, LensDistortionCoefficient) },
|
||||||
|
{ "kcube", UniformType::Vec4, offsetof(LensUniforms, CubicDistortionValue) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PPLensDistort
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Render(PPRenderState *renderstate);
|
||||||
|
|
||||||
|
private:
|
||||||
|
PPShader Lens = { "shaders/glsl/lensdistortion.fp", "", LensUniforms::Desc() };
|
||||||
|
};
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct FXAAUniforms
|
||||||
|
{
|
||||||
|
FVector2 ReciprocalResolution;
|
||||||
|
float Padding0, Padding1;
|
||||||
|
|
||||||
|
static std::vector<UniformFieldDesc> Desc()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
{
|
||||||
|
{ "ReciprocalResolution", UniformType::Vec2, offsetof(FXAAUniforms, ReciprocalResolution) },
|
||||||
|
{ "Padding0", UniformType::Float, offsetof(FXAAUniforms, Padding0) },
|
||||||
|
{ "Padding1", UniformType::Float, offsetof(FXAAUniforms, Padding1) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PPFXAA
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Render(PPRenderState *renderstate);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void CreateShaders();
|
||||||
|
int GetMaxVersion();
|
||||||
|
FString GetDefines();
|
||||||
|
|
||||||
|
PPShader FXAALuma;
|
||||||
|
PPShader FXAA;
|
||||||
|
int LastQuality = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct ExposureExtractUniforms
|
||||||
|
{
|
||||||
|
FVector2 Scale;
|
||||||
|
FVector2 Offset;
|
||||||
|
|
||||||
|
static std::vector<UniformFieldDesc> Desc()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
{
|
||||||
|
{ "Scale", UniformType::Vec2, offsetof(ExposureExtractUniforms, Scale) },
|
||||||
|
{ "Offset", UniformType::Vec2, offsetof(ExposureExtractUniforms, Offset) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ExposureCombineUniforms
|
||||||
|
{
|
||||||
|
float ExposureBase;
|
||||||
|
float ExposureMin;
|
||||||
|
float ExposureScale;
|
||||||
|
float ExposureSpeed;
|
||||||
|
|
||||||
|
static std::vector<UniformFieldDesc> Desc()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
{
|
||||||
|
{ "ExposureBase", UniformType::Float, offsetof(ExposureCombineUniforms, ExposureBase) },
|
||||||
|
{ "ExposureMin", UniformType::Float, offsetof(ExposureCombineUniforms, ExposureMin) },
|
||||||
|
{ "ExposureScale", UniformType::Float, offsetof(ExposureCombineUniforms, ExposureScale) },
|
||||||
|
{ "ExposureSpeed", UniformType::Float, offsetof(ExposureCombineUniforms, ExposureSpeed) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PPExposureLevel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PPViewport Viewport;
|
||||||
|
PPTexture Texture;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PPCameraExposure
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Render(PPRenderState *renderstate, int sceneWidth, int sceneHeight);
|
||||||
|
|
||||||
|
PPTexture CameraTexture = { 1, 1, PixelFormat::R32f };
|
||||||
|
|
||||||
|
private:
|
||||||
|
void UpdateTextures(int width, int height);
|
||||||
|
|
||||||
|
std::vector<PPExposureLevel> ExposureLevels;
|
||||||
|
bool FirstExposureFrame = true;
|
||||||
|
|
||||||
|
PPShader ExposureExtract = { "shaders/glsl/exposureextract.fp", "", ExposureExtractUniforms::Desc() };
|
||||||
|
PPShader ExposureAverage = { "shaders/glsl/exposureaverage.fp", "", {}, 400 };
|
||||||
|
PPShader ExposureCombine = { "shaders/glsl/exposurecombine.fp", "", ExposureCombineUniforms::Desc() };
|
||||||
|
};
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct ColormapUniforms
|
||||||
|
{
|
||||||
|
FVector4 MapStart;
|
||||||
|
FVector4 MapRange;
|
||||||
|
|
||||||
|
static std::vector<UniformFieldDesc> Desc()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
{
|
||||||
|
{ "uFixedColormapStart", UniformType::Vec4, offsetof(ColormapUniforms, MapStart) },
|
||||||
|
{ "uFixedColormapRange", UniformType::Vec4, offsetof(ColormapUniforms, MapRange) },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class PPTonemap
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Render(PPRenderState *renderstate);
|
||||||
|
void ClearTonemapPalette() { PaletteTexture = {}; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void UpdateTextures();
|
||||||
|
|
||||||
|
PPTexture PaletteTexture;
|
||||||
|
|
||||||
|
PPShader LinearShader = { "shaders/glsl/tonemap.fp", "#define LINEAR\n", {} };
|
||||||
|
PPShader ReinhardShader = { "shaders/glsl/tonemap.fp", "#define REINHARD\n", {} };
|
||||||
|
PPShader HejlDawsonShader = { "shaders/glsl/tonemap.fp", "#define HEJLDAWSON\n", {} };
|
||||||
|
PPShader Uncharted2Shader = { "shaders/glsl/tonemap.fp", "#define UNCHARTED2\n", {} };
|
||||||
|
PPShader PaletteShader = { "shaders/glsl/tonemap.fp", "#define PALETTE\n", {} };
|
||||||
|
|
||||||
|
enum TonemapMode
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Uncharted2,
|
||||||
|
HejlDawson,
|
||||||
|
Reinhard,
|
||||||
|
Linear,
|
||||||
|
Palette,
|
||||||
|
NumTonemapModes
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct LinearDepthUniforms
|
||||||
|
{
|
||||||
|
int SampleIndex;
|
||||||
|
float LinearizeDepthA;
|
||||||
|
float LinearizeDepthB;
|
||||||
|
float InverseDepthRangeA;
|
||||||
|
float InverseDepthRangeB;
|
||||||
|
float Padding0, Padding1, Padding2;
|
||||||
|
FVector2 Scale;
|
||||||
|
FVector2 Offset;
|
||||||
|
|
||||||
|
static std::vector<UniformFieldDesc> Desc()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
{
|
||||||
|
{ "SampleIndex", UniformType::Int, offsetof(LinearDepthUniforms, SampleIndex) },
|
||||||
|
{ "LinearizeDepthA", UniformType::Float, offsetof(LinearDepthUniforms, LinearizeDepthA) },
|
||||||
|
{ "LinearizeDepthB", UniformType::Float, offsetof(LinearDepthUniforms, LinearizeDepthB) },
|
||||||
|
{ "InverseDepthRangeA", UniformType::Float, offsetof(LinearDepthUniforms, InverseDepthRangeA) },
|
||||||
|
{ "InverseDepthRangeB", UniformType::Float, offsetof(LinearDepthUniforms, InverseDepthRangeB) },
|
||||||
|
{ "Padding0", UniformType::Float, offsetof(LinearDepthUniforms, Padding0) },
|
||||||
|
{ "Padding1", UniformType::Float, offsetof(LinearDepthUniforms, Padding1) },
|
||||||
|
{ "Padding2", UniformType::Float, offsetof(LinearDepthUniforms, Padding2) },
|
||||||
|
{ "Scale", UniformType::Vec2, offsetof(LinearDepthUniforms, Scale) },
|
||||||
|
{ "Offset", UniformType::Vec2, offsetof(LinearDepthUniforms, Offset) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SSAOUniforms
|
||||||
|
{
|
||||||
|
FVector2 UVToViewA;
|
||||||
|
FVector2 UVToViewB;
|
||||||
|
FVector2 InvFullResolution;
|
||||||
|
float NDotVBias;
|
||||||
|
float NegInvR2;
|
||||||
|
float RadiusToScreen;
|
||||||
|
float AOMultiplier;
|
||||||
|
float AOStrength;
|
||||||
|
int SampleIndex;
|
||||||
|
float Padding0, Padding1;
|
||||||
|
FVector2 Scale;
|
||||||
|
FVector2 Offset;
|
||||||
|
|
||||||
|
static std::vector<UniformFieldDesc> Desc()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
{
|
||||||
|
{ "UVToViewA", UniformType::Vec2, offsetof(SSAOUniforms, UVToViewA) },
|
||||||
|
{ "UVToViewB", UniformType::Vec2, offsetof(SSAOUniforms, UVToViewB) },
|
||||||
|
{ "InvFullResolution", UniformType::Vec2, offsetof(SSAOUniforms, InvFullResolution) },
|
||||||
|
{ "NDotVBias", UniformType::Float, offsetof(SSAOUniforms, NDotVBias) },
|
||||||
|
{ "NegInvR2", UniformType::Float, offsetof(SSAOUniforms, NegInvR2) },
|
||||||
|
{ "RadiusToScreen", UniformType::Float, offsetof(SSAOUniforms, RadiusToScreen) },
|
||||||
|
{ "AOMultiplier", UniformType::Float, offsetof(SSAOUniforms, AOMultiplier) },
|
||||||
|
{ "AOStrength", UniformType::Float, offsetof(SSAOUniforms, AOStrength) },
|
||||||
|
{ "SampleIndex", UniformType::Int, offsetof(SSAOUniforms, SampleIndex) },
|
||||||
|
{ "Padding0", UniformType::Float, offsetof(SSAOUniforms, Padding0) },
|
||||||
|
{ "Padding1", UniformType::Float, offsetof(SSAOUniforms, Padding1) },
|
||||||
|
{ "Scale", UniformType::Vec2, offsetof(SSAOUniforms, Scale) },
|
||||||
|
{ "Offset", UniformType::Vec2, offsetof(SSAOUniforms, Offset) },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DepthBlurUniforms
|
||||||
|
{
|
||||||
|
float BlurSharpness;
|
||||||
|
float PowExponent;
|
||||||
|
float Padding0, Padding1;
|
||||||
|
|
||||||
|
static std::vector<UniformFieldDesc> Desc()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
{
|
||||||
|
{ "BlurSharpness", UniformType::Float, offsetof(DepthBlurUniforms, BlurSharpness) },
|
||||||
|
{ "PowExponent", UniformType::Float, offsetof(DepthBlurUniforms, PowExponent) },
|
||||||
|
{ "Padding0", UniformType::Float, offsetof(DepthBlurUniforms, Padding0) },
|
||||||
|
{ "Padding1", UniformType::Float, offsetof(DepthBlurUniforms, Padding1) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AmbientCombineUniforms
|
||||||
|
{
|
||||||
|
int SampleCount;
|
||||||
|
int DebugMode, Padding1, Padding2;
|
||||||
|
FVector2 Scale;
|
||||||
|
FVector2 Offset;
|
||||||
|
|
||||||
|
static std::vector<UniformFieldDesc> Desc()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
{
|
||||||
|
{ "SampleCount", UniformType::Int, offsetof(AmbientCombineUniforms, SampleCount) },
|
||||||
|
{ "DebugMode", UniformType::Int, offsetof(AmbientCombineUniforms, DebugMode) },
|
||||||
|
{ "Padding1", UniformType::Int, offsetof(AmbientCombineUniforms, Padding1) },
|
||||||
|
{ "Padding2", UniformType::Int, offsetof(AmbientCombineUniforms, Padding2) },
|
||||||
|
{ "Scale", UniformType::Vec2, offsetof(AmbientCombineUniforms, Scale) },
|
||||||
|
{ "Offset", UniformType::Vec2, offsetof(AmbientCombineUniforms, Offset) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PPAmbientOcclusion
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PPAmbientOcclusion();
|
||||||
|
void Render(PPRenderState *renderstate, float m5, int sceneWidth, int sceneHeight);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void CreateShaders();
|
||||||
|
void UpdateTextures(int width, int height);
|
||||||
|
|
||||||
|
enum Quality
|
||||||
|
{
|
||||||
|
Off,
|
||||||
|
LowQuality,
|
||||||
|
MediumQuality,
|
||||||
|
HighQuality,
|
||||||
|
NumQualityModes
|
||||||
|
};
|
||||||
|
|
||||||
|
int AmbientWidth = 0;
|
||||||
|
int AmbientHeight = 0;
|
||||||
|
|
||||||
|
int LastQuality = -1;
|
||||||
|
int LastWidth = 0;
|
||||||
|
int LastHeight = 0;
|
||||||
|
|
||||||
|
PPShader LinearDepth;
|
||||||
|
PPShader LinearDepthMS;
|
||||||
|
PPShader AmbientOcclude;
|
||||||
|
PPShader AmbientOccludeMS;
|
||||||
|
PPShader BlurVertical;
|
||||||
|
PPShader BlurHorizontal;
|
||||||
|
PPShader Combine;
|
||||||
|
PPShader CombineMS;
|
||||||
|
|
||||||
|
PPTexture LinearDepthTexture;
|
||||||
|
PPTexture Ambient0;
|
||||||
|
PPTexture Ambient1;
|
||||||
|
|
||||||
|
enum { NumAmbientRandomTextures = 3 };
|
||||||
|
PPTexture AmbientRandomTexture[NumAmbientRandomTextures];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PresentUniforms
|
||||||
|
{
|
||||||
|
float InvGamma;
|
||||||
|
float Contrast;
|
||||||
|
float Brightness;
|
||||||
|
float Saturation;
|
||||||
|
int GrayFormula;
|
||||||
|
int WindowPositionParity; // top-of-window might not be top-of-screen
|
||||||
|
FVector2 Scale;
|
||||||
|
FVector2 Offset;
|
||||||
|
float ColorScale;
|
||||||
|
int HdrMode;
|
||||||
|
|
||||||
|
static std::vector<UniformFieldDesc> Desc()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
{
|
||||||
|
{ "InvGamma", UniformType::Float, offsetof(PresentUniforms, InvGamma) },
|
||||||
|
{ "Contrast", UniformType::Float, offsetof(PresentUniforms, Contrast) },
|
||||||
|
{ "Brightness", UniformType::Float, offsetof(PresentUniforms, Brightness) },
|
||||||
|
{ "Saturation", UniformType::Float, offsetof(PresentUniforms, Saturation) },
|
||||||
|
{ "GrayFormula", UniformType::Int, offsetof(PresentUniforms, GrayFormula) },
|
||||||
|
{ "WindowPositionParity", UniformType::Int, offsetof(PresentUniforms, WindowPositionParity) },
|
||||||
|
{ "UVScale", UniformType::Vec2, offsetof(PresentUniforms, Scale) },
|
||||||
|
{ "UVOffset", UniformType::Vec2, offsetof(PresentUniforms, Offset) },
|
||||||
|
{ "ColorScale", UniformType::Float, offsetof(PresentUniforms, ColorScale) },
|
||||||
|
{ "HdrMode", UniformType::Int, offsetof(PresentUniforms, HdrMode) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PPPresent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PPPresent();
|
||||||
|
|
||||||
|
PPTexture Dither;
|
||||||
|
|
||||||
|
PPShader Present = { "shaders/glsl/present.fp", "", PresentUniforms::Desc() };
|
||||||
|
PPShader Checker3D = { "shaders/glsl/present_checker3d.fp", "", PresentUniforms::Desc() };
|
||||||
|
PPShader Column3D = { "shaders/glsl/present_column3d.fp", "", PresentUniforms::Desc() };
|
||||||
|
PPShader Row3D = { "shaders/glsl/present_row3d.fp", "", PresentUniforms::Desc() };
|
||||||
|
};
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class Postprocess
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PPBloom bloom;
|
||||||
|
PPLensDistort lens;
|
||||||
|
PPFXAA fxaa;
|
||||||
|
PPCameraExposure exposure;
|
||||||
|
PPTonemap tonemap;
|
||||||
|
PPAmbientOcclusion ssao;
|
||||||
|
PPPresent present;
|
||||||
|
|
||||||
|
|
||||||
|
void Pass1(PPRenderState *state, int fixedcm, int sceneWidth, int sceneHeight);
|
||||||
|
void Pass2(PPRenderState* state, int fixedcm, int sceneWidth, int sceneHeight);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Postprocess hw_postprocess;
|
|
@ -0,0 +1,104 @@
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// 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_postprocess.cpp
|
||||||
|
** Post processing effects in the render pipeline
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "hw_postprocess_cvars.h"
|
||||||
|
#include "v_video.h"
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// CVARs
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
CVAR(Bool, gl_bloom, false, CVAR_ARCHIVE);
|
||||||
|
CUSTOM_CVAR(Float, gl_bloom_amount, 1.4f, CVAR_ARCHIVE)
|
||||||
|
{
|
||||||
|
if (self < 0.1f) self = 0.1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
CVAR(Float, gl_exposure_scale, 1.3f, CVAR_ARCHIVE)
|
||||||
|
CVAR(Float, gl_exposure_min, 0.35f, CVAR_ARCHIVE)
|
||||||
|
CVAR(Float, gl_exposure_base, 0.35f, CVAR_ARCHIVE)
|
||||||
|
CVAR(Float, gl_exposure_speed, 0.05f, CVAR_ARCHIVE)
|
||||||
|
|
||||||
|
CUSTOM_CVAR(Int, gl_tonemap, 0, CVAR_ARCHIVE)
|
||||||
|
{
|
||||||
|
if (self < 0 || self > 5)
|
||||||
|
self = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CVAR(Bool, gl_lens, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||||
|
|
||||||
|
CVAR(Float, gl_lens_k, -0.12f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||||
|
CVAR(Float, gl_lens_kcube, 0.1f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||||
|
CVAR(Float, gl_lens_chromatic, 1.12f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||||
|
|
||||||
|
CUSTOM_CVAR(Int, gl_fxaa, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||||
|
{
|
||||||
|
if (self < 0 || self >= IFXAAShader::Count)
|
||||||
|
{
|
||||||
|
self = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_CVAR(Int, gl_ssao, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||||
|
{
|
||||||
|
if (self < 0 || self > 3)
|
||||||
|
self = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_CVAR(Int, gl_ssao_portals, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||||
|
{
|
||||||
|
if (self < 0)
|
||||||
|
self = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CVAR(Float, gl_ssao_strength, 0.7f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||||
|
CVAR(Int, gl_ssao_debug, 0, 0)
|
||||||
|
CVAR(Float, gl_ssao_bias, 0.2f, 0)
|
||||||
|
CVAR(Float, gl_ssao_radius, 80.0f, 0)
|
||||||
|
CUSTOM_CVAR(Float, gl_ssao_blur, 16.0f, 0)
|
||||||
|
{
|
||||||
|
if (self < 0.1f) self = 0.1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_CVAR(Float, gl_ssao_exponent, 1.8f, 0)
|
||||||
|
{
|
||||||
|
if (self < 0.1f) self = 0.1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_CVAR(Float, gl_paltonemap_powtable, 2.0f, CVAR_ARCHIVE | CVAR_NOINITCALL)
|
||||||
|
{
|
||||||
|
screen->UpdatePalette();
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_CVAR(Bool, gl_paltonemap_reverselookup, true, CVAR_ARCHIVE | CVAR_NOINITCALL)
|
||||||
|
{
|
||||||
|
screen->UpdatePalette();
|
||||||
|
}
|
||||||
|
|
||||||
|
CVAR(Float, gl_menu_blur, -1.0f, CVAR_ARCHIVE)
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "c_cvars.h"
|
||||||
|
|
||||||
|
class IFXAAShader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Quality
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Low,
|
||||||
|
Medium,
|
||||||
|
High,
|
||||||
|
Extreme,
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// CVARs
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
EXTERN_CVAR(Bool, gl_bloom)
|
||||||
|
EXTERN_CVAR(Float, gl_bloom_amount)
|
||||||
|
EXTERN_CVAR(Float, gl_exposure_scale)
|
||||||
|
EXTERN_CVAR(Float, gl_exposure_min)
|
||||||
|
EXTERN_CVAR(Float, gl_exposure_base)
|
||||||
|
EXTERN_CVAR(Float, gl_exposure_speed)
|
||||||
|
EXTERN_CVAR(Int, gl_tonemap)
|
||||||
|
EXTERN_CVAR(Int, gl_bloom_kernel_size)
|
||||||
|
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_fxaa)
|
||||||
|
EXTERN_CVAR(Int, gl_ssao)
|
||||||
|
EXTERN_CVAR(Int, gl_ssao_portals)
|
||||||
|
EXTERN_CVAR(Float, gl_ssao_strength)
|
||||||
|
EXTERN_CVAR(Int, gl_ssao_debug)
|
||||||
|
EXTERN_CVAR(Float, gl_ssao_bias)
|
||||||
|
EXTERN_CVAR(Float, gl_ssao_radius)
|
||||||
|
EXTERN_CVAR(Float, gl_ssao_blur)
|
||||||
|
EXTERN_CVAR(Float, gl_ssao_exponent)
|
||||||
|
EXTERN_CVAR(Float, gl_paltonemap_powtable)
|
||||||
|
EXTERN_CVAR(Bool, gl_paltonemap_reverselookup)
|
||||||
|
EXTERN_CVAR(Float, gl_menu_blur)
|
||||||
|
EXTERN_CVAR(Float, vid_brightness)
|
||||||
|
EXTERN_CVAR(Float, vid_contrast)
|
||||||
|
EXTERN_CVAR(Float, vid_saturation)
|
||||||
|
EXTERN_CVAR(Int, gl_satformula)
|
||||||
|
|
86
source/common/rendering/hwrenderer/utility/hw_cvars.cpp
Normal file
86
source/common/rendering/hwrenderer/utility/hw_cvars.cpp
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Copyright(C) 2005-2016 Christoph Oelckers
|
||||||
|
// 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 "c_cvars.h"
|
||||||
|
#include "c_dispatch.h"
|
||||||
|
#include "v_video.h"
|
||||||
|
#include "hw_cvars.h"
|
||||||
|
#include "menu/menu.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// OpenGL stuff moved here
|
||||||
|
// GL related CVARs
|
||||||
|
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Texture CVARs
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
#if 0
|
||||||
|
CUSTOM_CVAR(Float,gl_texture_filter_anisotropic,8.0f,CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL)
|
||||||
|
{
|
||||||
|
screen->TextureFilterChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
CCMD(gl_flush)
|
||||||
|
{
|
||||||
|
//TexMan.FlushAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_CVAR(Int, gl_texture_filter, 4, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL)
|
||||||
|
{
|
||||||
|
if (self < 0 || self > 6) self=4;
|
||||||
|
screen->TextureFilterChanged();
|
||||||
|
}
|
||||||
|
CVAR(Bool, gl_precache, false, CVAR_ARCHIVE)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Sprite CVARs
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
#if 0
|
||||||
|
CVAR(Bool, gl_usecolorblending, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||||
|
CVAR(Bool, gl_sprite_blend, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
|
||||||
|
CVAR(Int, gl_spriteclip, 1, CVAR_ARCHIVE)
|
||||||
|
CVAR(Float, gl_sclipthreshold, 10.0, CVAR_ARCHIVE)
|
||||||
|
CVAR(Float, gl_sclipfactor, 1.8f, CVAR_ARCHIVE)
|
||||||
|
CVAR(Int, gl_particles_style, 2, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // 0 = square, 1 = round, 2 = smooth
|
||||||
|
CVAR(Int, gl_billboard_mode, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||||
|
CVAR(Bool, gl_billboard_faces_camera, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||||
|
CVAR(Bool, gl_billboard_particles, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||||
|
CVAR(Int, gl_enhanced_nv_stealth, 3, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||||
|
CUSTOM_CVAR(Int, gl_fuzztype, 0, CVAR_ARCHIVE)
|
||||||
|
{
|
||||||
|
if (self < 0 || self > 8) self = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_CVAR(Int, gl_shadowmap_filter, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||||
|
{
|
||||||
|
if (self < 0 || self > 8) self = 1;
|
||||||
|
}
|
||||||
|
#endif
|
71
source/common/rendering/hwrenderer/utility/hw_cvars.h
Normal file
71
source/common/rendering/hwrenderer/utility/hw_cvars.h
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include "c_cvars.h"
|
||||||
|
|
||||||
|
EXTERN_CVAR(Bool,gl_enhanced_nightvision)
|
||||||
|
EXTERN_CVAR(Int, screenblocks);
|
||||||
|
EXTERN_CVAR(Bool, gl_texture)
|
||||||
|
EXTERN_CVAR(Int, gl_texture_filter)
|
||||||
|
EXTERN_CVAR(Float, gl_texture_filter_anisotropic)
|
||||||
|
EXTERN_CVAR(Int, gl_texture_format)
|
||||||
|
EXTERN_CVAR(Bool, gl_texture_usehires)
|
||||||
|
EXTERN_CVAR(Bool, gl_usefb)
|
||||||
|
|
||||||
|
EXTERN_CVAR(Int, gl_weaponlight)
|
||||||
|
|
||||||
|
EXTERN_CVAR (Bool, gl_light_sprites);
|
||||||
|
EXTERN_CVAR (Bool, gl_light_particles);
|
||||||
|
EXTERN_CVAR (Bool, gl_light_shadowmap);
|
||||||
|
EXTERN_CVAR (Int, gl_shadowmap_quality);
|
||||||
|
|
||||||
|
EXTERN_CVAR(Int, gl_fogmode)
|
||||||
|
EXTERN_CVAR(Int, gl_lightmode)
|
||||||
|
EXTERN_CVAR(Bool,gl_mirror_envmap)
|
||||||
|
|
||||||
|
EXTERN_CVAR(Bool,gl_mirrors)
|
||||||
|
EXTERN_CVAR(Bool,gl_mirror_envmap)
|
||||||
|
|
||||||
|
EXTERN_CVAR(Float, gl_mask_threshold)
|
||||||
|
EXTERN_CVAR(Float, gl_mask_sprite_threshold)
|
||||||
|
|
||||||
|
EXTERN_CVAR(Int, gl_multisample)
|
||||||
|
|
||||||
|
EXTERN_CVAR(Bool, gl_bloom)
|
||||||
|
EXTERN_CVAR(Float, gl_bloom_amount)
|
||||||
|
EXTERN_CVAR(Int, gl_bloom_kernel_size)
|
||||||
|
EXTERN_CVAR(Int, gl_tonemap)
|
||||||
|
EXTERN_CVAR(Float, gl_exposure)
|
||||||
|
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(Int, 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)
|
||||||
|
|
||||||
|
|
||||||
|
EXTERN_CVAR(Bool, gl_usecolorblending)
|
||||||
|
EXTERN_CVAR(Bool, gl_sprite_blend)
|
||||||
|
EXTERN_CVAR(Int, gl_spriteclip)
|
||||||
|
EXTERN_CVAR(Float, gl_sclipthreshold)
|
||||||
|
EXTERN_CVAR(Float, gl_sclipfactor)
|
||||||
|
EXTERN_CVAR(Int, gl_particles_style)
|
||||||
|
EXTERN_CVAR(Int, gl_billboard_mode)
|
||||||
|
EXTERN_CVAR(Bool, gl_billboard_faces_camera)
|
||||||
|
EXTERN_CVAR(Bool, gl_billboard_particles)
|
||||||
|
EXTERN_CVAR(Int, gl_enhanced_nv_stealth)
|
||||||
|
EXTERN_CVAR(Int, gl_fuzztype)
|
||||||
|
|
||||||
|
EXTERN_CVAR(Int, gl_shadowmap_filter)
|
||||||
|
|
||||||
|
EXTERN_CVAR(Bool, gl_brightfog)
|
||||||
|
EXTERN_CVAR(Bool, gl_lightadditivesurfaces)
|
||||||
|
EXTERN_CVAR(Bool, gl_notexturefill)
|
295
source/common/rendering/hwrenderer/utility/hw_shaderpatcher.cpp
Normal file
295
source/common/rendering/hwrenderer/utility/hw_shaderpatcher.cpp
Normal file
|
@ -0,0 +1,295 @@
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Copyright(C) 2004-2018 Christoph Oelckers
|
||||||
|
// 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/
|
||||||
|
//
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
/*
|
||||||
|
** hw_shaderpatcher.cpp
|
||||||
|
**
|
||||||
|
** Modifies shader source to account for different syntax versions or engine changes.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "hw_shaderpatcher.h"
|
||||||
|
|
||||||
|
|
||||||
|
static bool IsGlslWhitespace(char c)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case ' ':
|
||||||
|
case '\r':
|
||||||
|
case '\n':
|
||||||
|
case '\t':
|
||||||
|
case '\f':
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static FString NextGlslToken(const char *chars, long len, long &pos)
|
||||||
|
{
|
||||||
|
// Eat whitespace
|
||||||
|
long tokenStart = pos;
|
||||||
|
while (tokenStart != len && IsGlslWhitespace(chars[tokenStart]))
|
||||||
|
tokenStart++;
|
||||||
|
|
||||||
|
// Find token end
|
||||||
|
long tokenEnd = tokenStart;
|
||||||
|
while (tokenEnd != len && !IsGlslWhitespace(chars[tokenEnd]) && chars[tokenEnd] != ';')
|
||||||
|
tokenEnd++;
|
||||||
|
|
||||||
|
pos = tokenEnd;
|
||||||
|
return FString(chars + tokenStart, tokenEnd - tokenStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isShaderType(const char *name)
|
||||||
|
{
|
||||||
|
return !strcmp(name, "sampler1D") || !strcmp(name, "sampler2D") || !strcmp(name, "sampler3D") || !strcmp(name, "samplerCube") || !strcmp(name, "sampler2DMS");
|
||||||
|
}
|
||||||
|
|
||||||
|
FString RemoveLegacyUserUniforms(FString code)
|
||||||
|
{
|
||||||
|
// User shaders must declare their uniforms via the GLDEFS file.
|
||||||
|
|
||||||
|
code.Substitute("uniform sampler2D tex;", " ");
|
||||||
|
code.Substitute("uniform float timer;", " ");
|
||||||
|
|
||||||
|
// The following code searches for legacy uniform declarations in the shader itself and replaces them with whitespace.
|
||||||
|
|
||||||
|
long len = (long)code.Len();
|
||||||
|
char *chars = code.LockBuffer();
|
||||||
|
|
||||||
|
long startIndex = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
long matchIndex = code.IndexOf("uniform", startIndex);
|
||||||
|
if (matchIndex == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
bool isLegacyUniformName = false;
|
||||||
|
|
||||||
|
bool isKeywordStart = matchIndex == 0 || IsGlslWhitespace(chars[matchIndex - 1]);
|
||||||
|
bool isKeywordEnd = matchIndex + 7 == len || IsGlslWhitespace(chars[matchIndex + 7]);
|
||||||
|
if (isKeywordStart && isKeywordEnd)
|
||||||
|
{
|
||||||
|
long pos = matchIndex + 7;
|
||||||
|
FString type = NextGlslToken(chars, len, pos);
|
||||||
|
FString identifier = NextGlslToken(chars, len, pos);
|
||||||
|
|
||||||
|
isLegacyUniformName = type.Compare("float") == 0 && identifier.Compare("timer") == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLegacyUniformName)
|
||||||
|
{
|
||||||
|
long statementEndIndex = code.IndexOf(';', matchIndex + 7);
|
||||||
|
if (statementEndIndex == -1)
|
||||||
|
statementEndIndex = len;
|
||||||
|
for (long i = matchIndex; i <= statementEndIndex; i++)
|
||||||
|
{
|
||||||
|
if (!IsGlslWhitespace(chars[i]))
|
||||||
|
chars[i] = ' ';
|
||||||
|
}
|
||||||
|
startIndex = statementEndIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
startIndex = matchIndex + 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also remove all occurences of the token 'texture2d'. Some shaders may still use this deprecated function to access a sampler.
|
||||||
|
// Modern GLSL only allows use of 'texture'.
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
long matchIndex = code.IndexOf("texture2d", startIndex);
|
||||||
|
if (matchIndex == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Check if this is a real token.
|
||||||
|
bool isKeywordStart = matchIndex == 0 || !isalnum(chars[matchIndex - 1] & 255);
|
||||||
|
bool isKeywordEnd = matchIndex + 9 == len || !isalnum(chars[matchIndex + 9] & 255);
|
||||||
|
if (isKeywordStart && isKeywordEnd)
|
||||||
|
{
|
||||||
|
chars[matchIndex + 7] = chars[matchIndex + 8] = ' ';
|
||||||
|
}
|
||||||
|
startIndex = matchIndex + 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
code.UnlockBuffer();
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
FString RemoveSamplerBindings(FString code, TArray<std::pair<FString, int>> &samplerstobind)
|
||||||
|
{
|
||||||
|
long len = (long)code.Len();
|
||||||
|
char *chars = code.LockBuffer();
|
||||||
|
|
||||||
|
long startIndex = 0;
|
||||||
|
long startpos, endpos;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
long matchIndex = code.IndexOf("layout(binding", startIndex);
|
||||||
|
if (matchIndex == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
bool isSamplerUniformName = false;
|
||||||
|
|
||||||
|
bool isKeywordStart = matchIndex == 0 || IsGlslWhitespace(chars[matchIndex - 1]);
|
||||||
|
bool isKeywordEnd = matchIndex + 14 == len || IsGlslWhitespace(chars[matchIndex + 14]) || chars[matchIndex + 14] == '=';
|
||||||
|
if (isKeywordStart && isKeywordEnd)
|
||||||
|
{
|
||||||
|
long pos = matchIndex + 14;
|
||||||
|
startpos = matchIndex;
|
||||||
|
while (IsGlslWhitespace(chars[pos])) pos++;
|
||||||
|
if (chars[pos] == '=')
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
pos++;
|
||||||
|
auto val = strtol(&chars[pos], &p, 0);
|
||||||
|
if (p != &chars[pos])
|
||||||
|
{
|
||||||
|
pos = long(p - chars);
|
||||||
|
while (IsGlslWhitespace(chars[pos])) pos++;
|
||||||
|
if (chars[pos] == ')')
|
||||||
|
{
|
||||||
|
endpos = ++pos;
|
||||||
|
FString uniform = NextGlslToken(chars, len, pos);
|
||||||
|
FString type = NextGlslToken(chars, len, pos);
|
||||||
|
FString identifier = NextGlslToken(chars, len, pos);
|
||||||
|
|
||||||
|
isSamplerUniformName = uniform.Compare("uniform") == 0 && isShaderType(type);
|
||||||
|
if (isSamplerUniformName)
|
||||||
|
{
|
||||||
|
samplerstobind.Push(std::make_pair(identifier, val));
|
||||||
|
for (auto pos = startpos; pos < endpos; pos++)
|
||||||
|
{
|
||||||
|
if (!IsGlslWhitespace(chars[pos]))
|
||||||
|
chars[pos] = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSamplerUniformName)
|
||||||
|
{
|
||||||
|
startIndex = endpos;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
startIndex = matchIndex + 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
code.UnlockBuffer();
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
FString RemoveLayoutLocationDecl(FString code, const char *inoutkeyword)
|
||||||
|
{
|
||||||
|
long len = (long)code.Len();
|
||||||
|
char *chars = code.LockBuffer();
|
||||||
|
|
||||||
|
long startIndex = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
long matchIndex = code.IndexOf("layout(location", startIndex);
|
||||||
|
if (matchIndex == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
long endIndex = matchIndex;
|
||||||
|
|
||||||
|
// Find end of layout declaration
|
||||||
|
while (chars[endIndex] != ')' && chars[endIndex] != 0)
|
||||||
|
endIndex++;
|
||||||
|
|
||||||
|
if (chars[endIndex] == ')')
|
||||||
|
endIndex++;
|
||||||
|
else if (chars[endIndex] == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Skip whitespace
|
||||||
|
while (IsGlslWhitespace(chars[endIndex]))
|
||||||
|
endIndex++;
|
||||||
|
|
||||||
|
// keyword following the declaration?
|
||||||
|
bool keywordFound = true;
|
||||||
|
long i;
|
||||||
|
for (i = 0; inoutkeyword[i] != 0; i++)
|
||||||
|
{
|
||||||
|
if (chars[endIndex + i] != inoutkeyword[i])
|
||||||
|
{
|
||||||
|
keywordFound = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (keywordFound && IsGlslWhitespace(chars[endIndex + i]))
|
||||||
|
{
|
||||||
|
// yes - replace declaration with spaces
|
||||||
|
for (long i = matchIndex; i < endIndex; i++)
|
||||||
|
chars[i] = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
startIndex = endIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
code.UnlockBuffer();
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Note: the MaterialShaderIndex enum in gl_shader.h needs to be updated whenever this array is modified.
|
||||||
|
const FDefaultShader defaultshaders[] =
|
||||||
|
{
|
||||||
|
{"Default", "shaders/glsl/func_normal.fp", "shaders/glsl/material_normal.fp", ""},
|
||||||
|
{"Warp 1", "shaders/glsl/func_warp1.fp", "shaders/glsl/material_normal.fp", ""},
|
||||||
|
{"Warp 2", "shaders/glsl/func_warp2.fp", "shaders/glsl/material_normal.fp", ""},
|
||||||
|
{"Brightmap","shaders/glsl/func_brightmap.fp", "shaders/glsl/material_normal.fp", "#define BRIGHTMAP\n"},
|
||||||
|
{"Specular", "shaders/glsl/func_spec.fp", "shaders/glsl/material_specular.fp", "#define SPECULAR\n#define NORMALMAP\n"},
|
||||||
|
{"SpecularBrightmap", "shaders/glsl/func_spec.fp", "shaders/glsl/material_specular.fp", "#define SPECULAR\n#define NORMALMAP\n#define BRIGHTMAP\n"},
|
||||||
|
{"PBR","shaders/glsl/func_pbr.fp", "shaders/glsl/material_pbr.fp", "#define PBR\n#define NORMALMAP\n"},
|
||||||
|
{"PBRBrightmap","shaders/glsl/func_pbr.fp", "shaders/glsl/material_pbr.fp", "#define PBR\n#define NORMALMAP\n#define BRIGHTMAP\n"},
|
||||||
|
{"Paletted", "shaders/glsl/func_paletted.fp", "shaders/glsl/material_nolight.fp", ""},
|
||||||
|
{"No Texture", "shaders/glsl/func_notexture.fp", "shaders/glsl/material_normal.fp", ""},
|
||||||
|
{"Basic Fuzz", "shaders/glsl/fuzz_standard.fp", "shaders/glsl/material_normal.fp", ""},
|
||||||
|
{"Smooth Fuzz", "shaders/glsl/fuzz_smooth.fp", "shaders/glsl/material_normal.fp", ""},
|
||||||
|
{"Swirly Fuzz", "shaders/glsl/fuzz_swirly.fp", "shaders/glsl/material_normal.fp", ""},
|
||||||
|
{"Translucent Fuzz", "shaders/glsl/fuzz_smoothtranslucent.fp", "shaders/glsl/material_normal.fp", ""},
|
||||||
|
{"Jagged Fuzz", "shaders/glsl/fuzz_jagged.fp", "shaders/glsl/material_normal.fp", ""},
|
||||||
|
{"Noise Fuzz", "shaders/glsl/fuzz_noise.fp", "shaders/glsl/material_normal.fp", ""},
|
||||||
|
{"Smooth Noise Fuzz", "shaders/glsl/fuzz_smoothnoise.fp", "shaders/glsl/material_normal.fp", ""},
|
||||||
|
{"Software Fuzz", "shaders/glsl/fuzz_software.fp", "shaders/glsl/material_normal.fp", ""},
|
||||||
|
{nullptr,nullptr,nullptr,nullptr}
|
||||||
|
};
|
||||||
|
|
||||||
|
const FEffectShader effectshaders[] =
|
||||||
|
{
|
||||||
|
{ "fogboundary", "shaders/glsl/main.vp", "shaders/glsl/fogboundary.fp", nullptr, nullptr, "#define NO_ALPHATEST\n" },
|
||||||
|
{ "spheremap", "shaders/glsl/main.vp", "shaders/glsl/main.fp", "shaders/glsl/func_normal.fp", "shaders/glsl/material_normal.fp", "#define SPHEREMAP\n#define NO_ALPHATEST\n" },
|
||||||
|
{ "burn", "shaders/glsl/main.vp", "shaders/glsl/burn.fp", nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" },
|
||||||
|
{ "stencil", "shaders/glsl/main.vp", "shaders/glsl/stencil.fp", nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" },
|
||||||
|
};
|
|
@ -0,0 +1,30 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "tarray.h"
|
||||||
|
#include "zstring.h"
|
||||||
|
#include "utility"
|
||||||
|
|
||||||
|
FString RemoveLegacyUserUniforms(FString code);
|
||||||
|
FString RemoveSamplerBindings(FString code, TArray<std::pair<FString, int>> &samplerstobind); // For GL 3.3 compatibility which cannot declare sampler bindings in the sampler source.
|
||||||
|
FString RemoveLayoutLocationDecl(FString code, const char *inoutkeyword);
|
||||||
|
|
||||||
|
struct FDefaultShader
|
||||||
|
{
|
||||||
|
const char * ShaderName;
|
||||||
|
const char * gettexelfunc;
|
||||||
|
const char * lightfunc;
|
||||||
|
const char * Defines;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FEffectShader
|
||||||
|
{
|
||||||
|
const char *ShaderName;
|
||||||
|
const char *vp;
|
||||||
|
const char *fp1;
|
||||||
|
const char *fp2;
|
||||||
|
const char *fp3;
|
||||||
|
const char *defines;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const FDefaultShader defaultshaders[];
|
||||||
|
extern const FEffectShader effectshaders[];
|
|
@ -292,6 +292,9 @@ void DFrameBuffer::SetViewportRects(IntRect *bounds)
|
||||||
mSceneViewport.top = screenHeight - (height + viewwindowy - ((height - viewheight) / 2));
|
mSceneViewport.top = screenHeight - (height + viewwindowy - ((height - viewheight) / 2));
|
||||||
mSceneViewport.width = viewwidth;
|
mSceneViewport.width = viewwidth;
|
||||||
mSceneViewport.height = height;
|
mSceneViewport.height = height;
|
||||||
|
#else
|
||||||
|
// For now use the full screen. This needs to be done better.
|
||||||
|
mSceneViewport = mScreenViewport;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Scale viewports to fit letterbox
|
// Scale viewports to fit letterbox
|
||||||
|
@ -302,12 +305,12 @@ void DFrameBuffer::SetViewportRects(IntRect *bounds)
|
||||||
{
|
{
|
||||||
mScreenViewport.width = mOutputLetterbox.width;
|
mScreenViewport.width = mOutputLetterbox.width;
|
||||||
mScreenViewport.height = mOutputLetterbox.height;
|
mScreenViewport.height = mOutputLetterbox.height;
|
||||||
#if 0
|
|
||||||
mSceneViewport.left = (int)round(mSceneViewport.left * scaleX);
|
mSceneViewport.left = (int)round(mSceneViewport.left * scaleX);
|
||||||
mSceneViewport.top = (int)round(mSceneViewport.top * scaleY);
|
mSceneViewport.top = (int)round(mSceneViewport.top * scaleY);
|
||||||
mSceneViewport.width = (int)round(mSceneViewport.width * scaleX);
|
mSceneViewport.width = (int)round(mSceneViewport.width * scaleX);
|
||||||
mSceneViewport.height = (int)round(mSceneViewport.height * scaleY);
|
mSceneViewport.height = (int)round(mSceneViewport.height * scaleY);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -236,7 +236,7 @@ public:
|
||||||
//IShadowMap mShadowMap;
|
//IShadowMap mShadowMap;
|
||||||
|
|
||||||
IntRect mScreenViewport;
|
IntRect mScreenViewport;
|
||||||
//IntRect mSceneViewport;
|
IntRect mSceneViewport;
|
||||||
IntRect mOutputLetterbox;
|
IntRect mOutputLetterbox;
|
||||||
float mSceneClearColor[4];
|
float mSceneClearColor[4];
|
||||||
|
|
||||||
|
@ -259,7 +259,7 @@ public:
|
||||||
inline int GetWidth() const { return Width; }
|
inline int GetWidth() const { return Width; }
|
||||||
inline int GetHeight() const { return Height; }
|
inline int GetHeight() const { return Height; }
|
||||||
|
|
||||||
#if 0
|
// Currently there is no concept of a scene viewport in Build, but let's keep this stuff here to implement it later.
|
||||||
FVector2 SceneScale() const
|
FVector2 SceneScale() const
|
||||||
{
|
{
|
||||||
return { mSceneViewport.width / (float)mScreenViewport.width, mSceneViewport.height / (float)mScreenViewport.height };
|
return { mSceneViewport.width / (float)mScreenViewport.width, mSceneViewport.height / (float)mScreenViewport.height };
|
||||||
|
@ -269,7 +269,6 @@ public:
|
||||||
{
|
{
|
||||||
return { mSceneViewport.left / (float)mScreenViewport.width, mSceneViewport.top / (float)mScreenViewport.height };
|
return { mSceneViewport.left / (float)mScreenViewport.width, mSceneViewport.top / (float)mScreenViewport.height };
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Make the surface visible.
|
// Make the surface visible.
|
||||||
virtual void Update ();
|
virtual void Update ();
|
||||||
|
@ -395,8 +394,6 @@ extern DFrameBuffer *screen;
|
||||||
#define SCREENHEIGHT (screen->GetHeight ())
|
#define SCREENHEIGHT (screen->GetHeight ())
|
||||||
#define SCREENPITCH (screen->GetPitch ())
|
#define SCREENPITCH (screen->GetPitch ())
|
||||||
|
|
||||||
EXTERN_CVAR (Float, Gamma)
|
|
||||||
|
|
||||||
|
|
||||||
// Allocates buffer screens, call before R_Init.
|
// Allocates buffer screens, call before R_Init.
|
||||||
void V_InitScreenSize();
|
void V_InitScreenSize();
|
||||||
|
|
|
@ -71,6 +71,43 @@ namespace ImageHelpers
|
||||||
return bestcolor;
|
return bestcolor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [SP] Re-implemented BestColor for more precision rather than speed. This function is only ever called once until the game palette is changed.
|
||||||
|
|
||||||
|
int PTM_BestColor(int r, int g, int b, bool reverselookup, float powtable_val, int first, int num)
|
||||||
|
{
|
||||||
|
const PalEntry* pal = BaseColors;
|
||||||
|
static double powtable[256];
|
||||||
|
static bool firstTime = true;
|
||||||
|
static float trackpowtable = 0.;
|
||||||
|
|
||||||
|
double fbestdist = DBL_MAX, fdist;
|
||||||
|
int bestcolor = 0;
|
||||||
|
|
||||||
|
if (firstTime || trackpowtable != powtable_val)
|
||||||
|
{
|
||||||
|
auto pt = powtable_val;
|
||||||
|
trackpowtable = pt;
|
||||||
|
firstTime = false;
|
||||||
|
for (int x = 0; x < 256; x++) powtable[x] = pow((double)x / 255, (double)pt);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int color = first; color < num; color++)
|
||||||
|
{
|
||||||
|
double x = powtable[abs(r - pal[color].r)];
|
||||||
|
double y = powtable[abs(g - pal[color].g)];
|
||||||
|
double z = powtable[abs(b - pal[color].b)];
|
||||||
|
fdist = x + y + z;
|
||||||
|
if (color == first || (reverselookup ? (fdist <= fbestdist) : (fdist < fbestdist)))
|
||||||
|
{
|
||||||
|
if (fdist == 0 && !reverselookup)
|
||||||
|
return color;
|
||||||
|
|
||||||
|
fbestdist = fdist;
|
||||||
|
bestcolor = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestcolor;
|
||||||
|
}
|
||||||
|
|
||||||
void SetPalette(const PalEntry* colors)
|
void SetPalette(const PalEntry* colors)
|
||||||
{
|
{
|
||||||
|
|
|
@ -59,6 +59,7 @@ namespace ImageHelpers
|
||||||
|
|
||||||
// Todo: This should not pick fullbright colors.
|
// Todo: This should not pick fullbright colors.
|
||||||
int BestColor(int r, int g, int b, int first = 0, int num = 255);
|
int BestColor(int r, int g, int b, int first = 0, int num = 255);
|
||||||
|
int PTM_BestColor(int r, int g, int b, bool reverselookup, float powtable_val, int first, int num);
|
||||||
void SetPalette(const PalEntry* colors);
|
void SetPalette(const PalEntry* colors);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,14 @@ include_directories(
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/music
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/music
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/input
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/input
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../platform
|
${CMAKE_CURRENT_SOURCE_DIR}/../platform
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl_load
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/system
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/renderer
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/shaders
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/data
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/postprocessing
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/utility
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,16 @@ include_directories(
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/2d
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/2d
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/music
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/music
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/input
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/input
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../platform )
|
${CMAKE_CURRENT_SOURCE_DIR}/../platform
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl_load
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/system
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/renderer
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/shaders
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/data
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/postprocessing
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/utility
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -194,7 +194,7 @@ public:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
disttable[i] = 255 - eax >> 8;
|
disttable[i] = 255 - (eax >> 8);
|
||||||
|
|
||||||
eax = (eax * eax) >> 8;
|
eax = (eax * eax) >> 8;
|
||||||
}
|
}
|
||||||
|
@ -257,9 +257,8 @@ int LoadSound(const char* name)
|
||||||
else if (!ISDEMOVER) // demo tries to load sound files it doesn't have
|
else if (!ISDEMOVER) // demo tries to load sound files it doesn't have
|
||||||
{
|
{
|
||||||
Printf("Unable to open sound '%s'!\n", filename.GetChars());
|
Printf("Unable to open sound '%s'!\n", filename.GetChars());
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
|
|
|
@ -26,6 +26,14 @@ include_directories(
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/music
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/music
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/input
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/input
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../platform
|
${CMAKE_CURRENT_SOURCE_DIR}/../platform
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl_load
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/system
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/renderer
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/shaders
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/data
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/postprocessing
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/utility
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,14 @@ include_directories(
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/music
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/music
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../common/input
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/input
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../platform
|
${CMAKE_CURRENT_SOURCE_DIR}/../platform
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl_load
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/system
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/renderer
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/gl/shaders
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/data
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/postprocessing
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering/hwrenderer/utility
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../common/rendering
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue