A new little shader i did (glsl)

This is new for me. I never considered something like this was happening. I’m guessing this has to do specifically with RF demodulation. Otherwise, it could have to do with every input the TV has.

Amplifiers can act like a low pass filter because of their slew rate. The amplifiers feeding the electron guns probably had the strictest slew rate requirements because the voltages were relatively high. Ideally, amplifiers would be chosen so that the slew rate is not a limiting factor.

I don’t know how often this was an issue in CRTs, but I suspect that the main reason manufacturers started adding sharpening circuits is just that people preferred the look. TVs still have sharpening controls today despite being able to display a pixel-perfect digital image.

For some reason, my Panasonic TV from 2000 also applies sharpness on s-video and component signals.

My JVC D-Series also applies sharpening to the component input.

1 Like

Meanwhile i got bored of that single pass shader, gutted some of the previous work, tweaked the ntsc to be more accurate, and injected to this. It’s a real mod / de-mod / s-video as usual and consumes 1.6 watt on my HD630 laptop (zfast-crt is 1.2, gtu-v50 will top ~10 watt). Pretty damn good for what it offers. If any interest i’ll port it to SLANG too.

real composite

svideo

MD mode

NES artifacts test

7 Likes

hi, please Slang version. Thank you for your work!

1 Like

Composite video as seen on a TV is much sharper than that thanks to the TV’s sharpening circuit. I’d say that’s the next step. Keep up the good work!

2 Likes

Ok so i added an adjustable “Sharpness” switch, more or less how a window function would average texture fetches and preserve higher frequencies (sharp edges). That costed 0.2 watt lol, now it’s up to 1.8.

before (you can zero the sharpness and go back to this), it was more like RF sharpness

after (that’s closer to my RPI 2W sharpness on Composite)

First settle GLSL and then i’ll port the final version to SLANG.

a simple way to precalculate 2 texture fetches and a window function, one fetch at 0.5*1.0/TextureSize.x distance and one at 1.5 and get the values. I just used one out of my head and works just fine.

desmos

9 Likes

A good NTSC phase advance per pixel table. I must correct some values on my ntsc shaders heh.

5 Likes
6 Likes

Thanks @DariusG! Looks like I have a new shader stack to build!

1 Like

Do some testing and tell me what you think, that’s a pretty accurate NES/SNES composite there AFAIK.

1 Like

Heym thank you again for your work.

How do i load them ?

Do I have to update shaders?

Do I load each individual shader to test ? or is there some specific preset that load these shaders on that link?

Thank you

Yes maybe tomorrow update slang shaders. Preset is on crt folder and it’s called crt-consumer-1w-ntsc

I think have loaded your content properly.

It looks outstanding and once again, thank you a lot for your work.

Little suggestion:

Is there some way to bring rainbow FIX for BORDERS = FULL ?

Apparently, your rainbow was intended to work WITHOUT borders.

The thing is that on Genesis console, full borders is the original experience. So if its enabled on GPGX or BlastEm (by default) it will not show up properly.

Amazing work. Picture one is NO borders. Second its enabled.

1 Like

Probably would need to change that 170.666 color cycles to how many on full border (probably something like 185.0) but then it will be non-universal and genesis plus gx specific.

1 Like

Full borders should really have been more of an exceptional experience, as those borders would be most often hidden in the overscan area, unless you were a power user that adjusted their TV for that (or for people who used more easily adjusted monitors). You couldn’t even be certain to see the full picture with no borders. I agree of course that NTSC shaders should work if possible for any borders config.

2 Likes

That’s just a matter of driving the “subcarrier phase” with ntsc clock (3.57mhz) / emulated system clock (which is what’s happening in reality) for it to work. Then you should make it a 10 heads Hydra, with clocks for Snes low, high, genesis low/high, ps1 256/320 and on and on.

Now it relies on TextureSize.x which changes if you enable borders and breaks the relationship between ntsc and clock that suggests no borders.

2 Likes

Kinda amazing how much variance exists with border emulation in the first place still today, sizes differ substantially between emulators for the same system or authors don’t bother with implementing them at all. I guess this is the result of different approaches/goals.

1 Like

Because not needed, borders are time actually, like ~1/4 of visible area to have some time space to create signal sync, pulse, burst etc. NES in example has 341 pixels with borders but only displays 256, not needed to display all and they are not in real hardware too as they are in non visible time space.

I was thinking about the borders you can actually see, e.g. not bother with emulating beyond active 256x192 pixels for TMS graphic based systems, or in the NES example you’d just show 256x224 for NTSC I guess. Im not sure there are still emulators around for that nowadys which don’t allow to show 240 vertical res.

You can handle borders, overscan cropping, and the different Genesis modes in the shader if you want to. See the following function, where t is equivalent to vTexCoord.x. This returns the chroma subcarrier value at t.

float chroma_carrier_genesis(float t, vec4 original_size) {
    // The Genesis master clock (MCLK) is 53693175 Hz, and the chroma clock is
    // MCLK/15 (3579545 Hz). In H40 mode (320 pixel width), each pixel in the
    // active area is 8 cycles long[1]. In H32 mode (256 pixel width), each
    // pixel is 10 cycles long.
    //
    // In both modes, a full line is 3420 cycles long. Because 3420 % 15 == 0,
    // there is no phase offset between lines. With 262 lines per field[2],
    // there is also no phase offset between fields. When interlacing, there
    // are 262.5 lines per field. (3420 / 2) % 15 == 0, so there is no phase
    // offset between fields when interlacing, either.
    //
    // [1] Note that in the blanking area, some pixels in H40 mode are
    // different sizes. We can ignore that for our purposes.
    // [2] There are some half lines in the blanking area which add to full
    // lines.
    //
    // See: http://gendev.spritesmind.net/forum/viewtopic.php?f=2&t=3221

    float carrier_normalized;
    // TODO handle upscaled input and interlacing
    if (original_size.x > 300) { // H40
        carrier_normalized = 8.0 / 15.0 * original_size.x;
    } else { // H32
        carrier_normalized = 10.0 / 15.0 * original_size.x;
    }
    return PI * 2.0 * carrier_normalized * t;
}

Edit: Just to clarify, chroma_subcarrier * number_input_pixels / pixel_rate will give you the number of chroma periods across your input, whatever actual size it is. That’s all I am doing.

3 Likes

Yeah that’s the proper approach, i was just trying to keep it simple and possibly more performant without many branches (if / else).

This is after switching to NTSC 3.579 Mhz / pixel_clock on Genesis Plus GX, as i said it depends on a stable pixel clock so it won’t ever change.

3 Likes