#version 450
layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform sampler2D norm_rough;
layout(set = 0, binding = 1) uniform sampler2D in_depth;
layout(set = 0, binding = 2) uniform sampler2D hdr_color;
layout(set = 0, binding = 3) uniform sampler2D velocity_buffer;
layout(set = 0, binding = 4) writeonly uniform image2D half_res_specular;
layout(push_constant) uniform ProjectionData
{
mat4 mv;
mat4 p;
//vec3 view_pos;
} projection_data;
#include<common.h>
#include<pbr.h>
vec2 clip_to_uv(vec2 clip)
{
return clip / 2 - 0.5;
}
bool ray_march(ivec2 res, vec3 o, vec3 d, mat4 pv, float near_plane, float thickness, float stride, float jitter, const float max_steps, float max_distance, out vec2 hit)
{
float ray_length = ((o.z + d.z * max_distance) < near_plane) ?
(near_plane - o.z) / d.z:
max_distance;
vec3 target = o + d * ray_length;
hit = vec2(-1, -1);
//project into screen space
vec4 H0 = pv * vec4(o, 1.0),
H1 = pv * vec4(target, 1.0);
float k0 = 1.0 / H0.w,
k1 = 1.0 / H1.w;
vec3 Q0 = o * k0,
Q1 = target * k1;
//screen space endpoints
vec2 P0 = H0.xy * k0,
P1 = H1.xy * k1;
float x_max = res.x + 0.5,
x_min = 0.5,
y_max = res.y + 0.5,
y_min = 0.5,
alpha = 0;
if (P1.y > y_max || P1.y < y_min)
alpha = (P1.y - ((P1.y > y_max) ? y_max : y_min)) / (P1.y - P0.y);
if ((P1.x > x_max) || (P1.x < x_min))
alpha = max(alpha, (P1.x - ((P1.x > x_max) ? x_max : x_min)) / (P1.x - P0.x));
P1 = mix(P1, P0, alpha);
k1 = mix(k1, k0, alpha);
Q1 = mix(Q1, Q0, alpha);
vec2 delta = P1 - P0;
P1 += vec2((dot(delta, delta) < 0.0001) ? 0.01: 0.0);
delta = P1 - P0;
bool permute = false;
if(abs(delta.x) < abs(delta.y))
{
permute = true;
delta = delta.yx;
P0 = P0.yx;
P1 = P1.yx;
}
float step_dir = sign(delta.x),
invdx = step_dir / delta.x;
//trach the derivatives of Q and k
vec3 dQ = (Q1 - Q0) * invdx;
float dk = (k1 - k0) * invdx;
vec2 dP = vec2(step_dir, delta.y * invdx);
dP *= stride; dQ *= stride; dk *= stride;
P0 += dP * jitter; Q0 += dQ * jitter; k0 += dk * jitter;
float prev_z_max_estimate = o.z;
//slide ...
vec3 Q = Q0;
float k = k0,
step_count = 0.0,
end = P1.x * step_dir;
for(vec2 P = P0;
((P.x * step_dir) <= end) && (step_count < max_steps);
P += dP, Q.z += dQ.z, k += dk, step_count += 1.0)
{
//project from homogeneuous to camera space
hit = permute ? P.yx : P;
float ray_z_min = prev_z_max_estimate;
float ray_z_max = (dQ.z * 0.5 + Q.z) / (dk * 0.5 + k);
prev_z_max_estimate = ray_z_max;
if(ray_z_min > ray_z_max) swap(ray_z_min, ray_z_max);
float scene_z_max = texelFetch(in_depth, ivec2(hit * 2), 0).x;
float ssr_bias = scene_z_max; ssr_bias *= ssr_bias;
vec2 scene_z_max_h = (inverse(pv) * vec4(0, 0, scene_z_max, 1.0)).zw; //todo optimize
scene_z_max = scene_z_max_h.x / scene_z_max_h.y;
float scene_z_min = scene_z_max - thickness;
float d = ray_z_max - scene_z_max;
//if(step_count > 150 && d > 0 && d < thickness)
if((ray_z_max + ssr_bias >= scene_z_min) && (ray_z_min + ssr_bias <= scene_z_max))
return all(lessThanEqual(abs(hit - (res * 0.5)), (res * 0.5)));
}
return false;
}
float fresnel(vec3 normal, vec3 vdir)
{
float F0 = 0.04;
return fresnel_schlick(max(dot(normal, vdir), 0.0), F0);
}
void main()
{
ivec2 res = imageSize(half_res_specular);
if(gl_GlobalInvocationID.x > res.x || gl_GlobalInvocationID.y > res.y)
return;
vec2 uv = vec2(gl_GlobalInvocationID.xy) / vec2(res);
float depth = texture(in_depth, uv).x;
vec3 point = world_space_location(uv, depth);
vec3 normal = texture(norm_rough, uv).xyz * 2.0 - 1.0;
mat4 p = projection_data.p;
p[0][3] = 0.0;
p[1][3] = 0.0;
p[2][3] = -1.0;
//ssr
/*vec4 camera = inverse(p*projection_data.mv)*vec4(0.0, 0.0, 0.0, 1.0);
camera.xyz /= camera.w; //not necessary*/
vec3 vdir = normalize(point - get_view_pos());
vec3 reflected = reflect(vdir, normal);
/*vec4 clip_target = (p*projection_data.mv) * vec4(point + reflected, 1.0);
clip_target.xyz /= clip_target.w;
float step_ratio = 1 / length(clip_target.xyz - point);
//vpr /= vpr.w; //useless since we normalize
vec3 cpos = vec3(uv * 2 - 1, depth);
vec2 unit_dir = normalize(clip_target.xy - cpos.xy);
float start_reciprocal = 1 / point.z;
float target_reciprocal = 1 / clip_target.z;
// ray march
float step_len = 0.01;//1/ res.x * 1.41;
bool hit = false;
cpos.xy += step_len * unit_dir * 3;
for(int i = 3; i < 100; i++)
{
if(texture(in_depth, clip_to_uv(cpos.xy)).x > 1/mix(start_reciprocal, target_reciprocal, i * step_len)
&& cpos.x < 1.0 && cpos.y < 1.0 && cpos.x > -1.0 && cpos.y > -1.0)
{
cpos.xy += step_len * unit_dir;
}
else
{
hit = cpos.x < 1.0 && cpos.y < 1.0 && cpos.x > -1.0 && cpos.y > -1.0;
}
}*/
mat4 clip_ro_screen = mat4(
vec4(res.x / 2, 0, 0, 0),
vec4(0, res.y / 2, 0, 0),
vec4(0, 0, 1, 0),
vec4(res.x / 2, res.y /2, 0, 1)
);
vec2 hitp;
vec4 point_vs = (projection_data.mv * vec4(point, 1.0)),
refl_vs = (projection_data.mv * vec4(point + reflected, 1.0));
point_vs.xyz /= point_vs.w;
refl_vs.xyz /= refl_vs.w;
float near_plane = 0.1;
/*vec2 cnp = (inverse(p) * vec4(0, 0, near_plane, 1.0)).zw;
near_plane = cnp.x / cnp.y;*/
bool hit = ray_march(res, point_vs.xyz, normalize(point_vs.xyz - refl_vs.xyz),
clip_ro_screen*p, near_plane, 1.5, 4.0, 0, 64, 500, hitp);
/*vec3 o = point;
mat4 mvp = p*projection_data.mv;
bool hit = false;
vec2 hitp;
for(int i = 0; i < 64; i++)
{
o += reflected*0.08;
vec4 o_screen = mvp * vec4(o, 1.0);
o_screen.xyz /= o_screen.w;
float s_depth = texture(in_depth, o_screen.xy / 2 + .5).x;
if(o_screen.z > s_depth + 0.01)
{
hit = true;
hitp = res * (o_screen.xy / 2 + .5);
break;
}
}*/
vec2 cpos = hitp/res;
vec2 velocity_vector = texture(velocity_buffer, cpos).xy * 2 - 1;
vec2 puv = cpos - velocity_vector;
puv = clamp(puv, 0, 1);
vec3 reflected_color;
if(hit && cpos.x > 0 && cpos.x < 1.0 && cpos.y > 0 && cpos.x < 1.0)
{
reflected_color = texture(hdr_color, puv).xyz *
fresnel(normal, -vdir);
}
else
{
reflected_color = vec3(0);
}
imageStore(half_res_specular, ivec2(gl_GlobalInvocationID.xy), reflected_color.xyzz);
}