Skip to content

Instantly share code, notes, and snippets.

@FransBouma
Last active September 6, 2023 20:40
Show Gist options
  • Save FransBouma/5a357b439c5aadc4ed8ab1de83dc95c4 to your computer and use it in GitHub Desktop.
Save FransBouma/5a357b439c5aadc4ed8ab1de83dc95c4 to your computer and use it in GitHub Desktop.
/*
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;
// }
}
@FransBouma
Copy link
Author

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