Skip to content

Instantly share code, notes, and snippets.

@FransBouma
Last active November 29, 2018 17:03
Show Gist options
  • Save FransBouma/fa059f9db98dd12ed256b3c94aba60a5 to your computer and use it in GitHub Desktop.
Save FransBouma/fa059f9db98dd12ed256b3c94aba60a5 to your computer and use it in GitHub Desktop.
Kuwahara filter implementation from: https://www.raywenderlich.com/100-unreal-engine-4-paint-filter-tutorial, for reshade
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
// By Otis / Infuse Project
///////////////////////////////////////////////////////////////////
uniform int XRadius <
ui_type = "drag";
ui_min = 1; ui_max=16;
> = 2;
uniform int YRadius <
ui_type = "drag";
ui_min = 1; ui_max=16;
> = 2;
uniform int XRadiusDirectional <
ui_type = "drag";
ui_min = 1; ui_max=16;
> = 2;
uniform int YRadiusDirectional <
ui_type = "drag";
ui_min = 1; ui_max=16;
> = 2;
#include "Reshade.fxh"
//////////////////////////////////////
// textures and samplers
//////////////////////////////////////
texture texBuffer1 { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA8;};
sampler2D sampler2DBuffer1 { Texture = texBuffer1; };
//////////////////////////////////////
// functions
//////////////////////////////////////
float4 GetKernelMeanAndVariance(float2 texcoord, float4 range)
{
float4 mean=0;
float3 variance=0;
for(float x = range.x; x <= range.y; x++)
{
for(float y = range.z; y<=range.w;y++)
{
float2 offset = float2(x, y) * ReShade::PixelSize;
float3 pixelColor = tex2Dlod(ReShade::BackBuffer, float4(texcoord + offset, 0, 0)).rgb;
mean.rgb +=pixelColor.rgb;
variance.rgb += pixelColor.rgb * pixelColor.rgb;
mean.a+=1.0;
}
}
mean.rgb/=(mean.a + (mean.a==0));
variance.rgb = variance.rgb / (mean.a + (mean.a==0)) - (mean.rgb * mean.rgb);
float totalVariance = variance.r + variance.g + variance.b;
return float4(mean.rgb, totalVariance);
}
float4 GetKernelMeanAndVariance(float2 texcoord, float4 range, float2x2 rotationMatrix)
{
float4 mean=0;
float3 variance=0;
for(float x = range.x; x <= range.y; x++)
{
for(float y = range.z; y<=range.w;y++)
{
float2 offset = mul(float2(x, y) * ReShade::PixelSize, rotationMatrix);
float3 pixelColor = tex2Dlod(ReShade::BackBuffer, float4(texcoord + offset, 0, 0)).rgb;
mean.rgb +=pixelColor.rgb;
variance.rgb += pixelColor.rgb * pixelColor.rgb;
mean.a+=1.0;
}
}
mean.rgb/=(mean.a + (mean.a==0));
variance.rgb = variance.rgb / (mean.a + (mean.a==0)) - (mean.rgb * mean.rgb);
float totalVariance = variance.r + variance.g + variance.b;
return float4(mean.rgb, totalVariance);
}
float GetPixelAngle(float2 texcoord)
{
float gradientX = 0;
float gradientY = 0;
float sobelX[9] = {-1, -2, -1, 0, 0, 0, 1, 2, 1};
float sobelY[9] = {-1, 0, 1, -2, 0, 2, -1, 0, 1};
int i = 0;
for (int x = -1; x <= 1; x++)
{
for (int y = -1; y <= 1; y++)
{
float2 offset = float2(x, y) * ReShade::PixelSize;
float3 pixelColor = tex2Dlod(ReShade::BackBuffer, float4(texcoord + offset, 0, 0)).rgb;
float pixelValue = dot(pixelColor, float3(0.3,0.59,0.11));
gradientX += pixelValue * sobelX[i];
gradientY += pixelValue * sobelY[i];
i++;
}
}
return atan(gradientY/(gradientX + (gradientX==0)));
}
//////////////////////////////////////
// shaders
//////////////////////////////////////
void PS_SimpleKuwahara(float4 vpos: SV_Position, float2 texcoord: TEXCOORD, out float4 fragment: SV_Target0)
{
float4 meanAndVariance[4];
float4 range;
range = float4(-XRadius, 0, -YRadius, 0);
meanAndVariance[0] = GetKernelMeanAndVariance(texcoord, range);
range = float4(0, XRadius, -YRadius, 0);
meanAndVariance[1] = GetKernelMeanAndVariance(texcoord, range);
range = float4(-XRadius, 0, 0, YRadius);
meanAndVariance[2] = GetKernelMeanAndVariance(texcoord, range);
range = float4(0, XRadius, 0, YRadius);
meanAndVariance[3] = GetKernelMeanAndVariance(texcoord, range);
float3 finalColor = meanAndVariance[0].rgb;
float minimumVariance = meanAndVariance[0].a;
for (int i = 1; i < 4; i++)
{
if (meanAndVariance[i].a < minimumVariance)
{
finalColor = meanAndVariance[i].rgb;
minimumVariance = meanAndVariance[i].a;
}
}
fragment = float4(finalColor, 1.0);
}
void PS_DirectionalKuwahara(float4 vpos: SV_Position, float2 texcoord: TEXCOORD, out float4 fragment: SV_Target0)
{
float4 meanAndVariance[4];
float4 range;
float angle = GetPixelAngle(texcoord);
float2x2 rotationMatrix = float2x2(cos(angle), -sin(angle), sin(angle), cos(angle));
range = float4(-XRadiusDirectional, 0, -YRadiusDirectional, 0);
meanAndVariance[0] = GetKernelMeanAndVariance(texcoord, range, rotationMatrix);
range = float4(0, XRadiusDirectional, -YRadiusDirectional, 0);
meanAndVariance[1] = GetKernelMeanAndVariance(texcoord, range, rotationMatrix);
range = float4(-XRadiusDirectional, 0, 0, YRadiusDirectional);
meanAndVariance[2] = GetKernelMeanAndVariance(texcoord, range, rotationMatrix);
range = float4(0, XRadiusDirectional, 0, YRadiusDirectional);
meanAndVariance[3] = GetKernelMeanAndVariance(texcoord, range, rotationMatrix);
float3 finalColor = meanAndVariance[0].rgb;
float minimumVariance = meanAndVariance[0].a;
for (int i = 1; i < 4; i++)
{
if (meanAndVariance[i].a < minimumVariance)
{
finalColor = meanAndVariance[i].rgb;
minimumVariance = meanAndVariance[i].a;
}
}
fragment = float4(finalColor, 1.0);
}
//////////////////////////////////////
// techniques
//////////////////////////////////////
technique SimpleKuwahara
{
pass SimpleKuwahara { VertexShader = PostProcessVS; PixelShader = PS_SimpleKuwahara; }
}
technique DirectionalKuwahara
{
pass DirectionalKuwahara { VertexShader = PostProcessVS; PixelShader = PS_DirectionalKuwahara; }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment