diff options
| -rw-r--r-- | shaders/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | shaders/shaders.cc | 3 | ||||
| -rw-r--r-- | shaders/shaders.hh | 1 | ||||
| -rw-r--r-- | shaders/subpixel.cc | 22 | ||||
| -rw-r--r-- | shaders/subpixel.glsl | 90 |
5 files changed, 117 insertions, 0 deletions
diff --git a/shaders/CMakeLists.txt b/shaders/CMakeLists.txt index c78d42b..a0227d5 100644 --- a/shaders/CMakeLists.txt +++ b/shaders/CMakeLists.txt @@ -10,6 +10,7 @@ add_executable(shaders aero.cc polygon.cc + subpixel.cc vfog.cc ) target_link_libraries(shaders PRIVATE common) diff --git a/shaders/shaders.cc b/shaders/shaders.cc index 214d598..86dcfdb 100644 --- a/shaders/shaders.cc +++ b/shaders/shaders.cc @@ -598,6 +598,9 @@ SDL_AppResult SDLCALL SDL_AppEvent(void*, SDL_Event* event) if (event->key.key == SDLK_TAB) { G.ui_hidden = !G.ui_hidden; } + if (event->key.key == SDLK_SPACE) { + G.pause = !G.pause; + } } break; case SDL_EVENT_MOUSE_MOTION: { if (is_3d && (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_MASK(SDL_BUTTON_LEFT))) { diff --git a/shaders/shaders.hh b/shaders/shaders.hh index 00365fb..d23a13d 100644 --- a/shaders/shaders.hh +++ b/shaders/shaders.hh @@ -7,6 +7,7 @@ #include "imgui.h" #include <algorithm> +#include <cstring> #include <vector> #include <SDL3/SDL.h> diff --git a/shaders/subpixel.cc b/shaders/subpixel.cc new file mode 100644 index 0000000..73a5d51 --- /dev/null +++ b/shaders/subpixel.cc @@ -0,0 +1,22 @@ +#include "shaders.hh" + +static float u_quad[2] = { 256.0f, 2.0f }; + +static void ui() +{ + ImGui::SliderFloat("quad width", &u_quad[0], 1.0f, 512.0f); + ImGui::SliderFloat("quad height", &u_quad[1], 1.0f, 512.0f); +} + +static void uniforms(Shader* shader) +{ + GL(glUniform2fv(shader->get_required_uniform("u_quad"), 1, u_quad)); +} + +static Shader subpixel = { + .path = "subpixel.glsl", + .model = MODEL_QUAD, + .ui_fn = ui, + .uf_fn = uniforms, +}; +ENABLE_SHADER(subpixel); diff --git a/shaders/subpixel.glsl b/shaders/subpixel.glsl new file mode 100644 index 0000000..df65b91 --- /dev/null +++ b/shaders/subpixel.glsl @@ -0,0 +1,90 @@ +#version 330 core + +// -------------------------------------------------------------------------------- +// Basic subpixel rendering with LCD filtering. +// Assumes R-G-B LCD with no compositor scaling +// +// ref: https://www.shadertoy.com/view/NtVXWc (spinning quad) +// +// SPDX-License-Identifier: 0BSD +// -------------------------------------------------------------------------------- + +// -------------------------------------------------------------------------------- +// Uniforms +// -------------------------------------------------------------------------------- +uniform float u_time; +uniform vec2 u_res; +uniform vec2 u_quad; + +// -------------------------------------------------------------------------------- +// Vertex outputs +// -------------------------------------------------------------------------------- +in vec2 v_p; +in vec2 v_t; + +// -------------------------------------------------------------------------------- +// Fragment outputs +// -------------------------------------------------------------------------------- +out vec4 f_c; + +// -------------------------------------------------------------------------------- +// Entry point +// -------------------------------------------------------------------------------- + +vec2 rotate(vec2 v, float theta) +{ + // ref: https://en.wikipedia.org/wiki/Rotation_matrix + return mat2(cos(theta), -sin(theta), sin(theta), cos(theta)) * v; +} + +float box_d(vec2 v) +{ + return max(v.x, v.y); +} + +float spinning_quad(vec2 pos) +{ + pos = rotate(pos, u_time * -0.25f); + vec2 halfsize = u_quad / 2.0f; + vec2 q = abs(pos) - halfsize; + return 1.0f - step(0.0f, box_d(q)); +} + +vec3 spinning_quad_subpixel(vec2 pos) +{ + vec2 one_third_x = vec2(1.0f / 3.0f, 0.0f); + + float sL = spinning_quad(pos - one_third_x); + float sC = spinning_quad(pos); + float sR = spinning_quad(pos + one_third_x); + +#if 1 + // 3-tap low pass filter + float weights[3] = float[3](0.25f, 0.50f, 0.25f); + float r = weights[1] * sL + weights[0] * sC; + float g = weights[0] * sL + weights[1] * sC + weights[2] * sR; + float b = weights[0] * sC + weights[1] * sR; + + r *= (1.0f / 0.75f); + b *= (1.0f / 0.75f); +#else + float r = sL; + float g = sC; + float b = sR; +#endif + + return vec3(r, g, b); +} + +void main() +{ + vec2 cursor = gl_FragCoord.xy - (u_res / 2.0f); + + // top quad + vec3 c1 = vec3(spinning_quad(cursor - vec2(0, u_res.y / 4.0f))); + + // bottom quad + vec3 c2 = spinning_quad_subpixel(cursor + vec2(0, u_res.y / 4.0f)); + + f_c = vec4(mix(c2, c1, step(u_res.y / 2.0f, gl_FragCoord.y)), 1.0f); +} |