1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
|
#include "noise-functions.effect"
uniform float4x4 ViewProj;
uniform texture2d image;
uniform float2 uv_size;
uniform float frame;
uniform float y_offset;
uniform float tuning_offset;
uniform float luma_noise;
#define PI 3.1415926535
#define TAU 6.2831853071
float mod(float x, float y)
{
return x - y * floor(x / y);
}
sampler_state textureSampler{
Filter = Linear;
AddressU = Wrap;
AddressV = Wrap;
};
sampler_state borderSampler{
Filter = Linear;
AddressU = Border;
AddressV = Border;
BorderColor = FF000000;
};
float3 rgb2yiq(float3 c)
{
return float3(
dot(c, float3(0.2979, 0.5870, 0.1140)),
dot(c, float3(0.5959, -0.2744, -0.3216)),
dot(c, float3(0.2115, -0.5229, 0.3114))
);
}
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
{
// Allow for a 10% of viewport black bar to the top annd bottom of the screen
// and a 5% viewport black bar to the left/right of the screen.
float height = uv_size.y * 1.10;
float width = uv_size.x * 1.05;
float2 coord = v_in.uv * uv_size;
float noise = hash31(float3(1.0, coord.y, frame));
// Calculate tuning error- if tuning error is less than 900, noise * tuning error, otherwise
// increase this value greatly. Tuning error is used to offset the x-offset/smear
float tuning_error = abs(tuning_offset) < 900.0 ? noise * tuning_offset : noise * (900.0 + (tuning_offset - 900) * 10.0);
tuning_error = noise < 0.996 ? tuning_error : tuning_error * 3.1;
float2 offset_coord = coord + float2(tuning_error, y_offset);
// Wrap the X and the Y coordinate offset including black bars.
offset_coord.y = mod(offset_coord.y, height);
offset_coord.x = mod(offset_coord.x, width);
float4 color = image.Sample(borderSampler, offset_coord / uv_size);
float3 yiq = rgb2yiq(color.rgb);
// Luma Noise
yiq.x += 2.0 * (hash31(float3(coord + 2000.0, frame)) - 0.5) * luma_noise;
// Split YIQ into portions of the NTSC signal:
// ntsc.x is luminance signal
// ntsc.y is color wave amplitude (Saturation), which is the length of the IQ vector
// ntsc.y is the color wave phase shift (hue), which is the angle of the IQ line
float saturation = length(yiq.yz);
float phase_shift = (atan2(yiq.z, yiq.y) + PI)/TAU;
float3 ntsc = float3(yiq.x, saturation, phase_shift);
// Simulate signal tuning error by dropping luminance value towards random noise
float signal_error = hash31(float3(coord, frame)) * abs(tuning_offset) / 1000.0;
ntsc.x = ntsc.x * (1.0 - abs(tuning_offset) / 1000.0) + abs(signal_error);
//ntsc.yz -= 0.25 * signal_error;
ntsc.yz *= 1.0 - saturate(abs(tuning_offset) / 500.0);
return float4(ntsc, color.a);
}
technique Draw
{
pass
{
vertex_shader = mainTransform(v_in);
pixel_shader = mainImage(v_in);
}
}
|