#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); } } }