diff --git a/client/shaders/fxaa/opengl_fragment.glsl b/client/shaders/fxaa/opengl_fragment.glsl new file mode 100644 index 0000000000000..55b0d69c196e6 --- /dev/null +++ b/client/shaders/fxaa/opengl_fragment.glsl @@ -0,0 +1,119 @@ +uniform sampler2D baseTexture; + +uniform vec2 resolution; + +// The MIT License (MIT) Copyright (c) 2014 Matt DesLauriers + +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +#define rendered baseTexture + +#ifndef FXAA_REDUCE_MIN + #define FXAA_REDUCE_MIN (1.0/ 128.0) +#endif +#ifndef FXAA_REDUCE_MUL + #define FXAA_REDUCE_MUL (1.0 / 8.0) +#endif +#ifndef FXAA_SPAN_MAX + #define FXAA_SPAN_MAX 8.0 +#endif + +void texcoords(vec2 fragCoord, vec2 resl, + out vec2 v_rgbNW, out vec2 v_rgbNE, + out vec2 v_rgbSW, out vec2 v_rgbSE, + out vec2 v_rgbM) { + vec2 inverseVP = 1.0 / resl.xy; + v_rgbNW = (fragCoord + vec2(-1.0, -1.0)) * inverseVP; + v_rgbNE = (fragCoord + vec2(1.0, -1.0)) * inverseVP; + v_rgbSW = (fragCoord + vec2(-1.0, 1.0)) * inverseVP; + v_rgbSE = (fragCoord + vec2(1.0, 1.0)) * inverseVP; + v_rgbM = vec2(fragCoord * inverseVP); +} + +//optimized version for mobile, where dependent +//texture reads can be a bottleneck +vec4 fxaa(sampler2D tex, vec2 fragCoord, vec2 resl, + vec2 v_rgbNW, vec2 v_rgbNE, + vec2 v_rgbSW, vec2 v_rgbSE, + vec2 v_rgbM) { + vec4 color; + mediump vec2 inverseVP = vec2(1.0 / resl.x, 1.0 / resl.y); + vec3 rgbNW = texture2D(tex, v_rgbNW).xyz; + vec3 rgbNE = texture2D(tex, v_rgbNE).xyz; + vec3 rgbSW = texture2D(tex, v_rgbSW).xyz; + vec3 rgbSE = texture2D(tex, v_rgbSE).xyz; + vec4 texColor = texture2D(tex, v_rgbM); + vec3 rgbM = texColor.xyz; + vec3 luma = vec3(0.299, 0.587, 0.114); + float lumaNW = dot(rgbNW, luma); + float lumaNE = dot(rgbNE, luma); + float lumaSW = dot(rgbSW, luma); + float lumaSE = dot(rgbSE, luma); + float lumaM = dot(rgbM, luma); + float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); + float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); + + mediump vec2 dir; + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + + float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * + (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); + + float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); + dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), + max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), + dir * rcpDirMin)) * inverseVP; + + vec3 rgbA = 0.5 * ( + texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + + texture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz); + vec3 rgbB = rgbA * 0.5 + 0.25 * ( + texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + + texture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz); + + float lumaB = dot(rgbB, luma); + if ((lumaB < lumaMin) || (lumaB > lumaMax)) + color = vec4(rgbA, texColor.a); + else + color = vec4(rgbB, texColor.a); + return color; +} + +vec4 apply(sampler2D tex, vec2 fragCoord, vec2 resl) { + mediump vec2 v_rgbNW; + mediump vec2 v_rgbNE; + mediump vec2 v_rgbSW; + mediump vec2 v_rgbSE; + mediump vec2 v_rgbM; + + //compute the texture coords + texcoords(fragCoord, resl, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM); + + //compute FXAA + return fxaa(tex, fragCoord, resl, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM); +} + +void main(void) +{ + gl_FragColor = apply(rendered, gl_TexCoord[0].st * resolution, resolution); + //gl_FragColor = texture2D(rendered, gl_TexCoord[0].st).rgba; +} \ No newline at end of file diff --git a/client/shaders/fxaa/opengl_vertex.glsl b/client/shaders/fxaa/opengl_vertex.glsl new file mode 100644 index 0000000000000..8892d5cbb8f48 --- /dev/null +++ b/client/shaders/fxaa/opengl_vertex.glsl @@ -0,0 +1,6 @@ +void main(void) +{ + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_Position = gl_Vertex; + gl_FrontColor = gl_BackColor = gl_Color; +} \ No newline at end of file diff --git a/src/client/render/plain.cpp b/src/client/render/plain.cpp index a130a14eb0256..94736cc6181b4 100644 --- a/src/client/render/plain.cpp +++ b/src/client/render/plain.cpp @@ -21,6 +21,57 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "plain.h" #include "settings.h" +#include "client/client.h" +#include "client/shader.h" +#include "client/tile.h" + +// With the IShaderConstantSetter, uniforms of the shader can be set (probably) +class OversampleShaderConstantSetter : public IShaderConstantSetter +{ +public: + OversampleShaderConstantSetter(RenderingCorePlain *core) : + m_core(core), m_resolution("resolution") + { + } + + ~OversampleShaderConstantSetter() override = default; + + void onSetConstants(video::IMaterialRendererServices *services) override + { + // TODO: Why is the resolution uniform never set? + + v2u32 render_size = m_core->getScreensize(); + float as_array[2] = { + (float)render_size.X, + (float)render_size.Y, + }; + m_resolution.set(as_array, services); + } + + //~ void onSetMaterial(const video::SMaterial& material) override + //~ { + //~ m_render_size = render_size; + //~ } + +private: + RenderingCorePlain *m_core; + CachedPixelShaderSetting m_resolution; +}; + +// Each shader requires a constant setter and a factory for it +class OversampleShaderConstantSetterFactory : public IShaderConstantSetterFactory +{ + RenderingCorePlain *m_core; + +public: + OversampleShaderConstantSetterFactory(RenderingCorePlain *core) : m_core(core) {} + + virtual IShaderConstantSetter *create() + { + return new OversampleShaderConstantSetter(m_core); + } +}; + inline u32 scaledown(u32 coef, u32 size) { return (size + coef - 1) / coef; @@ -31,10 +82,28 @@ RenderingCorePlain::RenderingCorePlain( : RenderingCore(_device, _client, _hud) { scale = g_settings->getU16("undersampling"); + + IWritableShaderSource *s = client->getShaderSource(); + s->addShaderConstantSetterFactory( + new OversampleShaderConstantSetterFactory(this)); + u32 shader = s->getShader("fxaa", TILE_MATERIAL_BASIC, NDT_NORMAL); + renderMaterial.UseMipMaps = false; + renderMaterial.MaterialType = s->getShaderInfo(shader).material; + renderMaterial.TextureLayer[0].AnisotropicFilter = false; + renderMaterial.TextureLayer[0].BilinearFilter = false; + renderMaterial.TextureLayer[0].TrilinearFilter = false; + renderMaterial.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE; + renderMaterial.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; + } void RenderingCorePlain::initTextures() { + rendered = driver->addRenderTargetTexture(screensize, "3d_render", + //~ video::ECF_A8R8G8B8); + video::ECF_A16B16G16R16F); + renderMaterial.TextureLayer[0].Texture = rendered; + if (scale <= 1) return; v2u32 size{scaledown(scale, screensize.X), scaledown(scale, screensize.Y)}; @@ -44,6 +113,8 @@ void RenderingCorePlain::initTextures() void RenderingCorePlain::clearTextures() { + driver->removeTexture(rendered); + if (scale <= 1) return; driver->removeTexture(lowres); @@ -69,8 +140,24 @@ void RenderingCorePlain::upscale() void RenderingCorePlain::drawAll() { + driver->setRenderTarget(rendered, true, true, skycolor); draw3D(); + driver->setRenderTarget(nullptr, false, false, skycolor); + static const video::S3DVertex vertices[4] = { + video::S3DVertex(1.0, -1.0, 0.0, 0.0, 0.0, -1.0, + video::SColor(255, 0, 255, 255), 1.0, 0.0), + video::S3DVertex(-1.0, -1.0, 0.0, 0.0, 0.0, -1.0, + video::SColor(255, 255, 0, 255), 0.0, 0.0), + video::S3DVertex(-1.0, 1.0, 0.0, 0.0, 0.0, -1.0, + video::SColor(255, 255, 255, 0), 0.0, 1.0), + video::S3DVertex(1.0, 1.0, 0.0, 0.0, 0.0, -1.0, + video::SColor(255, 255, 255, 255), 1.0, 1.0), + }; + static const u16 indices[6] = {0, 1, 2, 2, 3, 0}; + driver->setMaterial(renderMaterial); + driver->drawVertexPrimitiveList(&vertices, 4, &indices, 2); + drawPostFx(); - upscale(); + // upscale(); drawHUD(); } diff --git a/src/client/render/plain.h b/src/client/render/plain.h index 80c17ed9f96da..ff0f1c21226c0 100644 --- a/src/client/render/plain.h +++ b/src/client/render/plain.h @@ -32,7 +32,12 @@ class RenderingCorePlain : public RenderingCore void beforeDraw() override; void upscale(); + video::ITexture *rendered = nullptr; + video::SMaterial renderMaterial; + + public: RenderingCorePlain(IrrlichtDevice *_device, Client *_client, Hud *_hud); void drawAll() override; + v2u32 getScreensize() const { return screensize; } };