[retro-gtk/wip/aplazas/gl-display] fixes and shadertoy CRT filter



commit a7d77ebd3dfa89a16b66c45b3c6fe95d26e64cda
Author: Adrien Plazas <kekun plazas laposte net>
Date:   Tue Nov 14 19:25:05 2017 +0100

    fixes and shadertoy CRT filter

 retro-gtk/retro-gl-display.c        |   16 +++-
 retro-gtk/retro-gtk.gresource.xml   |    4 +
 retro-gtk/shaders/bicubic.fs        |   90 +++++++++++++++++++++
 retro-gtk/shaders/curvature.fs      |   21 +++++
 retro-gtk/shaders/edge-detection.fs |   25 ++++++
 retro-gtk/shaders/shadertoy.fs      |  146 +++++++++++++++++++++++++++++++++++
 6 files changed, 298 insertions(+), 4 deletions(-)
---
diff --git a/retro-gtk/retro-gl-display.c b/retro-gtk/retro-gl-display.c
index fb637e1..29702b3 100644
--- a/retro-gtk/retro-gl-display.c
+++ b/retro-gtk/retro-gl-display.c
@@ -142,7 +142,7 @@ retro_gl_display_realize (RetroGLDisplay *self)
 
   fragment_shader =
     create_shader_from_resource (GL_FRAGMENT_SHADER,
-                                 "/org/gnome/Retro/shaders/scanline.fs",
+                                 "/org/gnome/Retro/shaders/shadertoy.fs",
                                  0,
                                  NULL);
 
@@ -204,7 +204,9 @@ retro_gl_display_render (RetroGLDisplay *self)
   gdouble y = 0.0;
   GLenum filter;
   GLint relative_aspect_ratio;
-  GLfloat target_width, target_height, output_width, output_height;
+  GLfloat source_width, source_height;
+  GLfloat target_width, target_height;
+  GLfloat output_width, output_height;
 
   g_return_val_if_fail (self != NULL, FALSE);
 
@@ -247,11 +249,17 @@ retro_gl_display_render (RetroGLDisplay *self)
                (gfloat) gtk_widget_get_allocated_height (GTK_WIDGET (self)) /
                self->aspect_ratio);
 
-  target_width = (GLfloat) gdk_pixbuf_get_width (self->pixbuf);
-  target_height = (GLfloat) gdk_pixbuf_get_height (self->pixbuf);
+  source_width = (GLfloat) gdk_pixbuf_get_width (self->pixbuf);
+  source_height = (GLfloat) gdk_pixbuf_get_height (self->pixbuf);
+  target_width = (GLfloat) gtk_widget_get_allocated_width (GTK_WIDGET (self));
+  target_height = (GLfloat) gtk_widget_get_allocated_height (GTK_WIDGET (self));
   output_width = (GLfloat) gtk_widget_get_allocated_width (GTK_WIDGET (self));
   output_height = (GLfloat) gtk_widget_get_allocated_height (GTK_WIDGET (self));
 
+  set_gl_uniform_4f (self->shader_program, "sourceSize[0]",
+                     source_width, source_height,
+                     1.0f / source_width, 1.0f / source_height);
+
   set_gl_uniform_4f (self->shader_program, "targetSize",
                      target_width, target_height,
                      1.0f / target_width, 1.0f / target_height);
diff --git a/retro-gtk/retro-gtk.gresource.xml b/retro-gtk/retro-gtk.gresource.xml
index 5133261..c1c41bb 100644
--- a/retro-gtk/retro-gtk.gresource.xml
+++ b/retro-gtk/retro-gtk.gresource.xml
@@ -3,8 +3,12 @@
   <gresource prefix="/org/gnome/Retro">
     <file>retro-gl-display-fragment.glsl</file>
     <file>retro-gl-display-vertex.glsl</file>
+    <file>shaders/bicubic.fs</file>
     <file>shaders/crt-simple.fs</file>
     <file>shaders/crt-simple.vs</file>
+    <file>shaders/curvature.fs</file>
+    <file>shaders/edge-detection.fs</file>
     <file>shaders/scanline.fs</file>
+    <file>shaders/shadertoy.fs</file>
   </gresource>
 </gresources>
diff --git a/retro-gtk/shaders/bicubic.fs b/retro-gtk/shaders/bicubic.fs
new file mode 100644
index 0000000..8024039
--- /dev/null
+++ b/retro-gtk/shaders/bicubic.fs
@@ -0,0 +1,90 @@
+#version 150
+
+uniform sampler2D source[];
+uniform vec4 sourceSize[];
+
+in Vertex {
+  vec2 texCoord;
+};
+
+               out vec4 fragColor;
+
+       float weight(float x)
+       {
+           float ax = abs(x);
+           // Mitchel-Netravali coefficients.
+           // Best psychovisual result.
+           const float B = 1.0 / 3.0;
+           const float C = 1.0 / 3.0;
+
+           if (ax < 1.0) {
+               return (
+                   pow(x, 2.0) * (
+                       (12.0 - 9.0 * B - 6.0 * C) * ax +
+                       (-18.0 + 12.0 * B + 6.0 * C)
+                   ) +
+                   (6.0 - 2.0 * B)
+               ) / 6.0;
+
+           } else if ((ax >= 1.0) && (ax < 2.0)) {
+               return (
+                   pow(x, 2.0) * (
+                       (-B - 6.0 * C) * ax +
+                       (6.0 * B + 30.0 * C)
+                   ) +
+                   (-12.0 * B - 48.0 * C) * ax +
+                   (8.0 * B + 24.0 * C)
+               ) / 6.0;
+
+           } else {
+               return 0.0;
+           }
+       }
+
+       vec4 weight4(float x)
+       {
+           return vec4(
+               weight(x + 1.0),
+               weight(x),
+               weight(1.0 - x),
+               weight(2.0 - x));
+       }
+
+       vec3 pixel(float xpos, float ypos)
+       {
+           return texture2D(source[0], vec2(xpos, ypos)).rgb;
+       }
+
+       vec3 line(float ypos, vec4 xpos, vec4 linetaps)
+       {
+           return
+               pixel(xpos.r, ypos) * linetaps.r +
+               pixel(xpos.g, ypos) * linetaps.g +
+               pixel(xpos.b, ypos) * linetaps.b +
+               pixel(xpos.a, ypos) * linetaps.a;
+       }
+
+       void main()
+       {
+           vec2 stepxy = 1.0 / sourceSize[0].xy;
+           vec2 pos = texCoord.xy + stepxy * 0.5;
+           vec2 f = fract(pos / stepxy);
+
+           vec4 linetaps   = weight4(f.x);
+           vec4 columntaps = weight4(f.y);
+
+           // make sure all taps added together is exactly 1.0, otherwise some
+           // (very small) distortion can occur
+           linetaps /= linetaps.r + linetaps.g + linetaps.b + linetaps.a;
+           columntaps /= columntaps.r + columntaps.g + columntaps.b + columntaps.a;
+
+           vec2 xystart = (-1.5 - f) * stepxy + pos;
+           vec4 xpos = vec4(
+               xystart.x,
+               xystart.x + stepxy.x,
+               xystart.x + stepxy.x * 2.0,
+               xystart.x + stepxy.x * 3.0);
+
+               fragColor.a = 1.0;
+           fragColor.rgb = line(xystart.y, xpos, linetaps) * columntaps.r + line(xystart.y + stepxy.y, xpos, 
linetaps) * columntaps.g + line(xystart.y + stepxy.y * 2.0, xpos, linetaps) * columntaps.b + line(xystart.y + 
stepxy.y * 3.0, xpos, linetaps) * columntaps.a;
+       }
diff --git a/retro-gtk/shaders/curvature.fs b/retro-gtk/shaders/curvature.fs
new file mode 100644
index 0000000..d6db5b8
--- /dev/null
+++ b/retro-gtk/shaders/curvature.fs
@@ -0,0 +1,21 @@
+#version 150
+#define distortion 0.2
+
+uniform sampler2D source[];
+uniform vec4 sourceSize[];
+
+in Vertex {
+  vec2 texCoord;
+};
+
+out vec4 fragColor;
+
+vec2 radialDistortion(vec2 coord) {
+  vec2 cc = coord - vec2(0.5);
+  float dist = dot(cc, cc) * distortion;
+  return coord + cc * (1.0 - dist) * dist;
+}
+
+void main() {
+  fragColor = texture(source[0], radialDistortion(texCoord));
+}
diff --git a/retro-gtk/shaders/edge-detection.fs b/retro-gtk/shaders/edge-detection.fs
new file mode 100644
index 0000000..bf00fc2
--- /dev/null
+++ b/retro-gtk/shaders/edge-detection.fs
@@ -0,0 +1,25 @@
+#version 150
+
+uniform sampler2D source[];
+uniform vec4 sourceSize[];
+
+in Vertex {
+  vec2 texCoord;
+};
+
+out vec4 fragColor;
+
+vec3 grayscale(vec3 color) {
+  return vec3(dot(color, vec3(0.3, 0.59, 0.11)));
+}
+
+void main() {
+  vec2 offset = fract(texCoord * sourceSize[0].xy) - 0.5;
+  offset /= sourceSize[0].xy;
+
+  vec3 cx = texture(source[0], texCoord - offset).xyz;
+  vec3 cy = texture(source[0], texCoord).xyz;
+  vec3 cz = vec3(5.0 * grayscale(abs(cx - cy)));
+
+  fragColor = vec4(clamp(cz, 0.0, 1.0), 1.0);
+}
diff --git a/retro-gtk/shaders/shadertoy.fs b/retro-gtk/shaders/shadertoy.fs
new file mode 100644
index 0000000..c86cbbb
--- /dev/null
+++ b/retro-gtk/shaders/shadertoy.fs
@@ -0,0 +1,146 @@
+//
+// PUBLIC DOMAIN CRT STYLED SCAN-LINE SHADER
+//
+//   by Timothy Lottes
+//
+// This is more along the style of a really good CGA arcade monitor.
+// With RGB inputs instead of NTSC.
+// The shadow mask example has the mask rotated 90 degrees for less chromatic aberration.
+//
+// Left it unoptimized to show the theory behind the algorithm.
+//
+// It is an example what I personally would want as a display option for pixel art games.
+// Please take and use, change, or whatever.
+//
+
+#version 150
+
+uniform sampler2D source[];
+uniform vec4 sourceSize[];
+uniform vec4 targetSize;
+uniform int phase;
+
+in Vertex {
+  vec2 texCoord;
+};
+
+out vec4 fragColor;
+
+// Emulated input resolution.
+  vec2 res=sourceSize[0].xy;
+
+// Hardness of scanline.
+//  -8.0 = soft
+// -16.0 = medium
+float hardScan=-8.0;
+
+// Hardness of pixels in scanline.
+// -2.0 = soft
+// -4.0 = hard
+float hardPix=-3.0;
+
+// Display warp.
+// 0.0 = none
+// 1.0/8.0 = extreme
+vec2 warp=vec2(1.0/32.0,1.0/24.0); 
+
+// Amount of shadow mask.
+float maskDark=0.5;
+float maskLight=1.5;
+
+//------------------------------------------------------------------------
+
+// sRGB to Linear.
+// Assuing using sRGB typed textures this should not be needed.
+float ToLinear1(float c){return(c<=0.04045)?c/12.92:pow((c+0.055)/1.055,2.4);}
+vec3 ToLinear(vec3 c){return vec3(ToLinear1(c.r),ToLinear1(c.g),ToLinear1(c.b));}
+
+// Linear to sRGB.
+// Assuing using sRGB typed textures this should not be needed.
+float ToSrgb1(float c){return(c<0.0031308?c*12.92:1.055*pow(c,0.41666)-0.055);}
+vec3 ToSrgb(vec3 c){return vec3(ToSrgb1(c.r),ToSrgb1(c.g),ToSrgb1(c.b));}
+
+// Nearest emulated sample given floating point position and texel offset.
+// Also zero's off screen.
+vec3 Fetch(vec2 pos,vec2 off){
+  pos=(floor(pos*res+off)+vec2(0.5,0.5))/res;
+  return ToLinear(1.2 * texture(source[0],pos.xy,-16.0).rgb);}
+
+// Distance in emulated pixels to nearest texel.
+vec2 Dist(vec2 pos){pos=pos*res;return -((pos-floor(pos))-vec2(0.5));}
+    
+// 1D Gaussian.
+float Gaus(float pos,float scale){return exp2(scale*pos*pos);}
+
+// 3-tap Gaussian filter along horz line.
+vec3 Horz3(vec2 pos,float off){
+  vec3 b=Fetch(pos,vec2(-1.0,off));
+  vec3 c=Fetch(pos,vec2( 0.0,off));
+  vec3 d=Fetch(pos,vec2( 1.0,off));
+  float dst=Dist(pos).x;
+  // Convert distance to weight.
+  float scale=hardPix;
+  float wb=Gaus(dst-1.0,scale);
+  float wc=Gaus(dst+0.0,scale);
+  float wd=Gaus(dst+1.0,scale);
+  // Return filtered sample.
+  return (b*wb+c*wc+d*wd)/(wb+wc+wd);}
+
+// 5-tap Gaussian filter along horz line.
+vec3 Horz5(vec2 pos,float off){
+  vec3 a=Fetch(pos,vec2(-2.0,off));
+  vec3 b=Fetch(pos,vec2(-1.0,off));
+  vec3 c=Fetch(pos,vec2( 0.0,off));
+  vec3 d=Fetch(pos,vec2( 1.0,off));
+  vec3 e=Fetch(pos,vec2( 2.0,off));
+  float dst=Dist(pos).x;
+  // Convert distance to weight.
+  float scale=hardPix;
+  float wa=Gaus(dst-2.0,scale);
+  float wb=Gaus(dst-1.0,scale);
+  float wc=Gaus(dst+0.0,scale);
+  float wd=Gaus(dst+1.0,scale);
+  float we=Gaus(dst+2.0,scale);
+  // Return filtered sample.
+  return (a*wa+b*wb+c*wc+d*wd+e*we)/(wa+wb+wc+wd+we);}
+
+// Return scanline weight.
+float Scan(vec2 pos,float off){
+  float dst=Dist(pos).y;
+  return Gaus(dst+off,hardScan);}
+
+// Allow nearest three lines to effect pixel.
+vec3 Tri(vec2 pos){
+  vec3 a=Horz3(pos,-1.0);
+  vec3 b=Horz5(pos, 0.0);
+  vec3 c=Horz3(pos, 1.0);
+  float wa=Scan(pos,-1.0);
+  float wb=Scan(pos, 0.0);
+  float wc=Scan(pos, 1.0);
+  return a*wa+b*wb+c*wc;}
+
+// Distortion of scanlines, and end of screen alpha.
+vec2 Warp(vec2 pos){
+  pos=pos*2.0-1.0;    
+  pos*=vec2(1.0+(pos.y*pos.y)*warp.x,1.0+(pos.x*pos.x)*warp.y);
+  return pos*0.5+0.5;}
+
+// Shadow mask.
+vec3 Mask(vec2 pos){
+  pos.x+=pos.y*3.0;
+  vec3 mask=vec3(maskDark,maskDark,maskDark);
+  pos.x=fract(pos.x/6.0);
+  if(pos.x<0.333)mask.r=maskLight;
+  else if(pos.x<0.666)mask.g=maskLight;
+  else mask.b=maskLight;
+  return mask;}    
+
+void main() {
+    vec2 pos = gl_FragCoord.xy/targetSize.xy;
+      hardScan=-12.0;
+//      maskDark=maskLight;
+      pos=Warp(gl_FragCoord.xy/targetSize.xy);
+    fragColor.rgb=Tri(pos)*Mask(gl_FragCoord.xy);    
+  fragColor.a=1.0;
+  fragColor = vec4(ToSrgb(fragColor.rgb), fragColor.a);
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]