I was wondering if it was possible to merge these two shaders (dotmask.glsl and scanline-fract.glsl) into a single separate shader?
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
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.
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.
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.
Thank you very much!
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?
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.
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?
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.
Ok that’s what I’m seeing, thanks.
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?)
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.
@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.
@hunterk Sorry to bother you but could you possibly split the darkness for the scanlines into high and low like lottes mask is?
Is that not what “lines black” and “lines white” are for?
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.
Are you referring to the boost bright and darkness settings?
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?
Yeah I’m trying to brighten the image up without doing weird things to colors.