New CRT shader from Guest + CRT Guest Advanced updates

Yeah, I always thought s-video was closer to RGB but a bit more shitty and on a consumer CRT, the difference shouldn’t be that noticeable?

S-video is kinda in-between RGB and Composite, less color bleed than later but softer than the former, but not enough to blend dithering. This video shows this well:

Do You have any sharpening pre-shaders loaded? I discovered they can mess with blend modes.

2 Likes

Nope. That’s just what the blend modes look like. Just took this shot, all default settings except I selected composite and blend mode 3.

2 Likes

It’s just how the ntsc shaders blend it. Here is a screenie from the ‘original’ ntsc preset in the ntsc folder of the slang shaders/presets:

These screenshots don’t reflect the temporal blending component though.

4 Likes

Yes, it looks very nice in motion. Is this temporal blending more accurate to how composite actually worked? Why use this approach? Just curious.

Also could you briefly describe the different blend modes and how they differ, or is it just different levels of blending?

Thanks!

1 Like

It’s my best evaluation that the ntsc shaders in use have a realistic background in signal modulation and demodulation, quite properly emulating some bandwidth limitations. Limitations exist up to this day on modern displays, but it’s a different digital issue. S-video uses two signals, one for luma and one for chroma, therefore the representation is more accurate. RF is similar to composite, but with some degradations. Temporal effects, i presume, are bandwidth limitation based.

  • Mode 0.0 : ntsc chromatics, hybrid ntsc/original min. luminance, original image based scanline flow.
  • Mode 1.0 : ntsc chromatics, hybrid ntsc/original min. luminance, ntsc blend based scanline flow.
  • Mode 2.0 : ntsc chromatics, ntsc based luminance, original image based scanline flow.
  • Mode 3.0 : ntsc chromatics, ntsc based luminance, ntsc blend based scanline flow.

I’m sometimes amazed how differently the ntsc transformation handles different color environments in combination with source resolution. Hence more blending modes allow better tweaking.

5 Likes

Hi @guest.r

On the start page of the 240p test suite, you can choose “Video: 256 x 224 p” or toggle it to “Video: 256 x 480i”

In below gif animation I captured the greyscale pattern difference for the normal versus interlaced mode. The brightness of the midtones differs just that noticable bit between them: interlaced -midtones- being brighter. I used the default guest-advanced preset for both.

Since the 240p test suite 480i toggle is only line doubling, temporal issues should not really be an issue. I.e. this capture should provide a good representation. (On a real CRT switching from 256x224 p to 256x480i should not change the brightness of the grey scale).

Would be supernice if you could tweak the gamma of the interlace mode that little bit to match the brightness of the normal mode. If you would rather not change anything anymore on this, could you then possibly point me to the code part where I can set a slightly darker gamma myself for the interlaced mode?

I also noticed the difference gets more pronounced when choosing Scanline Type 1 or 2, so maybe you could accommodate for these too?

Cheers and thanks again for the awesome shader. ^^ Above is just nitpicking to make it slightly better :smiley: ^^

2 Likes

Honestly I really need to sit down and test some of the newer updates to this, I haven’t really tested anything thoroughly since like March, I think.

The updates having been looking great, and I absolutely love that you’re taking feedback from chat on tweaks and updates for the shader.

Honestly the longer this goes on the more this feels like the closest thing we have to a “community” shader for the CRT. (I mean that even though you’re doing all the coding, multiple people are helping determine if things look “right” lol)

@guest.r thanks again for all your work!

2 Likes

Here is the linearize-hires.slang file with additional gamma option. I think it’s not too hard to add this feature to other linearize shaders too. I’m hesitating to update the release version a bit, since this is a bit nichy situation, since most games run either progressive or interlaced, but you shall have some more fun. :grin:

#version 450

/*
   Interlacing
   
   Copyright (C) 2020 guest(r) - [email protected]

   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.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   
*/  

layout(push_constant) uniform Push
{
	vec4 OriginalSize;
	vec4 OutputSize;
	uint FrameCount;
	float GAMMA_INPUT;
	float inter;
	float interm;
	float iscan;
	float intres;
	float iscans;
	float downsample_levelx;
	float downsample_levely;	
	float prescalex;
	float interlace_gamma;
} params;


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



#pragma parameter GAMMA_INPUT "Gamma Input" 2.4 1.0 5.0 0.05
#define GAMMA_INPUT params.GAMMA_INPUT

#pragma parameter interlace_gamma "Gamma Input (Interlacing)" 2.4 1.0 5.0 0.05
#define interlace_gamma params.interlace_gamma

#pragma parameter bogus_interlacing "[ INTERLACING OPTIONS ]: " 0.0 0.0 0.0 1.0

#pragma parameter inter "          Interlace Trigger Resolution :" 350.0 0.0 800.0 25.0
#define inter         params.inter     // interlace resolution

#pragma parameter interm "          Interlace Mode: OFF, Normal 1-3, Interpolation 4-5" 1.0 0.0 5.0 1.0
#define interm         params.interm     // interlace mode 

#pragma parameter iscan "          Interlacing Scanline Effect" 0.20 0.0 1.0 0.05
#define iscan  params.iscan     // interlacing effect scanlining

#pragma parameter intres "          Internal Resolution Y: 224p/240p, 1.5...y-dowsample" 0.0 0.0 6.0 0.5 // Joint parameter with main pass, values must match
#define intres         params.intres     // interlace resolution

#pragma parameter downsample_levelx "          Downsampling-X (High-res content, pre-scalers)" 0.0 0.0 2.0 0.25
#define downsample_levelx         params.downsample_levelx     // downsample level

#pragma parameter downsample_levely "          Downsampling-Y (High-res content, pre-scalers)" 0.0 0.0 2.0 0.25
#define downsample_levely         params.downsample_levely     // downsample level

#pragma parameter iscans "          Interlacing (Scanline) Saturation" 0.40 0.0 1.0 0.05
#define iscans        params.iscans     // interlace saturation

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

void main()
{
   gl_Position = global.MVP * Position;
   vTexCoord = TexCoord * 1.000001;
}

#pragma stage fragment
layout(location = 0) in vec2 vTexCoord;
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D PrePass;

#define COMPAT_TEXTURE(c,d) texture(c,d)


vec3 plant (vec3 tar, float r)
{
	float t = max(max(tar.r,tar.g),tar.b) + 0.00001;
	return tar * r / t;
}


vec3 fetch_pixel(vec2 coord)
{
	vec2 dx = vec2(global.SourceSize.z, 0.0) * downsample_levelx;
	vec2 dy = vec2(0.0, global.SourceSize.w) * downsample_levely;	
	vec2 d1 = dx + dy;
	vec2 d2 = dx - dy;
	
	float sum = 15.0;
	vec3 result = 3.0*COMPAT_TEXTURE(PrePass, coord     ).rgb + 
	              2.0*COMPAT_TEXTURE(PrePass, coord + dx).rgb +
	              2.0*COMPAT_TEXTURE(PrePass, coord - dx).rgb +
	              2.0*COMPAT_TEXTURE(PrePass, coord + dy).rgb +
	              2.0*COMPAT_TEXTURE(PrePass, coord - dy).rgb +
	              COMPAT_TEXTURE(PrePass, coord + d1).rgb +
	              COMPAT_TEXTURE(PrePass, coord - d1).rgb +
	              COMPAT_TEXTURE(PrePass, coord + d2).rgb +
	              COMPAT_TEXTURE(PrePass, coord - d2).rgb;  
				  
	return result/sum;
}


void main()
{
	vec3 c1 = COMPAT_TEXTURE(PrePass, vTexCoord).rgb;
	vec3 c2 = COMPAT_TEXTURE(PrePass, vTexCoord + vec2(0.0, params.OriginalSize.w)).rgb;
	
	if ((downsample_levelx + downsample_levely) > 0.2)
	{
		c1 = fetch_pixel(vTexCoord);
		c2 = fetch_pixel(vTexCoord + vec2(0.0, params.OriginalSize.w));	
	}

	vec3  c  = c1;

	float intera = 1.0;
	float gamma_in = clamp(GAMMA_INPUT, 1.0, 5.0);

	float m1 = max(max(c1.r,c1.g),c1.b);
	float m2 = max(max(c2.r,c2.g),c2.b);
	vec3 df = abs(c1-c2);
		
	float d = max(max(df.r,df.g),df.b);
	if (interm == 2.0) d = mix(0.1*d,10.0*d, step(m1/(m2+0.0001),m2/(m1+0.0001)));

	float r = m1;

	float yres_div = 1.0; if (intres > 1.25) yres_div = intres;
		
	if (inter < params.OriginalSize.y/yres_div && interm > 0.5 && intres != 1.0 && intres != 0.5) 
	{
		intera = 0.5;
		float line_no  = clamp(floor(mod(params.OriginalSize.y*vTexCoord.y, 2.0)), 0.0, 1.0);
		float frame_no = clamp(floor(mod(float(params.FrameCount),2.0)), 0.0, 1.0);
		float ii = abs(line_no-frame_no);
		
		if (interm < 3.5)
		{
			c2 = plant(mix(c2, c2*c2, iscans), max(max(c2.r,c2.g),c2.b));
			r = clamp(max(m1*ii, (1.0-iscan)*min(m1,m2)), 0.0, 1.0);
			c = plant( mix(mix(c1,c2, min(mix(m1, 1.0-m2, min(m1,1.0-m1))/(d+0.00001),1.0)), c1, ii), r);
			if (interm == 3.0) c = (1.0-0.5*iscan)*mix(c2, c1, ii);
			intera = 0.0;
		}
		if (interm == 4.0) c = plant(mix(c, c*c, 0.5*iscans), max(max(c.r,c.g),c.b));
		if (interm == 5.0) { c = mix(c2, c1, 0.5); c = plant(mix(c, c*c, 0.5*iscans), max(max(c.r,c.g),c.b));}

		gamma_in = clamp(interlace_gamma, 1.0, 5.0);		
	}
	c = pow(c, vec3(gamma_in));
	
	if (vTexCoord.x > 0.5) gamma_in = intera; else gamma_in = 1.0/gamma_in;
	
	FragColor = vec4(c, gamma_in);
}

Thanks @Syh, i’m also very grateful for the correct and meaningful feedback, it made the shader lot better.

3 Likes

Sooo much gamma control :joy:

I think my chain has like 10+ gamma controls at this point…

1 Like

I tried a lot of automatic brightness corrections with the guest-sm shader and it turned out it can be a bit too artificial regarding contrast and brightness compression, also clipping was an issue. A good advice for games which combine output modes is to use brightboost instead of gamma correct for stronger scanline brightness loss compensation, since interlacing is regarded.

4 Likes

Noticing some unsightly artifacts when using blend modes 2 and 3- check out the text. Some details are getting blurred too much, it seems.

1 Like

It’s how the ntsc passes blend it. Without these also dithering couldn’t get a proper handling. The old approach also produces similar results, but a sharper overall image can’t be achieved without some other artifacts.

2 Likes

So we’re playing that game of trade offs and diminishing returns now?

3 Likes

Only with some setups. Once using a bit more intense masks and other nice effects, the benefits are stronger.

3 Likes

I’m guessing this also gets better with higher resolutions, too. Another reason to buy a 4K TV?

4 Likes

New Release Version (2021-07-06-r1):

Notable changes:

  • Fixed a regressions regarding scanline spike removal (important).
  • Negative values for glow apply glow with mask effect (it’s documented in release now).
  • NTSC modes somewhat improved and also simplified down to 3.
  • Composite is the new default NTSC mode.
  • Interlacing threshold increased to 400 due some vertical games with high resolution. 400px should trigger it by new conditioning though.

Download link:

https://mega.nz/file/h1o0nLKC#06o-PuXBhbnnbUy-OFssKPhYK1duPcsQN-IeU3mls78

8 Likes

I guess a purchase of this sort will eventually happen, more of a question is how much money to spend on it and what company/model/size/panel. 4k goes well with some masks, which are also fine with 1080p, plus there are currently two 4k masks (4 and 8).

4 Likes

To wit:

Blargg’s filter + gdva looks good, but Blargg’s filter isn’t available on all cores.

So, use the custom snes video filter. But, the custom video filter only works for 256px wide systems and video filters aren’t supported by all cores.

So, use the ntsc-shaders. The NTSC shaders can’t be successfully combined with gdva.

So, use gdv-ntsc. But, the blend modes can result in some rather unsightly artifacts, and can be hard to look at IMO.

So, don’t use gdva and use an NTSC shader. But, then you’re not using gdva :frowning:

and round and round we go.

2 Likes

Such is life, such is life…:laughing:

2 Likes