Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/ntsc filter #8

Merged
merged 5 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ target_sources(${PROJECT_NAME} PRIVATE
src/filters/frame-skip.h
src/filters/interlace.c
src/filters/interlace.h
src/filters/ntsc.c
src/filters/ntsc.h
src/filters/posterize.c
src/filters/posterize.h
src/version.h)
Expand Down
14 changes: 14 additions & 0 deletions data/locale/en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,17 @@ RetroEffects.CRT.Geometry="CRT Geometry"
RetroEffects.CRT.ColorCorrection="Color Correction"
RetroEffects.CRT.BlackLevel="Black Level"
RetroEffects.CRT.WhiteLevel="White Level"
RetroEffects.NTSC="NTSC"
RetroEffects.NTSC.TuningOffset="Tuning Offset"
RetroEffects.NTSC.Luma="Luma Settings"
RetroEffects.NTSC.LumaNoise="Noise"
RetroEffects.NTSC.LumaBandSize="Band Size"
RetroEffects.NTSC.LumaBandStrength="Band Strength"
RetroEffects.NTSC.LumaBandCount="Band Count"
RetroEffects.NTSC.Chroma="Chroma Settings"
RetroEffects.NTSC.ChromaBleedSize="Bleed Size"
RetroEffects.NTSC.ChromaBleedSteps="Bleed Steps"
RetroEffects.NTSC.ChromaBleedStrength="Bleed Strength"
RetroEffects.NTSC.Picture="Picture Adjustment"
RetroEffects.NTSC.Brightness="Brightness"
RetroEffects.NTSC.Saturation="Saturation"
140 changes: 140 additions & 0 deletions data/shaders/noise-functions.effect
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// pcg44
#ifdef OPENGL
uvec4 pcg4d(uvec4 v)
#else
uint4 pcg4d(uint4 v)
#endif
{
v = v * 1664525u + 1013904223u;

v.x += v.y * v.w;
v.y += v.z * v.x;
v.z += v.x * v.y;
v.w += v.y * v.z;

v ^= v >> 16u;

v.x += v.y * v.w;
v.y += v.z * v.x;
v.z += v.x * v.y;
v.w += v.y * v.z;

return v;
}

// pcg33
#ifdef OPENGL
uvec3 pcg3d(uvec3 v)
#else
uint3 pcg3d(uint3 v)
#endif
{

v = v * 1664525u + 1013904223u;

v.x += v.y * v.z;
v.y += v.z * v.x;
v.z += v.x * v.y;

v ^= v >> 16u;

v.x += v.y * v.z;
v.y += v.z * v.x;
v.z += v.x * v.y;

return v;
}

#ifdef OPENGL
uvec2 pcg32(uvec3 v)
#else
uint2 pcg32(uint3 v)
#endif
{

v = v * 1664525u + 1013904223u;
v.x += v.y * v.z;
v.y += v.z * v.x;
v.z += v.x * v.y;

v ^= v >> 16u;

v.x += v.y * v.z;
v.y += v.z * v.x;

return v.xy;
}

#ifdef OPENGL
uint pcg31(uvec3 v)
#else
uint pcg31(uint3 v)
#endif
{

v = v * 1664525u + 1013904223u;
v.x += v.y * v.z;
v.y += v.z * v.x;
v.z += v.x * v.y;

v ^= v >> 16u;

v.x += v.y * v.z;

return v.x;
}

uint pcg(uint v)
{
uint state = v * 747796405u + 2891336453u;
uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
return (word >> 22u) ^ word;
}

float4 hash44(float4 src)
{
#ifdef OPENGL
uvec4 u = uvec4(src + 8000000.0f);
#else
uint4 u = uint4(src + 8000000.0f);
#endif
return float4(pcg4d(u)) * (1.0 / float(0xffffffffu));
}

// 3 outputs, 3 inputs
float3 hash33(float3 src)
{
#ifdef OPENGL
uvec3 u = uvec3(src + 8000000.0f);
#else
uint3 u = uint3(src + 8000000.0f);
#endif
return float3(pcg3d(u)) * (1.0 / float(0xffffffffu));
}

// 2 outputs, 3 inputs
float2 hash32(float3 src)
{
#ifdef OPENGL
uvec3 u = uvec3(src + 8000000.0f);
#else
uint3 u = uint3(src + 8000000.0f);
#endif
return float2(pcg32(u)) * (1.0 / float(0xffffffffu));
}

// 1 outputs, 3 inputs
float hash31(float3 src)
{
#ifdef OPENGL
uvec3 u = uvec3(src + 8000000.0f);
#else
uint3 u = uint3(src + 8000000.0f);
#endif
return float(pcg31(u)) * (1.0 / float(0xffffffffu));
}

float hash11(float src)
{
return float(pcg(uint(src)) * 1.0 / float(0xffffffffu));
}
139 changes: 139 additions & 0 deletions data/shaders/ntsc-decode.effect
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
uniform float4x4 ViewProj;
uniform texture2d image;
uniform float2 uv_size;
uniform float luma_band_size;
uniform float luma_band_strength;
uniform int luma_band_count;
uniform float chroma_bleed_size;
uniform int chroma_bleed_steps;
uniform float chroma_bleed_strength;

uniform float saturation;
uniform float brightness;

#define PI 3.1415926535
#define TAU 6.2831853071
#define EPS 1.e-8

float3 yiq2rgb(float3 c) {
return float3(
dot(c, float3(1.0, 0.9560, 0.6210)),
dot(c, float3(1.0, -0.2720, -0.6474)),
dot(c, float3(1.0, -1.1060, 1.7046))
);
}

float decay_wave(float x, float cycles)
{
float x2 = x * PI * cycles;
return cos(x2) * (1.0 - x);
}

sampler_state textureSampler{
Filter = Linear;
AddressU = Clamp;
AddressV = Clamp;
MinLOD = 0;
MaxLOD = 0;
};

struct VertData
{
float4 pos : POSITION;
float2 uv : TEXCOORD0;
};

VertData mainTransform(VertData v_in)
{
v_in.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);
return v_in;
}

float4 mainImage(VertData v_in) : TARGET
{
float2 coord = v_in.uv * uv_size;
float4 ntsc = image.Sample(textureSampler, v_in.uv);

// Denormalize the current point's phase angle
ntsc.z = ntsc.z * TAU - PI;

// *** Luminance Banding ***
// Simulates luminance banding with adjustable number of bands.
// Uses cos(x*pi*cycles) * (1.0 - x) as a decaying wave function
// Samples surrounding pixels, and applies decaying wave
// convolution to generate luma bands around high contrast areas.

// Sample current pixel luma value, with weight 1.0
float luma_band = ntsc.x;
float luma_coef_sum = 1.0;
for (int i = 1; i <= luma_band_size; i++)
{
float2 step = float2(float(i), 0.0);
// Sample at +/- i step
float l1 = image.Sample(textureSampler, (coord + step) / uv_size).x;
float l2 = image.Sample(textureSampler, (coord - step) / uv_size).x;
// Get sample weight from decaying cos wave function
float coef = decay_wave(float(i) / luma_band_size, luma_band_count);
luma_coef_sum += coef * 2.0;
// Sum samples * their weight.
luma_band += (l1 + l2) * coef;
}
// Normalize weighted sum by sum of weights used.
luma_band /= luma_coef_sum;
// Apply luma_band value scaled by luma_band_strength value
ntsc.x = (1.0 - luma_band_strength) * ntsc.x + luma_band_strength * luma_band;

// *** Chroma Bleeding ***
// Same concept as luma banding, but applied to chroma values.
// Use a smoothstep convolution to sample surrounding pixels.

// Sample current pixel chroma values with weight 1.0
float chroma_coef_sum = 1.0;
float2 chroma_bleed = float2(ntsc.yz);
float step_size = chroma_bleed_size / float(chroma_bleed_steps);
for (int i = 1; i <= chroma_bleed_steps; i++)
{
float2 step = float2(float(i) * step_size, 0.0);
// Sample the + and - step
float2 c1 = image.Sample(textureSampler, (coord + step) / uv_size).yz;
float2 c2 = image.Sample(textureSampler, (coord - step) / uv_size).yz;
// Denormalize the phase angle
c1.y = c1.y * TAU - PI;
c2.y = c2.y * TAU - PI;
// Use smoothstep as convolution
float coef = smoothstep(0.0, 1.0, 1.0 - float(i) / float(chroma_bleed_steps));
chroma_coef_sum += coef * 2.0;
// Add weighted contribution to bleed
chroma_bleed += (c1 + c2) * coef;
}
// Normalize weighted sum by weight coefficients
chroma_bleed /= chroma_coef_sum;

// Apply chroma bleed scaled by chroma_bleed_strength
ntsc.yz = (1.0 - chroma_bleed_strength) * ntsc.yz + chroma_bleed_strength * chroma_bleed;

// Apply receiver brightness and saturation adjustments.
ntsc.x *= brightness;
ntsc.y *= saturation;

// Reconstruct yiq values from ntsc luminance (.x), iq length (.y), and phase angle (.z).
// Luminance passes through as Y
// I = cos(phase)*iq_length
// Q = sin(phase)*iq_length
float sz;
float cz;
sincos(ntsc.z, sz, cz);
float3 yiq = ntsc.xyy * float3(1.0, cz, sz);

// Convert YIQ to RGB and return. Alpha is passed through from original source.
return float4(yiq2rgb(yiq), ntsc.a);
}

technique Draw
{
pass
{
vertex_shader = mainTransform(v_in);
pixel_shader = mainImage(v_in);
}
}
Loading
Loading