Created
November 4, 2017 17:30
-
-
Save anonymous/c7b10bdf2cd04687b7f17ac1acd85c38 to your computer and use it in GitHub Desktop.
Shader version of xBRx2 filtering for pixel art in Shaderlab format (for Unity 2017)
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
// Originally from: | |
// https://gamedev.stackexchange.com/questions/87275/how-do-i-perform-an-xbr-or-hqx-filter-in-xna | |
// Tweaked for Shaderlab by twitter.com/yankooliveira | |
// (Having almost no idea what he was doing) | |
Shader "Custom/xBRx2" { | |
Properties { | |
_PixelTexture ("Texture to filter (RGB)", 2D) = "white" {} | |
texture_size ("Pixel Texture Size", Vector) = (16,16,0,0) | |
} | |
SubShader { | |
Tags { "RenderType"="Opaque" } | |
Pass { | |
CGPROGRAM | |
// Upgrade NOTE: excluded shader from OpenGL ES 2.0 because it uses non-square matrices | |
// (auto-added by 2017.1) | |
#pragma exclude_renderers gles | |
#include "UnityCG.cginc" | |
#pragma vertex main_vertex | |
#pragma fragment main_fragment | |
#pragma target 3.0 | |
float4 texture_size; | |
uniform sampler2D _PixelTexture : TEXUNIT0; | |
const static float coef = 2.0; | |
const static float3 yuv_weighted = float3(14.352, 28.176, 5.472); | |
float4 df(float4 A, float4 B) | |
{ | |
float4 result = float4(A.x - B.x, A.y - B.y, A.z - B.z, A.w - B.w); | |
return abs(result); | |
} | |
float4 weighted_distance(float4 a, float4 b, float4 c, float4 d, float4 e, float4 f, float4 g, float4 h) | |
{ | |
return (df(a, b) + df(a, c) + df(d, e) + df(d, f) + 4.0 * df(g, h)); | |
} | |
struct out_vertex { | |
half4 position : POSITION; | |
float2 texCoord : TEXCOORD0; | |
half4 t1 : TEXCOORD1; | |
}; | |
out_vertex main_vertex( appdata_base v) | |
{ | |
out_vertex OUT; | |
float2 ps = float2(1.0 / texture_size.x, 1.0 / texture_size.y); | |
float4 t1; | |
t1.xy = float2(ps.x, 0); // F | |
t1.zw = float2(0, ps.y); // H | |
// For 5.x, you'll probably use | |
// OUT.position = mul(UNITY_MATRIX_MVP, v.vertex); | |
// instead of: | |
OUT.position = UnityObjectToClipPos(v.vertex); | |
OUT.texCoord = v.texcoord; | |
OUT.t1 = t1; | |
return OUT; | |
} | |
/* FRAGMENT SHADER */ | |
float4 main_fragment(out_vertex v) : COLOR | |
{ | |
float2 tex0 = v.texCoord; | |
float4 tex1 = v.t1; | |
bool4 edr, edr_left, edr_up, px; // px = pixel, edr = edge detection rule | |
bool4 ir_lv1, ir_lv2_left, ir_lv2_up; | |
bool4 nc; // new_color | |
bool4 fx, fx_left, fx_up; // inequations of straight lines. | |
float2 fp = frac(tex0 * texture_size); | |
float2 dx = tex1.xy; | |
float2 dy = tex1.zw; | |
float3 A = tex2D(_PixelTexture, tex0 - dx - dy).xyz; | |
float3 B = tex2D(_PixelTexture, tex0 - dy).xyz; | |
float3 C = tex2D(_PixelTexture, tex0 + dx - dy).xyz; | |
float3 D = tex2D(_PixelTexture, tex0 - dx).xyz; | |
float3 E = tex2D(_PixelTexture, tex0).xyz; | |
float3 F = tex2D(_PixelTexture, tex0 + dx).xyz; | |
float3 G = tex2D(_PixelTexture, tex0 - dx + dy).xyz; | |
float3 H = tex2D(_PixelTexture, tex0 + dy).xyz; | |
float3 I = tex2D(_PixelTexture, tex0 + dx + dy).xyz; | |
float3 A1 = tex2D(_PixelTexture, tex0 - dx - 2.0*dy).xyz; | |
float3 C1 = tex2D(_PixelTexture, tex0 + dx - 2.0*dy).xyz; | |
float3 A0 = tex2D(_PixelTexture, tex0 - 2.0*dx - dy).xyz; | |
float3 G0 = tex2D(_PixelTexture, tex0 - 2.0*dx + dy).xyz; | |
float3 C4 = tex2D(_PixelTexture, tex0 + 2.0*dx - dy).xyz; | |
float3 I4 = tex2D(_PixelTexture, tex0 + 2.0*dx + dy).xyz; | |
float3 G5 = tex2D(_PixelTexture, tex0 - dx + 2.0*dy).xyz; | |
float3 I5 = tex2D(_PixelTexture, tex0 + dx + 2.0*dy).xyz; | |
float3 B1 = tex2D(_PixelTexture, tex0 - 2.0*dy).xyz; | |
float3 D0 = tex2D(_PixelTexture, tex0 - 2.0*dx).xyz; | |
float3 H5 = tex2D(_PixelTexture, tex0 + 2.0*dy).xyz; | |
float3 F4 = tex2D(_PixelTexture, tex0 + 2.0*dx).xyz; | |
float4 b = mul(float4x3(B, D, H, F), yuv_weighted); | |
float4 c = mul(float4x3(C, A, G, I), yuv_weighted); | |
float4 e = mul(float4x3(E, E, E, E), yuv_weighted); | |
float4 d = b.yzwx; | |
float4 f = b.wxyz; | |
float4 g = c.zwxy; | |
float4 h = b.zwxy; | |
float4 i = c.wxyz; | |
float4 i4 = mul(float4x3(I4, C1, A0, G5), yuv_weighted); | |
float4 i5 = mul(float4x3(I5, C4, A1, G0), yuv_weighted); | |
float4 h5 = mul(float4x3(H5, F4, B1, D0), yuv_weighted); | |
float4 f4 = h5.yzwx; | |
float4 Ao = float4(1.0, -1.0, -1.0, 1.0); | |
float4 Bo = float4(1.0, 1.0, -1.0, -1.0); | |
float4 Co = float4(1.5, 0.5, -0.5, 0.5); | |
float4 Ax = float4(1.0, -1.0, -1.0, 1.0); | |
float4 Bx = float4(0.5, 2.0, -0.5, -2.0); | |
float4 Cx = float4(1.0, 1.0, -0.5, 0.0); | |
float4 Ay = float4(1.0, -1.0, -1.0, 1.0); | |
float4 By = float4(2.0, 0.5, -2.0, -0.5); | |
float4 Cy = float4(2.0, 0.0, -1.0, 0.5); | |
// These inequations define the line below which interpolation occurs. | |
fx.x = (Ao.x*fp.y + Bo.x*fp.x > Co.x); | |
fx_left.x = (Ax.x*fp.y + Bx.x*fp.x > Cx.x); | |
fx_up.x = (Ay.x*fp.y + By.x*fp.x > Cy.x); | |
fx.y = (Ao.y*fp.y + Bo.y*fp.x > Co.y); | |
fx_left.y = (Ax.y*fp.y + Bx.y*fp.x > Cx.y); | |
fx_up.y = (Ay.y*fp.y + By.y*fp.x > Cy.y); | |
fx.z = (Ao.z*fp.y + Bo.z*fp.x > Co.z); | |
fx_left.z = (Ax.z*fp.y + Bx.z*fp.x > Cx.z); | |
fx_up.z = (Ay.z*fp.y + By.z*fp.x > Cy.z); | |
fx.w = (Ao.w*fp.y + Bo.w*fp.x > Co.w); | |
fx_left.w = (Ax.w*fp.y + Bx.w*fp.x > Cx.w); | |
fx_up.w = (Ay.w*fp.y + By.w*fp.x > Cy.w); | |
ir_lv1 = ((e != f) && (e != h)); | |
ir_lv2_left = ((e != g) && (d != g)); | |
ir_lv2_up = ((e != c) && (b != c)); | |
float4 w1 = weighted_distance(e, c, g, i, h5, f4, h, f); | |
float4 w2 = weighted_distance(h, d, i5, f, i4, b, e, i); | |
// begin optimization: reduction of 6 instruction slots | |
float4 df_fg = df(f, g); | |
float4 df_hc = df(h, c); | |
// end optimization | |
float4 t1 = (coef * df_fg); | |
float4 t2 = df_hc; | |
float4 t3 = df_fg; | |
float4 t4 = (coef * df_hc); | |
edr = (w1 < w2) && ir_lv1; | |
edr_left = (t1 <= t2) && ir_lv2_left; | |
edr_up = (t4 <= t3) && ir_lv2_up; | |
nc = (edr && (fx || edr_left && fx_left || edr_up && fx_up)); | |
// to actually compile this shader, uncomment the following line | |
// which reduces the instruction count to under 512 | |
//nc.zw = (float2)0; | |
t1 = df(e, f); | |
t2 = df(e, h); | |
px = t1 <= t2; | |
float3 res = nc.x ? px.x ? F : H : nc.y ? px.y ? B : F : nc.z ? px.z ? D : B : nc.w ? px.w ? H : D : E; | |
return float4(res.x, res.y, res.z, 1.0); | |
} | |
ENDCG | |
} | |
} | |
FallBack "Diffuse" | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment