Last active
September 6, 2023 20:40
-
-
Save FransBouma/5a357b439c5aadc4ed8ab1de83dc95c4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
A pretty close port of Anisotropic Kuwahara Filtering on the GPU | |
source: | |
www.kyprianidis.com | |
Paper : http://www.kyprianidis.com/p/pg2009/jkyprian-pg2009.pdf | |
Preview App' : https://code.google.com/archive/p/gpuakf/downloads | |
Code : https://code.google.com/archive/p/gpuakf/source/default/source | |
Ported over to Reshade by Eideren | |
*/ | |
#include "ReShade.fxh" | |
uniform float Direction < | |
ui_type = "drag"; | |
ui_min = 0.0; | |
ui_max = 32.0; | |
ui_label = "Direction"; | |
> = 10; | |
#define Alpha (Direction) | |
uniform float Radius < | |
ui_type = "drag"; | |
ui_min = 0.1; | |
ui_max = 32; | |
ui_label = "Radius"; | |
> = 2.5; | |
uniform float Sharpness < | |
ui_type = "drag"; | |
ui_min = 0.0; | |
ui_max = 32; | |
ui_label = "Sharpness"; | |
> = 5; | |
#define q (Sharpness) | |
uniform int Precision < | |
ui_type = "drag"; | |
ui_min = 2.0; | |
ui_max = 8.0; | |
ui_label = "Precision (Carefull)"; | |
> = 4; | |
#define N (Precision) | |
uniform float Detail < | |
ui_type = "drag"; | |
ui_min = 0.0; | |
ui_max = 32; | |
ui_label = "Smooth out detail (Carefull)"; | |
> = 2; | |
#define sigma (Detail) | |
texture K0 <source="Kuwahara/Kernel111.png";> { Width = 32; Height = 32; }; | |
texture jet <source="Kuwahara/jet.png";> { Width = 256; Height = 1; Format=RGBA8; }; | |
texture SceneProxy | |
{ | |
Width = BUFFER_WIDTH; | |
Height = BUFFER_HEIGHT; | |
Format = RGBA8; | |
}; | |
texture tmp0 | |
{ | |
Width = BUFFER_WIDTH; | |
Height = BUFFER_HEIGHT; | |
Format = RGBA16F; | |
}; | |
texture tmp1 | |
{ | |
Width = BUFFER_WIDTH; | |
Height = BUFFER_HEIGHT; | |
Format = RGBA16F; | |
}; | |
sampler SceneProxySampler | |
{ | |
Texture = SceneProxy; | |
}; | |
sampler K0Sampler { Texture = K0; }; | |
sampler tmp0Sampler {Texture = tmp0;}; | |
sampler tmp1Sampler {Texture = tmp1;}; | |
sampler jetSampler {Texture = jet;}; | |
#define backbuffer (ReShade::BackBuffer) | |
#define backbuffer_size (ReShade::ScreenSize.xy) | |
#define SSTSampler tmp0Sampler | |
#define Kuwa_SceneSampler SceneProxySampler | |
#define TFMSampler tmp1Sampler | |
struct SSTAndScene | |
{ | |
float4 Backbuffer: SV_Target0; | |
float4 Scene: SV_Target1; | |
}; | |
SSTAndScene SSTPass(float4 vpos : SV_Position, float2 tex : TexCoord) : SV_Target | |
{ | |
float2 uv = tex.xy; | |
float2 d = 1.0 / backbuffer_size; | |
float4 c = tex2D(backbuffer, uv); | |
float3 u = ( | |
-1.0 * tex2D(backbuffer, uv + float2(-d.x, -d.y)).xyz + | |
-2.0 * tex2D(backbuffer, uv + float2(-d.x, 0.0)).xyz + | |
-1.0 * tex2D(backbuffer, uv + float2(-d.x, d.y)).xyz + | |
+1.0 * tex2D(backbuffer, uv + float2( d.x, -d.y)).xyz + | |
+2.0 * tex2D(backbuffer, uv + float2( d.x, 0.0)).xyz + | |
+1.0 * tex2D(backbuffer, uv + float2( d.x, d.y)).xyz | |
) / 4.0; | |
float3 v = ( | |
-1.0 * tex2D(backbuffer, uv + float2(-d.x, -d.y)).xyz + | |
-2.0 * tex2D(backbuffer, uv + float2( 0.0, -d.y)).xyz + | |
-1.0 * tex2D(backbuffer, uv + float2( d.x, -d.y)).xyz + | |
+1.0 * tex2D(backbuffer, uv + float2(-d.x, d.y)).xyz + | |
+2.0 * tex2D(backbuffer, uv + float2( 0.0, d.y)).xyz + | |
+1.0 * tex2D(backbuffer, uv + float2( d.x, d.y)).xyz | |
) / 4.0; | |
float4 fragColor = float4(dot(u, u), dot(v, v), dot(u, v), 1.0); | |
SSTAndScene o; | |
o.Backbuffer = fragColor; | |
o.Scene = c; | |
return o;//fragColor; | |
} | |
float4 SSTBlurAndTFMPass(float4 vpos : SV_Position, float2 tex : TexCoord) : SV_Target | |
{ | |
float2 uv = tex.xy; | |
float twoSigma2 = 2.0 * sigma * sigma; | |
int halfWidth = int(ceil( 2.0 * sigma )); | |
float3 sum = (0.0); | |
float norm = 0.0; | |
if (halfWidth > 0) { | |
for ( int i = -halfWidth; i <= halfWidth; ++i ) { | |
for ( int j = -halfWidth; j <= halfWidth; ++j ) { | |
float d = length(float2(i,j)); | |
float kernel = exp( -d *d / twoSigma2 ); | |
float3 c = tex2D(SSTSampler, uv + float2(i,j) / backbuffer_size ).rgb; | |
sum += kernel * c; | |
norm += kernel; | |
} | |
} | |
} else { | |
sum = tex2D(SSTSampler, uv).rgb; | |
norm = 1.0; | |
} | |
float4 blur = float4(sum / norm, 1); | |
// TFM | |
float4 g = blur; | |
float lambda1 = 0.5 * (g.y + g.x + sqrt(g.y*g.y - 2.0*g.x*g.y + g.x*g.x + 4.0*g.z*g.z)); | |
float lambda2 = 0.5 * (g.y + g.x - sqrt(g.y*g.y - 2.0*g.x*g.y + g.x*g.x + 4.0*g.z*g.z)); | |
float2 v = float2(lambda1 - g.x, -g.z); | |
float2 t; | |
if (length(v) > 0.0) { | |
t = normalize(v); | |
} else { | |
t = float2(0.0, 1.0); | |
} | |
float phi = atan2(t.y, t.x); | |
float A = (lambda1 + lambda2 > 0.0) ? (lambda1 - lambda2) / (lambda1 + lambda2) : 0.0; | |
// note that A isn't used inside any pass of this shader, this pass was used by other shaders as well on the source app | |
return float4(t, phi, A); | |
} | |
float4 KuwaharaAKF1Pass(float4 vpos : SV_Position, float2 tex : TexCoord) : SV_Target | |
{ | |
const float PI = 3.14159265358979323846; | |
float2 uv = tex.xy; | |
float4 m[8]; | |
float3 s[8]; | |
for (int k = 0; k < N; ++k) { | |
m[k] = (0.0); | |
s[k] = (0.0); | |
} | |
float piN = 2.0 * PI / float(N); | |
float2x2 X = float2x2(cos(piN), sin(piN), -sin(piN), cos(piN)); | |
float4 sampledTFM = tex2D(TFMSampler, uv); | |
float a = Radius * clamp((Alpha + sampledTFM.w) / Alpha, 0.1, 2.0); | |
float b = Radius * clamp(Alpha / (Alpha + sampledTFM.w), 0.1, 2.0); | |
float cos_phi = cos(sampledTFM.z); | |
float sin_phi = sin(sampledTFM.z); | |
float2x2 R = float2x2(cos_phi, -sin_phi, sin_phi, cos_phi); | |
float2x2 S = float2x2(0.5/a, 0.0, 0.0, 0.5/b); | |
float2x2 SR = S * R; | |
int max_x = int(sqrt(a*a * cos_phi*cos_phi + | |
b*b * sin_phi*sin_phi)); | |
int max_y = int(sqrt(a*a * sin_phi*sin_phi + | |
b*b * cos_phi*cos_phi)); | |
for (int j = -max_y; j <= max_y; ++j) { | |
for (int i = -max_x; i <= max_x; ++i) { | |
float2 v = mul(SR, float2(i,j)); | |
if (dot(v,v) <= 0.25) { | |
float4 c_fix = tex2Dlod(Kuwa_SceneSampler, float4(uv + float2(i,j) / backbuffer_size, 0, 0)); | |
float3 c = c_fix.rgb; | |
for (int k = 0; k < N; ++k) { | |
float w = tex2Dlod(K0Sampler, float4(float2(0.5, 0.5) + v, 0, 0)).x; | |
m[k] += float4(c * w, w); | |
s[k] += c * c * w; | |
v = mul(X, v);//v *= X; | |
} | |
} | |
} | |
} | |
float4 o = (0.0); | |
for (int segment = 0; segment < N; ++segment) { | |
m[segment].rgb /= m[segment].w; | |
s[segment] = abs(s[segment] / m[segment].w - m[segment].rgb * m[segment].rgb); | |
float sigma2 = s[segment].r + s[segment].g + s[segment].b; | |
float w = 1.0 / (1.0 + pow(abs(255.0 * sigma2), 0.5 * q)); | |
o += float4(m[segment].rgb * w, w); | |
} | |
return float4(o.rgb / o.w, 1.0); | |
} | |
void PS_DEBUG(float4 vpos: SV_Position, float2 texcoord: TEXCOORD, out float4 fragment: SV_Target0) | |
{ | |
float4 tfmValue = tex2D(tmp1Sampler, texcoord); | |
fragment = tex2D(jetSampler, float2(tfmValue.w, 0.5)); | |
} | |
technique KuwaharaAnisotropic | |
{ | |
pass SST | |
{ | |
VertexShader = PostProcessVS; | |
PixelShader = SSTPass; | |
RenderTarget0 = tmp0; | |
RenderTarget1 = SceneProxy; | |
ClearRenderTargets = false; | |
} | |
pass SSTTFM | |
{ | |
VertexShader = PostProcessVS; | |
PixelShader = SSTBlurAndTFMPass; | |
RenderTarget = tmp1; | |
ClearRenderTargets = false; | |
} | |
pass | |
{ | |
VertexShader = PostProcessVS; | |
PixelShader = KuwaharaAKF1Pass; | |
} | |
// pass DEBUG { | |
// VertexShader = PostProcessVS; | |
// PixelShader = PS_DEBUG; | |
// } | |
} |
From what I remember, this gist isn't fully functional either, and a mix of code from elsewhere hacked together to make it work as a basis, but I don't htink it ever worked
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Oh I have absolutely NO idea what these are, sorry!