Shader "Hidden/FXAA" {
|
|
Properties {
|
|
_MainTex ("Texture", 2D) = "white" {}
|
|
}
|
|
|
|
CGINCLUDE
|
|
#include "UnityCG.cginc"
|
|
|
|
sampler2D _MainTex;
|
|
half4 _MainTex_TexelSize;
|
|
|
|
half _ContrastThreshold, _RelativeThreshold;
|
|
half _SubpixelBlending;
|
|
|
|
struct VertexData {
|
|
half4 vertex : POSITION;
|
|
half2 uv : TEXCOORD0;
|
|
};
|
|
|
|
struct Interpolators {
|
|
half4 pos : SV_POSITION;
|
|
half2 uv : TEXCOORD0;
|
|
};
|
|
|
|
Interpolators VertexProgram (VertexData v) {
|
|
Interpolators i;
|
|
i.pos = UnityObjectToClipPos(v.vertex);
|
|
i.uv = v.uv;
|
|
return i;
|
|
}
|
|
|
|
half4 Sample (half2 uv) {
|
|
return tex2Dlod(_MainTex, half4(uv, 0, 0));
|
|
}
|
|
|
|
half SampleLuminance (half2 uv) {
|
|
return Sample(uv).g;
|
|
// #if defined(LUMINANCE_GREEN)
|
|
// return Sample(uv).g;
|
|
// #else
|
|
// return Sample(uv).a;
|
|
// #endif
|
|
}
|
|
|
|
half SampleLuminance (half2 uv, half uOffset, half vOffset) {
|
|
uv += _MainTex_TexelSize * half2(uOffset, vOffset);
|
|
return SampleLuminance(uv);
|
|
}
|
|
|
|
struct LuminanceData {
|
|
half m, n, e, s, w;
|
|
half ne, nw, se, sw;
|
|
half highest, lowest, contrast;
|
|
};
|
|
|
|
LuminanceData SampleLuminanceNeighborhood (half2 uv) {
|
|
LuminanceData l;
|
|
l.m = SampleLuminance(uv);
|
|
l.n = SampleLuminance(uv, 0, 1);
|
|
l.e = SampleLuminance(uv, 1, 0);
|
|
l.s = SampleLuminance(uv, 0, -1);
|
|
l.w = SampleLuminance(uv, -1, 0);
|
|
|
|
l.ne = SampleLuminance(uv, 1, 1);
|
|
l.nw = SampleLuminance(uv, -1, 1);
|
|
l.se = SampleLuminance(uv, 1, -1);
|
|
l.sw = SampleLuminance(uv, -1, -1);
|
|
|
|
l.highest = max(max(max(max(l.n, l.e), l.s), l.w), l.m);
|
|
l.lowest = min(min(min(min(l.n, l.e), l.s), l.w), l.m);
|
|
l.contrast = l.highest - l.lowest;
|
|
return l;
|
|
}
|
|
|
|
bool ShouldSkipPixel (LuminanceData l) {
|
|
half threshold =
|
|
max(_ContrastThreshold, _RelativeThreshold * l.highest);
|
|
return l.contrast < threshold;
|
|
}
|
|
|
|
half DeterminePixelBlendFactor (LuminanceData l) {
|
|
half filter = 2 * (l.n + l.e + l.s + l.w);
|
|
filter += l.ne + l.nw + l.se + l.sw;
|
|
filter *= 1.0 / 12.0;
|
|
filter = abs(filter - l.m);
|
|
filter = saturate(filter / l.contrast);
|
|
|
|
half blendFactor = smoothstep(0, 1, filter);
|
|
return blendFactor * blendFactor * _SubpixelBlending;
|
|
}
|
|
|
|
struct EdgeData {
|
|
bool isHorizontal;
|
|
half pixelStep;
|
|
half oppositeLuminance, gradient;
|
|
};
|
|
|
|
EdgeData DetermineEdge (LuminanceData l) {
|
|
EdgeData e;
|
|
half horizontal =
|
|
abs(l.n + l.s - 2 * l.m) * 2 +
|
|
abs(l.ne + l.se - 2 * l.e) +
|
|
abs(l.nw + l.sw - 2 * l.w);
|
|
half vertical =
|
|
abs(l.e + l.w - 2 * l.m) * 2 +
|
|
abs(l.ne + l.nw - 2 * l.n) +
|
|
abs(l.se + l.sw - 2 * l.s);
|
|
e.isHorizontal = horizontal >= vertical;
|
|
|
|
half pLuminance = e.isHorizontal ? l.n : l.e;
|
|
half nLuminance = e.isHorizontal ? l.s : l.w;
|
|
half pGradient = abs(pLuminance - l.m);
|
|
half nGradient = abs(nLuminance - l.m);
|
|
|
|
e.pixelStep =
|
|
e.isHorizontal ? _MainTex_TexelSize.y : _MainTex_TexelSize.x;
|
|
|
|
if (pGradient < nGradient) {
|
|
e.pixelStep = -e.pixelStep;
|
|
e.oppositeLuminance = nLuminance;
|
|
e.gradient = nGradient;
|
|
}
|
|
else {
|
|
e.oppositeLuminance = pLuminance;
|
|
e.gradient = pGradient;
|
|
}
|
|
|
|
return e;
|
|
}
|
|
|
|
#if defined(LOW_QUALITY)
|
|
#define EDGE_STEP_COUNT 4
|
|
#define EDGE_STEPS 1, 1.5, 2, 4
|
|
#define EDGE_GUESS 12
|
|
#else
|
|
#define EDGE_STEP_COUNT 10
|
|
#define EDGE_STEPS 1, 1.5, 2, 2, 2, 2, 2, 2, 2, 4
|
|
#define EDGE_GUESS 8
|
|
#endif
|
|
|
|
static const half edgeSteps[EDGE_STEP_COUNT] = { EDGE_STEPS };
|
|
|
|
half DetermineEdgeBlendFactor (LuminanceData l, EdgeData e, half2 uv) {
|
|
half2 uvEdge = uv;
|
|
half2 edgeStep;
|
|
if (e.isHorizontal) {
|
|
uvEdge.y += e.pixelStep * 0.5;
|
|
edgeStep = half2(_MainTex_TexelSize.x, 0);
|
|
}
|
|
else {
|
|
uvEdge.x += e.pixelStep * 0.5;
|
|
edgeStep = half2(0, _MainTex_TexelSize.y);
|
|
}
|
|
|
|
half edgeLuminance = (l.m + e.oppositeLuminance) * 0.5;
|
|
half gradientThreshold = e.gradient * 0.25;
|
|
|
|
half2 puv = uvEdge + edgeStep * edgeSteps[0];
|
|
half pLuminanceDelta = SampleLuminance(puv) - edgeLuminance;
|
|
bool pAtEnd = abs(pLuminanceDelta) >= gradientThreshold;
|
|
|
|
UNITY_UNROLL
|
|
for (int i = 1; i < EDGE_STEP_COUNT && !pAtEnd; i++) {
|
|
puv += edgeStep * edgeSteps[i];
|
|
pLuminanceDelta = SampleLuminance(puv) - edgeLuminance;
|
|
pAtEnd = abs(pLuminanceDelta) >= gradientThreshold;
|
|
}
|
|
if (!pAtEnd) {
|
|
puv += edgeStep * EDGE_GUESS;
|
|
}
|
|
|
|
half2 nuv = uvEdge - edgeStep * edgeSteps[0];
|
|
half nLuminanceDelta = SampleLuminance(nuv) - edgeLuminance;
|
|
bool nAtEnd = abs(nLuminanceDelta) >= gradientThreshold;
|
|
|
|
UNITY_UNROLL
|
|
for (int i = 1; i < EDGE_STEP_COUNT && !nAtEnd; i++) {
|
|
nuv -= edgeStep * edgeSteps[i];
|
|
nLuminanceDelta = SampleLuminance(nuv) - edgeLuminance;
|
|
nAtEnd = abs(nLuminanceDelta) >= gradientThreshold;
|
|
}
|
|
if (!nAtEnd) {
|
|
nuv -= edgeStep * EDGE_GUESS;
|
|
}
|
|
|
|
half pDistance, nDistance;
|
|
if (e.isHorizontal) {
|
|
pDistance = puv.x - uv.x;
|
|
nDistance = uv.x - nuv.x;
|
|
}
|
|
else {
|
|
pDistance = puv.y - uv.y;
|
|
nDistance = uv.y - nuv.y;
|
|
}
|
|
|
|
half shortestDistance;
|
|
bool deltaSign;
|
|
if (pDistance <= nDistance) {
|
|
shortestDistance = pDistance;
|
|
deltaSign = pLuminanceDelta >= 0;
|
|
}
|
|
else {
|
|
shortestDistance = nDistance;
|
|
deltaSign = nLuminanceDelta >= 0;
|
|
}
|
|
|
|
if (deltaSign == (l.m - edgeLuminance >= 0)) {
|
|
return 0;
|
|
}
|
|
return 0.5 - shortestDistance / (pDistance + nDistance);
|
|
}
|
|
|
|
half4 ApplyFXAA (half2 uv) {
|
|
LuminanceData l = SampleLuminanceNeighborhood(uv);
|
|
if (ShouldSkipPixel(l)) {
|
|
return Sample(uv);
|
|
}
|
|
|
|
half pixelBlend = DeterminePixelBlendFactor(l);
|
|
EdgeData e = DetermineEdge(l);
|
|
half edgeBlend = DetermineEdgeBlendFactor(l, e, uv);
|
|
half finalBlend = max(pixelBlend, edgeBlend);
|
|
|
|
if (e.isHorizontal) {
|
|
uv.y += e.pixelStep * finalBlend;
|
|
}
|
|
else {
|
|
uv.x += e.pixelStep * finalBlend;
|
|
}
|
|
return half4(Sample(uv).rgb, l.m);
|
|
}
|
|
ENDCG
|
|
|
|
SubShader {
|
|
Cull Off
|
|
ZTest Always
|
|
ZWrite Off
|
|
|
|
Pass { // 0 luminancePass
|
|
CGPROGRAM
|
|
#pragma vertex VertexProgram
|
|
#pragma fragment FragmentProgram
|
|
// #pragma enable_d3d11_debug_symbols
|
|
#pragma multi_compile _ GAMMA_BLENDING
|
|
|
|
half4 FragmentProgram (Interpolators i) : SV_Target {
|
|
half4 sample = tex2D(_MainTex, i.uv);
|
|
sample.rgb = saturate(sample.rgb);
|
|
sample.a = LinearRgbToLuminance(sample.rgb);
|
|
#if defined(GAMMA_BLENDING)
|
|
sample.rgb = LinearToGammaSpace(sample.rgb);
|
|
#endif
|
|
return sample;
|
|
}
|
|
ENDCG
|
|
}
|
|
|
|
Pass { // 1 fxaaPass
|
|
CGPROGRAM
|
|
#pragma vertex VertexProgram
|
|
#pragma fragment FragmentProgram
|
|
// #pragma enable_d3d11_debug_symbols
|
|
// #pragma multi_compile _ LUMINANCE_GREEN
|
|
#pragma multi_compile _ LOW_QUALITY
|
|
#pragma multi_compile _ GAMMA_BLENDING
|
|
|
|
half4 FragmentProgram (Interpolators i) : SV_Target {
|
|
half4 sample = ApplyFXAA(i.uv);
|
|
#if defined(GAMMA_BLENDING)
|
|
sample.rgb = GammaToLinearSpace(sample.rgb);
|
|
#endif
|
|
return sample;
|
|
}
|
|
ENDCG
|
|
}
|
|
}
|
|
}
|