// "LeiFX" shader - Pixel filtering process // // Copyright (C) 2013-2015 leilei // // 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. // New undither version #include "ReShade.fxh" //16-BIT DITHER VARIABLES #ifndef USE_DITHER #define USE_DITHER 0 //[0 or 1] Enables the old Dithering option #endif #if (USE_DITHER == 1) #ifndef USE_RAMDAC #define USE_RAMDAC 0 //[0 or 1] Use the RAMDAC 4x1 filter #endif uniform int DITHER_AMOUNT < ui_type = "drag"; ui_min = -16; ui_max = 16; ui_label = "Dither Amount [3DFX]"; > = 0; uniform int DITHER_BIAS < ui_type = "drag"; ui_min = -16; ui_max = 16; ui_label = "Dither Bias [3DFX]"; > = 0; #if USE_RAMDAC uniform float LEIFX_LINES < ui_type = "drag"; ui_min = 0.0; ui_max = 2.0; ui_label = "Lines Intensity [3DFX]"; > = 1.0; #endif #endif //3DFX VARIABLES #ifndef BOXFILTER #define BOXFILTER 0 //[0 or 1] Enables a Box Filter for the 3DFX effect. #endif #if BOXFILTER #define PIXELWIDTH 1.0f #define FILTCAP (32.0f / 255) // filtered pixel should not exceed this #define FILTCAPG (FILTCAP / 2) static const float filtertable_x[4] = { 1, -1,-1,1 }; static const float filtertable_y[4] = { -1,1,-1,1 }; #else #define PIXELWIDTH 1.0f #define FILTCAP (64.0f / 255) // filtered pixel should not exceed this #define FILTCAPG (FILTCAP / 2) static const float filtertable_x[4] = { 1,-1,1,1 }; static const float filtertable_y[4] = { 0,0,0,0 }; #endif //ACTUAL CODE #define mod2(x,y) (x-y*floor(x/y)) float fmod(float a, float b) { float c = frac(abs(a/b))*abs(b); return (a < 0) ? -c : c; /* if ( a < 0 ) c = 0-c */ } void PS_LeiFX_Dither(in float4 pos : SV_POSITION, in float2 uv : TEXCOORD0, out float4 col : COLOR0) { float erroredtable[16] = { 16,4,13,1, 8,12,5,9, 14,2,15,3, 6,10,7,11 }; // Sampling The Texture And Passing It To The Frame Buffer col = tex2D(ReShade::BackBuffer, uv); float3 what; float aht = 1.0f; what.x = aht; // shut what.y = aht; // up what.z = aht; // warnings float3 huh; // ***************** // STAGE 0 // Grab our sampled pixels for processing...... // ***************** float2 px; // shut float egh = 1.0f; px.x = (1 / ReShade::ScreenSize.x); px.y = (1 / ReShade::ScreenSize.y); float3 pe1 = tex2D(ReShade::BackBuffer, uv); // first pixel... #if (USE_DITHER == 1) float3 colord = float3(0.0,0.0,0.0); float3 color; float2 res; res.x = ReShade::ScreenSize.x; res.y = ReShade::ScreenSize.y; int yeh = 0; int ohyes = 0; float2 ditheu = uv.xy * res.xy; ditheu.x = uv.x * res.x; ditheu.y = uv.y * res.y; int ditx = int(fmod(ditheu.x, 4.0)); int dity = int(fmod(ditheu.y, 4.0)); int ditdex = ditx * 4 + dity; // 4x4! pe1.r = pe1.r * 255; pe1.g = pe1.g * 255; pe1.b = pe1.b * 255; // looping through a lookup table matrix //for (yeh=ditdex; yeh<(ditdex+16); yeh++) ohyes = pow(erroredtable[yeh-15], 0.72f); // Unfortunately, RetroArch doesn't support loops so I have to unroll this. =( // Dither method adapted from xTibor on Shadertoy ("Ordered Dithering"), generously // put into the public domain. Thanks! if (yeh++==ditdex) ohyes = erroredtable[0]; else if (yeh++==ditdex) ohyes = erroredtable[1]; else if (yeh++==ditdex) ohyes = erroredtable[2]; else if (yeh++==ditdex) ohyes = erroredtable[3]; else if (yeh++==ditdex) ohyes = erroredtable[4]; else if (yeh++==ditdex) ohyes = erroredtable[5]; else if (yeh++==ditdex) ohyes = erroredtable[6]; else if (yeh++==ditdex) ohyes = erroredtable[7]; else if (yeh++==ditdex) ohyes = erroredtable[8]; else if (yeh++==ditdex) ohyes = erroredtable[9]; else if (yeh++==ditdex) ohyes = erroredtable[10]; else if (yeh++==ditdex) ohyes = erroredtable[11]; else if (yeh++==ditdex) ohyes = erroredtable[12]; else if (yeh++==ditdex) ohyes = erroredtable[13]; else if (yeh++==ditdex) ohyes = erroredtable[14]; else if (yeh++==ditdex) ohyes = erroredtable[15]; // Adjust the dither thing ohyes = 17 - (ohyes - 1); // invert ohyes *= DITHER_AMOUNT; ohyes += DITHER_BIAS; colord.r = pe1.r + ohyes; colord.g = pe1.g + (ohyes / 2); colord.b = pe1.b + ohyes; pe1.rgb = colord.rgb * 0.003921568627451; // divide by 255, i don't trust em #endif // ***************** // STAGE 2 // Reduce color depth of sampled pixels.... // ***************** { float3 reduct; // 16 bits per pixel (5-6-5) reduct.r = 32; reduct.g = 64; // gotta be 16bpp for that green! reduct.b = 32; pe1 = pow(pe1, what); pe1 *= reduct; pe1 = floor(pe1); pe1 = pe1 / reduct; pe1 = pow(pe1, what); } col = float4(pe1,1.0); } void PS_LeiFX_Gamma(in float4 pos : SV_POSITION, in float2 uv : TEXCOORD0, out float4 col : COLOR0) { float2 px; // shut px.x = 1 / ReShade::ScreenSize.x; px.y = 1 / ReShade::ScreenSize.x; //px.y = 1 / ReShade::ScreenSize.y; float gammaed = 0.3; float leifx_linegamma = gammaed; float leifx_liney = 0.0078125;0.01568 / 2; // 0.0390625 #if (USE_RAMDAC == 1) float2 res; res.x = ReShade::ScreenSize.x; res.y = ReShade::ScreenSize.y; float3 pe1 = tex2D(ReShade::BackBuffer, uv); // first pixel... float2 ditheu = uv.xy * res.xy; ditheu.x = uv.x * res.x; ditheu.y = uv.y * res.y; leifx_linegamma = (LEIFX_LINES / 10); float horzline1 = (fmod(ditheu.y, 2.0)); if (horzline1 < 1) leifx_linegamma = 0; pe1.r += leifx_linegamma; pe1.g += leifx_linegamma; pe1.b += leifx_linegamma; #endif // Sampling The Texture And Passing It To The Frame Buffer col = tex2D(ReShade::BackBuffer, uv); float lines = fmod(col.y, 2.0); // I realize the 3dfx doesn't actually line up the picture in the gamma process #if (USE_RAMDAC == 1) if (lines < 1.0) leifx_linegamma = 0; #else if (lines < 1.0) leifx_liney = 0; #endif #if (USE_RAMDAC == 1) col.r += leifx_liney; // it's a slight purple line. col.b += leifx_liney; // it's a slight purple line. #endif float leifx_gamma = 1.0 + gammaed;// + leifx_linegamma; #if (USE_RAMDAC == 1) leifx_gamma = 1.0 + leifx_linegamma; #endif col.r = pow(col.r, 1.0 / leifx_gamma); col.g = pow(col.g, 1.0 / leifx_gamma); col.b = pow(col.b, 1.0 / leifx_gamma); } float3 leifx_filter(float2 uv, int passnum) { float2 px; // shut float2 res; res.x = ReShade::ScreenSize.x; res.y = ReShade::ScreenSize.y; px.x = uv.x * res.x; px.y = uv.y * res.y; float PIXELWIDTHH = (1 / ReShade::ScreenSize.x); float2 texture_coordinate = uv; px.x = (1 / ReShade::ScreenSize.x) / 1.5; px.y = (1 / ReShade::ScreenSize.y) / 1.5; // Sampling The Texture And Passing It To The Frame Buffer float3 color = tex2D(ReShade::BackBuffer, texture_coordinate); float3 pixel1 = tex2D(ReShade::BackBuffer, texture_coordinate + float2(px.x * (filtertable_x[passnum] * PIXELWIDTH), px.y * (PIXELWIDTH * filtertable_y[passnum]))).rgb; float3 pixel2 = tex2D(ReShade::BackBuffer, texture_coordinate + float2(-px.x * (filtertable_x[passnum] * PIXELWIDTH), px.y * (PIXELWIDTH * filtertable_y[passnum]))).rgb; float3 pixeldiff; // Pixel difference for the dither check float3 pixelmake; // Pixel to make for adding to the buffer float3 pixeldiffleft; // Pixel to the left float3 pixelblend; { pixelmake.rgb = 0; pixeldiff.rgb = pixel2.rgb-color.rgb; if (pixeldiff.r > FILTCAP) pixeldiff.r = FILTCAP; if (pixeldiff.g > FILTCAPG) pixeldiff.g = FILTCAPG; if (pixeldiff.b > FILTCAP) pixeldiff.b = FILTCAP; if (pixeldiff.r < -FILTCAP) pixeldiff.r = -FILTCAP; if (pixeldiff.g < -FILTCAPG) pixeldiff.g = -FILTCAPG; if (pixeldiff.b < -FILTCAP) pixeldiff.b = -FILTCAP; pixelmake.rgb = (pixeldiff.rgb / 4); color.rgb= (color.rgb + pixelmake.rgb); } return color; } void PS_LeiFX_Filter_1(in float4 pos : SV_POSITION, in float2 uv : TEXCOORD0, out float4 col : COLOR0) { col.rgb = leifx_filter(uv,0); col.w = 1; } void PS_LeiFX_Filter_2(in float4 pos : SV_POSITION, in float2 uv : TEXCOORD0, out float4 col : COLOR0) { col.rgb = leifx_filter(uv,1); col.w = 1; } void PS_LeiFX_Filter_3(in float4 pos : SV_POSITION, in float2 uv : TEXCOORD0, out float4 col : COLOR0) { col.rgb = leifx_filter(uv,2); col.w = 1; } void PS_LeiFX_Filter_4(in float4 pos : SV_POSITION, in float2 uv : TEXCOORD0, out float4 col : COLOR0) { col.rgb = leifx_filter(uv,3); col.w = 1; } technique LeiFx_OA { pass LeiFX_Dither { VertexShader = PostProcessVS; PixelShader = PS_LeiFX_Dither; } pass LeiFX_Filter1 { VertexShader = PostProcessVS; PixelShader = PS_LeiFX_Filter_1; } pass LeiFX_Filter2 { VertexShader = PostProcessVS; PixelShader = PS_LeiFX_Filter_2; } pass LeiFX_Filter3 { VertexShader = PostProcessVS; PixelShader = PS_LeiFX_Filter_3; } pass LeiFX_Filter4 { VertexShader = PostProcessVS; PixelShader = PS_LeiFX_Filter_4; } pass LeiFX_Gamma { VertexShader = PostProcessVS; PixelShader = PS_LeiFX_Gamma; } }