Skip to content

Instantly share code, notes, and snippets.

@mariodivece
Created October 26, 2025 00:33
Show Gist options
  • Save mariodivece/3fac33939f3342706ea69f12beee0e1c to your computer and use it in GitHub Desktop.
Save mariodivece/3fac33939f3342706ea69f12beee0e1c to your computer and use it in GitHub Desktop.
zfast_crt_standard Ported for Dolphin emulator
/*
zfast_crt_standard - A simple, fast CRT shader.
Copyright (C) 2017 Greg Hogan (SoltanGris42)
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.
Ported for Dolphin Emulator by Mario Di Vece, 2025
*/
/*
[configuration]
# zfast_crt (Dolphin) — scanlines locked to OUTPUT pixel rows
# Version 6 — 2025-10-25
[OptionRangeFloat]
GUIName = Blur Amount (X)
OptionName = BLURSCALEX
MinValue = 0.0
MaxValue = 5.0
StepAmount = 0.05
DefaultValue = 0.10
[OptionRangeFloat]
GUIName = Scanline Strength (Low Luma)
OptionName = LOWLUMSCAN
MinValue = 0.0
MaxValue = 1.5
StepAmount = 0.05
DefaultValue = 0.60
[OptionRangeFloat]
GUIName = Scanline Strength (High Luma)
OptionName = HILUMSCAN
MinValue = 0.0
MaxValue = 1.5
StepAmount = 0.05
DefaultValue = 0.60
[OptionRangeFloat]
GUIName = Dark Pixel Brightness Boost
OptionName = BRIGHTBOOST
MinValue = 0.5
MaxValue = 5.0
StepAmount = 0.10
DefaultValue = 1.80
[OptionRangeFloat]
GUIName = Mask Effect Amount
OptionName = MASK_DARK
MinValue = 0.0
MaxValue = 4.0
StepAmount = 0.05
DefaultValue = 0.50
[OptionRangeFloat]
GUIName = Mask/Scanline Fade by Brightness
OptionName = MASK_FADE
MinValue = 0.0
MaxValue = 1.0
StepAmount = 0.05
DefaultValue = 0.80
[OptionRangeFloat]
GUIName = Scanline Thickness (relative)
OptionName = SL_THICK
MinValue = 0.05
MaxValue = 4.0
StepAmount = 0.05
DefaultValue = 1.50
[OptionRangeFloat]
GUIName = Pixels per Scanline
OptionName = PX_PER_SL
MinValue = 2.25
MaxValue = 25.0
StepAmount = 0.25
DefaultValue = 6.0
[/configuration]
*/
#define saturate(x) clamp((x), 0.0, 1.0)
void main()
{
float blurX = GetOption(BLURSCALEX);
float loScan = GetOption(LOWLUMSCAN);
float hiScan = GetOption(HILUMSCAN);
float boost = GetOption(BRIGHTBOOST);
float maskDark = GetOption(MASK_DARK);
float maskFade = GetOption(MASK_FADE);
// Compute pixels-per-scanline as integer multiple of 4
float pxPerSL = GetOption(PX_PER_SL);
float slThick = saturate(GetOption(SL_THICK));
float2 uv = GetCoordinates();
float2 winRes = GetWindowResolution();
float2 invRes = GetInvResolution();
float4 src = Sample();
if (blurX > 0.0)
{
float2 dx = float2(invRes.x, 0.0);
float3 L = SampleLocation(uv - dx).rgb;
float3 R = SampleLocation(uv + dx).rgb;
float3 avg = 0.5 * (L + R);
src.rgb = mix(src.rgb, avg, saturate(blurX));
}
float3 col = src.rgb;
float luma = dot(col, float3(0.299, 0.587, 0.114));
// --- Scanlines tied to OUTPUT pixel grid with 4-multiple spacing ---
float vPix = uv.y * winRes.y;
float cell = (vPix / pxPerSL);
float phase = fract(cell);
float dist = abs(phase - 0.5);
float halfWidth = clamp(slThick * 0.5, 0.01, 0.50);
float core = 1.0 - smoothstep(0.0, halfWidth, dist);
float strength = mix(loScan, hiScan, saturate(luma * maskFade));
float scanMul = 1.0 - (strength * core);
// --- Aperture mask aligned to OUTPUT columns ---
float xPix = uv.x * winRes.x;
float mstep = mod(floor(xPix), 2.0);
float mask = 1.0 - maskDark * (1.0 - mstep);
float3 outRGB = col * boost * scanMul * mask;
outRGB = clamp(outRGB, 0.0, 1.125);
SetOutput(float4(outRGB, 1.0));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment