diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0918229d3..e92cd0dd3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -952,6 +952,7 @@ set( FASTMATH_SOURCES gl/shaders/gl_tonemapshader.cpp gl/shaders/gl_lensshader.cpp gl/shaders/gl_fxaashader.cpp + gl/shaders/gl_shadowmapshader.cpp gl/system/gl_interface.cpp gl/system/gl_framebuffer.cpp gl/system/gl_debug.cpp diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 6021411be..b68ef57f9 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -60,6 +60,7 @@ #include "gl/shaders/gl_fxaashader.h" #include "gl/shaders/gl_presentshader.h" #include "gl/shaders/gl_present3dRowshader.h" +#include "gl/shaders/gl_shadowmapshader.h" #include "gl/stereo3d/gl_stereo3d.h" #include "gl/textures/gl_texture.h" #include "gl/textures/gl_translate.h" @@ -125,6 +126,7 @@ FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb) mSSAOCombineShader = nullptr; mFXAAShader = nullptr; mFXAALumaShader = nullptr; + mShadowMapShader = nullptr; } void gl_LoadModels(); @@ -153,6 +155,7 @@ void FGLRenderer::Initialize(int width, int height) mPresent3dCheckerShader = new FPresent3DCheckerShader(); mPresent3dColumnShader = new FPresent3DColumnShader(); mPresent3dRowShader = new FPresent3DRowShader(); + mShadowMapShader = new FShadowMapShader(); m2DDrawer = new F2DDrawer; // needed for the core profile, because someone decided it was a good idea to remove the default VAO. @@ -223,6 +226,7 @@ FGLRenderer::~FGLRenderer() if (mTonemapPalette) delete mTonemapPalette; if (mColormapShader) delete mColormapShader; if (mLensShader) delete mLensShader; + if (mShadowMapShader) delete mShadowMapShader; delete mFXAAShader; delete mFXAALumaShader; } diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 2ba1cbc4d..b655f6d60 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -41,6 +41,7 @@ class FPresent3DColumnShader; class FPresent3DRowShader; class F2DDrawer; class FHardwareTexture; +class FShadowMapShader; inline float DEG2RAD(float deg) { @@ -123,6 +124,7 @@ public: FPresent3DCheckerShader *mPresent3dCheckerShader; FPresent3DColumnShader *mPresent3dColumnShader; FPresent3DRowShader *mPresent3dRowShader; + FShadowMapShader *mShadowMapShader; FLightBSP mLightBSP; diff --git a/src/gl/shaders/gl_shadowmapshader.cpp b/src/gl/shaders/gl_shadowmapshader.cpp new file mode 100644 index 000000000..674d0a04f --- /dev/null +++ b/src/gl/shaders/gl_shadowmapshader.cpp @@ -0,0 +1,45 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2016 Magnus Norddahl +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + +#include "gl/system/gl_system.h" +#include "files.h" +#include "m_swap.h" +#include "v_video.h" +#include "gl/gl_functions.h" +#include "vectors.h" +#include "gl/system/gl_interface.h" +#include "gl/system/gl_framebuffer.h" +#include "gl/system/gl_cvars.h" +#include "gl/shaders/gl_shadowmapshader.h" + +void FShadowMapShader::Bind() +{ + if (!mShader) + { + mShader.Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", 430); + mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/shadowmap.fp", "", 430); + mShader.SetFragDataLocation(0, "FragColor"); + mShader.Link("shaders/glsl/shadowmap"); + mShader.SetAttribLocation(0, "PositionInProjection"); + } + mShader.Bind(); +} diff --git a/src/gl/shaders/gl_shadowmapshader.h b/src/gl/shaders/gl_shadowmapshader.h new file mode 100644 index 000000000..7d01f9974 --- /dev/null +++ b/src/gl/shaders/gl_shadowmapshader.h @@ -0,0 +1,15 @@ +#ifndef __GL_SHADOWMAPSHADER_H +#define __GL_SHADOWMAPSHADER_H + +#include "gl_shaderprogram.h" + +class FShadowMapShader +{ +public: + void Bind(); + +private: + FShaderProgram mShader; +}; + +#endif \ No newline at end of file diff --git a/wadsrc/static/shaders/glsl/shadowmap.fp b/wadsrc/static/shaders/glsl/shadowmap.fp new file mode 100644 index 000000000..d29108fff --- /dev/null +++ b/wadsrc/static/shaders/glsl/shadowmap.fp @@ -0,0 +1,116 @@ + +in vec2 TexCoord; +out vec4 FragColor; + +struct GPUNode +{ + vec4 plane; + int children[2]; + int linecount[2]; +}; + +struct GPUSeg +{ + vec2 pos; + vec2 delta; + vec4 bSolid; +}; + +layout(std430, binding = 2) buffer LightNodes +{ + GPUNode bspNodes[]; +}; + +layout(std430, binding = 3) buffer LightSegs +{ + GPUSeg bspSegs[]; +}; + +//=========================================================================== +// +// Ray/BSP collision test. Returns 0 if the ray hit a line, 1 otherwise. +// +//=========================================================================== + +float rayTest(vec2 from, vec2 to) +{ + const int max_iterations = 50; + const float epsilon = 0.0000001; + + // Avoid wall acne by adding some margin + vec2 margin = normalize(to - from); + to -= margin; + + vec2 raydelta = to - from; + float raydist2 = dot(raydelta, raydelta); + vec2 raynormal = vec2(raydelta.y, -raydelta.x); + float rayd = dot(raynormal, from); + + if (raydist2 < 1.0 || bspNodes.length() == 0) + return 1.0; + + int nodeIndex = bspNodes.length() - 1; + + for (int iteration = 0; iteration < max_iterations; iteration++) + { + GPUNode node = bspNodes[nodeIndex]; + int side = (dot(node.plane, vec4(from, 0.0, 1.0)) > 0.0) ? 1 : 0; + int linecount = node.linecount[side]; + if (linecount < 0) + { + nodeIndex = node.children[side]; + } + else + { + int startLineIndex = node.children[side]; + + // Ray/line test each line segment. + bool hit_line = false; + for (int i = 0; i < linecount; i++) + { + GPUSeg seg = bspSegs[startLineIndex + i]; + + float den = dot(raynormal, seg.delta); + if (abs(den) > epsilon) + { + float t_seg = (rayd - dot(raynormal, seg.pos)) / den; + if (t_seg >= 0.0 && t_seg <= 1.0) + { + vec2 seghitdelta = seg.pos + seg.delta * t_seg - from; + if (dot(raydelta, seghitdelta) > 0.0 && dot(seghitdelta, seghitdelta) < raydist2) // We hit a line segment. + { + if (seg.bSolid.x > 0.0) // segment line is one-sided + return 0.0; + + // We hit a two-sided segment line. Move to the other side and continue ray tracing. + from = from + seghitdelta + margin; + + raydelta = to - from; + raydist2 = dot(raydelta, raydelta); + raynormal = vec2(raydelta.y, -raydelta.x); + rayd = dot(raynormal, from); + + if (raydist2 < 1.0 || bspNodes.length() == 0) + return 1.0; + + nodeIndex = bspNodes.length() - 1; + + hit_line = true; + break; + } + } + } + } + + if (!hit_line) + return 1.0; + } + } + + return 0.0; +} + +void main() +{ + FragColor = vec4(rayTest(vec2(0.0, 0.0), vec2(1.0, 1.0)); +}