#version 330
in vec3 fragPosition;
in vec3 fragNormal;
in vec2 fragTexCoord;
in vec4 fragColor;
uniform sampler2D texture0;
uniform sampler2D depth_tex;
uniform sampler2D normal_tex;
uniform float tan_half_fov;
uniform float aspect;
out vec4 finalColor;
// dither
const mat4 bayerMatrix = mat4(
0.0/16.0, 12.0/16.0, 3.0/16.0, 15.0/16.0,
8.0/16.0, 4.0/16.0, 11.0/16.0, 7.0/16.0,
2.0/16.0, 14.0/16.0, 1.0/16.0, 13.0/16.0,
10.0/16.0, 6.0/16.0, 9.0/16.0, 5.0/16.0
);
// ssao
const int AO_SAMPLES = 16;
const float AO_RADIUS = 1.5;
const float AO_BIAS = 0.05;
const float AO_STRENGTH = 0.6;
// pixelization + other
float pixel_size = 1.0;
float contrast = 1.5;
float levels = 32.0;
vec2 gamma = vec2(0.5, 1.5);
// isoteric.
const float near_plane = 0.01;
const float far_plane = 1000.0;
const float fog_far = 150.0;
float linearize_depth(float d) {
float z = d * 2.0 - 1.0;
float lin = (2.0 * near_plane * far_plane) / (far_plane + near_plane - z * (far_plane - near_plane));
return clamp(lin / fog_far, 0.0, 1.0);
}
float view_dist(float d) {
float z = d * 2.0 - 1.0;
return (2.0 * near_plane * far_plane) / (far_plane + near_plane - z * (far_plane - near_plane));
}
vec3 view_pos(vec2 uv, float d) {
float lz = view_dist(d);
vec2 ndc = uv * 2.0 - 1.0;
return vec3(ndc * vec2(aspect, 1.0) * tan_half_fov * lz, -lz);
}
float hash(vec2 p) {
return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
}
float ssao(vec2 uv, float depth, vec2 texsize) {
vec3 P = view_pos(uv, depth);
vec3 N = normalize(texture(normal_tex, uv).xyz * 2.0 - 1.0);
float ang = hash(floor(uv * texsize)) * 6.2831853;
vec3 rvec = vec3(cos(ang), sin(ang), 0.0);
vec3 T = normalize(rvec - N * dot(rvec, N));
vec3 B = cross(N, T);
mat3 TBN = mat3(T, B, N);
float occ = 0.0;
for (int i = 0; i < AO_SAMPLES; i++) {
float e = (float(i) + 0.5) / float(AO_SAMPLES);
float phi = float(i) * 2.399963;
vec3 dir = vec3(cos(phi) * sqrt(e), sin(phi) * sqrt(e), sqrt(1.0 - e));
float s = fract(float(i) * 0.618034 + 0.1);
float scale = mix(0.1, 1.0, s * s);
vec3 sp = P + TBN * dir * (AO_RADIUS * scale);
if (sp.z >= -near_plane) continue;
vec2 suv = (sp.xy / (-sp.z * vec2(aspect, 1.0) * tan_half_fov)) * 0.5 + 0.5;
if (any(lessThan(suv, vec2(0.0))) || any(greaterThan(suv, vec2(1.0)))) continue;
float sd = texture(depth_tex, suv).r;
if (sd >= 0.99999) continue;
float scene_dist = view_dist(sd);
float sample_dist = -sp.z;
float hit = (scene_dist <= sample_dist - AO_BIAS) ? 1.0 : 0.0;
float range = smoothstep(0.0, 1.0, AO_RADIUS / abs(view_dist(depth) - scene_dist));
occ += hit * range;
}
return 1.0 - AO_STRENGTH * occ / float(AO_SAMPLES);
}
void main() {
// pixelization
vec2 texsize = textureSize(texture0, 0);
vec2 pixel_c = texsize / (pixel_size * 2.0);
vec2 pixel_uv = floor(fragTexCoord * pixel_c) / pixel_c;
vec2 p = pixel_uv - vec2(0.5);
float r = length(p);
vec2 dist = p * (1.0 + 0.25 * r);
pixel_uv = dist * 0.5 + vec2(0.5);
float depth = texture(depth_tex, pixel_uv).r;
vec3 normal = texture(normal_tex, pixel_uv).xyz * 2.0 - 1.0;
vec4 color = texture(texture0, pixel_uv) * fragColor;
// ssao
if (depth < 1) {
color.rgb *= ssao(pixel_uv, depth, texsize);
}
// dither
{
float lum = dot(color.rgb, vec3(0.299, 0.587, 0.114));
int x = int(mod(gl_FragCoord.x, 4.0));
int y = int(mod(gl_FragCoord.y, 4.0));
float thres = bayerMatrix[x][y];
color.rgb = (lum > thres) ? color.rgb * 2.0 : vec3(0.0);
}
// gamma + posterization
color.rgb = pow(color.rgb, vec3(gamma.x));
{
float gray = max(color.r, max(color.g, color.b));
float low = floor(gray * levels) / levels;
float high = ceil(gray * levels) / levels;
float level = abs(low - gray) < abs(high - gray) ? low : high;
float adj = level / gray;
color.rgb *= adj;
}
color.rgb = pow(color.rgb, vec3(gamma.y));
// vignette
float diff = length(fragTexCoord - vec2(0.5));
color.rgb *= 1.0 - diff;
color.rgb *= pow(1.0 - linearize_depth(depth), 4.0);
finalColor = color;
}