Shader question

In Flycast, I’m using the zfast-crt-composite.slang shader and I have a question: Is it possible to add screen curvature to this shader like in crt-lottes.slang (warp x, warp y)? I’ve tried merging shaders but it doesn’t look right. I would just like to have screen curvature in zfast-crt-composite.slang without any additional effects.

Sure you can, i wrote that. Save this to a slang file and load it. If you like it i can open a PR.

#version 450
/*
    zfast_crt - A very simple CRT shader.

    Copyright (C) 2017 Greg Hogan (SoltanGris42)
	edited by metallic 77.
	ported to slang by gregoricavichioli & hunterk.

    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the Free
    Software Foundation; either version 2 of the License, or (at your option)
    any later version.

*/

layout(push_constant) uniform Push
{
    vec4 SourceSize;
    vec4 OriginalSize;
    vec4 OutputSize;
    uint FrameCount;
float pi, blurx, blury, HIGHSCANAMOUNT1, HIGHSCANAMOUNT2, MASK_DARK, MASK_FADE, sat, SEGA;
} params;

#pragma parameter blurx "Convergence X-Axis" 0.45 -1.0 2.0 0.05
#pragma parameter blury "Convergence Y-Axis" -0.25 -1.0 2.0 0.05
#pragma parameter HIGHSCANAMOUNT1 "Scanline Amount (Low)" 0.3 0.0 1.0 0.05
#pragma parameter HIGHSCANAMOUNT2 "Scanline Amount (High)" 0.2 0.0 1.0 0.05
#pragma parameter MASK_DARK "Mask Effect Amount" 0.25 0.0 1.0 0.05
#pragma parameter MASK_FADE "Mask/Scanline Fade" 0.8 0.0 1.0 0.05
#pragma parameter sat "Saturation" 1.1 0.0 3.0 0.05
#pragma parameter SEGA "Sega Brightness Fix" 0.0 0.0 1.0 1.0

#define pi 3.14159
#define blurx params.blurx
#define blury params.blury
#define HIGHSCANAMOUNT1 params.HIGHSCANAMOUNT1
#define HIGHSCANAMOUNT2 params.HIGHSCANAMOUNT2
#define BRIGHTBOOST params.BRIGHTBOOST
#define MASK_DARK params.MASK_DARK
#define MASK_FADE params.MASK_FADE
#define sat params.sat
#define SEGA params.SEGA

layout(std140, set = 0, binding = 0) uniform UBO
{
    mat4 MVP;
} global;

#pragma stage vertex
layout(location = 0) in vec4 Position;
layout(location = 1) in vec2 TexCoord;
layout(location = 0) out vec2 vTexCoord;
layout(location = 1) out float maskFade;

void main()
{
    gl_Position = global.MVP * Position;
    vTexCoord = TexCoord*1.0001;
    maskFade = 0.3333*MASK_FADE;
}


#pragma stage fragment
layout(location = 0) in vec2 vTexCoord;
layout(location = 1) in float maskFade;
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D Source;
vec2 Warp(vec2 pos)
{
    pos  = 2.0*pos - 1.0;    
    pos *= vec2(1.0 + (pos.y*pos.y) * 0.02, 
                1.0 + (pos.x*pos.x) * 0.04);
    
    return pos*0.5 + 0.5;
}
void main()
{
    vec2 pos = Warp(vTexCoord.xy);
    
    vec3 sample1 = texture(Source,vec2(pos.x + blurx/1000.0, pos.y - blury/1000.0)).rgb;
    vec3 sample2 = texture(Source,pos).rgb;
    vec3 sample3 = texture(Source,vec2(pos.x - blurx/1000.0, pos.y + blury/1000.0)).rgb;
    
    vec3 colour = vec3 (sample1.r*0.5+sample2.r*0.5, sample1.g*0.25 + sample2.g*0.5 + sample3.g*0.25, sample2.b*0.5 + sample3.b*0.5);
    float lum = colour.r*0.4 + colour.g*0.4 + colour.b*0.2;
    
    vec3 lumweight=vec3(0.3,0.6,0.1);
    float gray = dot(colour,lumweight);
    vec3 graycolour = vec3(gray);

    //Gamma-like
    colour*=mix(0.8,1.0,lum);    
    
    float SCANAMOUNT = mix(HIGHSCANAMOUNT1,HIGHSCANAMOUNT2,lum);
    float scanLine =  SCANAMOUNT * sin(2.0*pi*pos.y*params.SourceSize.y);
    
    float whichmask = fract((pos.x*params.OutputSize.x)*0.4999);
    float mask = 1.0 + float(whichmask < 0.5) * -MASK_DARK;


    colour.rgb *= mix(mask*(1.0-scanLine), 1.0-scanLine, dot(colour,vec3(maskFade)));
    colour = vec3(mix(graycolour,colour,sat));

    if (SEGA == 1.0) colour *= 1.067;
    FragColor.rgb = colour.rgb;
}

2 Likes

I’m not sure, not an expert, but to load @DariusG shader (let’s say zfastcurv.slang ) as a preset you might need a slangp file like this:

shaders = "1"
shader0 = "zfastcurv.slang"
filter_linear0 = "true"
wrap_mode0 = "clamp_to_border"
mipmap_input0 = "false"
alias0 = ""
float_framebuffer0 = "false"
srgb_framebuffer0 = "false"
scale_type0 = viewport

named as you like .slangp

Now you can load that as a preset (keep both files in the same directory).

2 Likes

Thank you so much for help. However, I have a small problem with the mask effect amount option. It creates strange transparent lines at the top and bottom of the screen that I have marked. Can it be fixed?

This is an effect called “moire” and it happens when curvature is applied to repeating high-frequency information, like masks, scanlines or grids.

It’ll be less noticeable if you reduce the curvature, reduce the intensity of the repeating effect or apply some blur (aka low-pass filtering).

2 Likes

There is moire in that image but I think OP is not referring to that, zoom in and have a look! i’m not quite sure what it can be but I think is not moire. Am I wrong?

Thanks for the information. The option to change curvatures does not work in this case. Maybe it will be better to stay on default settings without curvatures.

oh yeah, i see them now. I think they’re still moire, technically, but those are indeed different from what I thought they were referring to.

1 Like

These lines are hardly visible in the image, they are more visible on the monitor screen. I use lottes.slang in most cores and this shader works great at native resolution (FB neo, Amiga, Sega Genesis etc), plus screen curvature. However, at Flaycast native 640x480, zfast.composite looks much better in my opinion. It’s not a big problem, but it would be nice to have a curved screen in every game :slight_smile:

Probably need to change mask to use vTexCoord, instead of warp(vTexCoord)

1 Like

I mostly use guest’s crt shaders, they offer curvature and many parameters to adjust to your liking, there are alse fast/fastest version with fewer options but better performance. This are what I use with dreamcast @640x480. I don’t get any moire effect and have 1080p screen.

This one is guest hd + smaa linear

1 Like

Unfortunately, my knowledge of where to change it is zero :slight_smile:

Try this, copy-save as yourfilename.slang. Before pos was vTexCoord and stayed after i added the curvature. That’s why you must doouble check everything. Usually i edit at the speed of light (seconds) without checking anything lol.

#version 450
/*
    zfast_crt - A very simple CRT shader.

    Copyright (C) 2017 Greg Hogan (SoltanGris42)
	edited by metallic 77.
	ported to slang by gregoricavichioli & hunterk.

    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the Free
    Software Foundation; either version 2 of the License, or (at your option)
    any later version.

*/

layout(push_constant) uniform Push
{
    vec4 SourceSize;
    vec4 OriginalSize;
    vec4 OutputSize;
    uint FrameCount;
float pi, blurx, blury, HIGHSCANAMOUNT1, HIGHSCANAMOUNT2, MASK_DARK, MASK_FADE, sat, SEGA;
} params;

#pragma parameter blurx "Convergence X-Axis" 0.45 -1.0 2.0 0.05
#pragma parameter blury "Convergence Y-Axis" -0.25 -1.0 2.0 0.05
#pragma parameter HIGHSCANAMOUNT1 "Scanline Amount (Low)" 0.3 0.0 1.0 0.05
#pragma parameter HIGHSCANAMOUNT2 "Scanline Amount (High)" 0.2 0.0 1.0 0.05
#pragma parameter MASK_DARK "Mask Effect Amount" 0.25 0.0 1.0 0.05
#pragma parameter MASK_FADE "Mask/Scanline Fade" 0.8 0.0 1.0 0.05
#pragma parameter sat "Saturation" 1.1 0.0 3.0 0.05
#pragma parameter SEGA "Sega Brightness Fix" 0.0 0.0 1.0 1.0

#define pi 3.14159
#define blurx params.blurx
#define blury params.blury
#define HIGHSCANAMOUNT1 params.HIGHSCANAMOUNT1
#define HIGHSCANAMOUNT2 params.HIGHSCANAMOUNT2
#define BRIGHTBOOST params.BRIGHTBOOST
#define MASK_DARK params.MASK_DARK
#define MASK_FADE params.MASK_FADE
#define sat params.sat
#define SEGA params.SEGA

layout(std140, set = 0, binding = 0) uniform UBO
{
    mat4 MVP;
} global;

#pragma stage vertex
layout(location = 0) in vec4 Position;
layout(location = 1) in vec2 TexCoord;
layout(location = 0) out vec2 vTexCoord;
layout(location = 1) out float omega;
layout(location = 2) out vec2 blur;

void main()
{
    gl_Position = global.MVP * Position;
    vTexCoord = TexCoord*1.0001;
    omega = 2.0*pi*params.SourceSize.y;
    blur = vec2(blurx/1000.0,blury/1000.0);
}

#pragma stage fragment
layout(location = 0) in vec2 vTexCoord;
layout(location = 1) in float omega;
layout(location = 2) in vec2 blur;

layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D Source;

vec2 Warp(vec2 pos)
{
    pos  = 2.0*pos - 1.0;    
    pos *= vec2(1.0 + (pos.y*pos.y) * 0.02, 
                1.0 + (pos.x*pos.x) * 0.04);
    
    return pos*0.5 + 0.5;
}

void main()
{
    vec2 pos = Warp(vTexCoord.xy);
  
    vec3 sample1 = texture(Source,vec2(pos.x + blur.x, pos.y - blur.y)).rgb;
    vec3 sample2 = texture(Source,pos).rgb*0.5;
    vec3 sample3 = texture(Source,vec2(pos.x - blur.x, pos.y + blur.y)).rgb;
    
    vec3 colour = vec3 (sample1.r*0.5  + sample2.r, 
                        sample1.g*0.25 + sample2.g + sample3.g*0.25,
                                         sample2.b + sample3.b*0.5);
    
    vec3 lumweight=vec3(0.22,0.7,0.08);
    float lum = dot(colour,lumweight);
    vec3 graycolour = vec3(lum);

    //Gamma-like
    colour*=mix(0.8,1.0,lum);    
    
    float SCANAMOUNT = mix(HIGHSCANAMOUNT1,HIGHSCANAMOUNT2,lum);
    float scanLine =  SCANAMOUNT * sin(pos.y*omega);
    
    float whichmask = fract((vTexCoord.x*params.OutputSize.x)*0.4999);
    float mask = 1.0 + float(whichmask < 0.5) * -MASK_DARK;
    colour = vec3(mix(graycolour,colour,sat));

    colour *= mix(mask*(1.0-scanLine), 1.0-scanLine, dot(lum, MASK_FADE));

    if (SEGA == 1.0) colour *= 1.0625;
    FragColor.rgb = colour.rgb;
}
2 Likes

guest hd It looks great. I didn’t notice before that this shader has screen curvature. Thanks.

1 Like

Now everything works great. Thank you very much !

1 Like