Dotmask+scanline-fract?


#1

I was wondering if it was possible to merge these two shaders (dotmask.glsl and scanline-fract.glsl) into a single separate shader?


#2

Sure. Here you go:

// Super-basic scanline shader
// (looks bad at non-integer scales)
// by hunterk
// license: public domain

// Parameter lines go here:
#pragma parameter THICKNESS "Scanline Thickness" 2.0 1.0 12.0 1.0
#pragma parameter DARKNESS "Scanline Darkness" 0.50 0.0 1.0 0.05
#pragma parameter BRIGHTBOOST "Scanline Boost Bright" 1.1 1.0 1.2 0.1
#pragma parameter shadowMask "Mask Style" 3.0 0.0 4.0 1.0
#pragma parameter DOTMASK_STRENGTH "CGWG Dot Mask Strength" 0.3 0.0 1.0 0.01
#pragma parameter maskDark "Lottes maskDark" 0.5 0.0 2.0 0.1
#pragma parameter maskLight "Lottes maskLight" 1.5 0.0 2.0 0.1

#if defined(VERTEX)

#if __VERSION__ >= 130
#define COMPAT_VARYING out
#define COMPAT_ATTRIBUTE in
#define COMPAT_TEXTURE texture
#else
#define COMPAT_VARYING varying 
#define COMPAT_ATTRIBUTE attribute 
#define COMPAT_TEXTURE texture2D
#endif

#ifdef GL_ES
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif

COMPAT_ATTRIBUTE vec4 VertexCoord;
COMPAT_ATTRIBUTE vec4 COLOR;
COMPAT_ATTRIBUTE vec4 TexCoord;
COMPAT_VARYING vec4 COL0;
COMPAT_VARYING vec4 TEX0;

vec4 _oPosition1; 
uniform mat4 MVPMatrix;
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;

// compatibility #defines
#define vTexCoord TEX0.xy
#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define OutSize vec4(OutputSize, 1.0 / OutputSize)

void main()
{
    gl_Position = MVPMatrix * VertexCoord;
    TEX0.xy = TexCoord.xy * 1.0004;
}

#elif defined(FRAGMENT)

#if __VERSION__ >= 130
#define COMPAT_VARYING in
#define COMPAT_TEXTURE texture
out vec4 FragColor;
#else
#define COMPAT_VARYING varying
#define FragColor gl_FragColor
#define COMPAT_TEXTURE texture2D
#endif

#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif

uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
uniform sampler2D Texture;
COMPAT_VARYING vec4 TEX0;

// compatibility #defines
#define Source Texture
#define vTexCoord TEX0.xy

#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define OutSize vec4(OutputSize, 1.0 / OutputSize)

#ifdef PARAMETER_UNIFORM
// All parameter floats need to have COMPAT_PRECISION in front of them
uniform COMPAT_PRECISION float THICKNESS;
uniform COMPAT_PRECISION float DARKNESS;
uniform COMPAT_PRECISION float BRIGHTBOOST;
uniform COMPAT_PRECISION float shadowMask;
uniform COMPAT_PRECISION float DOTMASK_STRENGTH;
uniform COMPAT_PRECISION float maskDark;
uniform COMPAT_PRECISION float maskLight;
#else
#define THICKNESS 1.0
#define DARKNESS 0.5
#define BRIGHTBOOST 1.1
#define shadowMask 3.0
#define DOTMASK_STRENGTH 0.3
#define maskDark 0.5
#define maskLight 1.5
#endif

#define mod_factor vTexCoord.x * SourceSize.x * OutSize.x / SourceSize.x

vec3 RGBtoYIQ(vec3 RGB){
	const mat3 yiqmat = mat3(
		0.2989, 0.5870, 0.1140,
		0.5959, -0.2744, -0.3216,
		0.2115, -0.5229, 0.3114);
	return RGB * yiqmat;
}

vec3 YIQtoRGB(vec3 YIQ){
	const mat3 rgbmat = mat3(
		1.0, 0.956, 0.6210,
		1.0, -0.2720, -0.6474,
		1.0, -1.1060, 1.7046);
	return YIQ * rgbmat;
}

// Shadow mask.
vec3 Mask(vec2 pos)
{
   vec3 mask = vec3(maskDark, maskDark, maskDark);
   
   // Very compressed TV style shadow mask.
   if (shadowMask == 1.0)
   {
      float line = maskLight;
      float odd  = 0.0;

      if (fract(pos.x/6.0) < 0.5)
         odd = 1.0;
      if (fract((pos.y + odd)/2.0) < 0.5)
         line = maskDark;

      pos.x = fract(pos.x/3.0);
    
      if      (pos.x < 0.333) mask.r = maskLight;
      else if (pos.x < 0.666) mask.g = maskLight;
      else                    mask.b = maskLight;
      mask*=line;  
   } 

   // Aperture-grille.
   else if (shadowMask == 2.0)
   {
      pos.x = fract(pos.x/3.0);

      if      (pos.x < 0.333) mask.r = maskLight;
      else if (pos.x < 0.666) mask.g = maskLight;
      else                    mask.b = maskLight;
   } 

   // Stretched VGA style shadow mask (same as prior shaders).
   else if (shadowMask == 3.0)
   {
      pos.x += pos.y*3.0;
      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;
   }

   // VGA style shadow mask.
   else if (shadowMask == 4.0)
   {
      pos.xy = floor(pos.xy*vec2(1.0, 0.5));
      pos.x += pos.y*3.0;
      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()
{
	float lines = fract(vTexCoord.y * SourceSize.y);
	float scale_factor = floor((OutputSize.y / InputSize.y) + 0.4999);
   vec3 res = pow(COMPAT_TEXTURE(Source, vTexCoord).rgb, vec3(2.2,2.2,2.2));

   float mask = 1.0 - DOTMASK_STRENGTH;

   //cgwg's dotmask emulation:
   //Output pixels are alternately tinted green and magenta
   vec3 dotMaskWeights = mix(vec3(1.0, mask, 1.0),
                             vec3(mask, 1.0, mask),
                             floor(mod(mod_factor, 2.0)));
   if (shadowMask == 0.) 
   {
      res *= dotMaskWeights;
   }
   else 
   {
      res *= Mask(floor(1.000001 * gl_FragCoord.xy + vec2(0.5,0.5)));
   }
   
   vec4 screen = vec4(pow(res, vec3(1.0/2.2, 1.0/2.2, 1.0/2.2)), 1.0);
	screen.rgb = RGBtoYIQ(screen.rgb);
	screen.r *= BRIGHTBOOST;
	screen.rgb = YIQtoRGB(screen.rgb);

    FragColor = (lines > (1.0 / scale_factor * THICKNESS)) ? screen : screen * vec4(1.0 - DARKNESS);
} 
#endif

#3

Could you please add this shader and zfast_CRT+dotmask to the repository when you get a chance? I think both are pretty useful as lightweight shaders.


#4

Hello all.

I’ve been trying to combine these two, without success:

  • retro/sharp-bilinear-simple
  • misc/scanlines-sine-abs

I thought I could combine them as a first pass (sharp bilinear) and second pass (scanlines), but this does not seem to work.

Any thoughts on how to do this? I’m testing to see if I can replace using the scanline overlays.

Thanks.


#5

Sure. Here you go:

/*
 * sharp-bilinear
 * Author: Themaister
 * License: Public domain
 * 
 * Does a bilinear stretch, with a preapplied Nx nearest-neighbor scale, giving a
 * sharper image than plain bilinear.
 */

// Parameter lines go here:
#pragma parameter SHARP_BILINEAR_PRE_SCALE "Sharp Bilinear Prescale" 4.0 1.0 10.0 1.0
#pragma parameter AUTO_PRESCALE "Automatic Prescale" 1.0 0.0 1.0 1.0
#pragma parameter amp          "Amplitude"      1.2500  0.000 2.000 0.05
#pragma parameter phase        "Phase"          0.5000  0.000 2.000 0.05
#pragma parameter lines_black  "Lines Blacks"   0.0000  0.000 1.000 0.05
#pragma parameter lines_white  "Lines Whites"   1.0000  0.000 2.000 0.05
 
#define freq             0.500000
#define offset           0.000000
#define pi               3.141592654

#ifndef PARAMETER_UNIFORM
#define amp              1.250000
#define phase            0.500000
#define lines_black      0.000000
#define lines_white      1.000000
#endif

#if defined(VERTEX)

#if __VERSION__ >= 130
#define COMPAT_VARYING out
#define COMPAT_ATTRIBUTE in
#define COMPAT_TEXTURE texture
#else
#define COMPAT_VARYING varying 
#define COMPAT_ATTRIBUTE attribute 
#define COMPAT_TEXTURE texture2D
#endif

#ifdef GL_ES
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif

COMPAT_ATTRIBUTE vec4 VertexCoord;
COMPAT_ATTRIBUTE vec4 COLOR;
COMPAT_ATTRIBUTE vec4 TexCoord;
COMPAT_VARYING vec4 COL0;
COMPAT_VARYING vec4 TEX0;
COMPAT_VARYING float angle;

uniform mat4 MVPMatrix;
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;

// vertex compatibility #defines
#define vTexCoord TEX0.xy
#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define outsize vec4(OutputSize, 1.0 / OutputSize)

#ifdef PARAMETER_UNIFORM
uniform COMPAT_PRECISION float amp;
uniform COMPAT_PRECISION float phase;
uniform COMPAT_PRECISION float lines_black;
uniform COMPAT_PRECISION float lines_white;
#endif

void main()
{
    gl_Position = MVPMatrix * VertexCoord;
    COL0 = COLOR;
    TEX0.xy = TexCoord.xy;
    float omega = 2.0 * pi * freq;              // Angular frequency
    angle = TEX0.y * omega * TextureSize.y + phase;
}

#elif defined(FRAGMENT)

#if __VERSION__ >= 130
#define COMPAT_VARYING in
#define COMPAT_TEXTURE texture
out vec4 FragColor;
#else
#define COMPAT_VARYING varying
#define FragColor gl_FragColor
#define COMPAT_TEXTURE texture2D
#endif

#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif

uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
uniform sampler2D Texture;
COMPAT_VARYING vec4 TEX0;
COMPAT_VARYING float angle;

// fragment compatibility #defines
#define Source Texture
#define vTexCoord TEX0.xy

#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define outsize vec4(OutputSize, 1.0 / OutputSize)

#ifdef PARAMETER_UNIFORM
// All parameter floats need to have COMPAT_PRECISION in front of them
uniform COMPAT_PRECISION float SHARP_BILINEAR_PRE_SCALE;
uniform COMPAT_PRECISION float AUTO_PRESCALE;
uniform COMPAT_PRECISION float amp;
uniform COMPAT_PRECISION float phase;
uniform COMPAT_PRECISION float lines_black;
uniform COMPAT_PRECISION float lines_white;
#else
#define SHARP_BILINEAR_PRE_SCALE 4.0
#define AUTO_PRESCALE 1.0
#endif

void main()
{
   vec2 texel = vTexCoord * SourceSize.xy;
   vec2 texel_floored = floor(texel);
   vec2 s = fract(texel);
   float scale = (AUTO_PRESCALE > 0.5) ? floor(outsize.y / InputSize.y + 0.01) : SHARP_BILINEAR_PRE_SCALE;
   float region_range = 0.5 - 0.5 / scale;

   // Figure out where in the texel to sample to get correct pre-scaled bilinear.
   // Uses the hardware bilinear interpolator to avoid having to sample 4 times manually.

   vec2 center_dist = s - 0.5;
   vec2 f = (center_dist - clamp(center_dist, -region_range, region_range)) * scale + 0.5;

   vec2 mod_texel = texel_floored + f;

   vec3 color = COMPAT_TEXTURE(Source, mod_texel / SourceSize.xy).rgb;
   
    float grid;
 
    float lines;
 
    lines = sin(angle);
    lines *= amp;
    lines += offset;
    lines = abs(lines);
    lines *= lines_white - lines_black;
    lines += lines_black;
    color *= lines;
    
    FragColor = vec4(color.xyz, 1.0);
} 
#endif

@Nesguy I try to avoid including shader files with redundant code in them just so we don’t end up with a bunch of code duplication (I’m less of a stickler about presets, obviously), but let me see if I can come up with a good solution there.


#6

Thank you very much!


#7

Thanks so much for this!

Maybe we need a shaders folder just for potatoes? Lol

Not to jump subject but anybody else try out Guest’s New CRT shader?


#8

Would it be worth it to replace the scanline code in zfast_CRT+dotmask with scanline-fract’s scanline code?

I’m going to attempt it… Going to grab a fire extinguisher, everyone else should just grab some popcorn.


#9

Well… Nothing caught fire, but I also couldn’t get the shader to run. Edit: The shader I modified didn’t run, the shader Hunter did works.

Also @Nesguy have you used the shader Hunter did for me? If you have did you notice the slight color difference between this and zfast-crt+dotmask?


#10

Yeah there’s definitely a difference because they’re doing slightly different things. Zfast CRT has parameters that can alter the brightness and appearance of the colors, and it uses a different scanline pattern.


#11

Ok that’s what I’m seeing, thanks.


#12

I’m really liking this combo shader, I’ve realized if you set the scanline thickness to 3.0, you get what almost looks 1.1 scanlines on integer scaling. (Should be half a pixel right?)


#13

if you’re using a 6x vertical scale then 3.0 will be perfect 1:1. At 5x vertical scale they will be thicker than 1:1. The only way to get “perfect” 1:1 scanlines at 5x scale is by altering the transparency of the lines so that the result is a 50% reduction in brightness per line. At 5x scale I think it looks best to set the width to 3.0 and lower the strength to 80%, which gets you pretty close to 1:1 scanlines. At 5x scale, a thickness of 2.0 will look too thin.

Real scanlines on a CRT vary in thickness depending on the adjacent color, with the gaps between lines becoming wider over darker colors and thinner over brighter colors. Scanline-fract gives you “perfect” scanlines and doesn’t try to replicate any of the beam width dynamics. This may look too “digital” for some people’s taste. I’ve come to prefer a bit of beam dynamics with my scanlines, which is one reason why I use zfast_crt now. Most CRT shaders try to replicate the beam dynamics to some extent. There’s no best option, it just depends on what look you’re going for.


#14

@Nesguy Thanks for your insights, your right I’m running 6x vertical integer scaling.

Over the last couple days I’ve been testing shaders alot. I don’t feel like there’s any perfect solutions, all the CRT shaders have flaws. I’ve started to think of them just as different crts. This way I just need change CRTs depending on systems or in some cases content.


#15

@hunterk Sorry to bother you but could you possibly split the darkness for the scanlines into high and low like lottes mask is?


#16

Is that not what “lines black” and “lines white” are for?


#17

I’m asking about your scanlines-fract, sorry should have clarified.

EDIT: I’m wanting to know if we would be able to split the darkness for the scanlines into low and high like lottes mask or zfast-crt scanlines.


#18

Are you referring to the boost bright and darkness settings?


#19

No, I thought you were talking about scanline-sine-abs. Scanline-fract works differently from those other shaders, so the brightboost and darkness parameters are indeed the closest things.

What are you wanting to do? Brighten the image up, I guess?


#20

Yeah I’m trying to brighten the image up without doing weird things to colors.