New CRT shader from Guest + CRT Guest Advanced updates

Hello Guest, I was having a look at your fast grade shaders and saw some optimization tricks.

You can multiply linear transformation matrices to create a new joint matrix, maybe the same can be applied to other shaders of your pack but in pre-shaders.slang at line 315:

	color = (m_in*m_out)*color;

	color = clamp(color, 0.0, 1.0);

	color = pow(color, vec3(1.0/p));	
	
	if (CP == -1.0) color = c;
	
	vec3 scolor1 = plant(pow(color, vec3(wp_saturation)), max(max(color.r,color.g),color.b));
	float luma = dot(color, vec3(0.299, 0.587, 0.114));
	vec3 scolor2 = mix(vec3(luma), color, wp_saturation);
	color = (wp_saturation > 1.0) ? scolor1 : scolor2;

	color = plant(color, contrast(max(max(color.r,color.g),color.b)));

	p = 2.2;
	color = clamp(color, 0.0, 1.0); 	
	color = pow(color, vec3(p)); 
	
	mat3 warm = ToSRGB*D65_to_D55;
	vec3 warmer = warm*color;
	
	mat3 cool   = ToSRGB*D65_to_D93;
	vec3 cooler = cool*color;
2 Likes

Yeah, these two (warm and cool) can be pre-calculated since there are constant values involved. Otherwise mat3x3 * mat3x3 should require 3x more ops compared with mat3x3 * vec3. Nevertheless, an optimization is possible, thanks for the hint.

1 Like

‘color’ is a vec3 array variable so it’s like running mat3vec3pixels, if you precompute (not necessarily a constant) a mat3 * mat3 you save one mat3vec3pixels operation which is huge. Maybe try to profile them, not sure how though not an expert on shaders (fps?)

2 Likes

You still need to use resources for a mat3*mat3 multiply doing it on the fly. But you know the math…(9 dot products)

It can be done very fast due to parallelism, true, but mat3*vec3 should be simpler (3 dot products).

Moving it to the vertex shader should help indeed though, maybe it’s a good trade.

2 Likes

Hey, I noticed in the ReShade port the Integer Scaling setting also scales the mask and not just the game. I’m not sure if this is an issue on my end or intended functionality, but I’d like to request that it be fixed/changed if possible.

(Also just a friendly nudge to update the port in general, please!)

1 Like

For anyone interested I’ve ported CRT-Guest-Advanced, HD and NTSC to ReShade.

Some screenshots at default settings.

Advanced -

HD -

NTSC -

15 Likes

That’s amazing. I need to figure how to convert shaders to Reshade, I’ve gotten multiple requests to port my presets over there but don’t have the slightest idea on how to do it but your pics make me want to learn.

2 Likes

Great work, @DevilSingh, i will put a link to your git page in my first post also.

Good timing, lol, you can now try and use the newest versions ported to ReShade.

4 Likes

Thanks! This port wouldn’t be as good as it is without your help and testing.

3 Likes

*Says “Ive been away for awhile”… *proceeds to go away for awhile

Anyway the reason I never made that third preset I promised was a lack of ideas. I have ideas now.

Recent screenshots look great guys.

2 Likes

@guest.r Could it be possible also apart from negative mask bloom, you could implement negative post brightness? just a numb question :sweat_smile:

2 Likes

A question @guest.r about handling non-square aspect ratios in CRT shaders (I’m just asking you as you must be familiar with the concept because of your Amiga shaders that handle 640x256 and 320x512 and similar resolutions properly).

The beam illuminating the phosphors in a CRT has the shape of a perfect circle with Gaussian falloff. So far so good, and if you feed such a CRT shader 1:1 pixel aspect ratio content (e.g. 640x480 or 320x240, assuming a 4:3 DAR emulated monitor), the shape of the emulated beam will remain a perfect circle in the shaded output.

But when dealing with non-square aspect ratios, typically ~1:2 PAR “wide” or ~2:1 PAR “tall” pixels (or anything in between or much beyond that, e.g. the 1280x200 Super-HiRes Amiga mode featuring 1:4.8 PAR “super tall” pixels!), the shader effectively processes a say 640x200 image, which then gets stretched to 4:3 display aspect ratio, meaning the shape of the emulated beam effectively gets elongated.

I’ve inspected the source code of some CRT shaders, and I haven’t noticed any mechanism in any of them that would “pre-distort” the shape of the beam by the inverse of the pixel aspect ratio so when the result is stretched to the final 4:3 display aspect ratio output, the shape of the beam would come out as a perfect circle. Moreover, output resolution is not taken into account either; an accurate CRT shader should not produce a “more blurry” picture for 320x240 input vs 640x480 input, for example (the CRT does not “vary” the size of the electron beam based on what signal is fed to it).

This is a bit of a problem for me because without such pre-distortion, it’s just not possible to use a single shader with a single preset that would handle say 320x240, 640x240, 320x480 and 640x480 so that the size of the emulated beam remains a perfect circle with the exact same radius in all resolutions (I need this for my authentic CGA/EGA/VGA shader presets where such variations are common).

I’m guessing most people just don’t care about such detail? Or maybe it’s technically very hard to apply such pre-distortion at the shader level to keep the beam shape and size constant no matter the variation in input dimensions (and of the PAR, by extension)?

Thanks, and I hope I managed to explain all this slightly complicated topic clearly :slight_smile:

7 Likes

Yes, this is an issue with CRT emulation and the most common way is to use a filtering approach which works properly only with low resolution input.

The main problem is that CRT emulation isn’t done on a low level, but uses HW features of an adapter (like point or bilinear filtering), a basic filtering approach (like adapted bilinear, quilez…), a standard filtering approach (like lanczos, spline, sinc, gaussian…) or something more advanced.

Currently i don’t know many CRT shaders that would consider advanced filtering, because special kernels need to be implemented which consider doubled (tripled) pixels and thus don’t skip details.

From my Retroarch repertoire the only shader capable of doing so properly is the guest-advanced-HD version, where it’s simple to change the internal resolution parameter, but it’s not automated for an input resolution for various reasons.

On the WinUAE side i needed to write special kernels to handle the hires (only hires, not superhires…) mode properly, but interlacing support (for example) is missing due to limited capabilities of the emulator’s shader environment.

There exists a ReShade version of the ‘HD’ shader, but you need to manually input the resolution every time it changes etc.

What i’m saying is that these problems aren’t addressed in general and it’s also hard to address them. A good and persisting example is the SNES game Trials of Mana (Seiken Densetsu 3), which changes the resolution to the ‘hires’ when talking and going to menues. Most common way is to ‘derez’ the game to 256x224, but here details are going missing.

So, a single preset or a single configured shader to cover every resolutional situation properly hasn’t been developed yet afaik and trends show there is much more probability that any shader will require manual tuning for special cases.

Unless there is something done from scratch especially for these.

4 Likes

Thanks for sharing your great insights into the matter @guest.r!

For the record, I started down this path because I wasn’t content with how an emulated 320x200 EGA image looks like when using the Hyllian shader (which is fantastic for 640x400+ content, otherwise, e.g. for double-scanned VGA). I just couldn’t get the results look like a real EGA monitor; there was always too much softness horizontally.

When I width-doubled the input of the shader to 640x200, the results were a lot closer to the output of a typical sharp EGA monitor, but then I started to think about how realistic this is. In a way, it is because the same 320x200 image displayed in 320x200 mode, or width-doubled to 640x200 and displayed in 640x200 mode yields the exact same results, just like on a real CRT. On the other hand, I have effectively squashed the circular beam shape into an oval…

I’m still vacillating whether this is the right solution or not. I have a bit of a distaste for it as literally sending a double-width image to the shader seems kind of crude. On the other hand, that’s literally what we have to do for accurate line and pixel doubled VGA emulation (e.g. low-res 320x200 gets doubled to 640x400; from the VGA monitor’s perspective, “fake 320x200” (doubled by the VGA adapter) and “real 640x400” screen modes are 100% identical down to the signal level).

So maybe that’s what I’ll do after all; at least, then it’s consistent with the VGA pixel/line-doubling emulation. Plus the beam shape will get distorted in the myriad of tweaked VGA modes anyway (320x400, 360x200, 360x480, 320x400, 360x270, 376x308 — it’s an unending list…)

Comparison screenshots for reference.

320x200 sent to the shader

640x200 sent to the shader

Closeup

5 Likes

Horizontal resolutions are managable with my current codebase, but i’m not this confident with vertical resolutions and scanlines.

A 4k display should be mandatory for everything above 250px or/and double scan. But i guess the ‘stranger’ the resolution the seldom it appears.

6 Likes

Hello again.

I’ve continued to work on my shader skills, and I’ve turned my eye towards 480i/p content. Because I haven’t seen as many screenshots for this type of content, I’ve included a few slot mask / aperture grille screen grabs below for Dreamcast and Playstation 2.

Constructive criticism and advice is always welcome, and if there is none to be given, I hope you find it to be eye candy.

8 Likes

Looking good. What mask are you using in the second shot?

1 Like

Very nice settings. For an optional no-scanline setup definitelly try the HD version and slotmask.

My TV is a 4K display, so you might have to adjust these mask settings for your display resolution. The second screenshot used the following CRT mask settings:

  • Mask: 6
  • Mask Size: 2
  • Mask Zoom: -1
  • Mask Strength: 1.0
  • Mask Boost: 1.4
  • Red Horizontal/Vertical Deconvergence: 1.0
  • Blue Horizontal/Vertical Deconvergence: -1.0

I dont have a lot of CRT reference photos where you can see the mask itself, so I’m not sure if deconverging the blue vertically is accurate. It’s just something I’ve played with.

2 Likes

Is the hd version designed for 480p content?

1 Like