group_bot/assets/voxygen/shaders/lod-terrain-frag.glsl

402 lines
20 KiB
Plaintext
Raw Normal View History

2024-06-13 23:16:24 +00:00
#version 440 core
#include <constants.glsl>
#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION
#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
#if (FLUID_MODE == FLUID_MODE_LOW)
#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
#endif
// #define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_VOXEL
#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET
#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN
#define HAS_LOD_FULL_INFO
#include <globals.glsl>
#include <cloud.glsl>
#include <lod.glsl>
layout(location = 0) in vec3 f_pos;
layout(location = 1) in vec3 f_norm;
layout(location = 2) in float pull_down;
// in vec2 v_pos_orig;
// in vec4 f_shadow;
// in vec4 f_square;
layout(location = 0) out vec4 tgt_color;
layout(location = 1) out uvec4 tgt_mat;
/// const vec4 sun_pos = vec4(0);
// const vec4 light_pos[2] = vec4[](vec4(0), vec4(0)/*, vec3(00), vec3(0), vec3(0), vec3(0)*/);
#include <sky.glsl>
void main() {
// tgt_color = vec4(vec3(1.0), 1.0);
// return;
// vec3 f_pos = lod_pos(f_pos.xy);
// vec3 f_col = lod_col(f_pos.xy);
// vec4 vert_pos4 = view_mat * vec4(f_pos, 1.0);
// vec3 view_dir = normalize(-vec3(vert_pos4)/* / vert_pos4.w*/);
#ifdef EXPERIMENTAL_BAREMINIMUM
tgt_color = vec4(simple_lighting(f_pos.xyz, lod_col(f_pos.xy), 1.0), 1);
return;
#endif
float my_alt = /*f_pos.z;*/alt_at_real(f_pos.xy);
// vec3 f_pos = vec3(f_pos.xy, max(my_alt, f_pos.z));
/* gl_Position =
proj_mat *
view_mat *
vec4(f_pos, 1);
gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); */
vec3 my_pos = vec3(f_pos.xy, my_alt);
//vec3 my_norm = lod_norm(f_pos.xy/*, f_square*/);
//float which_norm = dot(my_norm, normalize(cam_pos.xyz - my_pos));
// which_norm = 0.5 + which_norm * 0.5;
// which_norm = pow(max(0.0, which_norm), /*0.03125*/1 / 8.0);// * 0.5;
// smoothstep
//which_norm = which_norm * which_norm * (3 - 2 * abs(which_norm));
// which_norm = mix(0.0, 1.0, which_norm > 0.0);
// vec3 normals[6] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1));
vec3 f_norm = lod_norm(f_pos.xy);//mix(faceforward(f_norm, cam_pos.xyz - f_pos, -f_norm), my_norm, which_norm);
vec3 f_pos = mix(f_pos, my_pos, f_norm);
// vec3 fract_pos = fract(f_pos);
/* if (length(f_pos - cam_pos.xyz) <= view_distance.x + 32.0) {
vec4 new_f_pos;
float depth = 10000000.0;
vec4 old_coord = all_mat * vec4(f_pos.xyz, 1.0);
for (int i = 0; i < 6; i ++) {
// vec4 square = focus_pos.xy + vec4(splay(pos - vec2(1.0, 1.0), splay(pos + vec2(1.0, 1.0))));
vec3 my_f_norm = normals[i];
vec3 my_f_tan = normals[(i + 2) % 6];
vec3 my_f_bitan = normals[(i + 4) % 6];
mat4 foo = mat4(vec4(my_f_tan, 0), vec4(my_f_bitan, 0), vec4(my_f_norm, 0), vec4(0, 0, 0, 1));
mat4 invfoo = foo * inverse(foo * all_mat);
vec4 my_f_pos = invfoo * (old_coord);//vec4(f_pos, 1.0);
vec4 my_f_proj = all_mat * my_f_pos;
if (my_f_proj.z <= depth) {
new_f_pos = my_f_pos;
f_norm = my_f_norm;
depth = my_f_proj.z;
}
}
// f_pos = new_f_pos.xyz;
} */
// Test for distance to all 6 sides of the enclosing cube.
// if (/*any(lessThan(fract(f_pos.xy), 0.01))*/fract_pos.x <= 0.1) {
// f_norm = faceforward(vec3(-1, 0, 0), f_norm, vec3(1, 0, 0));
// f_tan = vec3(0, 1, 0);
// } else if (fract_pos.y <= 0.1) {
// f_norm = faceforward(vec3(0, -1, 0), f_norm, vec3(0, 1, 0));
// f_tan = vec3(0, 0, 1);
// } else {
// f_norm = faceforward(vec3(0, 0, -1), f_norm, vec3(0, 0, 1));
// f_tan = vec3(1, 0, 0);
// }
// vec3 f_bitan = cross(f_norm, f_tan);
// mat4 foo = mat4(vec4(f_tan, 0), vec4(f_bitan, 0), vec4(f_norm, 0), vec4(0, 0, 0, 1));
// mat4 invfoo = foo * inverse(foo * all_mat);
// vec3 old_coord = all_mat * vec4(f_pos.xyz, 1.0);
// vec4 new_f_pos = invfoo * (old_coord);//vec4(f_pos, 1.0);
vec3 f_col_raw = mix(lod_col(f_pos.xy), vec3(0), clamp(pull_down / 30, 0, 1));
// tgt_color = vec4(f_col, 1.0);
vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz);
vec3 view_dir = -cam_to_frag;
float f_ao = 1.0;
vec3 voxel_norm = f_norm;
const float VOXELIZE_DIST = 2000;
float voxelize_factor = clamp(1.0 - (distance(cam_pos.xy, f_pos.xy) - view_distance.x) * (1.0 / VOXELIZE_DIST), 0, 1);
vec3 cam_dir = cam_to_frag;
#ifdef EXPERIMENTAL_NOLODVOXELS
//vec3 side_norm = normalize(vec3(my_norm.xy, 0.01));
//vec3 top_norm = vec3(0, 0, 1);
voxel_norm = f_norm;//normalize(mix(side_norm, top_norm, max(cam_dir.z, 0.0)));
#else
#ifdef EXPERIMENTAL_PROCEDURALLODDETAIL
float nz_offset = floor((noise_2d((floor(f_pos.xy) + focus_off.xy) * 0.01) - 0.5) * 3.0 / max(f_norm.z, 0.01));
#else
const float nz_offset = 0.0;
#endif
float t = -2.0 + nz_offset;
while (t < 2.0 + nz_offset) {
vec3 deltas = (step(vec3(0), -cam_dir) - fract(f_pos - cam_dir * t)) / -cam_dir;
float m = min(min(deltas.x, deltas.y), deltas.z);
t += max(m, 0.01);
vec3 block_pos = floor(f_pos - cam_dir * t) + 0.5;
if (dot(block_pos - f_pos - nz_offset * f_norm, -f_norm) < 0.0) {
vec3 to_center = abs(block_pos - (f_pos - cam_dir * t));
voxel_norm = step(max(max(to_center.x, to_center.y), to_center.z), to_center) * sign(-cam_dir);
voxel_norm = mix(f_norm, voxel_norm, voxelize_factor);
f_ao = mix(1.0, clamp(1.0 + (t - nz_offset) * 0.5, 0.1, 1.0), voxelize_factor * max(0.0, -dot(cam_dir, f_norm)));
break;
}
}
#endif
/* vec3 sun_dir = get_sun_dir(time_of_day.x);
vec3 moon_dir = get_moon_dir(time_of_day.x); */
// voxel_norm = vec3(0.0);
#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE >= FLUID_MODE_MEDIUM)
float shadow_alt = /*f_pos.z;*/alt_at(f_pos.xy);//max(alt_at(f_pos.xy), f_pos.z);
// float shadow_alt = f_pos.z;
#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_LOW)
float shadow_alt = f_pos.z;
#endif
#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP)
vec4 f_shadow = textureMaybeBicubic(t_horizon, s_horizon, pos_to_tex(f_pos.xy));
float sun_shade_frac = horizon_at2(f_shadow, shadow_alt, f_pos, sun_dir);
// float sun_shade_frac = 1.0;
#elif (SHADOW_MODE == SHADOW_MODE_NONE)
float sun_shade_frac = 1.0;//horizon_at2(f_shadow, shadow_alt, f_pos, sun_dir);
#endif
float moon_shade_frac = 1.0;//horizon_at2(f_shadow, shadow_alt, f_pos, moon_dir);
// Magic stop-gap code without any physical justification.
//vec3 lerpy_norm;
//if (my_norm.z/*f_norm.z*/ > 0.99999) {
// lerpy_norm = vec3(0, 0, 1);
//} else {
// vec3 side_norm = normalize(vec3(my_norm.xy, 0));
// // lerpy_norm = f_norm;
// float mix_factor = clamp(abs(dot(f_orig_view_dir, side_norm)), 0, 1);
// lerpy_norm = mix(
// mix(my_norm, side_norm, clamp(dot(side_norm, my_norm) + 0.5, 0, 1)),
// my_norm,
// mix_factor
// );
//}
//const float DIST = 0.07;
/* voxel_norm = normalize(mix(voxel_norm, lerpy_norm, clamp(my_norm.z * my_norm.z - (1.0 - DIST), 0, 1) / DIST)); */
//f_pos.xyz += abs(voxel_norm) * delta_sides;
/* voxel_norm = mix(my_norm, voxel_norm == vec3(0.0) ? f_norm : voxel_norm, voxelize_factor); */
//vec3 hash_pos = f_pos + focus_off.xyz;
//const float A = 0.055;
//const float W_INV = 1 / (1 + A);
//const float W_2 = W_INV * W_INV;//pow(W_INV, 2.4);
//const float NOISE_FACTOR = 0.02;//pow(0.02, 1.2);
//float noise = hash(vec4(floor(hash_pos * 3.0 - voxel_norm * 0.5), 0));//0.005/* - 0.01*/;
//vec3 noise_delta = (sqrt(f_col_raw) * W_INV + noise * NOISE_FACTOR);
// noise_delta = noise_delta * noise_delta * W_2 - f_col;
// lum = W ⋅ col
// lum + noise = W ⋅ (col + delta)
// W ⋅ col + noise = W ⋅ col + W ⋅ delta
// noise = W ⋅ delta
// delta = noise / W
// vec3 col = (f_col + noise_delta);
// vec3 col = noise_delta * noise_delta * W_2;
vec3 f_col = f_col_raw;//noise_delta * noise_delta * W_2;
// f_col = /*srgb_to_linear*/(f_col + hash(vec4(floor(hash_pos * 3.0 - voxel_norm * 0.5), 0)) * 0.01/* - 0.01*/); // Small-scale noise
// f_ao = 1.0;
// f_ao = dot(f_ao_vec, sqrt(1.0 - delta_sides * delta_sides));
//f_ao *= dot(f_ao_vec, abs(voxel_norm));
// f_ao = sqrt(dot(f_ao_vec * abs(voxel_norm), sqrt(1.0 - delta_sides * delta_sides)) / 3.0);
// vec3 ao_pos2 = min(fract(f_pos), 1.0 - fract(f_pos));
// f_ao = sqrt(dot(ao_pos2, ao_pos2));
// // f_ao = dot(abs(voxel_norm), f_ao_vec);
// // voxel_norm = f_norm;
// Note: because voxels, we reduce the normal for reflections to just its z component, dpendng on distance to camera.
// Idea: the closer we are to facing top-down, the more the norm should tend towards up-z.
// vec3 l_norm; // = vec3(0.0, 0.0, 1.0);
// vec3 l_norm = normalize(vec3(f_norm.x / max(abs(f_norm.x), 0.001), f_norm.y / max(abs(f_norm.y), 0.001), f_norm.z / max(abs(f_norm.z), 0.001)));
// vec3 l_factor = 1.0 / (1.0 + max(abs(/*f_pos - cam_pos.xyz*//*-vec3(vert_pos4) / vert_pos4.w*/vec3(f_pos.xy, 0.0) - vec3(/*cam_pos*/focus_pos.xy, cam_to_frag)) - vec3(view_distance.x, view_distance.x, 0.0), 0.0) / vec3(32.0 * 2.0, 32.0 * 2.0, 1.0));
// l_factor.z =
// vec4 focus_pos4 = view_mat * vec4(focus_pos.xyz, 1.0);
// vec3 focus_dir = normalize(-vec3(focus_pos4) / focus_pos4.w);
// float l_factor = 1.0 - pow(clamp(0.5 + 0.5 * dot(/*-view_dir*/-cam_to_frag, l_norm), 0.0, 1.0), 2.0);//1.0 / (1.0 + 0.5 * pow(max(distance(/*focus_pos.xy*/vec3(focus_pos.xy, /*vert_pos4.z / vert_pos4.w*/f_pos.z), vec3(f_pos.xy, f_pos.z))/* - view_distance.x*/ - 32.0, 0.0) / (32.0 * 1.0), /*0.5*/1.0));
// l_factor = 1.0;
// l_norm = normalize(mix(l_norm, f_norm, l_factor));
// l_norm = f_norm;
/* l_norm = normalize(vec3(
mix(l_norm.x, f_norm.x, clamp(pow(f_norm.x * 0.5, 64), 0, 1)),
mix(-1.0, 1.0, clamp(pow(f_norm.y * 0.5, 64), 0, 1)),
mix(-1.0, 1.0, clamp(pow(f_norm.z * 0.5, 64), 0, 1))
)); */
// f_norm = mix(l_norm, f_norm, min(1.0 / max(cam_to_frag, 0.001), 1.0));
/* vec3 l_norm = normalize(vec3(
mix(-1.0, 1.0, clamp(pow(f_norm.x * 0.5, 64), 0, 1)),
mix(-1.0, 1.0, clamp(pow(f_norm.y * 0.5, 64), 0, 1)),
mix(-1.0, 1.0, clamp(pow(f_norm.z * 0.5, 64), 0, 1))
)); */
// vec3 view_dir = normalize(f_pos - cam_pos.xyz);
// vec3 sun_dir = get_sun_dir(time_of_day.x);
// vec3 moon_dir = get_moon_dir(time_of_day.x);
// // float sun_light = get_sun_brightness(sun_dir);
// // float moon_light = get_moon_brightness(moon_dir);
// // float my_alt = f_pos.z;//alt_at_real(f_pos.xy);
// // vec3 f_norm = my_norm;
// // vec4 f_shadow = textureMaybeBicubic(t_horizon, pos_to_tex(f_pos.xy));
// // float shadow_alt = /*f_pos.z;*/alt_at(f_pos.xy);//max(alt_at(f_pos.xy), f_pos.z);
// // float my_alt = alt_at(f_pos.xy);
// float sun_shade_frac = horizon_at2(f_shadow, shadow_alt, f_pos, sun_dir);
// float moon_shade_frac = horizon_at2(f_shadow, shadow_alt, f_pos, moon_dir);
// // float sun_shade_frac = horizon_at(/*f_shadow, f_pos.z, */f_pos, sun_dir);
// // float moon_shade_frac = horizon_at(/*f_shadow, f_pos.z, */f_pos, moon_dir);
// // Globbal illumination "estimate" used to light the faces of voxels which are parallel to the sun or moon (which is a very common occurrence).
// // Will be attenuated by k_d, which is assumed to carry any additional ambient occlusion information (e.g. about shadowing).
// // float ambient_sides = clamp(mix(0.5, 0.0, abs(dot(-f_norm, sun_dir)) * 10000.0), 0.0, 0.5);
// // NOTE: current assumption is that moon and sun shouldn't be out at the sae time.
// // This assumption is (or can at least easily be) wrong, but if we pretend it's true we avoids having to explicitly pass in a separate shadow
// // for the sun and moon (since they have different brightnesses / colors so the shadows shouldn't attenuate equally).
// // float shade_frac = sun_shade_frac + moon_shade_frac;
// // float brightness_denominator = (ambient_sides + vec3(SUN_AMBIANCE * sun_light + moon_light);
// DirectionalLight sun_info = get_sun_info(sun_dir, sun_shade_frac, light_pos);
DirectionalLight sun_info = get_sun_info(sun_dir, sun_shade_frac, /*sun_pos*/f_pos);
DirectionalLight moon_info = get_moon_info(moon_dir, moon_shade_frac/*, light_pos*/);
float alpha = 1.0;//0.1;//0.2;///1.0;//sqrt(2.0);
const float n2 = 1.5;
const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2);
const float R_s1s0 = pow((1.3325 - n2) / (1.3325 + n2), 2);
const float R_s2s1 = pow((1.0 - 1.3325) / (1.0 + 1.3325), 2);
const float R_s1s2 = pow((1.3325 - 1.0) / (1.3325 + 1.0), 2);
float cam_alt = alt_at(cam_pos.xy);
float fluid_alt = medium.x == MEDIUM_WATER ? max(cam_alt + 1, floor(shadow_alt)) : view_distance.w;
float R_s = (f_pos.z < my_alt) ? mix(R_s2s1 * R_s1s0, R_s1s0, medium.x) : mix(R_s2s0, R_s1s2 * R_s2s0, medium.x);
vec3 emitted_light, reflected_light;
vec3 mu = medium.x == MEDIUM_WATER ? MU_WATER : vec3(0.0);
// NOTE: Default intersection point is camera position, meaning if we fail to intersect we assume the whole camera is in water.
vec3 cam_attenuation = compute_attenuation_point(f_pos, view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz);
// Use f_norm here for better shadows.
// vec3 light_frac = light_reflection_factor(f_norm/*l_norm*/, view_dir, vec3(0, 0, -1.0), vec3(1.0), vec3(/*1.0*/R_s), alpha);
// vec3 light, diffuse_light, ambient_light;
// get_sun_diffuse(f_norm, time_of_day.x, cam_to_frag, (0.25 * shade_frac + 0.25 * light_frac) * f_col, 0.5 * shade_frac * f_col, 0.5 * shade_frac * /*vec3(1.0)*/f_col, 2.0, emitted_light, reflected_light);
float max_light = 0.0;
vec3 k_a = vec3(1.0);
vec3 k_d = vec3(1.0);
max_light += get_sun_diffuse2(sun_info, moon_info, voxel_norm/*l_norm*/, view_dir, f_pos, vec3(0.0), cam_attenuation, fluid_alt, k_a/* * (0.5 * light_frac + vec3(0.5 * shade_frac))*/, k_d, /*0.5 * shade_frac * *//*vec3(1.0)*//*f_col*/vec3(R_s), alpha, voxel_norm, 0.0/*max(distance(focus_pos.xy, f_pos.xyz) - view_distance.x, 0.0) / 1000 < 1.0*/, emitted_light, reflected_light);
// emitted_light = vec3(1.0);
// emitted_light *= max(shade_frac, MIN_SHADOW);
// reflected_light *= shade_frac;
// max_light *= shade_frac;
// reflected_light = vec3(0.0);
// dot(diffuse_factor, /*R_r * */vec4(abs(norm) * (1.0 - dist), dist))
// corner_xy = mix(all(lessThan(corner_xy, 1.0)) ? vec2(0.0) : 0.4 * (), 1.0
//
// TODO: Handle similar logic for z.
// So we repeat this for all three sides to find the "next" position on each side.
// vec3 delta_sides = 1.0 + sides * fract(-sides * f_pos);
// Now, we
// Now, all we have to do is find out whether (again, assuming f_pos is positive) next_sides represents a new integer.
// We currently just treat this as "new floor != old floor".
// So to find the position at the nearest voxel, we just subtract voxel_norm * fract(sides * ) from f_pos.z.
// Then to find out whether we meet a new "block" in 1 voxel, we just
// on the "other" side can be found (according to my temporary theory) as the cross product
// vec3 norm = normalize(cross(
// vec3(/*2.0 * SAMPLE_W*/square.z - square.x, 0.0, altx1 - altx0),
// vec3(0.0, /*2.0 * SAMPLE_W*/square.w - square.y, alty1 - alty0)
// ));
// vec3 norm = normalize(vec3(
// (altx0 - altx1) / (square.z - square.x),
// (alty0 - alty1) / (square.w - square.y),
// 1.0
// //(abs(square.w - square.y) + abs(square.z - square.x)) / (slope + 0.00001) // Avoid NaN
// ));
//
// If a side coordinate is 0, then it counts as no AO;
// otherwise, it counts as fractional AO. So what we need is to know whether the fractional AO to the next block in that direction pushes us to a new integer.
//
// vec3 ao_pos_z = floor(f_pos + f_norm);
// vec3 ao_pos_z = corner_distance;
// vec3 ao_pos = 0.5 - clamp(min(fract(abs(f_pos)), 1.0 - fract(abs(f_pos))), 0.0, 0.5);
//
// f_ao = /*sqrt*/1.0 - 2.0 * sqrt(dot(ao_pos, ao_pos) / 2.0);
// f_ao = /*sqrt*/1.0 - (dot(ao_pos, ao_pos)/* / 2.0*/);
// f_ao = /*sqrt*/1.0 - 2.0 * (dot(ao_pos, ao_pos)/* / 2.0*/);
// f_ao = /*sqrt*/1.0 - 2.0 * sqrt(dot(ao_pos, ao_pos) / 2.0);
float ao = f_ao;// /*pow(f_ao, 0.5)*/f_ao * 0.9 + 0.1;
emitted_light *= ao;
reflected_light *= ao;
// emitted_light += 0.5 * vec3(SUN_AMBIANCE * sun_shade_frac * sun_light + moon_shade_frac * moon_light) * f_col * (ambient_sides + 1.0);
// Ambient lighting attempt: vertical light.
// reflected_light += /*0.0125*/0.15 * 0.25 * _col * light_reflection_factor(f_norm, cam_to_frag, vec3(0, 0, -1.0), 0.5 * f_col, 0.5 * f_col, 2.0);
// emitted_light += /*0.0125*/0.25 * f_col * ;
// vec3 light, diffuse_light, ambient_light;
// get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0);
// vec3 surf_color = illuminate(f_col, light, diffuse_light, ambient_light);
// f_col = f_col + (hash(vec4(floor(vec3(focus_pos.xy + splay(v_pos_orig), f_pos.z)) * 3.0 - round(f_norm) * 0.5, 0)) - 0.5) * 0.05; // Small-scale noise
vec3 surf_color;
float surf_alpha = 1.0;
uint mat;
// NOTE: On nvidea vulkan drivers a `pow` with negative base results in NaN even if the
// exponent is an integer.
vec3 water_col_diff = f_col_raw - vec3(0.02, 0.06, 0.22);
if (dot(water_col_diff * water_col_diff, vec3(1)) < 0.01 && dot(vec3(0, 0, 1), f_norm) > 0.9) {
mat = MAT_FLUID;
vec3 reflect_ray = cam_to_frag * vec3(1, 1, -1);
#if (FLUID_MODE >= FLUID_MODE_MEDIUM)
vec3 water_color = (1.0 - MU_WATER) * MU_SCATTER;
float passthrough = dot(faceforward(f_norm, f_norm, cam_to_frag), -cam_to_frag);
vec3 reflect_color;
#if (FLUID_MODE == FLUID_MODE_HIGH)
reflect_color = get_sky_color(reflect_ray, f_pos, vec3(-100000), 0.125, false, 1.0, true, sun_shade_frac);
reflect_color = get_cloud_color(reflect_color, reflect_ray, cam_pos.xyz, 100000.0, 0.1);
#else
reflect_color = get_sky_color(reflect_ray, f_pos, vec3(-100000), 0.125, false, 1.0, true, sun_shade_frac);
#endif
reflect_color *= sun_shade_frac * 0.75 + 0.25;
const float REFLECTANCE = 1.0;
surf_color = illuminate(max_light, view_dir, f_col * emitted_light, reflect_color * REFLECTANCE + water_color * reflected_light);
const vec3 underwater_col = vec3(0.0);
float min_refl = min(emitted_light.r, min(emitted_light.g, emitted_light.b));
surf_color = mix(underwater_col, surf_color, (1.0 - passthrough) * 1.0 / (1.0 + min_refl));
surf_alpha = 1.0 - passthrough;
#else
surf_alpha = 0.9;
surf_color = get_sky_color(reflect_ray, f_pos, vec3(-100000), 0.125, true, 1.0, true, sun_shade_frac);
#endif
} else {
mat = MAT_LOD;
surf_color = illuminate(max_light, view_dir, f_col * emitted_light, f_col * reflected_light);
}
tgt_color = vec4(surf_color, surf_alpha);
tgt_mat = uvec4(uvec3((f_norm + 1.0) * 127.0), mat);
}