Dotmask+scanline-fract?

Ok, that shader is set up different from the one I was looking at, so the instructions I gave you before don’t really apply exactly.

Replace the res *= res; line with mask = vec3(1.0); and you should be good.

@Nesguy I just use RetroArch’s verbose output, which includes whatever errors your driver’s shader compiler spits out.

2 Likes

That did it! Thanks!

1 Like

while we’re at it, can you also combine sharp-bilinear-scanlines with the code from dotmask whenever it’s convenient for you?

I’m trying to find a lightweight solution for adding scanlines and a mask to PSX games while also dealing with those pesky non-integer scale scaling artifacts. There are lots of ways to tackle this problem. Another option would be to merge pixellate or sharp-bilinear with zfast_crt+dotmask, although this might mess with the adjustable blur in zfast. Unfortunately, my shader skills are stuck at an intermediate level and I don’t know how to go about doing this.

Ideally, the combined shader would eliminate/hide scaling artifacts, add scanlines and mask, and provide adjustable blur/sharpness.

Also: Is there anything like a guide to writing shaders, and/or a good online IDE for working on/editing shaders?

It seems increasingly unlikely that the problem of multiple in-game resolutions being scaled to the same resolution with PSX emulators will ever be addressed (I’ve had an issue open on Github for months), so this is the next best solution.

EDIT: got the following result using pixellate + image-adjustment + zfast_CRT. This is pretty good, I’d say, as far as dealing with the scaling artifacts and adding scanlines and adjustable sharpness. Unfortunately, however, I wasn’t able to get any of the Lottes masks to look good. The Lottes masks always seem to produce artifacts when the horizontal scale isn’t a multiple of 3. I’d be happy to know if anyone can improve on this.

No shaders:

Preset:

alias0 = ""
alias1 = ""
alias2 = ""
BLURSCALEX = "0.300000"
BRIGHTBOOST = "1.250000"
feedback_pass = "0"
filter_linear2 = "true"
float_framebuffer0 = "false"
float_framebuffer1 = "false"
float_framebuffer2 = "false"
HILUMSCAN = "8.000000"
ia_B = "1.000000"
ia_black_level = "0.000000"
ia_BOTMASK = "0.000000"
ia_bright_boost = "0.000000"
ia_contrast = "1.000000"
ia_FLIP_HORZ = "0.000000"
ia_FLIP_VERT = "0.000000"
ia_G = "1.000000"
ia_GRAIN_STR = "0.000000"
ia_LMASK = "0.000000"
ia_luminance = "1.000000"
ia_monitor_gamma = "2.200000"
ia_overscan_percent_x = "0.000000"
ia_overscan_percent_y = "0.000000"
ia_R = "1.000000"
ia_RMASK = "0.000000"
ia_saturation = "1.000000"
ia_SHARPEN = "0.000000"
ia_target_gamma = "2.200000"
ia_TOPMASK = "0.000000"
ia_XPOS = "0.000000"
ia_YPOS = "0.000000"
ia_ZOOM = "1.000000"
INTERPOLATE_IN_LINEAR_GAMMA = "1.000000"
LOWLUMSCAN = "9.000000"
MASK_DARK = "0.000000"
MASK_FADE = "0.000000"
mipmap_input0 = "false"
mipmap_input1 = "false"
mipmap_input2 = "false"
parameters = "INTERPOLATE_IN_LINEAR_GAMMA;ia_target_gamma;ia_monitor_gamma;ia_overscan_percent_x;ia_overscan_percent_y;ia_saturation;ia_contrast;ia_luminance;ia_black_level;ia_bright_boost;ia_R;ia_G;ia_B;ia_ZOOM;ia_XPOS;ia_YPOS;ia_TOPMASK;ia_BOTMASK;ia_LMASK;ia_RMASK;ia_GRAIN_STR;ia_SHARPEN;ia_FLIP_HORZ;ia_FLIP_VERT;BLURSCALEX;LOWLUMSCAN;HILUMSCAN;BRIGHTBOOST;MASK_DARK;MASK_FADE"
shader0 = "D:\retroarch\shaders\shaders_glsl\retro\shaders\pixellate.glsl"
shader1 = "D:\retroarch\shaders\shaders_glsl\misc\image-adjustment.glsl"
shader2 = "D:\retroarch\shaders\shaders_glsl\crt\shaders\zfast_crt.glsl"
shaders = "3"
srgb_framebuffer0 = "false"
srgb_framebuffer1 = "false"
srgb_framebuffer2 = "false"
wrap_mode0 = "clamp_to_border"
wrap_mode1 = "clamp_to_border"
wrap_mode2 = "clamp_to_border"
1 Like
/*
 * 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
#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
 
#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;
uniform COMPAT_PRECISION float shadowMask;
uniform COMPAT_PRECISION float DOTMASK_STRENGTH;
uniform COMPAT_PRECISION float maskDark;
uniform COMPAT_PRECISION float maskLight;
#else
#define SHARP_BILINEAR_PRE_SCALE 4.0
#define AUTO_PRESCALE 1.0
#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

// 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()
{
   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;
    
   color = pow(color, vec3(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.) 
   {
      color *= dotMaskWeights;
   }
   else 
   {
      color *= Mask(floor(1.000001 * gl_FragCoord.xy + vec2(0.5,0.5)));
   }
   
   FragColor = vec4(pow(color, vec3(1.0/2.2, 1.0/2.2, 1.0/2.2)), 1.0);
} 
#endif
2 Likes