diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e768e945f..7307e6eec 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1125,6 +1125,7 @@ set( FASTMATH_SOURCES gl/shaders/gl_texshader.cpp gl/shaders/gl_shaderprogram.cpp gl/shaders/gl_presentshader.cpp + gl/shaders/gl_present3dRowshader.cpp gl/shaders/gl_bloomshader.cpp gl/shaders/gl_blurshader.cpp gl/shaders/gl_colormapshader.cpp diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 42b4a8e1b..fbfa51440 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -57,6 +57,7 @@ #include "gl/shaders/gl_colormapshader.h" #include "gl/shaders/gl_lensshader.h" #include "gl/shaders/gl_presentshader.h" +#include "gl/shaders/gl_present3dRowshader.h" #include "gl/stereo3d/gl_stereo3d.h" #include "gl/textures/gl_texture.h" #include "gl/textures/gl_translate.h" @@ -103,6 +104,7 @@ FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb) mTonemapPalette = nullptr; mBuffers = nullptr; mPresentShader = nullptr; + mPresent3dRowShader = nullptr; mBloomExtractShader = nullptr; mBloomCombineShader = nullptr; mExposureExtractShader = nullptr; @@ -132,6 +134,7 @@ void FGLRenderer::Initialize(int width, int height) mTonemapPalette = nullptr; mLensShader = new FLensShader(); mPresentShader = new FPresentShader(); + mPresent3dRowShader = new FPresent3DRowShader(); m2DDrawer = new F2DDrawer; // needed for the core profile, because someone decided it was a good idea to remove the default VAO. @@ -184,6 +187,7 @@ FGLRenderer::~FGLRenderer() } if (mBuffers) delete mBuffers; if (mPresentShader) delete mPresentShader; + if (mPresent3dRowShader) delete mPresent3dRowShader; if (mBloomExtractShader) delete mBloomExtractShader; if (mBloomCombineShader) delete mBloomCombineShader; if (mExposureExtractShader) delete mExposureExtractShader; diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 71c52474a..460b5685a 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -29,6 +29,7 @@ class FTonemapShader; class FColormapShader; class FLensShader; class FPresentShader; +class FPresent3DRowShader; class F2DDrawer; class FHardwareTexture; @@ -104,6 +105,7 @@ public: FHardwareTexture *mTonemapPalette; FLensShader *mLensShader; FPresentShader *mPresentShader; + FPresent3DRowShader *mPresent3dRowShader; FTexture *gllight; FTexture *glpart2; diff --git a/src/gl/shaders/gl_present3dRowshader.cpp b/src/gl/shaders/gl_present3dRowshader.cpp new file mode 100644 index 000000000..607e4ede6 --- /dev/null +++ b/src/gl/shaders/gl_present3dRowshader.cpp @@ -0,0 +1,60 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2016 Christopher Bruns +// 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_3dRowshader.cpp +** Copy rendered texture to back buffer, possibly with gamma correction +** while interleaving rows from two independent viewpoint textures, +** representing the left-eye and right-eye views. +** +*/ + +#include "gl/system/gl_system.h" +#include "files.h" +#include "m_swap.h" +#include "v_video.h" +#include "gl/gl_functions.h" +#include "vectors.h" +#include "gl/system/gl_interface.h" +#include "gl/system/gl_framebuffer.h" +#include "gl/system/gl_cvars.h" +#include "gl/shaders/gl_present3dRowshader.h" + +void FPresent3DRowShader::Bind() +{ + if (!mShader) + { + mShader.Compile(FShaderProgram::Vertex, "shaders/glsl/screenquadscale.vp", "", 330); + mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/present_row3d.fp", "", 330); + mShader.SetFragDataLocation(0, "FragColor"); + mShader.Link("shaders/glsl/presentRow3d"); + mShader.SetAttribLocation(0, "PositionInProjection"); + mShader.SetAttribLocation(1, "UV"); + LeftEyeTexture.Init(mShader, "LeftEyeTexture"); + RightEyeTexture.Init(mShader, "RightEyeTexture"); + InvGamma.Init(mShader, "InvGamma"); + Contrast.Init(mShader, "Contrast"); + Brightness.Init(mShader, "Brightness"); + Scale.Init(mShader, "UVScale"); + VerticalPixelOffset.Init(mShader, "VerticalPixelOffset"); + } + mShader.Bind(); +} diff --git a/src/gl/shaders/gl_present3dRowshader.h b/src/gl/shaders/gl_present3dRowshader.h new file mode 100644 index 000000000..8e3bd772d --- /dev/null +++ b/src/gl/shaders/gl_present3dRowshader.h @@ -0,0 +1,51 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2015 Christopher Bruns +// 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_present3dRowshader.h +** Final composition and present shader for row-interleaved stereoscopic 3D mode +** +*/ + +#ifndef GL_PRESENT3DROWSHADER_H_ +#define GL_PRESENT3DROWSHADER_H_ + +#include "gl_shaderprogram.h" + +class FPresent3DRowShader +{ +public: + void Bind(); + + FBufferedUniformSampler LeftEyeTexture; + FBufferedUniformSampler RightEyeTexture; + FBufferedUniform1f InvGamma; + FBufferedUniform1f Contrast; + FBufferedUniform1f Brightness; + FBufferedUniform2f Scale; + FBufferedUniform1i VerticalPixelOffset; + +private: + FShaderProgram mShader; +}; + +// GL_PRESENT3DROWSHADER_H_ +#endif \ No newline at end of file diff --git a/src/gl/stereo3d/gl_interleaved3d.cpp b/src/gl/stereo3d/gl_interleaved3d.cpp index 085efae51..28b9c373e 100644 --- a/src/gl/stereo3d/gl_interleaved3d.cpp +++ b/src/gl/stereo3d/gl_interleaved3d.cpp @@ -36,6 +36,12 @@ #include "gl_interleaved3d.h" #include "gl/renderer/gl_renderer.h" #include "gl/renderer/gl_renderbuffers.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/system/gl_framebuffer.h" +#include "gl/shaders/gl_present3dRowshader.h" + +EXTERN_CVAR(Float, vid_brightness) +EXTERN_CVAR(Float, vid_contrast) namespace s3d { @@ -46,6 +52,10 @@ const RowInterleaved3D& RowInterleaved3D::getInstance(float ipd) return instance; } +RowInterleaved3D::RowInterleaved3D(double ipdMeters) + : TopBottom3D(ipdMeters) +{} + void RowInterleaved3D::Present() const { GLRenderer->mBuffers->BindOutputFB(); @@ -60,8 +70,43 @@ void RowInterleaved3D::Present() const bottomHalfScreen.height = topHeight; bottomHalfScreen.top = 0; - GLRenderer->mBuffers->BlitFromEyeTexture(0, &topHalfScreen); - GLRenderer->mBuffers->BlitFromEyeTexture(1, &bottomHalfScreen); + // Bind each eye texture, for composition in the shader + GLRenderer->mBuffers->BindEyeTexture(0, 0); + GLRenderer->mBuffers->BindEyeTexture(1, 1); + + glActiveTexture(GL_TEXTURE0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glActiveTexture(GL_TEXTURE1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + const GL_IRECT& box = GLRenderer->mOutputLetterbox; + glViewport(box.left, box.top, box.width, box.height); + + bool applyGamma = true; + + GLRenderer->mPresent3dRowShader->Bind(); + GLRenderer->mPresent3dRowShader->LeftEyeTexture.Set(0); + GLRenderer->mPresent3dRowShader->RightEyeTexture.Set(1); + if (!applyGamma || GLRenderer->framebuffer->IsHWGammaActive()) + { + GLRenderer->mPresent3dRowShader->InvGamma.Set(1.0f); + GLRenderer->mPresent3dRowShader->Contrast.Set(1.0f); + GLRenderer->mPresent3dRowShader->Brightness.Set(0.0f); + } + else + { + GLRenderer->mPresent3dRowShader->InvGamma.Set(1.0f / clamp(Gamma, 0.1f, 4.f)); + GLRenderer->mPresent3dRowShader->Contrast.Set(clamp(vid_contrast, 0.1f, 3.f)); + GLRenderer->mPresent3dRowShader->Brightness.Set(clamp(vid_brightness, -0.8f, 0.8f)); + } + GLRenderer->mPresent3dRowShader->Scale.Set( + GLRenderer->mScreenViewport.width / (float)GLRenderer->mBuffers->GetWidth(), + GLRenderer->mScreenViewport.height / (float)GLRenderer->mBuffers->GetHeight()); + GLRenderer->mPresent3dRowShader->VerticalPixelOffset.Set(0); // fixme: vary with window location + GLRenderer->RenderScreenQuad(); } diff --git a/src/gl/stereo3d/gl_interleaved3d.h b/src/gl/stereo3d/gl_interleaved3d.h index 1f22fa356..30aef7d5a 100644 --- a/src/gl/stereo3d/gl_interleaved3d.h +++ b/src/gl/stereo3d/gl_interleaved3d.h @@ -42,13 +42,15 @@ #include "gl/system/gl_system.h" #include "gl/renderer/gl_renderstate.h" +class FPresent3DRowShader; + namespace s3d { class RowInterleaved3D : public TopBottom3D { public: static const RowInterleaved3D& getInstance(float ipd); - RowInterleaved3D(double ipdMeters) : TopBottom3D(ipdMeters) {} + RowInterleaved3D(double ipdMeters); void Present() const override; }; diff --git a/src/gl/stereo3d/gl_sidebyside3d.cpp b/src/gl/stereo3d/gl_sidebyside3d.cpp index d2b0cafdd..ecb3ec1cc 100644 --- a/src/gl/stereo3d/gl_sidebyside3d.cpp +++ b/src/gl/stereo3d/gl_sidebyside3d.cpp @@ -125,9 +125,10 @@ void TopBottom3D::Present() const int bottomHeight = GLRenderer->mOutputLetterbox.height - topHeight; GL_IRECT topHalfScreen = GLRenderer->mOutputLetterbox; topHalfScreen.height = topHeight; + topHalfScreen.top = topHeight; GL_IRECT bottomHalfScreen = GLRenderer->mOutputLetterbox; bottomHalfScreen.height = bottomHeight; - bottomHalfScreen.top += topHeight; + bottomHalfScreen.top = 0; GLRenderer->mBuffers->BindEyeTexture(0, 0); GLRenderer->DrawPresentTexture(topHalfScreen, true); diff --git a/wadsrc/static/shaders/glsl/present_row3d.fp b/wadsrc/static/shaders/glsl/present_row3d.fp new file mode 100644 index 000000000..18bd3cd11 --- /dev/null +++ b/wadsrc/static/shaders/glsl/present_row3d.fp @@ -0,0 +1,36 @@ + +in vec2 TexCoord; +out vec4 FragColor; + +uniform sampler2D LeftEyeTexture; +uniform sampler2D RightEyeTexture; +uniform float InvGamma; +uniform float Contrast; +uniform float Brightness; +uniform int VerticalPixelOffset; // top-of-window might not be top-of-screen + +vec4 ApplyGamma(vec4 c) +{ + vec3 val = c.rgb * Contrast - (Contrast - 1.0) * 0.5; + val += Brightness * 0.5; + val = pow(max(val, vec3(0.0)), vec3(InvGamma)); + return vec4(val, c.a); +} + +void main() +{ + // NOTE we assume here that the full screen height is the sum of the left + // and right eye view heights + int screenHeightInPixels = textureSize(LeftEyeTexture, 0).y + textureSize(RightEyeTexture, 0).y; + int thisVerticalPixel = int(TexCoord.y * screenHeightInPixels + 0.5); + bool isLeftEye = (thisVerticalPixel + VerticalPixelOffset) % 2 == 0; + vec4 inputColor; + if (isLeftEye) { + inputColor = texture(LeftEyeTexture, TexCoord); + } + else { + // inputColor = vec4(0, 1, 0, 1); + inputColor = texture(RightEyeTexture, TexCoord); + } + FragColor = ApplyGamma(inputColor); +}