summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--shaders/CMakeLists.txt1
-rw-r--r--shaders/shaders.cc3
-rw-r--r--shaders/subpixel.cc22
-rw-r--r--shaders/subpixel.glsl90
4 files changed, 116 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/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);
+}