289 lines
9.3 KiB
GLSL
289 lines
9.3 KiB
GLSL
layout(location = 1) in vec2 f_uv;
|
|
layout(location = 5) in vec3 f_normal;
|
|
layout(location = 8) in vec4 f_world_position;
|
|
|
|
layout(location = 3) flat in int f_material_id;
|
|
|
|
layout(location = 0) out vec2 o_displace_mask;
|
|
layout(location = 1) out vec4 o_displace_ssr;
|
|
|
|
layout(push_constant) uniform Constants
|
|
{
|
|
vec4 m_displace_direction;
|
|
} u_push_constants;
|
|
|
|
#include "utils/camera.glsl"
|
|
#include "utils/constants_utils.glsl"
|
|
#include "utils/sample_mesh_texture.glsl"
|
|
#include "../utils/displace_utils.frag"
|
|
#include "../utils/screen_space_reflection.frag"
|
|
|
|
layout (set = 2, binding = 2) uniform samplerCube u_skybox_texture;
|
|
layout (set = 3, binding = 0) uniform sampler2D u_displace_color;
|
|
layout (set = 3, binding = 1) uniform sampler2DShadow u_depth;
|
|
layout (set = 3, binding = 2) uniform sampler2D u_hiz_depth;
|
|
|
|
#ifdef PBR_ENABLED
|
|
|
|
// Start tracing in this level.
|
|
#define HIZ_START_LEVEL 0
|
|
// Stop tracing if current level is higher than this. (higher level means lower value)
|
|
#define HIZ_STOP_LEVEL 0
|
|
#define HIZ_MAX_LEVEL 6
|
|
|
|
// Set to 1 to disable HiZ and perform naive linear search.
|
|
#define DEBUG_LINEAR_SEARCH 0
|
|
#define MAX_THICKNESS 0.001
|
|
|
|
vec3 intersectDepthPlane(vec3 o, vec3 d, float z)
|
|
{
|
|
return o + d * z;
|
|
}
|
|
|
|
// Index of the cell that contains the given 2D position.
|
|
ivec2 getCell(vec2 screenUV, ivec2 cellCount)
|
|
{
|
|
return ivec2(screenUV * cellCount);
|
|
}
|
|
|
|
// The number of cells in the quad tree at the given level.
|
|
ivec2 getCellCount(int level)
|
|
{
|
|
return textureSize(u_hiz_depth, level);
|
|
}
|
|
|
|
// Returns screen space position of the intersection
|
|
// between o + d*t and the closest cell boundary at current HiZ level.
|
|
vec3 intersectCellBoundary(
|
|
vec3 pos, vec3 dir,
|
|
ivec2 cell, ivec2 cellCount,
|
|
vec2 crossStep, vec2 crossOffset)
|
|
{
|
|
vec3 intersection = vec3(0.0);
|
|
|
|
vec2 index = cell + crossStep;
|
|
vec2 boundary = index / vec2(cellCount); // Screen space position of the boundary
|
|
boundary += crossOffset;
|
|
|
|
vec2 delta = boundary - pos.xy;
|
|
delta /= dir.xy;
|
|
float t = min(delta.x, delta.y);
|
|
|
|
intersection = intersectDepthPlane(pos, dir, t);
|
|
return intersection;
|
|
}
|
|
|
|
bool crossedCellBoundary(ivec2 oldCellIx, ivec2 newCellIx)
|
|
{
|
|
return any(notEqual(oldCellIx, newCellIx));
|
|
}
|
|
|
|
// Minimum depth of the current cell in the current HiZ level.
|
|
float getMinDepthPlane(ivec2 cellIx, int level)
|
|
{
|
|
return texelFetch(u_hiz_depth, cellIx, level).x;
|
|
}
|
|
|
|
float getMaxTraceDistance(vec3 p, vec3 v)
|
|
{
|
|
vec3 traceDistances;
|
|
if (v.x < 0.0)
|
|
traceDistances.x = p.x / (-v.x);
|
|
else
|
|
traceDistances.x = (1.0 - p.x) / v.x;
|
|
|
|
if (v.y < 0.0)
|
|
traceDistances.y = p.y / (-v.y);
|
|
else
|
|
traceDistances.y = (1.0 - p.y) / v.y;
|
|
|
|
if (v.z < 0.0)
|
|
traceDistances.z = p.z / (-v.z);
|
|
else
|
|
traceDistances.z = (1.0 - p.z) / v.z;
|
|
|
|
return min(traceDistances.x, min(traceDistances.y, traceDistances.z));
|
|
}
|
|
|
|
// p : Screen space position
|
|
// v : Screen space reflection direction
|
|
// hitPointSS : Returns screen space hit point
|
|
// Return value : Whether RT actually hit a surface
|
|
bool traceHiZ(vec3 p, vec3 v, out vec2 hitPointSS)
|
|
{
|
|
const int maxLevel = min(HIZ_MAX_LEVEL, textureQueryLevels(u_hiz_depth) - 1); // Last mip level
|
|
float maxTraceDistance = getMaxTraceDistance(p, v);
|
|
|
|
// Get the cell cross direction and a small offset to enter
|
|
// the next cell when doing cell crossing.
|
|
vec2 crossStep = vec2(v.x >= 0 ? 1 : -1, v.y >= 0 ? 1 : -1);
|
|
vec2 crossOffset = crossStep / u_camera.m_viewport.zw / 128.;
|
|
crossStep = clamp(crossStep, 0.0, 1.0);
|
|
|
|
// Set current ray to the original screen coordinate and depth.
|
|
vec3 ray = p;
|
|
float minZ = ray.z;
|
|
float maxZ = ray.z + v.z * maxTraceDistance;
|
|
float deltaZ = maxZ - minZ;
|
|
|
|
vec3 o = ray;
|
|
vec3 d = v * maxTraceDistance;
|
|
|
|
int level = HIZ_START_LEVEL;
|
|
int deepestLevel = level;
|
|
#if DEBUG_LINEAR_SEARCH
|
|
level = 0;
|
|
#endif
|
|
uint iterations = 0;
|
|
bool isBackwardRay = v.z < 0;
|
|
float rayDir = isBackwardRay ? -1.0 : 1.0;
|
|
|
|
// Cross to next cell s.t. we don't get a self-intersection immediately.
|
|
ivec2 startCellCount = getCellCount(level);
|
|
ivec2 rayCell = getCell(ray.xy, startCellCount);
|
|
ray = intersectCellBoundary(o, d, rayCell, startCellCount, crossStep, crossOffset * 64.);
|
|
|
|
while (level >= HIZ_STOP_LEVEL && ray.z * rayDir <= maxZ * rayDir &&
|
|
iterations < u_hiz_iterations)
|
|
{
|
|
// Get the cell number of our current ray.
|
|
ivec2 cellCount = getCellCount(level);
|
|
ivec2 oldCellIx = getCell(ray.xy, cellCount);
|
|
|
|
// Get the minimum depth plane in which the current ray resides.
|
|
float cellMinZ = getMinDepthPlane(oldCellIx, level);
|
|
|
|
// Intersect only if ray depth is below the minimum depth plane.
|
|
vec3 tempRay;
|
|
if (cellMinZ > ray.z && !isBackwardRay)
|
|
tempRay = intersectDepthPlane(o, d, (cellMinZ - minZ) / deltaZ);
|
|
else
|
|
tempRay = ray;
|
|
|
|
ivec2 newCellIx = getCell(tempRay.xy, cellCount);
|
|
float thickness = level == 0 ? (ray.z - cellMinZ) : 0;
|
|
|
|
bool crossed = (isBackwardRay && (cellMinZ > ray.z))
|
|
|| (thickness > MAX_THICKNESS) || crossedCellBoundary(oldCellIx, newCellIx);
|
|
|
|
if (crossed)
|
|
{
|
|
ray = intersectCellBoundary(o, d, oldCellIx, cellCount, crossStep, crossOffset);
|
|
level = min(maxLevel, level + 1);
|
|
deepestLevel = max(deepestLevel, level);
|
|
#if DEBUG_LINEAR_SEARCH
|
|
level = 0;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
ray = tempRay;
|
|
level = level - 1;
|
|
}
|
|
|
|
iterations += 1;
|
|
}
|
|
|
|
// Results
|
|
//debugDeepestLevel = deepestLevel;
|
|
//debugIterations = iterations;
|
|
hitPointSS = ray.xy;
|
|
return level < HIZ_STOP_LEVEL && iterations < u_hiz_iterations;
|
|
}
|
|
|
|
#endif
|
|
|
|
void main()
|
|
{
|
|
#ifdef PBR_ENABLED
|
|
float horiz = sampleMeshTexture2(f_material_id, f_uv + u_push_constants.m_displace_direction.xy * 150.).x;
|
|
float vert = sampleMeshTexture2(f_material_id, (f_uv.yx + u_push_constants.m_displace_direction.zw * 150.) * vec2(0.9)).x;
|
|
vec2 mask = getDisplaceShift(horiz, vert);
|
|
mask = (mask + 1.0) * 0.5;
|
|
o_displace_mask = mask;
|
|
if (u_ssr)
|
|
{
|
|
float alpha = sampleMeshTexture0(f_material_id, f_uv).a;
|
|
if (alpha == 0.0)
|
|
{
|
|
o_displace_ssr = vec4(0.0);
|
|
return;
|
|
}
|
|
// eye-space position
|
|
vec3 xpos = (u_camera.m_view_matrix * f_world_position).xyz;
|
|
// eye-space view direction (points from surface toward eye at origin)
|
|
vec3 eyedir = -normalize(xpos);
|
|
// eye-space normal
|
|
vec3 normal = (u_camera.m_view_matrix * vec4(normalize(f_normal), 0)).xyz;
|
|
|
|
// bail out immediately if normal is facing away from the camera,
|
|
// dot(normal, eyedir) <= 0 means back-facing
|
|
float NdotV = dot(normal, eyedir);
|
|
if (NdotV <= 0.0)
|
|
{
|
|
o_displace_ssr = vec4(0.0);
|
|
return;
|
|
}
|
|
|
|
// compute reflection in eye-space
|
|
vec3 reflected = reflect(-eyedir, normal);
|
|
// bring it back into world-space
|
|
vec3 world_reflection = (u_camera.m_inverse_view_matrix *
|
|
vec4(reflected, 0.0)).xyz;
|
|
|
|
// fallback to skybox
|
|
vec4 fallback = texture(u_skybox_texture, world_reflection);
|
|
|
|
// early exit if normal is facing camera too directly (no meaningful reflection)
|
|
if (normal.z < -0.75)
|
|
{
|
|
o_displace_ssr = fallback;
|
|
return;
|
|
}
|
|
|
|
vec4 result;
|
|
vec2 viewport_scale = u_camera.m_viewport.zw / u_camera.m_screensize;
|
|
vec2 viewport_offset = u_camera.m_viewport.xy / u_camera.m_screensize;
|
|
bool hit = true;
|
|
vec2 coords;
|
|
if (u_hiz_iterations == 0)
|
|
{
|
|
coords = RayCast(reflected, xpos, u_camera.m_projection_matrix,
|
|
viewport_scale, viewport_offset, u_depth);
|
|
}
|
|
else
|
|
{
|
|
vec3 positionSS = CalcCoordFromPosition(xpos,
|
|
u_camera.m_projection_matrix, vec2(1.0), vec2(0.0));
|
|
vec3 positionCS = positionSS;
|
|
positionCS.xy = 2.0 * positionCS.xy - 1.0;
|
|
vec3 position2VS = xpos + 1000.0 * reflected;
|
|
vec4 position2CS = u_camera.m_projection_matrix * vec4(position2VS, 1.0);
|
|
position2CS /= position2CS.w;
|
|
vec3 position2SS = position2CS.xyz;
|
|
position2SS.xy = vec2(0.5) + 0.5 * position2SS.xy;
|
|
vec3 reflectionDirSS = normalize(position2SS - positionSS);
|
|
// Trace HiZ to find the hit point.
|
|
hit = traceHiZ(positionSS, reflectionDirSS, coords);
|
|
coords = coords * viewport_scale + viewport_offset;
|
|
}
|
|
vec2 viewport_coords = (coords - viewport_offset) / viewport_scale;
|
|
if (!hit || viewport_coords.x < 0. || viewport_coords.x > 1. ||
|
|
viewport_coords.y < 0. || viewport_coords.y > 1.)
|
|
{
|
|
result = fallback;
|
|
}
|
|
else
|
|
{
|
|
result = texture(u_displace_color, coords);
|
|
float edge = GetEdgeFade(coords, viewport_scale, viewport_offset);
|
|
//float fresnel = pow(1.0 - NdotV, 2.0);
|
|
float fresnel = (1.0 - NdotV) * (1.0 - NdotV);
|
|
float blend_weight = edge * fresnel;
|
|
result = mix(fallback, result, blend_weight);
|
|
}
|
|
o_displace_ssr = result;
|
|
}
|
|
#endif
|
|
}
|