aboutsummaryrefslogtreecommitdiff
path: root/res/shaders/framebuffer.fs
blob: b644980016527b7db53e45c0ad38a2fb2cdb6e87 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#version 420 core

in vec2 _f_texture;

layout(binding = 2) uniform sampler2D main_colour;
layout(binding = 3) uniform sampler2D main_depth;
layout(binding = 4) uniform sampler2D water_colour;
layout(binding = 5) uniform sampler2D water_depth;

layout(std140, binding = 0) uniform _u_globals {
    mat4 proj;
    mat4 view;
    vec4 frustum[6];
    float znear;
    float zfar;
    float xfov;
    float yfov;
    int time;
    float xwindow;
    float ywindow;
};

uniform bool _u_is_underwater;

const vec3 fog_colour = vec3(0.5f, 0.7f, 0.9f);
const vec3 underwater_fog_colour = vec3(0.137f, 0.275f, 0.694f);

out vec4 _o_colour;

vec4 texture_sample(const sampler2D sampler, const vec2 coord) {
    return texture(sampler, coord);
}

float depth_sample(const sampler2D sampler, const vec2 coord) {
    return texture(sampler, coord).r;
}

float linearise_depth(const float depth) {
    const float z = depth * 2.0f - 1.0f; // normalised device coordinates
    const float linear_depth =
        (2.0 * znear * zfar) / (zfar + znear - (z * (zfar - znear)));
    // Usually we would just return linear depth, but this is incorrect! We need
    // to account for the additional distance of the frustum away from the
    // center of the screen. Just some trigonomety

    const float xsigma = tan(xfov * 0.5) * zfar * _f_texture.x;
    const float ysigma = tan(yfov * 0.5) * zfar * _f_texture.y;
    const float dist = sqrt(xsigma * xsigma + ysigma * ysigma) / zfar;

    const float o_dist = linear_depth * dist;
    return sqrt(o_dist * o_dist + linear_depth * linear_depth);
}

float get_fog_intensity(const float distance) {
    return clamp(pow((1.0f / (zfar * 0.6f)) * distance, 3.0f), 0.0f, 1.0f);
}

float get_water_intensity(const float distance) {
    return 1.0f - exp(0.5 * distance);
}

void main() {
    // We recycle the coordinates of our position into our texture coordinates.
    const vec2 coords =
        vec2((_f_texture.x + 1.0f) / 2.0f, (_f_texture.y + 1.0f) / 2.0f);

    const float main_depth = depth_sample(main_depth, coords);
    const float main_distance = linearise_depth(main_depth);
    const float water_depth = depth_sample(water_depth, coords);
    const float water_distance = linearise_depth(water_depth);

    _o_colour = texture_sample(main_colour, coords);
    if (!_u_is_underwater) {
        if (water_depth < main_depth) {
            _o_colour =
                vec4(mix(vec3(_o_colour.rgb),
                         texture_sample(water_colour, coords).rgb,
                         get_water_intensity(water_distance - main_distance)),
                     1.0f);
        }
        _o_colour =
            vec4(mix(vec3(_o_colour.rgb), fog_colour,
                     get_fog_intensity(min(main_distance, water_distance))),
                 1.0f);
    } else {
        _o_colour = vec4(mix(vec3(_o_colour.rgb), fog_colour,
                             get_fog_intensity(main_distance)),
                         1.0f);
        if (water_depth < main_depth) {
            _o_colour = vec4(mix(vec3(_o_colour.rgb),
                                 texture_sample(water_colour, coords).rgb,
                                 get_water_intensity(-water_distance)),
                             1.0f);
        } else {
            _o_colour = vec4(mix(vec3(_o_colour.rgb), underwater_fog_colour,
                                 get_water_intensity(-main_distance)),
                             1.0f);
        }
    }
}