New CRT shader from Guest + CRT Guest Advanced updates

Ye, I don’t really understand how to set color patterns with the masks myself tbh.

I found a sweet spot I enjoy between PVM like really visible scanlines while keeping adjacent pixels blending, comparable to what crt-geom does.
Clarity is great while still rounding corners to suit the intended drawings (like circle shapes).

shaders = "9"
shader0 = "shaders_slang/misc/ntsc-colors.slang"
filter_linear0 = "false"
wrap_mode0 = "clamp_to_border"
mipmap_input0 = "false"
alias0 = ""
float_framebuffer0 = "false"
srgb_framebuffer0 = "false"
scale_type_x0 = "source"
scale_x0 = "1.000000"
scale_type_y0 = "source"
scale_y0 = "1.000000"
shader1 = "crt-guest/shaders/guest/crt-gdv-new/stock.slang"
filter_linear1 = "false"
wrap_mode1 = "clamp_to_border"
mipmap_input1 = "false"
alias1 = "StockPass"
float_framebuffer1 = "false"
srgb_framebuffer1 = "false"
scale_type_x1 = "source"
scale_x1 = "1.000000"
scale_type_y1 = "source"
scale_y1 = "1.000000"
shader2 = "crt-guest/shaders/guest/crt-gdv-new/afterglow0.slang"
filter_linear2 = "false"
wrap_mode2 = "clamp_to_border"
mipmap_input2 = "false"
alias2 = "AfterglowPass"
float_framebuffer2 = "false"
srgb_framebuffer2 = "false"
scale_type_x2 = "source"
scale_x2 = "1.000000"
scale_type_y2 = "source"
scale_y2 = "1.000000"
shader3 = "crt-guest/shaders/guest/crt-gdv-new/pre-shaders-afterglow.slang"
filter_linear3 = "false"
wrap_mode3 = "clamp_to_border"
mipmap_input3 = "false"
alias3 = "PrePass"
float_framebuffer3 = "false"
srgb_framebuffer3 = "false"
scale_type_x3 = "source"
scale_x3 = "1.000000"
scale_type_y3 = "source"
scale_y3 = "1.000000"
shader4 = "crt-guest/shaders/guest/crt-gdv-new/avg-lum.slang"
filter_linear4 = "true"
wrap_mode4 = "clamp_to_border"
mipmap_input4 = "true"
alias4 = "AvgLumPass"
float_framebuffer4 = "false"
srgb_framebuffer4 = "false"
scale_type_x4 = "source"
scale_x4 = "1.000000"
scale_type_y4 = "source"
scale_y4 = "1.000000"
shader5 = "crt-guest/shaders/guest/crt-gdv-new/linearize.slang"
filter_linear5 = "true"
wrap_mode5 = "clamp_to_border"
mipmap_input5 = "false"
alias5 = "LinearizePass"
float_framebuffer5 = "true"
srgb_framebuffer5 = "false"
scale_type_x5 = "source"
scale_x5 = "1.000000"
scale_type_y5 = "source"
scale_y5 = "1.000000"
shader6 = "crt-guest/shaders/guest/crt-gdv-new/blur_horiz2.slang"
filter_linear6 = "true"
wrap_mode6 = "clamp_to_border"
mipmap_input6 = "false"
alias6 = ""
float_framebuffer6 = "false"
srgb_framebuffer6 = "false"
scale_type_x6 = "absolute"
scale_x6 = "800"
scale_type_y6 = "source"
scale_y6 = "1.000000"
shader7 = "crt-guest/shaders/guest/crt-gdv-new/blur_vert2.slang"
filter_linear7 = "true"
wrap_mode7 = "clamp_to_border"
mipmap_input7 = "false"
alias7 = "GlowPass"
float_framebuffer7 = "false"
srgb_framebuffer7 = "false"
scale_type_x7 = "absolute"
scale_x7 = "800"
scale_type_y7 = "absolute"
scale_y7 = "600"
shader8 = "crt-guest/shaders/guest/crt-gdv-new/crt-guest-dr-venom2.slang"
filter_linear8 = "true"
wrap_mode8 = "clamp_to_border"
mipmap_input8 = "false"
alias8 = ""
float_framebuffer8 = "false"
srgb_framebuffer8 = "false"
scale_type_x8 = "viewport"
scale_x8 = "1.000000"
scale_type_y8 = "viewport"
scale_y8 = "1.000000"
AS = "0.000000"
CP = "-1.000000"
glow = "0.000000"
gamma_c = "1.100000"
brightboost1 = "1.200000"
gsl = "2.000000"
scanline2 = "32.000000"
spike = "0.000000"
h_sharp = "3.600000"
s_sharp = "1.000000"
shadowMask = "-1.000000"
textures = "SamplerLUT1;SamplerLUT2;SamplerLUT3"
SamplerLUT1 = "crt-guest/shaders/guest/lut/sony_trinitron1.png"
SamplerLUT1_linear = "true"
SamplerLUT1_wrap_mode = "clamp_to_border"
SamplerLUT1_mipmap = "false"
SamplerLUT2 = "crt-guest/shaders/guest/lut/sony_trinitron2.png"
SamplerLUT2_linear = "true"
SamplerLUT2_wrap_mode = "clamp_to_border"
SamplerLUT2_mipmap = "false"
SamplerLUT3 = "crt-guest/shaders/guest/lut/other1.png"
SamplerLUT3_linear = "true"
SamplerLUT3_wrap_mode = "clamp_to_border"
SamplerLUT3_mipmap = "false"
8 Likes

@guest.r

Looking at the CRT shots you posted I noticed one of the effects of the shadow mask is that it seems to “dither” the outside of the scanlines. Since all current mask simulation has the large drawback of darkening the image, I was thinking about only simulating that dithering effect on the outside of scanlines.

A naive and simple approach would be to shift Y column up by 1 pixel every other pixel / column . From normal viewing distance this creates the illusion of dithering on the outside of the scanlines, like real mask on CRT does, with the big win that brightness is not affected.

I’m sure better looking implementations of this concept can be thought of, this is purely a paint program mockup to demonstrate the basic idea and effect. Since it uses only 1 pixel shift this is -not- for 4K (it would need bigger size / shift of course :wink: ) Watch at true size from normal viewing distance on 1080p / 1440p.

metal slug - 1 pixel y shift effect

2 Likes

It could be also normal blending instead of dithering, but i get the idea. There are more downsides though related to low-res pixel fetching and attributing it in high res, otherwise i’d use something similar to smooth the scanlines at non-integer scaling etc.

An after-pass could do it, but i’m avoiding it in general. Scanline smoothing for example went very well with extra pass, not minding the speed loss. There is currently no afterpass for scanline smoothing, although it works nice, because the performance degradation.

1 Like

I’m not sure if normal blending is the same in this situation.

I got the idea by the “produced by” in the CRT shot, see cutout below. If you look at the top row of white pixels of the “produced by”, it’s as if every other pixel / column is shifted “one pixel” up (if we would talk in terms of shader output size) .

This effect makes the scanline very different from high TVL PVM, the scanline is not as flat as BVM, but has “an illusion” bulging pixel pattern to it. That’s where my idea came from :slight_smile:. As mentioned I think the big win could be that part of real mask is simulated without any brightness loss.

shifting y 1 pixel every other column

Shifting Y column up by some amount every other column would produce a similar effect with the shader, but we would need to load up that raw screenshot of turrican in the image viewer to see if applying this effect would produce similar "dithering " pattern on the top pixel line of “produced by” as below from real CRT.

5 Likes

It’s a bit similar to the ‘coarse’ trinitron mask in crt-guest-sm, but, as i said, is easily achievable by an extra pass. The screenshot jaggies though are mask related to deconvergence and unequally fading phosphors, since they are more saturated. I can post the pass you can try it a bit later.

Shader, last pass, Scale1x, Filtering Linear

#version 450

layout(push_constant) uniform Push
{
	vec4 SourceSize;
	vec4 OriginalSize;
	vec4 OutputSize;
	uint FrameCount;
	float dsize;
} params;

#pragma parameter dsize "Scanline dithering size" 1.0 0.0 2.0 0.10
#define dsize         params.dsize     // Dithering size

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;

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

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

void main()
{

   vec2 tex = vTexCoord;
   vec2 pos = tex * params.OutputSize.xy;
   vec2 dy  = dsize*vec2(0.0, params.OutputSize.w);
   float mixer = floor(mod(pos.x,2.0));
   tex = mix(tex + 0.0*dy, tex - dy, mixer);
   
   FragColor = vec4(texture(Source, tex).rgb, 1.0);
}
6 Likes

Looks very good, similar to my maxDR preset before I broke it. Thanks for sharing your settings!

edit: I think it’s safe to increase bright boost a bit, here it is with 1.80/1.80. I also added grade and set white point to ~7500K


@hunterk are we still waiting on github permissions to be restored? :rofl:

Also, what’s slang for “white” and “black”? I know how to say red, green and blue, but white and black still elude me. I want to try that mask!

5 posts were split to a new topic: Hunterk shader repos

Awesome thanks for making this, it’s quite another thing to be able to test quickly with many more games :slight_smile: The idea works okayish I would say, it’s interesting to see it’s not working as well as I expected, as white-black adjacent areas create more visible “sawtooths” patterns then when it’s not black as adjacent color. So overal the sawtooth pattern is not equally divided / visible as a normal mask would. Very nice to have tried this though, thanks as it satisfied the hunger! :smiley:

There’s one other thing: I’m testing a bit with the “CRT Color Profile” and would be interested where these things clip in sRGB space. I was having this idea that maybe would be possible to have a “test hack” for.

Could it be made such (as temp hack) that when a CRT Color Profile is set and colors clip outside of the sRGB that it is not given the max clipping value but instead turned into (for example) a bright neon green color?

With such a feature we could easily see IF, WHEN and WHERE colors from the CRT Color Profiles clip in the various games. Maybe this could work for the LUTs too?

3 Likes

It’s doable, but not much of it. :grin: Three lines must be calculated and three linear inequations solved in the xyY space. It’s not interesting for colorspace transformations the way they work, since the gamut is narrowed. Maybe, when i’m somewhat or very bored.

But gave me a nice idea though…

2 Likes

I think you can sort of get the desired effect with the slot mask in GDV. The sawtooths aren’t quite as pronounced as we’d like them to be, maybe, but I think it looks pretty nice nonetheless.

1 Like

I’m testing beam non-linearity, with default settings I found a linear response (no beam dynamics at all) and only a 10% loss on luminance output. What presets do you recommend for monitor CRTs and consumer CRTs so I can test further (no glow involved).

2 Likes

Great idea.

Mine are actually all a bit weird right now, as I was recently testing how high I could push brightness before I started to get clipping.

I’m currently working on a pro monitor setup (mask 0) and two consumer-grade setups (mask 8 and GDV slotmask).

They’re all currently too bright, but I think most of the other settings are pretty neutral, scanline shape and bright boost are probably where you want to make changes. Scanline dark @ 1.50 is arbitrary but I find it’s easy to work with; a lower value like 1.30 might be better. Scanline saturation and bright beam are at 1.00 for increased flexibility.

Gamma on my plasma is 2.4 so that’s what I have output gamma set to.

Glow is at 0.01; need to disable that.

Protip: I think if you raise/lower bright boost dark and bright boost bright by the same amount it will keep the gamma curve intact and just raise/lower the whole thing.

Edit: @Tatsuya79’s recent settings also look very good, especially after adding grade and increasing the bright boost a bit (I think 1.80/1.80 is reasonable with mask 0).

I’d recommend @Kurozumi’s excellent preset, but it no longer works with the new GDV.

3 Likes

I tested with your GDV preset and is spot on, phoshor gamma with Scanline dark @ 1.50. I was reading the paper I shared a few days ago and phosphor gamma has to be between 0.9 and 0.88 to retrieve an absolute output gamma of 2.2 from the signal gamma of 2.5 = (2.5 * 0.88).

So I would say you are right, 1.50 matches 0.9, I need to measure which value complies for 0.88 to have a working range. For crt-royale I found good values range from beam-min-sigma of 0.05 to 0.10 for 0.88 (2.2 absolute gamma output) and 0.9 (2.25 absolute gamma output) respectively.

Typically I measured between 10% and 15% brightness loss with crt-guest Scanline bright @ 1.00, so that’s something to be aware when adjusting the monitor brightness (two or three notches up when calibrated to 100 cd/m2)

EDIT: for crt-guest scanline dark pixels should be between 1.50 and 1.70, that is with scanline beam shape low of 14.0 (not sure what is its function)

3 Likes

Today i was working on new glow implementation, based on resizable and adjustable gaussian blur implementation.

First image is 13x13 gaussian blur:

Second image is 31x31 gaussian blur:

Last image is 51x51 gaussian blur:

I will set it to max. 31x31 in the shader, but otherwise it can go until it reaches the texture sample fetch limit.

Source image size is 256x224.

4 Likes

That’s interesting, I’d like to see some shots with it being completely implemented, as it’s kinda hard to get a complete feel for it XD

It’s implemented already, had to ‘unimplement’ it for the screenies. :grin: Much easier to tweak compared with the GDV repo version and supports resize.

1 Like

Looks very clean, you can try to mix them so they add each other for an exponential effect (think neons)

2 Likes

So does the bigger sizes mean larger halos?

1 Like

It would mean 2 extra buffers. I would need a second instance for bloom, for example, but the speed increase is more tempting.

Yes, but i will probably limit it to 0.5*viewport not to stall adapters with less bandwidth and minding 4k performance a bit. The main benefit is speed increase though, better ‘configurability’ and larger halos. It’s not doing as much compromises compared with two former implementations, although they both looked OK with moderate settings.

3 Likes