2023-07-30 07:32:34 +10:00
|
|
|
// See pixel_aa.slang for copyright and other information.
|
2023-06-27 08:08:52 +10:00
|
|
|
|
|
|
|
// clang-format off
|
2023-07-30 07:32:34 +10:00
|
|
|
#pragma parameter PIX_AA_SETTINGS "=== Pixel AA v1.2 settings ===" 0.0 0.0 1.0 1.0
|
|
|
|
#pragma parameter PIX_AA_SHARP "Pixel AA sharpening amount" 1.5 0.0 2.0 0.05
|
2023-06-27 08:08:52 +10:00
|
|
|
#pragma parameter PIX_AA_GAMMA "Enable gamma-correct blending" 1.0 0.0 1.0 1.0
|
|
|
|
#pragma parameter PIX_AA_SUBPX "Enable subpixel AA" 0.0 0.0 1.0 1.0
|
|
|
|
#pragma parameter PIX_AA_SUBPX_BGR "Use BGR subpx. instead of RGB" 0.0 0.0 1.0 1.0
|
|
|
|
// clang-format on
|
|
|
|
|
|
|
|
// Similar to smoothstep, but has a configurable slope at x = 0.5.
|
|
|
|
// Original smoothstep has a slope of 1.5 at x = 0.5
|
|
|
|
#define INSTANTIATE_SLOPESTEP(T) \
|
|
|
|
T slopestep(T edge0, T edge1, T x, float slope) { \
|
|
|
|
x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); \
|
|
|
|
const T s = sign(x - 0.5); \
|
|
|
|
const T o = (1.0 + s) * 0.5; \
|
|
|
|
return o - 0.5 * s * pow(2.0 * (o - s * x), T(slope)); \
|
|
|
|
}
|
|
|
|
INSTANTIATE_SLOPESTEP(float)
|
|
|
|
INSTANTIATE_SLOPESTEP(vec2)
|
|
|
|
|
|
|
|
vec3 to_lin(vec3 x) { return pow(x, vec3(2.2)); }
|
|
|
|
|
|
|
|
vec3 to_srgb(vec3 x) { return pow(x, vec3(1.0 / 2.2)); }
|
|
|
|
|
2023-07-30 07:32:34 +10:00
|
|
|
// Function to get a single sample using the "pixel AA" method.
|
2023-06-27 08:08:52 +10:00
|
|
|
// Params:
|
2023-07-30 07:32:34 +10:00
|
|
|
// tx_coord: Coordinate in source pixel (texel) coordinates
|
|
|
|
vec3 sample_aa(sampler2D tex, vec2 tx_per_px, vec2 tx_to_uv, vec2 tx_coord,
|
|
|
|
float sharpness, bool gamma_correct) {
|
|
|
|
// The offset for interpolation is a periodic function with
|
|
|
|
// a period length of 1 texel.
|
|
|
|
// The input coordinate is shifted so that the center of the texel
|
|
|
|
// aligns with the start of the period.
|
|
|
|
// First, get the period and phase.
|
|
|
|
vec2 period;
|
|
|
|
const vec2 phase = modf(tx_coord - 0.5, period);
|
|
|
|
// The function starts at 0, then starts transitioning at
|
|
|
|
// 0.5 - 0.5 / pixels_per_texel, then reaches 0.5 at 0.5,
|
|
|
|
// Then reaches 1 at 0.5 + 0.5 / pixels_per_texel.
|
|
|
|
// For sharpness values < 1.0, blend to bilinear filtering.
|
|
|
|
const vec2 offset =
|
|
|
|
slopestep(min(1.0, sharpness) * (0.5 - 0.5 * tx_per_px),
|
|
|
|
1.0 - min(1.0, sharpness) * (1.0 - (0.5 + 0.5 * tx_per_px)),
|
|
|
|
phase, max(1.0, sharpness));
|
|
|
|
|
|
|
|
// With gamma correct blending, we have to do 4 taps and interpolate
|
|
|
|
// manually. Without it, we can make use of a single tap using bilinear
|
|
|
|
// interpolation. The offsets are shifted back to the texel center before
|
|
|
|
// sampling.
|
2023-06-27 08:08:52 +10:00
|
|
|
if (gamma_correct) {
|
|
|
|
const vec3 samples[] = {
|
2023-07-30 07:32:34 +10:00
|
|
|
to_lin(texture(tex, (period + 0.5) * tx_to_uv).rgb),
|
|
|
|
to_lin(texture(tex, (period + vec2(1.5, 0.5)) * tx_to_uv).rgb),
|
|
|
|
to_lin(texture(tex, (period + vec2(0.5, 1.5)) * tx_to_uv).rgb),
|
|
|
|
to_lin(texture(tex, (period + 1.5) * tx_to_uv).rgb)};
|
|
|
|
return to_srgb(mix(mix(samples[0], samples[1], offset.x),
|
|
|
|
mix(samples[2], samples[3], offset.x), offset.y));
|
2023-06-27 08:08:52 +10:00
|
|
|
} else {
|
2023-07-30 07:32:34 +10:00
|
|
|
return texture(tex, (period + 0.5 + offset) * tx_to_uv).rgb;
|
2023-06-27 08:08:52 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-30 07:32:34 +10:00
|
|
|
// Function to get a pixel value, taking into consideration possible subpixel
|
|
|
|
// interpolation.
|
|
|
|
vec4 pixel_aa(sampler2D tex, vec2 tx_per_px, vec2 tx_to_uv, vec2 tx_coord,
|
|
|
|
float sharpness, bool gamma_correct, bool sample_subpx,
|
|
|
|
bool subpx_bgr) {
|
|
|
|
if (sample_subpx) {
|
|
|
|
// Subpixel sampling: Shift the sampling by 1/3rd of an output pixel for
|
|
|
|
// each subpixel, assuming that the output size is at monitor
|
|
|
|
// resolution.
|
|
|
|
const vec2 sub_tx_offset =
|
|
|
|
vec2(tx_per_px.x / 3.0 * (subpx_bgr ? -1.0 : 1.0), 0.0);
|
|
|
|
vec3 res;
|
|
|
|
for (int i = -1; i < 2; ++i) {
|
|
|
|
res[i + 1] = sample_aa(tex, tx_per_px, tx_to_uv,
|
|
|
|
tx_coord + sub_tx_offset * float(i),
|
|
|
|
sharpness, gamma_correct)[i + 1];
|
|
|
|
}
|
|
|
|
return vec4(res, 1.0);
|
|
|
|
} else {
|
|
|
|
return vec4(sample_aa(tex, tx_per_px, tx_to_uv, tx_coord, sharpness,
|
|
|
|
gamma_correct),
|
|
|
|
1.0);
|
|
|
|
}
|
2023-06-27 19:52:21 +10:00
|
|
|
}
|