Android/GoogleTV compatible shaders - Nitpicky

But an on/off creates branching for if else statement blocks. Also a simple ternary later on won’t prevent the vignette code from being parsed. Yes it’s a matter of compromises now.

I would do this too (add size 1.0 or 2.0)

vec2 MSCL = vec2(0.5/size);

COMPAT_PRECISION float whichmask = floor(vTexCoord.x*4.0*OutputSize.x)*-MSCL.x;

with size you mean OutputSize.y?

Anyway I tried that before but I think non integer values would give a bad mask so I don’t see the point.

You could use this linear fit, good for 1080p and for 2160p (a fract of 0.0 or so) but what about 1440p or 720p? I don’t know.

vec2 MSCL = vec2(-0.000185185*OutputSize.y+0.7);

Check this out too, tell me how it works and if you like it.

#version 110



// Parameter lines go here:

#pragma parameter MASK "Mask Strength" 0.3 0.0 1.0 0.05

#define PI 3.141592

#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 omega;
COMPAT_VARYING float omega2;
COMPAT_VARYING vec2 cent;
COMPAT_VARYING vec2 scale;

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

void main()
{
    gl_Position = MVPMatrix * VertexCoord;
    TEX0.xy = TexCoord.xy*1.0001;
    scale = TextureSize.xy/InputSize.xy;
    omega = TEX0.x*OutputSize.x*scale.x*PI;
    omega2 = TEX0.x*OutputSize.x*0.6667*scale.x*PI;
    cent = floor(TEX0.xy*TextureSize.xy)+0.5;

}

#elif defined(FRAGMENT)

#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

#if __VERSION__ >= 130
#define COMPAT_VARYING in
#define COMPAT_TEXTURE texture
out COMPAT_PRECISION vec4 FragColor;
#else
#define COMPAT_VARYING varying
#define FragColor gl_FragColor
#define COMPAT_TEXTURE texture2D
#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 omega;
COMPAT_VARYING float omega2;
COMPAT_VARYING vec2 cent;
COMPAT_VARYING vec2 scale;

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

#ifdef PARAMETER_UNIFORM
// All parameter floats need to have COMPAT_PRECISION in front of them
uniform COMPAT_PRECISION float SHARPX;
uniform COMPAT_PRECISION float SHARPY;
uniform COMPAT_PRECISION float MASK;

#else
#define SHARPX 0.5
#define SHARPY 0.5
#define MASK 0.7

#endif

#define SourceSize vec4(TextureSize.xy, 1.0/TextureSize.xy)

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

void main()
{   
    vec2 linear = vTexCoord;
    vec2 nearest = cent * SourceSize.zw;    
    float x = mix(linear.x, nearest.x, 0.2);
    float y = mix(linear.y, nearest.y, 0.35);
    vec2 coords = vec2(x,y);
    coords = Warp(coords*scale)/scale;
    vec3 res = COMPAT_TEXTURE(Source,coords).rgb;
    
    float scanline =  sin(fract(coords.y*SourceSize.y)*PI);
    float mask = InputSize.y < 224.0 ? MASK*sin(omega2)+1.0-MASK : MASK*sin(omega)+1.0-MASK;
    res *= mix(mask*scanline, 1.0, dot(res,vec3(0.2666)));
    
    FragColor = vec4(res,1.0);
}
#endif
2 Likes

Thanks, didn’t try on the Chromecast but on my 4K monitor the mask is not visible. Also missing rounded corners? Anyway I’m settled already for the version I uploaded to the repo, I noticed you are metallic77 on github, very active lol. Nice.

2 Likes

I was annoyed by the fact that raising scanlines and mask made the image so much darker, so I created a compensation gamma that keeps the gamma/brightness. Now, using a pow() is so expensive that I had to not only optimize the rest of the shader but also be creative, yet for some cores like Gen Plus X this is still not enough. Is there something (core) really lightweight for Sega Genesis? (EDIT: Picodrive?)

As for the shader this is the function:

// Returns gamma corrected output, compensated for scanline+mask embedded gamma
vec3 inv_gamma(vec3 col, vec3 power)
{
    vec3 cir  = col-1.0;
         cir *= cir;
         col  = mix(sqrt(col),sqrt(1.0-cir),power);
    return col;
}

The power bias is scanline and mask implementation dependent. for zfast_crt this is the mapping.

#define pwr vec3(1.0/((-0.0325*SCANLINE_WEIGHT+1.0)*(-0.311*MASK_DARK+1.0))-1.2)

I have almost finished, I’m still optimizing for Sega Genesis. For SNES only zfast_crt_geo_svideo which is the all-features shader is slow for 8:7 games.

EDIT: Nice, I enabled a few settings in RA for Chromecast and now all run full speed. I tested enabling multithreading but it added some judder so I disabled GPU screenshots, added 50ms of shader load delay, and enabled CPU and GPU sync. I’m going to add vignetting back to the S-Video variants and see how they fare.

3 Likes

If all CRT shaders could do this without clipping life would be much better for people like me.

It could be simple as measuring peak brightness before and after then compensating or as complex as measuring average brightness but any form of compensation with a clipping mitigation algorithm would be a Godsend.

Disabling GPU Screenshots improves performance?

All of this is while keeping multithreading enabled?

2 Likes

Yes, I mentioned this a long ago but everybody seems to forget about the emulated scanline+mask gamma, so the system (end-to-end) gamma becomes not 1 or 1.1 but much higher.

My function above basically is a mix of sqrt() to reencode the hacky ungamma, and an inverted gamma or no-clip brightness function whatever you prefer. This is why nesguy promoted to raise display brightness, but that is more or less a gain control not an inverted gamma operator so the look won’t be fully recovered.

A power function is not symmetrical, it gives more weight to darks so for the opposite effect (the behaviour of scanlines+mask gamma) you invert the source and apply a power function, then invert back.

Here is a comparison of sqrt() and the inverted gamma which exactly models zfast scalines and mask gamma. At a=1 the effect is like sqrt() at a=2 the curve is exactly symmetrical, a quarter of a circle, just as if adding brightness to a sqrt() but in one go.

https://www.desmos.com/calculator/eo4ardp927

I don’t know, I tried disabling a bunch of things and it seemed to do the trick. This is all with multithreading disabled.

Just uploaded the updated shaders. Only zfast_crt_geo_svideo needs further optimizations.

Here a capture of Sonic with Scanline=9.0, mask=0.15 and blur_x=1.0

3 Likes

Great job, that geo_composite hack runs full speed on my old htc one m7 while my zfast_compo runs 52-55 fps.

1 Like

If Chromecast can do without framedrops here is a matrix to go to NTSC colors in linear space. I am thinking to include this in some shaders i wrote.

// linear space (exact)
const mat3 NTSC = mat3(1.5073,  -0.3725, -0.0832, 
                            -0.0275, 0.9350,  0.0670,
                             -0.0272, -0.0401, 1.1677);


//non linear approximate (but not exact)
const mat3 NTSC2  = mat3 (
1.5164,	-0.4945,	-0.02,
-0.0372,	0.9571,	0.0802,
-0.0192,	-0.0309,	1.0500
);

Also quote from an old NewRisingSun post

“Japanese NTSC (“NTSC-J”) does not use a black setup of 7.5 IRE, but 0 IRE. Without compensation for the black setup, you’ll get the NTSC-J “look”. To get the “American” look, use signal = (signal - 0.075) / (1 - 0.075).” Actually a mix because J has colder colors too, but developers that used it definitely took that in to account, so games that made for J would look warmer in U.

2 Likes

The full implementation is in grade , here for performance I omitted many things but kept the important.

What are the primaries of that transformation ? in other words what phosphors do they replicate?

You have to keep in mind that phosphors are the last thing in the chain not part of the signal So what is always presented to the display before the phosphors is a normalized signal regardless of region . Then you have to cater phosphors depending on region or CRT unit

1 Like

Borrowed some ideas, check this out

#version 110

/* 
  work by DariusG 2023, some ideas borrowed from Dogway's zfast_crt_geo
*/

#pragma parameter curv "Curvature"  1.0 0.0 1.0 1.0
#pragma parameter blur "Interpolation: 0.0 nearest,0.25 bilinear,1.0 sharp" 0.12 0.0 1.0 0.01
#pragma parameter msize "Mask Size" 1.0 1.0 2.0 1.0
#pragma parameter slotx "Slot Size x" 3.0 1.0 4.0 1.0
#pragma parameter sloty "Slot Size Y" 3.0 1.0 4.0 1.0
#pragma parameter mask "Mask Strength" 0.3 0.0 1.0 0.05
#pragma parameter ssize "Scanline Size" 1.0 1.0 2.0 1.0
#pragma parameter scan "Scanline Strength" 0.4 0.0 1.0 0.05
#pragma parameter thresh "Effect Threshold on brights" 0.2 0.0 0.33 0.01
#pragma parameter colors "Colors: 0.0 RGB, 1.0 NTSC, 2.0:P22 D93" 0.0 0.0 2.0 1.0
#pragma parameter sat "Saturation" 1.0 0.0 2.0 0.01
#pragma parameter wp "White Point" 0.0 -0.2 0.2 0.01

#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;
}

#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
uniform COMPAT_PRECISION float blur;
uniform COMPAT_PRECISION float msize;
uniform COMPAT_PRECISION float sloty;
uniform COMPAT_PRECISION float slotx;
uniform COMPAT_PRECISION float mask;
uniform COMPAT_PRECISION float ssize;
uniform COMPAT_PRECISION float scan;
uniform COMPAT_PRECISION float curv;
uniform COMPAT_PRECISION float thresh;
uniform COMPAT_PRECISION float colors;
uniform COMPAT_PRECISION float sat;
uniform COMPAT_PRECISION float wp;

#else
#define blur 1.0
#define msize 1.0
#define sloty 1.0
#define slotx 1.0
#define mask 0.5
#define ssize 1.0
#define scan 0.4
#define curv 1.0
#define thresh 0.2
#define colors 0.0
#define sat 1.0
#define wp 0.0
#endif

#define pi 3.1415926    
#define pwr vec3(0.05/((1.0 - 0.05*scan)*(1.0 - 0.15*mask)))

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

const mat3 P22D93 = mat3(
     1.00000, 0.00000, -0.06173,
     0.07111, 0.96887, -0.01136,
     0.00000, 0.08197,  1.07280);

// NTSC to sRGB matrix, used in linear space
const mat3 NTSC = mat3(1.5073,  -0.3725, -0.0832, 
                    -0.0275, 0.9350,  0.0670,
                     -0.0272, -0.0401, 1.1677);

// Returns gamma corrected output, compensated for scanline+mask embedded gamma
vec3 inv_gamma(vec3 col, vec3 power)
{
    vec3 cir  = col-1.0;
         cir *= cir;
         col  = mix(sqrt(col),sqrt(1.0-cir),power);
    return col;
}

void main()
{
    vec2 scale = SourceSize.xy/InputSize.xy; vec2 pos,corn;
    if (curv == 1.0) { 
        pos = Warp(vTexCoord*scale); 
        corn   = min(pos,1.0-pos); // This is used to mask the rounded
        corn.x = 0.0001/corn.x;  // corners later on
        pos /= scale;
        } else pos = vTexCoord;

    vec2 textureCoords = pos*SourceSize.xy;
    vec2 screenCoords = vTexCoord*OutputSize.xy;
    float scalex = OutputSize.x/InputSize.x*blur;
    vec2 tex_pos = fract(textureCoords);

 //Interpolation: https://colececil.io/blog/2017/scaling-pixel-art-without-destroying-it/
 //If blur is 0.5 on an axis, then the color will not be interpolated on that axis. 
 //If it’s 0 or 1, then it will have maximum interpolation, with 0 being on one side of the texel 
 //and 1 being on the other. Anything between those values will cause interpolation somewhere 
 //between the minimum and maximum. 

    vec2 blur = clamp(tex_pos / scalex, 0.0,0.5) + clamp((tex_pos - 1.0) / scalex + 0.5, 0.0, 0.5);

//At this point, our texture coordinates are still in the range [0, texture size], 
//but we need to get them back into the range [0, 1] before we can use them to grab the color. 
//To do so, we simply divide by the texture width and height

    vec2 coords = (floor(textureCoords) + blur) * SourceSize.zw;

    vec4 res = COMPAT_TEXTURE(Source, coords) ;
    res *= res;
    res *= mix(((1.0-mask+1.0-scan)/2.0 + 
                    mask*sin(screenCoords.x/msize*SourceSize.x/InputSize.x*2.0/slotx*pi) +
                    mask*sin((screenCoords.y/msize+sloty*mod(screenCoords.x,2.0))*2.0*pi) +
                    scan*sin(textureCoords.y/ssize*2.0*pi)),1.0,dot(res.rgb,vec3(thresh))) ;
    
    if (colors == 1.0) res.rgb *= NTSC; else 
    if (colors == 2.0) res.rgb *= P22D93; else
    res.rgb;
 
    //CHEAP TEMPERATURE CONTROL     
       if (wp != 0.0) { res.rgb *= vec3(1.0+wp,1.0,1.0-wp);}
    

    res = clamp(res,0.0,1.0);
    vec3 lumweight = vec3(0.29,0.6,0.11);
    vec3 grays = vec3(dot(lumweight, res.rgb));
    res.rgb = mix(grays, res.rgb, sat);
    if (corn.y <= corn.x && curv == 1.0 || corn.x < 0.0001 && curv ==1.0 )
    res = vec4(0.0);
    res.rgb = inv_gamma(res.rgb,pwr);

    FragColor = res;
}

#endif
1 Like

Looks fine to me. I guess the pwr fit is adapted to your scanlines and mask implementation, I’m not sure what the 0.05/ means there if it’s part of the fit because it will modify the slope so brightness won’t be the same when no scanlines+mask is applied, probably a better alternative is to apply an offset at the end as I did with -1.2 (find yours as appropriate).

vec3(0.05/((1.0 - 0.05*scan)*(1.0 - 0.15*mask))-offset)

Everything else looks fine from a quick glance except I still don’t know the phosphor for the NTSC matrix. In grade I listed a few them.

2 Likes

It looks fine that way with and without scanlines & mask. Brightness is kept at the same level without mask/scanlines.

That corner function creates some rough edges, isn’t there a way to smooth things out?

Improved that slot mask a lot too.

#version 110

/* 
  work by DariusG 2023, some ideas borrowed from Dogway's zfast_crt_geo
*/

#pragma parameter curv "Curvature"  1.0 0.0 1.0 1.0
#pragma parameter blur "Interpolation: 0.0 nearest,0.25 bilinear,1.0 sharp" 0.12 0.0 1.0 0.01
#pragma parameter msize "Mask Size" 1.0 1.0 2.0 1.0
#pragma parameter slotx "Slot Size x" 3.0 2.0 3.0 1.0
#pragma parameter width "Mask Width 3.0/2.0 " 0.67 0.67 1.0 0.333
#pragma parameter mask "Mask Strength" 0.3 0.0 1.0 0.05
#pragma parameter ssize "Scanline Size" 1.0 1.0 2.0 1.0
#pragma parameter scan "Scanline Strength" 0.4 0.0 1.0 0.05
#pragma parameter thresh "Effect Threshold on brights" 0.2 0.0 0.33 0.01
#pragma parameter colors "Colors: 0.0 P22D93, 1.0 RGB, 2.0:NTSC" 1.0 0.0 2.0 1.0
#pragma parameter sat "Saturation" 1.0 0.0 2.0 0.01
#pragma parameter wp "White Point" 0.0 -0.2 0.2 0.01

#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;
}

#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
uniform COMPAT_PRECISION float blur;
uniform COMPAT_PRECISION float msize;
uniform COMPAT_PRECISION float width;
uniform COMPAT_PRECISION float slotx;
uniform COMPAT_PRECISION float mask;
uniform COMPAT_PRECISION float ssize;
uniform COMPAT_PRECISION float scan;
uniform COMPAT_PRECISION float curv;
uniform COMPAT_PRECISION float thresh;
uniform COMPAT_PRECISION float colors;
uniform COMPAT_PRECISION float sat;
uniform COMPAT_PRECISION float wp;

#else
#define blur 1.0
#define msize 1.0
#define width 1.0
#define slotx 1.0
#define mask 0.5
#define ssize 1.0
#define scan 0.4
#define curv 1.0
#define thresh 0.2
#define colors 0.0
#define sat 1.0
#define wp 0.0
#endif

#define pi 3.1415926    
#define pwr vec3(0.25/((1.0 - 0.05*scan)*(1.0 - 0.15*mask)))

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

const mat3 P22D93 = mat3(
     1.00000, 0.00000, -0.06173,
     0.07111, 0.96887, -0.01136,
     0.00000, 0.08197,  1.07280);

// NTSC to sRGB matrix, used in linear space
const mat3 NTSC = mat3(1.5073,  -0.3725, -0.0832, 
                    -0.0275, 0.9350,  0.0670,
                     -0.0272, -0.0401, 1.1677);

// Returns gamma corrected output, compensated for scanline+mask embedded gamma
vec3 inv_gamma(vec3 col, vec3 power)
{
    vec3 cir  = col-1.0;
         cir *= cir;
         col  = mix(sqrt(col),sqrt(1.0-cir),power);
    return col;
}

void main()
{
    vec2 scale = SourceSize.xy/InputSize.xy; vec2 pos,corn;
    if (curv == 1.0) { 
        pos = Warp(vTexCoord*scale); 
        corn   = min(pos,1.0-pos); // This is used to mask the rounded
        corn.x = 0.0001/corn.x;  // corners later on
        pos /= scale;
        } else pos = vTexCoord;

    vec2 textureCoords = pos*SourceSize.xy;
    vec2 screenCoords = vTexCoord*OutputSize.xy*SourceSize.xy/InputSize.xy;
    float scalex = OutputSize.x/InputSize.x*blur;
    vec2 tex_pos = fract(textureCoords);

 //Interpolation: https://colececil.io/blog/2017/scaling-pixel-art-without-destroying-it/
 //If blur is 0.5 on an axis, then the color will not be interpolated on that axis. 
 //If it’s 0 or 1, then it will have maximum interpolation, with 0 being on one side of the texel 
 //and 1 being on the other. Anything between those values will cause interpolation somewhere 
 //between the minimum and maximum. 

    vec2 blur = clamp(tex_pos / scalex, 0.0,0.5) + clamp((tex_pos - 1.0) / scalex + 0.5, 0.0, 0.5);

//At this point, our texture coordinates are still in the range [0, texture size], 
//but we need to get them back into the range [0, 1] before we can use them to grab the color. 
//To do so, we simply divide by the texture width and height

    vec2 coords = (floor(textureCoords) + blur) * SourceSize.zw;

    vec4 res = COMPAT_TEXTURE(Source, coords) ;
    res *= res;

    // similar mask to Lottes 1, done with sins, option for wide 2.0
    float oddx = mod(screenCoords.x,slotx*2.0) < slotx ? 1.0 : 0.0;
    res *= mix(((1.0-mask+1.0-scan)/2.0  
                    + mask*sin(screenCoords.x*width/msize*pi) 
                    + mask*sin(((screenCoords.y+oddx)/msize)*pi) 
                    + scan*sin(textureCoords.y/ssize*2.0*pi)),1.0,dot(res.rgb,vec3(thresh)));
    
    if (colors == 2.0) res.rgb *= NTSC; else 
    if (colors == 0.0) res.rgb *= P22D93; else
    res.rgb;
 
    //CHEAP TEMPERATURE CONTROL     
       if (wp != 0.0) { res.rgb *= vec3(1.0+wp,1.0,1.0-wp);}
    

    res = clamp(res,0.0,1.0);
    vec3 lumweight = vec3(0.29,0.6,0.11);
    vec3 grays = vec3(dot(lumweight, res.rgb));
    res.rgb = mix(grays, res.rgb, sat);
    if (corn.y <= corn.x && curv == 1.0 || corn.x < 0.0001 && curv ==1.0 )
    res = vec4(0.0);
    res.rgb = inv_gamma(res.rgb,pwr);

    FragColor = res;
}

#endif
2 Likes

You must change the mask line to

COMPAT_PRECISION float whichmask = floor(vTexCoord.x*OutputSize.x*TextureSize.x/InputSize.x)*-MSCL;

Instead of “4.0”

Or else funky stuff on android screen like screen splitting in 2 where right part has no mask

What’s the resolution of the android screen? 4.0 is the TVL designed for 1080p resolution.

That’s 1080p. If I change to source/input it’s fixed.

2 Likes

The terminal is 1920x1080 right?

That effect is strange because it was supposed to be fixed in the next line:

TEX0.xy = TexCoord.xy*1.00001;

Before for the MSCL definition instead of *-0.5 I had 0.499999 and I was told it was unnecesary since I already had the above line and worked for me in Chromecast.

Try to change it back to 0.499999 but keeping the 4.0*

Probably some cores handle Sourcesize, InputSize differently I guess that’s why 4.0 is not working.

Here on another phone

2 Likes