Playing around with Maister NTSC shader

I was trying to make Maister NTSC shader a bit more like Blargg filter, to use it on more systems than the ones where it is included.

I used the composite shader and applied the horizontal scale of the svideo one to make it less blurry. It makes something in-between Blargg composite and svideo somewhat, but still too blurry. There’s less ringing though which is nice. Then I added a sharpening pass.

I don’t know how to get rid of something which looks like a linear filtering to me. Like here if you watch the pipe and blocks black edges: NTSC sharp / CRT CGWG I really like how CGWG CRT makes those black lines thin, especially in Super Mario World which is the most obvious test to my eyes.

Anyway, I used 2 different sharpness shaders:

-First shaders\sharpen\adaptive-sharpen.cgp which is good for everything if not pushed too far. Over 0.4 makes some really visible artefacts.

-Then shaders est\lab\misc\sharpness.cg which can be pushed farther and makes for some really interesting results. But it can make artefacts in some scenarios, even with less sharpening than adaptive-sharpen. (like here in mario, on the top of the pipe plain light green strangely) It’s great in games where it works (Nec or Neo-Geo games look nice), you have to see it moving.

It’s in attachment if you want to test it. You can play with sharpness in the parameters for both.

If you have any idea about how to let CGWG CRT do most of the scaling, a better sharpness filter, you’re welcome.

edit: updated with cg/inc files I merged to make it easier to understand. Guess it’s that rgb / yiq conversion that makes linearisation of gamma impossible…

Dunno if it’ll do exactly what you want but the part of cgwg’s crt shaders that does that thinning is the conversion to linear gamma before doing any of the calculations. Doing everything on the regular gamma curve exaggerates dark colors.

So, to get around it, you can linearize it in the first pass (we’ll borrow easymode’s linearize pass), use srgb framebuffers on each subsequent pass and then re-apply the gamma curve in the last pass, like this:

shaders = 5
shader0 = ../crt/shaders/crt-easymode-halation/linearize.cg
srgb_framebuffer0 = true
shader1 = shaders/ntsc-pass1-composite-3phase.cg
srgb_framebuffer1 = true
shader2 = shaders/ntsc-pass2-3phase.cg
srgb_framebuffer2 = true
shader3 = shaders/ntsc-gauss-pass.cg
srgb_framebuffer3 = true
shader4 = shaders/ntsc-stock-resolve.cg

filter_linear1 = false
filter_linear2 = false
filter_linear3 = false
filter_linear4 = true 

scale_type1 = source
scale_x1 = 4.0
scale_y1 = 1.0
frame_count_mod1 = 2
float_framebuffer1 = true

scale_type2 = source
scale_x2 = 0.5
scale_y2 = 1.0

scale_type_x3 = source
scale_type_y3 = viewport
scale3 = 1.0

The last pass–ntsc-stock-resolve.cg–is just the regular ntsc-stock.cg but with the ‘return’ line changed like this:

return float4(pow(tex2D(s0, tex), 1.0/2.2));

Thanks that helped! :slight_smile:

It went from this to that now. It’s almost too thin for the black lines now.

And it makes more ringing too. A bit like a real analog connection could do though.

I updated the archive with it.

edit: no that’s bad, it makes some real issues

One thing you can do to reduce or enhance the amount of fringing and artifacting is to go into ntsc-param.inc in the ntsc shaders directory and edit the lines

#define ARTIFACTING 1.0 #define FRINGING 1.0

in either compositing or s-video depending on which you are using.

Thanks, I noticed those.

There’s a CHROMA_MOD_FREQ that seems to affect a bit the linearize issues. I’m not sure but it seems like the linearize doesn’t work on some brightness or saturation layer the ntsc shader is producing.

Can’t manage to fix that problem, I’m just tweaking and don’t understand cg code enough.

Found another good example of the linearize problem with Undead Line intro on Megadrive: Normal / Linearize

Should some processing not be done under linearize? there is rgb to yiq conversion and those different effects in ntsc-pass1-encode-demodulate.inc…?

Hmm. Yeah, I think you would need linear rgb to yiq (and vice versa) conversion matrices, which don’t appear to be a thing :stuck_out_tongue:

…or that’s something else:

srgb_framebuffer = true or false for each pass doesn’t change the end result on screen. Does it need some changes in each pass .cg code to work?

Sorry, I’m still bothered by this problem… :stuck_out_tongue:

I guess the srgb_framebuffer isn’t working because ntsc-pass1 return an yiq signal. So I could just convert it like this:


float3 rgb = yiq2rgb(yiq);
return float4(rgb, 1.0);

And then I should convert it back to yiq at the beginning of ntsc-pass2… that’s where I don’t know how to do it. pass2 directly call to the vertex with lines like:

#define fetch_offset(offset, one_x) \
   tex2D(s0, vertex.tex + float2((offset) * (one_x), 0.0)).xyz

float4 main_fragment (uniform input IN, in data vertex, uniform sampler2D s0 : TEXUNIT0) : COLOR
{
	float one_x = 1.0 / IN.texture_size.x;
	float3 signal = float3(0.0);
	
	for (int i = 0; i < TAPS; i++)
	{
	   float offset = float(i);

	   float3 sums = fetch_offset(offset - float(TAPS), one_x) +
		  fetch_offset(float(TAPS) - offset, one_x);

	   signal += sums * float3(luma_filter[i], chroma_filter[i], chroma_filter[i]);
	}
	
	signal += tex2D(s0, vertex.tex).xyz *
	   float3(luma_filter[TAPS], chroma_filter[TAPS], chroma_filter[TAPS]);
   float3 rgb = yiq2rgb(signal);
   return float4(rgb, 1.0);}

How should I write my conversion?

Basically, anywhere it samples the texture (that is, tex2D(s0, whatever)), you just have to wrap it in pow(, 2.2) to linearize it and pow(, 1.0 / 2.2) to get it back on the curve. You don’t really need to use srg_framebuffer at all. That just lets you carry the linearization across passes without adding on more pow functions, which eat up performance.

Tatsuya79: You are not thinking linear

If you have any idea about how to let CGWG CRT do most of the scaling, a better sharpness filter, you’re welcome.

If you want sharper, be sharp and do less blur.

Another precious advice from Dogway. I almost missed you. :frowning:

Thank you! You can always count on me, I can talk your language.