98 lines
3.3 KiB
GLSL
98 lines
3.3 KiB
GLSL
#if !defined PIXELATION_FILE
|
|
#define PIXELATION_FILE
|
|
|
|
#if PIXEL_SCALE == -2
|
|
#define PIXEL_TEXEL_SCALE 4.0
|
|
#elif PIXEL_SCALE == -1
|
|
#define PIXEL_TEXEL_SCALE 2.0
|
|
#elif PIXEL_SCALE == 2
|
|
#define PIXEL_TEXEL_SCALE 0.5
|
|
#elif PIXEL_SCALE == 3
|
|
#define PIXEL_TEXEL_SCALE 0.25
|
|
#elif PIXEL_SCALE == 4
|
|
#define PIXEL_TEXEL_SCALE 0.125
|
|
#elif PIXEL_SCALE == 5
|
|
#define PIXEL_TEXEL_SCALE 0.0625
|
|
#else // 1 or out of range
|
|
#define PIXEL_TEXEL_SCALE 1.0
|
|
#endif
|
|
|
|
#ifdef FRAGMENT_SHADER
|
|
// Thanks to Nestorboy
|
|
|
|
// Computes axis-aligned screen space offset to texel center.
|
|
// https://forum.unity.com/threads/the-quest-for-efficient-per-texel-lighting.529948/#post-7536023
|
|
vec2 ComputeTexelOffset(vec2 uv, vec4 texelSize) {
|
|
// 1. Calculate how much the texture UV coords need to shift to be at the center of the nearest texel.
|
|
vec2 uvCenter = (floor(uv * texelSize.zw) + 0.5) * texelSize.xy;
|
|
vec2 dUV = uvCenter - uv;
|
|
|
|
// 2. Calculate how much the texture coords vary over fragment space.
|
|
// This essentially defines a 2x2 matrix that gets texture space (UV) deltas from fragment space (ST) deltas.
|
|
vec2 dUVdS = dFdx(uv);
|
|
vec2 dUVdT = dFdy(uv);
|
|
|
|
if (abs(dUVdS) + abs(dUVdT) == vec2(0.0)) return vec2(0.0);
|
|
|
|
// 3. Invert the texture delta from fragment delta matrix. Where the magic happens.
|
|
mat2x2 dSTdUV = mat2x2(dUVdT[1], -dUVdT[0], -dUVdS[1], dUVdS[0]) * (1.0 / (dUVdS[0] * dUVdT[1] - dUVdT[0] * dUVdS[1]));
|
|
|
|
// 4. Convert the texture delta to fragment delta.
|
|
vec2 dST = dUV * dSTdUV;
|
|
return dST;
|
|
}
|
|
|
|
vec2 ComputeTexelOffset(sampler2D tex, vec2 uv) {
|
|
vec2 texSize = textureSize(tex, 0) * PIXEL_TEXEL_SCALE;
|
|
vec4 texelSize = vec4(1.0 / texSize.xy, texSize.xy);
|
|
|
|
return ComputeTexelOffset(uv, texelSize);
|
|
}
|
|
|
|
vec4 TexelSnap(vec4 value, vec2 texelOffset) {
|
|
if(all(equal(texelOffset, vec2(0.0)))) return value;
|
|
vec4 dx = dFdx(value);
|
|
vec4 dy = dFdy(value);
|
|
|
|
vec4 valueOffset = dx * texelOffset.x + dy * texelOffset.y;
|
|
valueOffset = clamp(valueOffset, -1.0, 1.0);
|
|
|
|
return value + valueOffset;
|
|
}
|
|
|
|
vec3 TexelSnap(vec3 value, vec2 texelOffset) {
|
|
if(all(equal(texelOffset, vec2(0.0)))) return value;
|
|
vec3 dx = dFdx(value);
|
|
vec3 dy = dFdy(value);
|
|
|
|
vec3 valueOffset = dx * texelOffset.x + dy * texelOffset.y;
|
|
valueOffset = clamp(valueOffset, -1.0, 1.0);
|
|
|
|
return value + valueOffset;
|
|
}
|
|
|
|
vec2 TexelSnap(vec2 value, vec2 texelOffset) {
|
|
if(all(equal(texelOffset, vec2(0.0)))) return value;
|
|
vec2 dx = dFdx(value);
|
|
vec2 dy = dFdy(value);
|
|
|
|
vec2 valueOffset = dx * texelOffset.x + dy * texelOffset.y;
|
|
valueOffset = clamp(valueOffset, -1.0, 1.0);
|
|
|
|
return value + valueOffset;
|
|
}
|
|
|
|
float TexelSnap(float value, vec2 texelOffset) {
|
|
if(all(equal(texelOffset, vec2(0.0)))) return value;
|
|
float dx = dFdx(value);
|
|
float dy = dFdy(value);
|
|
|
|
float valueOffset = dx * texelOffset.x + dy * texelOffset.y;
|
|
valueOffset = clamp(valueOffset, -1.0, 1.0);
|
|
|
|
return value + valueOffset;
|
|
}
|
|
#endif
|
|
|
|
#endif // !defined PIXELATION_FILE
|