Welcome to Doom9's Forum, THE in-place to be for everyone interested in DVD conversion. Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules. |
|
|
#361 | Link |
|
Registered User
Join Date: May 2008
Posts: 211
|
JanWillem32, First off.....Thank You for working on the hybrid shaders I've requested. I doubt if I would be able to find anyone else so willing to do that. Second, I have been doing some experimenting with different methods for cleaning up the image quality on a lot of my old XVID/DIVX/AVI files and it seems that they benefit more from deblocking (ffdshows raw filter mplayer deblocking) than denoising. I suppose since most of the files are old vhs to avi tv rips and old tvcard rips that makes sense. Anyways, would you be open to possibly doing an adjustable deblocking shader? I've read that ATI used to (I don't know if they still do) use it's shader core to do deblocking so I assume (whether correctly or not) it's possible to do it with an HLSL script. I don't have a clue what the math involved would be like so if you can't it's understandable.
|
|
|
|
|
|
#362 | Link |
|
Registered User
Join Date: May 2008
Posts: 1,840
|
MPEG4 ASP is notorious for banding (could be mistaken for blocks on flat surfaces and faces) are you sure it's not banding?
f3kdb is a must for me with ASP much less so for any decent AVC encode or MPEG2. Have you tried it through ffdshow's avisynth interface? Make sure to use setmemorymax(128 or more) to stop the leakage.
__________________
PC: FX-8320 GTS250 HTPC: G1610 GTX650 PotPlayer/MPC-BE LAVFilters MadVR-Bicubic75AR/Lanczos4AR/Lanczos4AR LumaSharpen -Strength0.9-Pattern3-Clamp0.1-OffsetBias2.0 |
|
|
|
|
|
#363 | Link | |
|
Registered User
Join Date: Jul 2013
Posts: 10
|
Quote:
![]() ![]() How about a aperture grille like this on Final burn alpha, it feels like a real old crt, or LG plasma ![]() Or a scanlines at 95% Last edited by jerrymh; 28th July 2013 at 05:27. |
|
|
|
|
|
|
#364 | Link |
|
Registered User
Join Date: May 2008
Posts: 211
|
@turbojet...Yes there is some banding but some of JanWillem32's shaders+his modified EVR-CP already help with that (as well as madVR all but eliminating banding) but blocking is still there without using fddshow's raw filter. Not that the raw filter is bad I just would like to eliminate it from my playback chain. If JanWillem32 can kindly make a deblocking shader that does as good of a job or better as the raw filter I would much rather use that.
Most of my problem video files are from old vhs recordings of TV shows converted to .avi's as well as some TIVO-type files and early PC TV card recordings so blocking is kind of a given as well as massive banding . Considering that madVR doesn't do deblocking (it actually accentuates the blocking on some of my files) using madVR for these is sort of a no no without the raw filter or a shader script (one the madVR will not neuter because of gamma manipulation or such).
|
|
|
|
|
|
#365 | Link |
|
Registered User
Join Date: Oct 2010
Location: The Netherlands
Posts: 1,083
|
Deblocking is mostly decoder territory. Many video codecs don't use the typical macroblocks at all. For those that do, you need the general blocking info for the luma, chroma and interlacing to deal with it. For h.264 (and some newer codecs) organized (de)blocking is mandatory for both encoder and decoder. The custom shader stages of the video renderer are a bit late in the rendering chain to properly work on blocking and such. I'm not sure if I can write a normal shader that can help with deblocking.
jerrymh, that picture mostly shows hand-drawn pixel art. No decent video will convert nicely to high contrast, low quantization images like that. I can approximate the effect by combining a few techniques, but note that posterization is a really messy effect (even in common 8-bit video and worst of all, it's everywhere). It's a two-pass shader chain again. The warning for "should be divisible by 4" isn't too strict, the few artifacts are hard to see. For common resolutions such as 720- and 1080-line systems I can also adapt special shaders to compensate for this issue. I can also try to boost some of the contrast or colorfulness before posterization as well, but I didn't see much improvement with those effects enabled on the samples I used. Code:
// (C) 2013 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// 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, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// horizontal 4-pixel averaging
// this shader only works properly on inputs that have a horizontal resolution that is evenly divisible by 4
sampler s0 : register(s0);
float c0 : register(c0);
float c1 : register(c1);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float pos = (trunc(tex.x*c0*.25)+.125)*c1*4.;// calculate the left positon of the current set of pixels
return (tex2D(s0, float2(pos, tex.y))+tex2D(s0, float2(pos+c1, tex.y))+tex2D(s0, float2(pos+2*c1, tex.y))+tex2D(s0, float2(pos+3*c1, tex.y)))*.25;// blur and output
}
// vertical 4-pixel averaging, dithering, posterizing and old CRT scan lines
// this shader only works properly on inputs that have a vertical resolution that is evenly divisible by 4
#define gamma 1// higher is brighter, fractions, either decimal or not are allowed
#define scanlinebasedarken .5// the default of .5 will darken outer pixels a bit on each set of vertical pixels to appear like old CRT scan lines, higher values will narrow the scan line beam
#define posterizedegamma 1// 0 or 1, apply dirty de-gamma for posterization, useful to preserve realistic gradients in low gamma modes
#define quantizationbits 4// posterization level, note that 'quantize' can actually take any amount, not just those based on powers of two
sampler s0 : register(s0);
float2 c0 : register(c0);
float2 c1 : register(c1);
static const float quantize = pow(2, quantizationbits)-1;
static const float quantizer = 1./quantize;
static const float qm = .0078125*quantizer;
static const float smalldithermap[8][8] = {
{-63*qm, qm, -47*qm, 17*qm, -59*qm, 5*qm, -43*qm, 21*qm},
{33*qm, -31*qm, 49*qm, -15*qm, 37*qm, -27*qm, 53*qm, -11*qm},
{-39*qm, 25*qm, -55*qm, 9*qm, -35*qm, 29*qm, -51*qm, 13*qm},
{57*qm, -7*qm, 41*qm, -23*qm, 61*qm, -3*qm, 45*qm, -19*qm},
{-57*qm, 7*qm, -41*qm, 23*qm, -61*qm, 3*qm, -45*qm, 19*qm},
{39*qm, -25*qm, 55*qm, -9*qm, 35*qm, -29*qm, 51*qm, -13*qm},
{-33*qm, 31*qm, -49*qm, 15*qm, -37*qm, 27*qm, -53*qm, 11*qm},
{63*qm, -qm, 47*qm, -17*qm, 59*qm, -5*qm, 43*qm, -21*qm}};
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float3 basepos = tex.xyy*c0.xyy*float3(.03125, .03125, .25);
float3 basefrac = frac(basepos);
float2 lookups = basefrac.xy*8.;
float dithers = smalldithermap[lookups.x][lookups.y];
float br = 1.-pow(abs(basefrac.z*2.*scanlinebasedarken-scanlinebasedarken), gamma);// generate scan lines
float pos = (basepos.z-basefrac.z+.125)*c1.y*4.;// calculate the top positon of the current set of pixels
float4 s1 = (tex2D(s0, float2(tex.x, pos))+tex2D(s0, float2(tex.x, pos+c1.y))+tex2D(s0, float2(tex.x, pos+2*c1.y))+tex2D(s0, float2(tex.x, pos+3*c1.y)))*.25;// blur input
#if posterizedegamma
float4 signbits = sign(s1);
s1 = signbits*pow(round(sqrt(s1)*quantize+dithers)*quantizer, 2);// dither and posterize
return s1*br;// modulate brightness and output
#else
s1 = round(s1*quantize+dithers);// dither and posterize
return s1*(br*quantizer);// shrink interval back to normal after posterization, modulate brightness and output
#endif
}
// vertical 4-pixel averaging, dithering, posterizing and old CRT scan lines for XYZ rendering on 16-bit integer surfaces
// this shader only works properly on inputs that have a vertical resolution that is evenly divisible by 4
#define gamma 1// higher is brighter, fractions, either decimal or not are allowed
#define scanlinebasedarken .5// the default of .5 will darken outer pixels a bit on each set of vertical pixels to appear like old CRT scan lines, higher values will narrow the scan line beam
#define posterizedegamma 1// 0 or 1, apply dirty de-gamma for posterization, useful to preserve realistic gradients in low gamma modes
#define quantizationbits 4// posterization level, note that 'quantize' can actually take any amount, not just those based on powers of two
sampler s0 : register(s0);
float2 c0 : register(c0);
float2 c1 : register(c1);
static const float quantize = pow(2, quantizationbits)-1;
static const float quantizer = 1./quantize;
static const float qm = .0078125*quantizer;
static const float smalldithermap[8][8] = {
{-63*qm, qm, -47*qm, 17*qm, -59*qm, 5*qm, -43*qm, 21*qm},
{33*qm, -31*qm, 49*qm, -15*qm, 37*qm, -27*qm, 53*qm, -11*qm},
{-39*qm, 25*qm, -55*qm, 9*qm, -35*qm, 29*qm, -51*qm, 13*qm},
{57*qm, -7*qm, 41*qm, -23*qm, 61*qm, -3*qm, 45*qm, -19*qm},
{-57*qm, 7*qm, -41*qm, 23*qm, -61*qm, 3*qm, -45*qm, 19*qm},
{39*qm, -25*qm, 55*qm, -9*qm, 35*qm, -29*qm, 51*qm, -13*qm},
{-33*qm, 31*qm, -49*qm, 15*qm, -37*qm, 27*qm, -53*qm, 11*qm},
{63*qm, -qm, 47*qm, -17*qm, 59*qm, -5*qm, 43*qm, -21*qm}};
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float3 basepos = tex.xyy*c0.xyy*float3(.03125, .03125, .25);
float3 basefrac = frac(basepos);
float2 lookups = basefrac.xy*8.;
float dithers = smalldithermap[lookups.x][lookups.y];
float br = 1.-pow(abs(basefrac.z*2.*scanlinebasedarken-scanlinebasedarken), gamma);// generate scan lines
float pos = (basepos.z-basefrac.z+.125)*c1.y*4.;// calculate the top positon of the current set of pixels
float4 s1 = (tex2D(s0, float2(tex.x, pos))+tex2D(s0, float2(tex.x, pos+c1.y))+tex2D(s0, float2(tex.x, pos+2*c1.y))+tex2D(s0, float2(tex.x, pos+3*c1.y)))*.25;// blur input
#if posterizedegamma
s1 = s1*65535/32767.-16384/32767.;
float4 signbits = sign(s1);
s1 = signbits*pow(round(sqrt(s1)*quantize+dithers)*quantizer, 2);// dither and posterize
return s1*(br*32767/65535.)+16384/65535.;// modulate brightness and output
#else
s1 = round((s1*65535/32767.-16384/32767.)*quantize+dithers);// dither and posterize
return s1*(br*quantizer*32767/65535.)+16384/65535.;// shrink interval back to normal after posterization, modulate brightness and output
#endif
}
// horizontal 5-pixel averaging
// this shader only works properly on inputs that have a horizontal resolution that is evenly divisible by 5
sampler s0 : register(s0);
float c0 : register(c0);
float c1 : register(c1);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float pos = (trunc(tex.x*c0*.2)+.1)*c1*5.;// calculate the left positon of the current set of pixels
return (tex2D(s0, float2(pos, tex.y))+tex2D(s0, float2(pos+c1, tex.y))+tex2D(s0, float2(pos+2*c1, tex.y))+tex2D(s0, float2(pos+3*c1, tex.y))+tex2D(s0, float2(pos+4*c1, tex.y)))*.2;// blur and output
}
// vertical 5-pixel averaging, dithering, posterizing and old CRT scan lines
// this shader only works properly on inputs that have a vertical resolution that is evenly divisible by 5
#define gamma 1// higher is brighter, fractions, either decimal or not are allowed
#define scanlinebasedarken .5// the default of .5 will darken outer pixels a bit on each set of vertical pixels to appear like old CRT scan lines, higher values will narrow the scan line beam
#define posterizedegamma 1// 0 or 1, apply dirty de-gamma for posterization, useful to preserve realistic gradients in low gamma modes
#define quantizationbits 4// posterization level, note that 'quantize' can actually take any amount, not just those based on powers of two
sampler s0 : register(s0);
float2 c0 : register(c0);
float2 c1 : register(c1);
static const float quantize = pow(2, quantizationbits)-1;
static const float quantizer = 1./quantize;
static const float qm = .0078125*quantizer;
static const float smalldithermap[8][8] = {
{-63*qm, qm, -47*qm, 17*qm, -59*qm, 5*qm, -43*qm, 21*qm},
{33*qm, -31*qm, 49*qm, -15*qm, 37*qm, -27*qm, 53*qm, -11*qm},
{-39*qm, 25*qm, -55*qm, 9*qm, -35*qm, 29*qm, -51*qm, 13*qm},
{57*qm, -7*qm, 41*qm, -23*qm, 61*qm, -3*qm, 45*qm, -19*qm},
{-57*qm, 7*qm, -41*qm, 23*qm, -61*qm, 3*qm, -45*qm, 19*qm},
{39*qm, -25*qm, 55*qm, -9*qm, 35*qm, -29*qm, 51*qm, -13*qm},
{-33*qm, 31*qm, -49*qm, 15*qm, -37*qm, 27*qm, -53*qm, 11*qm},
{63*qm, -qm, 47*qm, -17*qm, 59*qm, -5*qm, 43*qm, -21*qm}};
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float3 basepos = tex.xyy*c0.xyy*float3(.025, .025, .2);
float3 basefrac = frac(basepos);
float2 lookups = basefrac.xy*8.;
float dithers = smalldithermap[lookups.x][lookups.y];
float br = 1.-pow(abs(basefrac.z*2.*scanlinebasedarken-scanlinebasedarken), gamma);// generate scan lines
float pos = (basepos.z-basefrac.z+.1)*c1.y*5.;// calculate the top positon of the current set of pixels
float4 s1 = (tex2D(s0, float2(tex.x, pos))+tex2D(s0, float2(tex.x, pos+c1.y))+tex2D(s0, float2(tex.x, pos+2*c1.y))+tex2D(s0, float2(tex.x, pos+3*c1.y))+tex2D(s0, float2(tex.x, pos+4*c1.y)))*.2;// blur input
#if posterizedegamma
float4 signbits = sign(s1);
s1 = signbits*pow(round(sqrt(s1)*quantize+dithers)*quantizer, 2);// dither and posterize
return s1*br;// modulate brightness and output
#else
s1 = round(s1*quantize+dithers);// dither and posterize
return s1*(br*quantizer);// shrink interval back to normal after posterization, modulate brightness and output
#endif
}
// vertical 5-pixel averaging, dithering, posterizing and old CRT scan lines for XYZ rendering on 16-bit integer surfaces
// this shader only works properly on inputs that have a vertical resolution that is evenly divisible by 5
#define gamma 1// higher is brighter, fractions, either decimal or not are allowed
#define scanlinebasedarken .5// the default of .5 will darken outer pixels a bit on each set of vertical pixels to appear like old CRT scan lines, higher values will narrow the scan line beam
#define posterizedegamma 1// 0 or 1, apply dirty de-gamma for posterization, useful to preserve realistic gradients in low gamma modes
#define quantizationbits 4// posterization level, note that 'quantize' can actually take any amount, not just those based on powers of two
sampler s0 : register(s0);
float2 c0 : register(c0);
float2 c1 : register(c1);
static const float quantize = pow(2, quantizationbits)-1;
static const float quantizer = 1./quantize;
static const float qm = .0078125*quantizer;
static const float smalldithermap[8][8] = {
{-63*qm, qm, -47*qm, 17*qm, -59*qm, 5*qm, -43*qm, 21*qm},
{33*qm, -31*qm, 49*qm, -15*qm, 37*qm, -27*qm, 53*qm, -11*qm},
{-39*qm, 25*qm, -55*qm, 9*qm, -35*qm, 29*qm, -51*qm, 13*qm},
{57*qm, -7*qm, 41*qm, -23*qm, 61*qm, -3*qm, 45*qm, -19*qm},
{-57*qm, 7*qm, -41*qm, 23*qm, -61*qm, 3*qm, -45*qm, 19*qm},
{39*qm, -25*qm, 55*qm, -9*qm, 35*qm, -29*qm, 51*qm, -13*qm},
{-33*qm, 31*qm, -49*qm, 15*qm, -37*qm, 27*qm, -53*qm, 11*qm},
{63*qm, -qm, 47*qm, -17*qm, 59*qm, -5*qm, 43*qm, -21*qm}};
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float3 basepos = tex.xyy*c0.xyy*float3(.025, .025, .2);
float3 basefrac = frac(basepos);
float2 lookups = basefrac.xy*8.;
float dithers = smalldithermap[lookups.x][lookups.y];
float br = 1.-pow(abs(basefrac.z*2.*scanlinebasedarken-scanlinebasedarken), gamma);// generate scan lines
float pos = (basepos.z-basefrac.z+.1)*c1.y*5.;// calculate the top positon of the current set of pixels
float4 s1 = (tex2D(s0, float2(tex.x, pos))+tex2D(s0, float2(tex.x, pos+c1.y))+tex2D(s0, float2(tex.x, pos+2*c1.y))+tex2D(s0, float2(tex.x, pos+3*c1.y))+tex2D(s0, float2(tex.x, pos+4*c1.y)))*.2;// blur input
#if posterizedegamma
s1 = s1*65535/32767.-16384/32767.;
float4 signbits = sign(s1);
s1 = signbits*pow(round(sqrt(s1)*quantize+dithers)*quantizer, 2);// dither and posterize
return s1*(br*32767/65535.)+16384/65535.;// modulate brightness and output
#else
s1 = round((s1*65535/32767.-16384/32767.)*quantize+dithers);// dither and posterize
return s1*(br*quantizer*32767/65535.)+16384/65535.;// shrink interval back to normal after posterization, modulate brightness and output
#endif
}
__________________
development folder, containing MPC-HC experimental tester builds, pixel shaders and more: http://www.mediafire.com/?xwsoo403c53hv Last edited by JanWillem32; 29th July 2013 at 08:33. Reason: added extra shaders and functionality |
|
|
|
|
|
#366 | Link | |
|
Registered User
Join Date: Jul 2013
Posts: 10
|
Quote:
Maybe if you only try to draw the mask grille, not the other efects. (only a draw a mask in front the video) Any way I found the source code for the shader mask, but am to about codes.https://github.com/libretro/common-shaders/blob/master/crt/crt-geom-flat.cg Also found this variants of the shader http://emulation-general.wikia.com/wiki/CRT_Geom and the image should look like this Last edited by jerrymh; 28th July 2013 at 22:45. |
|
|
|
|
|
|
#367 | Link |
|
Registered User
Join Date: Oct 2010
Location: The Netherlands
Posts: 1,083
|
The host renderer for the shaders your link points to is organized very differently than the ones used for the shaders over here.
The first shaders I posted actually only blur and apply the scan line effect. The results are not stellar. The second version also properly degrades to low resolution and low quantization. It won't come close to pixel art like in that picture, but it will do a reasonable job on most typical video sources. The default quantization in the shader is rather high compared to that picture. If it's a 256-color mode, try quantizationbits at 8/3., posterizedegamma 0 and probably a different gamma for the scan lines effect for the renderer in 8-bit mode or 17/6. and posterizedegamma 1 in quality mode. (Quality mode wastes a few percent at the top of the usual [0, 1] interval for two of the three channels.) Note that I edited my previous post to fix a few bugs in the code with dithering and negative inputs.
__________________
development folder, containing MPC-HC experimental tester builds, pixel shaders and more: http://www.mediafire.com/?xwsoo403c53hv |
|
|
|
|
|
#368 | Link |
|
Registered User
Join Date: May 2008
Posts: 1,840
|
XRyche: can you post a short clip?
__________________
PC: FX-8320 GTS250 HTPC: G1610 GTX650 PotPlayer/MPC-BE LAVFilters MadVR-Bicubic75AR/Lanczos4AR/Lanczos4AR LumaSharpen -Strength0.9-Pattern3-Clamp0.1-OffsetBias2.0 |
|
|
|
|
|
#369 | Link |
|
Registered User
Join Date: Oct 2010
Location: The Netherlands
Posts: 1,083
|
Here are some extra shaders for larger pixels. I also edited the previous post because the forum has a maximum text length limit.
Code:
// (C) 2013 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// 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, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// horizontal 8-pixel averaging
// this shader only works properly on inputs that have a horizontal resolution that is evenly divisible by 8
sampler s0 : register(s0);
float c0 : register(c0);
float c1 : register(c1);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float pos = (trunc(tex.x*c0*.125)+.0625)*c1*8.;// calculate the left positon of the current set of pixels
return (tex2D(s0, float2(pos, tex.y))+tex2D(s0, float2(pos+c1, tex.y))+tex2D(s0, float2(pos+2*c1, tex.y))+tex2D(s0, float2(pos+3*c1, tex.y))+tex2D(s0, float2(pos+4*c1, tex.y))+tex2D(s0, float2(pos+5*c1, tex.y))+tex2D(s0, float2(pos+6*c1, tex.y))+tex2D(s0, float2(pos+7*c1, tex.y)))*.125;// blur and output
}
// vertical 8-pixel averaging, dithering, posterizing and old CRT scan lines
// this shader only works properly on inputs that have a vertical resolution that is evenly divisible by 8
#define gamma 1// higher is brighter, fractions, either decimal or not are allowed
#define scanlinebasedarken .5// the default of .5 will darken outer pixels a bit on each set of vertical pixels to appear like old CRT scan lines, higher values will narrow the scan line beam
#define posterizedegamma 1// 0 or 1, apply dirty de-gamma for posterization, useful to preserve realistic gradients in low gamma modes
#define quantizationbits 4// posterization level, note that 'quantize' can actually take any amount, not just those based on powers of two
sampler s0 : register(s0);
float2 c0 : register(c0);
float2 c1 : register(c1);
static const float quantize = pow(2, quantizationbits)-1;
static const float quantizer = 1./quantize;
static const float qm = .0078125*quantizer;
static const float smalldithermap[8][8] = {
{-63*qm, qm, -47*qm, 17*qm, -59*qm, 5*qm, -43*qm, 21*qm},
{33*qm, -31*qm, 49*qm, -15*qm, 37*qm, -27*qm, 53*qm, -11*qm},
{-39*qm, 25*qm, -55*qm, 9*qm, -35*qm, 29*qm, -51*qm, 13*qm},
{57*qm, -7*qm, 41*qm, -23*qm, 61*qm, -3*qm, 45*qm, -19*qm},
{-57*qm, 7*qm, -41*qm, 23*qm, -61*qm, 3*qm, -45*qm, 19*qm},
{39*qm, -25*qm, 55*qm, -9*qm, 35*qm, -29*qm, 51*qm, -13*qm},
{-33*qm, 31*qm, -49*qm, 15*qm, -37*qm, 27*qm, -53*qm, 11*qm},
{63*qm, -qm, 47*qm, -17*qm, 59*qm, -5*qm, 43*qm, -21*qm}};
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float3 basepos = tex.xyy*c0.xyy*float3(.015625, .015625, .125);
float3 basefrac = frac(basepos);
float2 lookups = basefrac.xy*8.;
float dithers = smalldithermap[lookups.x][lookups.y];
float br = 1.-pow(abs(basefrac.z*2.*scanlinebasedarken-scanlinebasedarken), gamma);// generate scan lines
float pos = (basepos.z-basefrac.z+.0625)*c1.y*8.;// calculate the top positon of the current set of pixels
float4 s1 = (tex2D(s0, float2(tex.x, pos))+tex2D(s0, float2(tex.x, pos+c1.y))+tex2D(s0, float2(tex.x, pos+2*c1.y))+tex2D(s0, float2(tex.x, pos+3*c1.y))+tex2D(s0, float2(tex.x, pos+4*c1.y))+tex2D(s0, float2(tex.x, pos+5*c1.y))+tex2D(s0, float2(tex.x, pos+6*c1.y))+tex2D(s0, float2(tex.x, pos+7*c1.y)))*.125;// blur input
#if posterizedegamma
float4 signbits = sign(s1);
s1 = signbits*pow(round(sqrt(s1)*quantize+dithers)*quantizer, 2);// dither and posterize
return s1*br;// modulate brightness and output
#else
s1 = round(s1*quantize+dithers);// dither and posterize
return s1*(br*quantizer);// shrink interval back to normal after posterization, modulate brightness and output
#endif
}
// vertical 8-pixel averaging, dithering, posterizing and old CRT scan lines for XYZ rendering on 16-bit integer surfaces
// this shader only works properly on inputs that have a vertical resolution that is evenly divisible by 8
#define gamma 1// higher is brighter, fractions, either decimal or not are allowed
#define scanlinebasedarken .5// the default of .5 will darken outer pixels a bit on each set of vertical pixels to appear like old CRT scan lines, higher values will narrow the scan line beam
#define posterizedegamma 1// 0 or 1, apply dirty de-gamma for posterization, useful to preserve realistic gradients in low gamma modes
#define quantizationbits 4// posterization level, note that 'quantize' can actually take any amount, not just those based on powers of two
sampler s0 : register(s0);
float2 c0 : register(c0);
float2 c1 : register(c1);
static const float quantize = pow(2, quantizationbits)-1;
static const float quantizer = 1./quantize;
static const float qm = .0078125*quantizer;
static const float smalldithermap[8][8] = {
{-63*qm, qm, -47*qm, 17*qm, -59*qm, 5*qm, -43*qm, 21*qm},
{33*qm, -31*qm, 49*qm, -15*qm, 37*qm, -27*qm, 53*qm, -11*qm},
{-39*qm, 25*qm, -55*qm, 9*qm, -35*qm, 29*qm, -51*qm, 13*qm},
{57*qm, -7*qm, 41*qm, -23*qm, 61*qm, -3*qm, 45*qm, -19*qm},
{-57*qm, 7*qm, -41*qm, 23*qm, -61*qm, 3*qm, -45*qm, 19*qm},
{39*qm, -25*qm, 55*qm, -9*qm, 35*qm, -29*qm, 51*qm, -13*qm},
{-33*qm, 31*qm, -49*qm, 15*qm, -37*qm, 27*qm, -53*qm, 11*qm},
{63*qm, -qm, 47*qm, -17*qm, 59*qm, -5*qm, 43*qm, -21*qm}};
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float3 basepos = tex.xyy*c0.xyy*float3(.015625, .015625, .125);
float3 basefrac = frac(basepos);
float2 lookups = basefrac.xy*8.;
float dithers = smalldithermap[lookups.x][lookups.y];
float br = 1.-pow(abs(basefrac.z*2.*scanlinebasedarken-scanlinebasedarken), gamma);// generate scan lines
float pos = (basepos.z-basefrac.z+.0625)*c1.y*8.;// calculate the top positon of the current set of pixels
float4 s1 = (tex2D(s0, float2(tex.x, pos))+tex2D(s0, float2(tex.x, pos+c1.y))+tex2D(s0, float2(tex.x, pos+2*c1.y))+tex2D(s0, float2(tex.x, pos+3*c1.y))+tex2D(s0, float2(tex.x, pos+4*c1.y))+tex2D(s0, float2(tex.x, pos+5*c1.y))+tex2D(s0, float2(tex.x, pos+6*c1.y))+tex2D(s0, float2(tex.x, pos+7*c1.y)))*.125;// blur input
#if posterizedegamma
s1 = s1*65535/32767.-16384/32767.;
float4 signbits = sign(s1);
s1 = signbits*pow(round(sqrt(s1)*quantize+dithers)*quantizer, 2);// dither and posterize
return s1*(br*32767/65535.)+16384/65535.;// modulate brightness and output
#else
s1 = round((s1*65535/32767.-16384/32767.)*quantize+dithers);// dither and posterize
return s1*(br*quantizer*32767/65535.)+16384/65535.;// shrink interval back to normal after posterization, modulate brightness and output
#endif
}
// horizontal 10-pixel averaging
// this shader only works properly on inputs that have a horizontal resolution that is evenly divisible by 10
sampler s0 : register(s0);
float c0 : register(c0);
float c1 : register(c1);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float pos = (trunc(tex.x*c0*.1)+.05)*c1*10.;// calculate the left positon of the current set of pixels
return (tex2D(s0, float2(pos, tex.y))+tex2D(s0, float2(pos+c1, tex.y))+tex2D(s0, float2(pos+2*c1, tex.y))+tex2D(s0, float2(pos+3*c1, tex.y))+tex2D(s0, float2(pos+4*c1, tex.y))+tex2D(s0, float2(pos+5*c1, tex.y))+tex2D(s0, float2(pos+6*c1, tex.y))+tex2D(s0, float2(pos+7*c1, tex.y))+tex2D(s0, float2(pos+8*c1, tex.y))+tex2D(s0, float2(pos+9*c1, tex.y)))*.1;// blur and output
}
// vertical 10-pixel averaging, dithering, posterizing and old CRT scan lines
// this shader only works properly on inputs that have a vertical resolution that is evenly divisible by 10
#define gamma 1// higher is brighter, fractions, either decimal or not are allowed
#define scanlinebasedarken .5// the default of .5 will darken outer pixels a bit on each set of vertical pixels to appear like old CRT scan lines, higher values will narrow the scan line beam
#define posterizedegamma 1// 0 or 1, apply dirty de-gamma for posterization, useful to preserve realistic gradients in low gamma modes
#define quantizationbits 4// posterization level, note that 'quantize' can actually take any amount, not just those based on powers of two
sampler s0 : register(s0);
float2 c0 : register(c0);
float2 c1 : register(c1);
static const float quantize = pow(2, quantizationbits)-1;
static const float quantizer = 1./quantize;
static const float qm = .0078125*quantizer;
static const float smalldithermap[8][8] = {
{-63*qm, qm, -47*qm, 17*qm, -59*qm, 5*qm, -43*qm, 21*qm},
{33*qm, -31*qm, 49*qm, -15*qm, 37*qm, -27*qm, 53*qm, -11*qm},
{-39*qm, 25*qm, -55*qm, 9*qm, -35*qm, 29*qm, -51*qm, 13*qm},
{57*qm, -7*qm, 41*qm, -23*qm, 61*qm, -3*qm, 45*qm, -19*qm},
{-57*qm, 7*qm, -41*qm, 23*qm, -61*qm, 3*qm, -45*qm, 19*qm},
{39*qm, -25*qm, 55*qm, -9*qm, 35*qm, -29*qm, 51*qm, -13*qm},
{-33*qm, 31*qm, -49*qm, 15*qm, -37*qm, 27*qm, -53*qm, 11*qm},
{63*qm, -qm, 47*qm, -17*qm, 59*qm, -5*qm, 43*qm, -21*qm}};
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float3 basepos = tex.xyy*c0.xyy*float3(.0125, .0125, .1);
float3 basefrac = frac(basepos);
float2 lookups = basefrac.xy*8.;
float dithers = smalldithermap[lookups.x][lookups.y];
float br = 1.-pow(abs(basefrac.z*2.*scanlinebasedarken-scanlinebasedarken), gamma);// generate scan lines
float pos = (basepos.z-basefrac.z+.05)*c1.y*10.;// calculate the top positon of the current set of pixels
float4 s1 = (tex2D(s0, float2(tex.x, pos))+tex2D(s0, float2(tex.x, pos+c1.y))+tex2D(s0, float2(tex.x, pos+2*c1.y))+tex2D(s0, float2(tex.x, pos+3*c1.y))+tex2D(s0, float2(tex.x, pos+4*c1.y))+tex2D(s0, float2(tex.x, pos+5*c1.y))+tex2D(s0, float2(tex.x, pos+6*c1.y))+tex2D(s0, float2(tex.x, pos+7*c1.y))+tex2D(s0, float2(tex.x, pos+8*c1.y))+tex2D(s0, float2(tex.x, pos+9*c1.y)))*.1;// blur input
#if posterizedegamma
float4 signbits = sign(s1);
s1 = signbits*pow(round(sqrt(s1)*quantize+dithers)*quantizer, 2);// dither and posterize
return s1*br;// modulate brightness and output
#else
s1 = round(s1*quantize+dithers);// dither and posterize
return s1*(br*quantizer);// shrink interval back to normal after posterization, modulate brightness and output
#endif
}
// vertical 10-pixel averaging, dithering, posterizing and old CRT scan lines for XYZ rendering on 16-bit integer surfaces
// this shader only works properly on inputs that have a vertical resolution that is evenly divisible by 10
#define gamma 1// higher is brighter, fractions, either decimal or not are allowed
#define scanlinebasedarken .5// the default of .5 will darken outer pixels a bit on each set of vertical pixels to appear like old CRT scan lines, higher values will narrow the scan line beam
#define posterizedegamma 1// 0 or 1, apply dirty de-gamma for posterization, useful to preserve realistic gradients in low gamma modes
#define quantizationbits 4// posterization level, note that 'quantize' can actually take any amount, not just those based on powers of two
sampler s0 : register(s0);
float2 c0 : register(c0);
float2 c1 : register(c1);
static const float quantize = pow(2, quantizationbits)-1;
static const float quantizer = 1./quantize;
static const float qm = .0078125*quantizer;
static const float smalldithermap[8][8] = {
{-63*qm, qm, -47*qm, 17*qm, -59*qm, 5*qm, -43*qm, 21*qm},
{33*qm, -31*qm, 49*qm, -15*qm, 37*qm, -27*qm, 53*qm, -11*qm},
{-39*qm, 25*qm, -55*qm, 9*qm, -35*qm, 29*qm, -51*qm, 13*qm},
{57*qm, -7*qm, 41*qm, -23*qm, 61*qm, -3*qm, 45*qm, -19*qm},
{-57*qm, 7*qm, -41*qm, 23*qm, -61*qm, 3*qm, -45*qm, 19*qm},
{39*qm, -25*qm, 55*qm, -9*qm, 35*qm, -29*qm, 51*qm, -13*qm},
{-33*qm, 31*qm, -49*qm, 15*qm, -37*qm, 27*qm, -53*qm, 11*qm},
{63*qm, -qm, 47*qm, -17*qm, 59*qm, -5*qm, 43*qm, -21*qm}};
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float3 basepos = tex.xyy*c0.xyy*float3(.0125, .0125, .1);
float3 basefrac = frac(basepos);
float2 lookups = basefrac.xy*8.;
float dithers = smalldithermap[lookups.x][lookups.y];
float br = 1.-pow(abs(basefrac.z*2.*scanlinebasedarken-scanlinebasedarken), gamma);// generate scan lines
float pos = (basepos.z-basefrac.z+.05)*c1.y*10.;// calculate the top positon of the current set of pixels
float4 s1 = (tex2D(s0, float2(tex.x, pos))+tex2D(s0, float2(tex.x, pos+c1.y))+tex2D(s0, float2(tex.x, pos+2*c1.y))+tex2D(s0, float2(tex.x, pos+3*c1.y))+tex2D(s0, float2(tex.x, pos+4*c1.y))+tex2D(s0, float2(tex.x, pos+5*c1.y))+tex2D(s0, float2(tex.x, pos+6*c1.y))+tex2D(s0, float2(tex.x, pos+7*c1.y))+tex2D(s0, float2(tex.x, pos+8*c1.y))+tex2D(s0, float2(tex.x, pos+9*c1.y)))*.1;// blur input
#if posterizedegamma
s1 = s1*65535/32767.-16384/32767.;
float4 signbits = sign(s1);
s1 = signbits*pow(round(sqrt(s1)*quantize+dithers)*quantizer, 2);// dither and posterize
return s1*(br*32767/65535.)+16384/65535.;// modulate brightness and output
#else
s1 = round((s1*65535/32767.-16384/32767.)*quantize+dithers);// dither and posterize
return s1*(br*quantizer*32767/65535.)+16384/65535.;// shrink interval back to normal after posterization, modulate brightness and output
#endif
}
__________________
development folder, containing MPC-HC experimental tester builds, pixel shaders and more: http://www.mediafire.com/?xwsoo403c53hv |
|
|
|
|
|
#370 | Link |
|
Registered User
Join Date: Feb 2012
Posts: 48
|
The shaders jerrymh posted are meant to be used with emulators(RetroArch/Libretro in this case) and they are tailored for a specific resolution as far as I know. RetroArch supports up to 8 passes and you can build https://github.com/libretro/libretro-ffmpeg if you wish to test them in video playback scenario.
|
|
|
|
|
|
#371 | Link | |
|
Registered User
Join Date: Jul 2013
Posts: 10
|
Quote:
|
|
|
|
|
|
|
#373 | Link |
|
Registered User
Join Date: Oct 2010
Location: The Netherlands
Posts: 1,083
|
XRyche, I made a three-stage chain that might work. It's currently rather restricted and I'll probably need to change a few more parameters, but it's a good start. I only made one chain, meant for the combination of HD video with the renderer settings on 16-bit integer surfaces with the disable initial pass shaders option enabled. I can add more shaders later, if these work well.
Code:
// (C) 2013 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// 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, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// R'G'B' to Y'CbCr for HD video input for XYZ rendering on 16-bit integer surfaces
sampler s0;
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float3 s1 = tex2Dlod(s0, float4(tex, 0, 0)).rgb;// original pixel
return ((s1.rrr*float3(.2126, -.1063/.9278, .5)+s1.ggg*float3(.7152, -.3576/.9278, -.3576/.7874)+s1.bbb*float3(.0722, .5, -.0361/.7874))*32767/65535.+float3(16384/65535., 32767/65535., 32767/65535.)).rgbb;// HD RGB to Y'CbCr output
}
// horizontal pass sharpen complex, deband and denoise for HD video input for XYZ rendering on 16-bit integer surfaces
#define SharpenLimitLuma 2// valid interval [0, 10], luma-specific sharpening limit, 0 is disabled, lower numbers will allow more sharpening on contours
#define SharpenLimitChroma 2// valid interval [0, 10], chroma-specific sharpening limit, 0 is disabled, lower numbers will allow more sharpening on contours
#define LumaDetectionFactor 64// valid interval (65535/32767., 250], luma-specific detection factor, if set to the lowest amount no contours can be detected, higher numbers will shift the detection on color difference intervals of debanding to noise detection limit to mimimum sharpening to maximum sharpening toward more sharpening
#define ChromaDetectionFactor 64// valid interval (65535/32767., 250], chroma-specific detection factor, if set to the lowest amount no contours can be detected, higher numbers will shift the detection on color difference intervals of debanding to noise detection limit to mimimum sharpening to maximum sharpening toward more sharpening
#define NoiseThreshold .0078125// valid interval [0, 32767/65535.), banding treshold, higher numbers mean stronger deband and denoise
sampler s0 : register(s0);
float2 c1 : register(c1);
#define sp(a) tex2Dlod(s0, float4(tex+c1*float2(a, 0), 0, 0)).rgb
static const float3 slimits = float3(-SharpenLimitLuma, -SharpenLimitChroma, -SharpenLimitChroma);
static const float3 dfactors = float3(LumaDetectionFactor, ChromaDetectionFactor, ChromaDetectionFactor);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float3 n, p, s1 = sp(0);// original pixel
{
float3 s2 = sp(-1);
float3 af = 1.;// accumulated amount of colors from the samples
float3 ac = s1;// accumulate color
float3 cd = abs(s1-s2);// color difference
float3 rcd = max(slimits, 1.-dfactors*cd);// factor for both base and multiplicand is 1.0, the output will be in the interval (-inf, 1]
// invert interval on sharpening
if(rcd.x < 0) rcd.x = SharpenLimitLuma-abs(rcd.x);
if(rcd.y < 0) rcd.y = SharpenLimitChroma-abs(rcd.y);
if(rcd.z < 0) rcd.z = SharpenLimitChroma-abs(rcd.z);
af += abs(rcd);
ac += s2*rcd;
[branch] if(max(max(cd.x, cd.y), cd.z) < NoiseThreshold) {// continue if all channels are below the noise threshold
float3 s3 = sp(-2);
cd = abs(s1-s3);
rcd = max(slimits, 1.-dfactors*cd);
if(rcd.x < 0) rcd.x = SharpenLimitLuma-abs(rcd.x);
if(rcd.y < 0) rcd.y = SharpenLimitChroma-abs(rcd.y);
if(rcd.z < 0) rcd.z = SharpenLimitChroma-abs(rcd.z);
af += abs(rcd);
ac += s3*rcd;
[branch] if(max(max(cd.x, cd.y), cd.z) < NoiseThreshold) {
float3 s4 = sp(-3);
cd = abs(s1-s4);
rcd = max(slimits, 1.-dfactors*cd);
if(rcd.x < 0) rcd.x = SharpenLimitLuma-abs(rcd.x);
if(rcd.y < 0) rcd.y = SharpenLimitChroma-abs(rcd.y);
if(rcd.z < 0) rcd.z = SharpenLimitChroma-abs(rcd.z);
af += abs(rcd);
ac += s4*rcd;
[branch] if(max(max(cd.x, cd.y), cd.z) < NoiseThreshold) {
float3 s5 = sp(-4);
cd = abs(s1-s5);
rcd = max(slimits, 1.-dfactors*cd);
if(rcd.x < 0) rcd.x = SharpenLimitLuma-abs(rcd.x);
if(rcd.y < 0) rcd.y = SharpenLimitChroma-abs(rcd.y);
if(rcd.z < 0) rcd.z = SharpenLimitChroma-abs(rcd.z);
af += abs(rcd);
ac += s5*rcd;
[branch] if(max(max(cd.x, cd.y), cd.z) < NoiseThreshold) {
float3 s6 = sp(-5);
cd = abs(s1-s6);
rcd = max(slimits, 1.-dfactors*cd);
if(rcd.x < 0) rcd.x = SharpenLimitLuma-abs(rcd.x);
if(rcd.y < 0) rcd.y = SharpenLimitChroma-abs(rcd.y);
if(rcd.z < 0) rcd.z = SharpenLimitChroma-abs(rcd.z);
af += abs(rcd);
ac += s6*rcd;
}
}
}
}
n = ac/af;
}
{
float3 s2 = sp(1);
float3 af = 1.;// accumulated amount of colors from the samples
float3 ac = s1;// accumulate color
float3 cd = abs(s1-s2);// color difference
float3 rcd = max(slimits, 1.-dfactors*cd);// factor for both base and multiplicand is 1.0, the output will be in the interval (-inf, 1]
if(rcd.x < 0) rcd.x = SharpenLimitLuma-abs(rcd.x);
if(rcd.y < 0) rcd.y = SharpenLimitChroma-abs(rcd.y);
if(rcd.z < 0) rcd.z = SharpenLimitChroma-abs(rcd.z);
af += abs(rcd);
ac += s2*rcd;
[branch] if(max(max(cd.x, cd.y), cd.z) < NoiseThreshold) {// continue if all channels are below the noise threshold
float3 s3 = sp(2);
cd = abs(s1-s3);
rcd = max(slimits, 1.-dfactors*cd);
if(rcd.x < 0) rcd.x = SharpenLimitLuma-abs(rcd.x);
if(rcd.y < 0) rcd.y = SharpenLimitChroma-abs(rcd.y);
if(rcd.z < 0) rcd.z = SharpenLimitChroma-abs(rcd.z);
af += abs(rcd);
ac += s3*rcd;
[branch] if(max(max(cd.x, cd.y), cd.z) < NoiseThreshold) {
float3 s4 = sp(3);
cd = abs(s1-s4);
rcd = max(slimits, 1.-dfactors*cd);
if(rcd.x < 0) rcd.x = SharpenLimitLuma-abs(rcd.x);
if(rcd.y < 0) rcd.y = SharpenLimitChroma-abs(rcd.y);
if(rcd.z < 0) rcd.z = SharpenLimitChroma-abs(rcd.z);
af += abs(rcd);
ac += s4*rcd;
[branch] if(max(max(cd.x, cd.y), cd.z) < NoiseThreshold) {
float3 s5 = sp(4);
cd = abs(s1-s5);
rcd = max(slimits, 1.-dfactors*cd);
if(rcd.x < 0) rcd.x = SharpenLimitLuma-abs(rcd.x);
if(rcd.y < 0) rcd.y = SharpenLimitChroma-abs(rcd.y);
if(rcd.z < 0) rcd.z = SharpenLimitChroma-abs(rcd.z);
af += abs(rcd);
ac += s5*rcd;
[branch] if(max(max(cd.x, cd.y), cd.z) < NoiseThreshold) {
float3 s6 = sp(5);
cd = abs(s1-s6);
rcd = max(slimits, 1.-dfactors*cd);
if(rcd.x < 0) rcd.x = SharpenLimitLuma-abs(rcd.x);
if(rcd.y < 0) rcd.y = SharpenLimitChroma-abs(rcd.y);
if(rcd.z < 0) rcd.z = SharpenLimitChroma-abs(rcd.z);
af += abs(rcd);
ac += s6*rcd;
}
}
}
}
p = ac/af;
}
return ((n+p)*.5).rgbb;
}
// vertical pass sharpen complex, deband, denoise and color controls for HD video input for XYZ rendering on 16-bit integer surfaces
#define SharpenLimitLuma 2// valid interval [0, 10], luma-specific sharpening limit, 0 is disabled, lower numbers will allow more sharpening on contours
#define SharpenLimitChroma 2// valid interval [0, 10], chroma-specific sharpening limit, 0 is disabled, lower numbers will allow more sharpening on contours
#define LumaDetectionFactor 64// valid interval (65535/32767., 250], luma-specific detection factor, if set to the lowest amount no contours can be detected, higher numbers will shift the detection on color difference intervals of debanding to noise detection limit to mimimum sharpening to maximum sharpening toward more sharpening
#define ChromaDetectionFactor 64// valid interval (65535/32767., 250], chroma-specific detection factor, if set to the lowest amount no contours can be detected, higher numbers will shift the detection on color difference intervals of debanding to noise detection limit to mimimum sharpening to maximum sharpening toward more sharpening
#define NoiseThreshold .0078125// valid interval [0, 32767/65535.), banding treshold, higher numbers mean stronger deband and denoise
// YCbCrColorControls, 0 is disabled, 1 is enabled
#define YCbCrColorControls 0
// Brightness, interval [-10, 10], default 0
#define Brightness 0
// Contrast, interval [0, 10], default 1
#define Contrast 1
// GrayscaleGamma and ColorfulnessGamma, interval (0, 10], default 1
#define GrayscaleGamma 1
#define ColorfulnessGamma 1
// Hue, interval [-180, 180], default 0
#define Hue 0
// Saturation, interval [0, 10], default 1
#define Saturation 1
// VideoRedGamma, VideoGreenGamma and VideoBlueGamma, interval (0, 10], default 2.4, the video gamma input factors used to convert between the video input RGB and linear RGB
#define VideoRedGamma 2.4
#define VideoGreenGamma 2.4
#define VideoBlueGamma 2.4
sampler s0 : register(s0);
float2 c1 : register(c1);
#define sp(a) tex2Dlod(s0, float4(tex+c1*float2(0, a), 0, 0)).rgb
static const float3 slimits = float3(-SharpenLimitLuma, -SharpenLimitChroma, -SharpenLimitChroma);
static const float3 dfactors = float3(LumaDetectionFactor, ChromaDetectionFactor, ChromaDetectionFactor);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float3 n, p, s1 = sp(0);// original pixel
{
float3 s2 = sp(-1);
float3 af = 1.;// accumulated amount of colors from the samples
float3 ac = s1;// accumulate color
float3 cd = abs(s1-s2);// color difference
float3 rcd = max(slimits, 1.-dfactors*cd);// factor for both base and multiplicand is 1.0, the output will be in the interval (-inf, 1]
// invert interval on sharpening
if(rcd.x < 0) rcd.x = SharpenLimitLuma-abs(rcd.x);
if(rcd.y < 0) rcd.y = SharpenLimitChroma-abs(rcd.y);
if(rcd.z < 0) rcd.z = SharpenLimitChroma-abs(rcd.z);
af += abs(rcd);
ac += s2*rcd;
[branch] if(max(max(cd.x, cd.y), cd.z) < NoiseThreshold) {// continue if all channels are below the noise threshold
float3 s3 = sp(-2);
cd = abs(s1-s3);
rcd = max(slimits, 1.-dfactors*cd);
if(rcd.x < 0) rcd.x = SharpenLimitLuma-abs(rcd.x);
if(rcd.y < 0) rcd.y = SharpenLimitChroma-abs(rcd.y);
if(rcd.z < 0) rcd.z = SharpenLimitChroma-abs(rcd.z);
af += abs(rcd);
ac += s3*rcd;
[branch] if(max(max(cd.x, cd.y), cd.z) < NoiseThreshold) {
float3 s4 = sp(-3);
cd = abs(s1-s4);
rcd = max(slimits, 1.-dfactors*cd);
if(rcd.x < 0) rcd.x = SharpenLimitLuma-abs(rcd.x);
if(rcd.y < 0) rcd.y = SharpenLimitChroma-abs(rcd.y);
if(rcd.z < 0) rcd.z = SharpenLimitChroma-abs(rcd.z);
af += abs(rcd);
ac += s4*rcd;
[branch] if(max(max(cd.x, cd.y), cd.z) < NoiseThreshold) {
float3 s5 = sp(-4);
cd = abs(s1-s5);
rcd = max(slimits, 1.-dfactors*cd);
if(rcd.x < 0) rcd.x = SharpenLimitLuma-abs(rcd.x);
if(rcd.y < 0) rcd.y = SharpenLimitChroma-abs(rcd.y);
if(rcd.z < 0) rcd.z = SharpenLimitChroma-abs(rcd.z);
af += abs(rcd);
ac += s5*rcd;
[branch] if(max(max(cd.x, cd.y), cd.z) < NoiseThreshold) {
float3 s6 = sp(-5);
cd = abs(s1-s6);
rcd = max(slimits, 1.-dfactors*cd);
if(rcd.x < 0) rcd.x = SharpenLimitLuma-abs(rcd.x);
if(rcd.y < 0) rcd.y = SharpenLimitChroma-abs(rcd.y);
if(rcd.z < 0) rcd.z = SharpenLimitChroma-abs(rcd.z);
af += abs(rcd);
ac += s6*rcd;
}
}
}
}
n = ac/af;
}
{
float3 s2 = sp(1);
float3 af = 1.;// accumulated amount of colors from the samples
float3 ac = s1;// accumulate color
float3 cd = abs(s1-s2);// color difference
float3 rcd = max(slimits, 1.-dfactors*cd);// factor for both base and multiplicand is 1.0, the output will be in the interval (-inf, 1]
if(rcd.x < 0) rcd.x = SharpenLimitLuma-abs(rcd.x);
if(rcd.y < 0) rcd.y = SharpenLimitChroma-abs(rcd.y);
if(rcd.z < 0) rcd.z = SharpenLimitChroma-abs(rcd.z);
af += abs(rcd);
ac += s2*rcd;
[branch] if(max(max(cd.x, cd.y), cd.z) < NoiseThreshold) {// continue if all channels are below the noise threshold
float3 s3 = sp(2);
cd = abs(s1-s3);
rcd = max(slimits, 1.-dfactors*cd);
if(rcd.x < 0) rcd.x = SharpenLimitLuma-abs(rcd.x);
if(rcd.y < 0) rcd.y = SharpenLimitChroma-abs(rcd.y);
if(rcd.z < 0) rcd.z = SharpenLimitChroma-abs(rcd.z);
af += abs(rcd);
ac += s3*rcd;
[branch] if(max(max(cd.x, cd.y), cd.z) < NoiseThreshold) {
float3 s4 = sp(3);
cd = abs(s1-s4);
rcd = max(slimits, 1.-dfactors*cd);
if(rcd.x < 0) rcd.x = SharpenLimitLuma-abs(rcd.x);
if(rcd.y < 0) rcd.y = SharpenLimitChroma-abs(rcd.y);
if(rcd.z < 0) rcd.z = SharpenLimitChroma-abs(rcd.z);
af += abs(rcd);
ac += s4*rcd;
[branch] if(max(max(cd.x, cd.y), cd.z) < NoiseThreshold) {
float3 s5 = sp(4);
cd = abs(s1-s5);
rcd = max(slimits, 1.-dfactors*cd);
if(rcd.x < 0) rcd.x = SharpenLimitLuma-abs(rcd.x);
if(rcd.y < 0) rcd.y = SharpenLimitChroma-abs(rcd.y);
if(rcd.z < 0) rcd.z = SharpenLimitChroma-abs(rcd.z);
af += abs(rcd);
ac += s5*rcd;
[branch] if(max(max(cd.x, cd.y), cd.z) < NoiseThreshold) {
float3 s6 = sp(5);
cd = abs(s1-s6);
rcd = max(slimits, 1.-dfactors*cd);
if(rcd.x < 0) rcd.x = SharpenLimitLuma-abs(rcd.x);
if(rcd.y < 0) rcd.y = SharpenLimitChroma-abs(rcd.y);
if(rcd.z < 0) rcd.z = SharpenLimitChroma-abs(rcd.z);
af += abs(rcd);
ac += s6*rcd;
}
}
}
}
p = ac/af;
}
float3 t0 = (n+p)*.5;
t0 = t0*65535/32767.-float3(16384/32767., 32767/65535.+.5, 32767/65535.+.5);
#if YCbCrColorControls == 1
t0.yz = mul(t0.yz, float2x2(cos(radians(Hue)), sin(radians(Hue)), -sin(radians(Hue)), cos(radians(Hue))));// process hue
t0.xyz *= float3(Contrast, 2*Saturation, 2*Saturation);// process contrast and saturation, extend the chroma interval from [-.5, .5] to [-1, 1] for gamma processing
t0.x += Brightness;// process brightness
// preserve the sign bits of Y'CbCr values
float3 sby = sign(t0);
t0 = sby*pow(abs(t0), float3(GrayscaleGamma, ColorfulnessGamma, ColorfulnessGamma));// gamma processing
t0 = t0.rrr+float3(0, -.5*.1674679/.894, .5*1.8556)*t0.ggg+float3(.5*1.5748, -.5*.4185031/.894, 0)*t0.bbb;// HD Y'CbCr to RGB, compensate for the chroma ranges
#else
t0 = t0.rrr+float3(0, -.1674679/.894, 1.8556)*t0.ggg+float3(1.5748, -.4185031/.894, 0)*t0.bbb;// HD Y'CbCr to RGB
#endif
// preserve the sign bits of RGB values
float3 sbl = sign(t0);
t0 = sbl*pow(abs(t0), float3(VideoRedGamma, VideoGreenGamma, VideoBlueGamma));// linear RGB gamma correction
t0 = mul(t0, float3x3(0.3786675215, 0.1952504408, 0.0177500401, 0.3283428626, 0.6566857251, 0.1094476209, 0.1657219631, 0.0662887852, 0.8728023391))*32767/65535.+16384/65535.;
return t0.rgbb;// XYZ output
}
__________________
development folder, containing MPC-HC experimental tester builds, pixel shaders and more: http://www.mediafire.com/?xwsoo403c53hv |
|
|
|
|
|
#374 | Link |
|
Registered User
Join Date: Oct 2010
Location: The Netherlands
Posts: 1,083
|
The "contour color expose banding" shader is useful for denoise and deband testing purposes.
Code:
// (C) 2013 Jan-Willem Krans (janwillem32 <at> hotmail.com)
// This file is part of Video pixel shader pack.
// 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, version 2.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// contour color expose banding for XYZ rendering on 16-bit integer surfaces
// This shader can be run as a screen space pixel shader.
// This shader requires compiling with ps_2_0, but higher is better, see http://en.wikipedia.org/wiki/Pixel_shader to look up what PS version your video card supports.
// Use this shader to add a color contoured effect to an image.
sampler s0;
float2 c1 : register(c1);
#define sp(a, b, c) float4 a = tex2D(s0, tex+c1*float2(b, c));
float4 main(float2 tex : TEXCOORD0) : COLOR
{
sp(s2, -1, -1) sp(s3, 0, -1) sp(s4, 1, -1) sp(s5, -1, 0) sp(s6, 1, 0) sp(s7, -1, 1) sp(s8, 0, 1) sp(s9, 1, 1)// sample surrounding pixels
return smoothstep(.0625, 0, abs(s2+s3+s4-s7-s8-s9)+abs(s2+s5+s7-s4-s6-s9)+abs(s2+s3+s5-s6-s8-s9)+abs(s3+s4+s6-s5-s7-s8))*32767/65535.+16384/65535.;// color contour output
}
__________________
development folder, containing MPC-HC experimental tester builds, pixel shaders and more: http://www.mediafire.com/?xwsoo403c53hv |
|
|
|
|
|
#375 | Link |
|
Registered User
Join Date: Oct 2010
Location: The Netherlands
Posts: 1,083
|
I just wrote some simple shaders for usage as a third pass, after the "vertical x-pixel averaging, dithering, posterizing and old CRT scan lines"-type shaders. These shaders separate RGB channels of the input video into multiple real pixels, imitating aperture grilles that use rectangular masks. (Imitating the other common shadow mask pattern would be a lot harder to program. I'm not sure it's worth the effort.) The warnings about divisibility in these shaders are not that important. The artifacts are barely visible if the input isn't evenly divisible.
Code:
// horizontal 4-pixel RGB separation
// this shader only works properly on inputs that have a horizontal resolution that is evenly divisible by 4
sampler s0 : register(s0);
float c0 : register(c0);
float c1 : register(c1);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float scaletexx = tex.x*c0*.25;
float prepos = trunc(scaletexx);// calculate the left positon of the current set of pixels
float posdif = scaletexx-prepos;
float4 mask;// create RGB mask based on the pixel location
if(posdif < .25) mask = float4(1, 0, 0, 0);
else if(posdif < .5) mask = float4(0, 1/3., 2/3., 0);
else if(posdif < .75) mask = float4(0, 2/3., 1/3., 0);
else mask = float4(0, 0, 1, 0);
return tex2D(s0, tex)*mask;// mask and output
}
// horizontal 5-pixel RGB separation
// this shader only works properly on inputs that have a horizontal resolution that is evenly divisible by 5
sampler s0 : register(s0);
float c0 : register(c0);
float c1 : register(c1);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float scaletexx = tex.x*c0*.2;
float prepos = trunc(scaletexx);// calculate the left positon of the current set of pixels
float posdif = scaletexx-prepos;
float4 mask;// create RGB mask based on the pixel location
if(posdif < .2) mask = float4(1, 0, 0, 0);
else if(posdif < .4) mask = float4(0, 2/3., 1/3., 0);
else if(posdif < .6) mask = float4(0, 1, 0, 0);
else if(posdif < .8) mask = float4(0, 1/3., 2/3., 0);
else mask = float4(0, 0, 1, 0);
return tex2D(s0, tex)*mask;// mask and output
}
// horizontal 8-pixel RGB separation
// this shader only works properly on inputs that have a horizontal resolution that is evenly divisible by 8
sampler s0 : register(s0);
float c0 : register(c0);
float c1 : register(c1);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float scaletexx = tex.x*c0*.125;
float prepos = trunc(scaletexx);// calculate the left positon of the current set of pixels
float posdif = scaletexx-prepos;
float4 mask;// create RGB mask based on the pixel location
if(posdif < .25) mask = float4(1, 0, 0, 0);
else if(posdif < 0.375) mask = float4(0, 2/3., 1/3., 0);
else if(posdif < 0.625) mask = float4(0, 1, 0, 0);
else if(posdif < .75) mask = float4(0, 1/3., 2/3., 0);
else mask = float4(0, 0, 1, 0);
return tex2D(s0, tex)*mask;// mask and output
}
// horizontal 10-pixel RGB separation
// this shader only works properly on inputs that have a horizontal resolution that is evenly divisible by 10
sampler s0 : register(s0);
float c0 : register(c0);
float c1 : register(c1);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float scaletexx = tex.x*c0*.1;
float prepos = trunc(scaletexx);// calculate the left positon of the current set of pixels
float posdif = scaletexx-prepos;
float4 mask;// create RGB mask based on the pixel location
if(posdif < .3) mask = float4(1, 0, 0, 0);
else if(posdif < .4) mask = float4(0, 1/3., 2/3., 0);
else if(posdif < .6) mask = float4(0, 1, 0, 0);
else if(posdif < .7) mask = float4(0, 2/3., 1/3., 0);
else mask = float4(0, 0, 1, 0);
return tex2D(s0, tex)*mask;// mask and output
}
// horizontal 4-pixel RGB separation for XYZ rendering on 16-bit integer surfaces
// this shader only works properly on inputs that have a horizontal resolution that is evenly divisible by 4
sampler s0 : register(s0);
float c0 : register(c0);
float c1 : register(c1);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float scaletexx = tex.x*c0*.25;
float prepos = trunc(scaletexx);// calculate the left positon of the current set of pixels
float posdif = scaletexx-prepos;
float4 mask;// create RGB mask based on the pixel location
if(posdif < .25) mask = float4(1, 0, 0, 0);
else if(posdif < .5) mask = float4(0, 1/3., 2/3., 0);
else if(posdif < .75) mask = float4(0, 2/3., 1/3., 0);
else mask = float4(0, 0, 1, 0);
return (tex2D(s0, tex)-16384/65535.)*mask+16384/65535.;// mask and output
}
// horizontal 5-pixel RGB separation for XYZ rendering on 16-bit integer surfaces
// this shader only works properly on inputs that have a horizontal resolution that is evenly divisible by 5
sampler s0 : register(s0);
float c0 : register(c0);
float c1 : register(c1);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float scaletexx = tex.x*c0*.2;
float prepos = trunc(scaletexx);// calculate the left positon of the current set of pixels
float posdif = scaletexx-prepos;
float4 mask;// create RGB mask based on the pixel location
if(posdif < .2) mask = float4(1, 0, 0, 0);
else if(posdif < .4) mask = float4(0, 2/3., 1/3., 0);
else if(posdif < .6) mask = float4(0, 1, 0, 0);
else if(posdif < .8) mask = float4(0, 1/3., 2/3., 0);
else mask = float4(0, 0, 1, 0);
return (tex2D(s0, tex)-16384/65535.)*mask+16384/65535.;// mask and output
}
// horizontal 8-pixel RGB separation for XYZ rendering on 16-bit integer surfaces
// this shader only works properly on inputs that have a horizontal resolution that is evenly divisible by 8
sampler s0 : register(s0);
float c0 : register(c0);
float c1 : register(c1);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float scaletexx = tex.x*c0*.125;
float prepos = trunc(scaletexx);// calculate the left positon of the current set of pixels
float posdif = scaletexx-prepos;
float4 mask;// create RGB mask based on the pixel location
if(posdif < .25) mask = float4(1, 0, 0, 0);
else if(posdif < 0.375) mask = float4(0, 2/3., 1/3., 0);
else if(posdif < 0.625) mask = float4(0, 1, 0, 0);
else if(posdif < .75) mask = float4(0, 1/3., 2/3., 0);
else mask = float4(0, 0, 1, 0);
return (tex2D(s0, tex)-16384/65535.)*mask+16384/65535.;// mask and output
}
// horizontal 10-pixel RGB separation for XYZ rendering on 16-bit integer surfaces
// this shader only works properly on inputs that have a horizontal resolution that is evenly divisible by 10
sampler s0 : register(s0);
float c0 : register(c0);
float c1 : register(c1);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
float scaletexx = tex.x*c0*.1;
float prepos = trunc(scaletexx);// calculate the left positon of the current set of pixels
float posdif = scaletexx-prepos;
float4 mask;// create RGB mask based on the pixel location
if(posdif < .3) mask = float4(1, 0, 0, 0);
else if(posdif < .4) mask = float4(0, 1/3., 2/3., 0);
else if(posdif < .6) mask = float4(0, 1, 0, 0);
else if(posdif < .7) mask = float4(0, 2/3., 1/3., 0);
else mask = float4(0, 0, 1, 0);
return (tex2D(s0, tex)-16384/65535.)*mask+16384/65535.;// mask and output
}
__________________
development folder, containing MPC-HC experimental tester builds, pixel shaders and more: http://www.mediafire.com/?xwsoo403c53hv Last edited by JanWillem32; 12th August 2013 at 17:27. |
|
|
|
|
|
#376 | Link |
|
Kid for Today
Join Date: Aug 2004
Posts: 3,494
|
Hi Jan,
So following your advice in another thread that was asking for a "film grain" PS script, I've played around with your "semi-random grayscale noise.txt" which looks quite good but do you think it would be possible to make a PS script version of GrainFactory3()? It allows you to choose the size, strength and sharpness of grain depending on dark/mid-tone/bright areas(whose limits can also be defined) and it can really be finetuned either for deblocking purposes, grain-based EE or artistic effects meant to mimick reel grain. The problem with Didée's script is that it quickly becomes a CPU hog, Avisynth works in 8bit only and the idea would be to process it in 32fp after scaling to Jinc3AR in mVR....so if there is any way you could work your magic to do the same within a PS script, this would be too good to be true ![]() you very much in advance for even considering it,
|
|
|
|
|
|
#377 | Link |
|
Registered User
Join Date: Oct 2010
Location: The Netherlands
Posts: 1,083
|
What I could find out about GrainFactory3 was: "noise generator that tries to simulate the behaviour of silver grain on film".
I already wrote some basic noise effect shaders, but maybe I could get closer to the look of silver grain on film. When I start coding to create an effect, I start with looking at examples on images. I don't just duplicate/imitate other filters. When I've gathered enough research materials, I just start writing out possible parameters for methods in an effect. After that, I try a few methods. These are just calculations that spring to mind, and I usually copy a lot of previously written methods, too. After a bit of tinkering, I usually get the desirable effect from a shader. When transforming the prototype shader to a final type, I optimize first, and add comments. After that, I extract the set of constants, give them names and offer them as user-configurable parameters. The reason I'm telling this is simple; I can practically guarantee that once I've finished something that resembles silver grain on film, the effect will not have a grand total 19 user input variables like GrainFactory3. On the other hand, I don't see any options in GrainFactory3 for using color. I would probably add an option or options for this type of filter related to color, for example; to use the properties for sepia toning instead of silver. (This possibly requires separate filters, though.) To start off simple, the pictures I could find of real film used in cinemas varied strongly over the decades. The most evident changes were the transition from grayscale or toned video to color. The form and amount of grain on film, and the cinema equipment varied, too. Some effects are available: projector film drive scratches, projector film dust, projector film lamp vignette, projector film sepia toning for SD&HD video input, grayscale, projector film shaking, semi-random colored surface noise and semi-random grayscale noise. What era are you targeting for this kind of vintage look? Which effects are currently missing to complete the illusion of such a look? Please specify with some true vintage cinematic examples and name some very specific factors.
__________________
development folder, containing MPC-HC experimental tester builds, pixel shaders and more: http://www.mediafire.com/?xwsoo403c53hv |
|
|
|
|
|
#378 | Link |
|
Kid for Today
Join Date: Aug 2004
Posts: 3,494
|
Hi Jan, thanks for the swift reply.
Well, it would appear that to simulate silver film grain you'd need the ability to set different chunk sizes for dark/mid/bright pixels as grain would appear to look thicker in dark areas for instance. I think 21 grams is a good example of what excessive reel grain can do, of course I want to keep it less intrusive. My real-world use of GrainF3 was to set very low values in order to deblock(which tends to increase the subjective pop-effect IME), add some subtle grain-based EE, give a DLP/silver reel look to sanitized "flat looking" digital movies the same way DLP videoprojectors look pretty grain in dark areas due to their very fast rotating mirrors for instance. I don't think I would be interested in chroma grain, but I would need is the abilities to: -choose the size and strength of grain depending on dark/mid/bright areas -choose the limits for dark/mid/bright, like in GrainF3 -choose the grain pattern or make it random, much like what foxyshadis explained here. Once you find your favorite grain patterns for dark/mid/bright areas, you can use them permanently and the "movie grain" won't be quite random anymore as it'll be finetuned to your liking. The intent of GrainF3 is not to mimick a 1930's sepia looking semi-busted projector but simply to add (possibly very subtle) silver looking grain. Once finetuned, it can really look great as adding grain does wonders for deblocking purposes IME. Of course it can also be used with excessive settings for artistic means. The more I talk about it, the more I wanna play around with GrainF3 all over again but I would really rather have it done via a PS script for all the aforementioned reasons ![]() PS: ouh, this looks impressive too: Cinema Film Grain Plugin for FCPX
Last edited by leeperry; 12th November 2013 at 18:30. |
|
|
|
|
|
#379 | Link |
|
Kid for Today
Join Date: Aug 2004
Posts: 3,494
|
Hi again Jan, so I've tried my old GrainF3() calls that looked so great on CRT/DLP but they look darn noisy on LCD....would have to rework them again from scratch with much more subtle settings I guess.
Anyway, I've got a question for you ![]() Here's another Avisynth script that has always impressed me: SmoothLevels() It's gone DLL but it used to be an .AVS script and here are a few older versions of it: SmoothLevels.rar (11 KB) Using it to convert TV to PC, I've always found its resulting picture to look "deeper" and simply more 3D-looking than whatever ffdshow or madVR could offer. I mentioned it to madshi a while ago who told me that its error diffusion was technically more advanced than the dithering currently done in mVR and that PS scripts couldn't process Floyd-Steinberg. I've made screenshots comparisons on a gray ramp and a REC709 test pattern available at SmoothL2.rar (15.3 MB) I'm colorblind so judging on banding and colorimetry is a very tedious task for me, but I've been told that banding didn't look any better than in mVR and that it seemed to make colorimetry shifts? Comparing screenshots back and forth it would appear to me that SmoothL is doing some sort of colorimetry-based EE? The borders of the color squares in that REC709 pattern look quite different with SmoothL, don't they? My point is that for instance in this very short sample Prisoners.mkv (22.9 MB) when comparing SmoothLevels(preset="tv2pc",HQ=true) to anything else, that animal in the woods seems to appear much deeper in the picture and the passing cars on the road feel less "flat" and far more natural to me ![]() "HQ" would stand for "HQ interpolation" BTW. All this said, do you agree that the picture looks deeper with that sample when using SmoothL? Is that because its error diffusion is more advanced than what mVR can do, or because of some -as I'm suspecting- colorimetry-based EE? If so, could you possibly provide the same kind of trick with a PS script? My problem is that SmoothL outputs 8bit from ffdshow to mVR, it's a real CPU hog(especially in HQ mode on 1.78 1080p) and I'm totally hooked to its 3D look ![]() Hope you can look into it, in advance!
Last edited by leeperry; 9th December 2013 at 18:51. |
|
|
|
|
|
#380 | Link |
|
Angel of Night
![]() Join Date: Nov 2004
Location: Tangled in the silks
Posts: 9,568
|
Hi Jan! Excellent work with the new filters. Can I request that the main archive be updated to include the last couple of years' work, as well? I know some aren't 100% finished, but it'd be nice to have them all handy for quick downloading onto the various systems I have set up for media.
|
|
|
|
![]() |
| Thread Tools | |
| Display Modes | |
|
|