crt-rgb-to-crt
This shader combines the current frame, the previous frame, screen mask, and diffusion into the final render.
It is a relatively complex shader, and certainly if there are features that aren't needed for a given use case, the shader could be simplified.
Index
Input Textures/Samplers
Uniform Buffer Values
Input Textures/Samplers
- g_currentFrameTexture
-
g_currentFrameTexture
Type
texture
(platform-specific)Description
This is the RGB current frame texture, i.e. the image that will be displayed on the virtual CRT screen. - g_currentFrameSampler
-
g_currentFrameSampler
Type
sampler
(platform-specific, does not exist on some platforms)Description
The sampler to use to sample g_currentFrameTexture. - g_previousFrameTexture
-
g_previousFrameTexture
Type
texture
(platform-specific)Description
This is the previous frame's texture (i.e. last frame's g_currentFrameTexture
). - g_previousFrameSampler
-
g_previousFrameSampler
Type
sampler
(platform-specific, does not exist on some platforms)Description
The sampler to use to sample g_previousFrameTexture. - g_screenMaskTexture
-
g_screenMaskTexture
Type
texture
(platform-specific)Description
This texture is the output of the generate-screen-texture shader, containing the (scaled, tiled, and antialiased) mask texture in the rgb channels and the edge-of-screen mask value in the alpha channel. It is expected to have been generated at our output resolution (i.e. it's 1:1 pixels with our output render target), using the same values of g_viewScale
,g_overscanScale
,g_overscanOffset
, andg_distortion
as this shader is given. - g_screenMaskSampler
-
g_screenMaskSampler
Type
sampler
(platform-specific, does not exist on some platforms)Description
The sampler to use to sample g_screenMaskTexture. - g_diffusionTexture
-
g_diffusionTexture
Type
texture
(platform-specific)Description
This texture contains a tonemapped/blurred version of the input texture, to emulate the diffusion of the light from the phosphors through the glass on the front of a CRT screen. - g_diffusionSampler
-
g_diffusionSampler
Type
sampler
(platform-specific, does not exist on some platforms)Description
The sampler to use to sample g_diffusionTexture.
Uniform Buffer Values
- g_viewScale
-
float2 g_viewScale
Type
float2
Description
NOTE: this value is expected to match the equivalent value in generate-screen-texture.
This value describes how to scale the screen to account for different aspect ratios between the output dimensions and the emulated visible CRT dimensions (i.e. excluding any overscan-clipped content).
This shader is intended to render a screen of the correct shape regardless of the output render target shape, effectively letterboxing or pillarboxing as needed (i.e. rendering a 4:3 screen to a 16:9 render target).
In the event the output render target is wider than the intended screen, the screen needs to be scaled down horizontally to pillarbox, usually like:
x = (renderTargetWidth / renderTargetHeight) * (crtScreenHeight / crtScreenWidth) y = 1.0
if the output render target is taller than the intended screen, it will end up letterboxed using something like:
x = 1.0 y = (renderTargetHeight / renderTargetWidth) * (crtScreenWidth / crtScreenHeight)
- g_overscanScale
-
float2 g_overscanScale
Type
float2
Description
NOTE: this value is expected to match the equivalent value in generate-screen-texture.
If overscan emulation is intended (where the edges of the screen cover up some of the picture), then this is the amount of signal texture scaling needed to account for that.
Given an overscan value named
overscanAmount
that is (where the given values are in texels):overscanAmount.x = overscanLeft + overscanRight overscanAmount.y = overscanTop + overscanBottom
the value of
g_overscanScale
should end up being:(inputImageSize.xy - overscanAmount.xy) * 0.5 / inputImageSize.xy
- g_overscanOffset
-
float2 g_overscanOffset
Type
float2
Description
NOTE: this value is expected to match the equivalent value in generate-screen-texture.
the texture coordinate offset to adjust for overscan. Because the screen coordinates are
[-1..1]
instead of[0..1]
, this is the offset needed to recenter the value.Given an overscan value named
overscanAmount
that is (where the given values are in texels):overscanDifference.x = overscanLeft - overscanRight overscanDifference.y = overscanTop - overscanBottom
the value of
g_overscanScale
should end up being:overscanDifference.xy * 0.5 / inputImageSize.xy
- g_distortion
-
float2 g_distortion
Type
float2
Description
NOTE: this value is expected to match the equivalent value in generate-screen-texture.
The amount along each axis to apply the virtual-curved screen distortion. Usually a value in
[0..1]
, where0
indicates no curvature (a flat screen) and1
indicates "quite curved" - g_backgroundColor
-
float4 g_backgroundColor
Type
float4
Description
The RGBA color of the area around the screen. - g_phosphorPersistence
-
float g_phosphorPersistence
Type
float
Description
How much of the previous frame's brightness to keep.
0
means "we don't use the previous frame at all" and1
means "the previous frame is at full brightness".In many CRTs, the phosphor persistence is short enough that it would be effectively 0 at 50-60fps (As a CRT's phospors could potentially be completely faded out by then). However, for some cases (for instance, interlaced video or for actual NES/SNES/probably other console output) it is generally preferable to turn on a little bit of persistance to lessen temporal flickering on an LCD screen as it can tend to look bad depending on the panel.
[Author's Note: seriously, check out https://www.youtube.com/watch?v=kA8CIY0DeS8 which is what my LCD panel was doing *after* the flickering interlace test truck I was using had been gone for 10 minutes]
- g_scanlineCount
-
float g_scanlineCount
Type
float
Description
How many scanlines there are in this field of the input (where a field is either the even or odd scanlines of an interlaced frame, or the entirety of a progressive-scan frame). In other words, the height of the input texture, in texels. - g_scanlineStrength
-
float g_scanlineStrength
Type
float
Description
The strength of the separation between scanlines. 0
means "no scanline separation at all" and1
means "separate the scanlines as much as possible" - on high-enough resolution output render target (at 4k for sure)1
means "fully black between scanlines", but to reduce aliasing that amount of separation will diminish at lower output resolutions. - g_curEvenOddTexelOffset
-
float g_curEvenOddTexelOffset
Type
float
Description
This is the scanline-space coordinate offset to use to adjust the input texture coordinate's y value based on whether this is a (1-based) odd frame or an even frame (in the context of interlacing). It will be 0.5
(shifting the texture up half a scanline) for an odd frame and-0.5
(shifting the texture down half a scanline) for an even frame. - g_prevEvenOddTexelOffset
-
float g_prevEvenOddTexelOffset
Type
float
Description
This value corresponds to the value that
g_curEvenOddTexelOffset
had on the previous frame.This should match
g_curEvenOddTexelOffset
for a progressive-scan signal and should be-g_curEvenOddTexelOffset
if interlaced. - g_diffusionStrength
-
float g_diffusionStrength
Type
float
Description
This is how much diffusion to apply, blending in the diffusion texture which is an emulation of the light from the screen scattering in the glass on the front of the CRT - 0
means "no diffusion" and1
means "a whole lot of diffusion." - g_maskStrength
-
float g_maskStrength
Type
float
Description
How much we want to blend in the mask. 0
means "mask is not visible" and1
means "mask is fully visible." - g_maskDepth
-
float g_maskDepth
Type
float
Description
The darkness of the darkest part of the mask. 0
means the area between the RGB sections is black,0.9
means the spaces between are nearly white.