lygia
/lighting
/ssr
)ScreenSpace Reflections
Dependencies:
lygia
/space
/view2screenPosition
.glsl
lygia
/sdf
/lineSDF
.glsl
lygia
/sdf
/planeSDF
.glsl
lygia
/sampler
.glsl
Use:
<float> ssao(<SAMPLER_TYPE> texPosition, <SAMPLER_TYPE> texNormal, vec2 <st> [, <float> radius, float <bias>])
// #define SSR_FRESNEL
#ifndef CAMERA_NEAR_CLIP
#define CAMERA_NEAR_CLIP u_cameraNearClip
#endif
#ifndef CAMERA_FAR_CLIP
#define CAMERA_FAR_CLIP u_cameraFarClip
#endif
#ifndef SSR_MAX_STEP
#define SSR_MAX_STEP 500
#endif
#ifndef SSR_MAX_DISTANCE
#define SSR_MAX_DISTANCE 180.0
#endif
#ifndef FNC_SSR
#define FNC_SSR
vec2 ssr(SAMPLER_TYPE texPosition, SAMPLER_TYPE texNormal, vec2 st, vec2 pixel, inout float op, inout float dist) {
vec3 viewPosition = SAMPLER_FNC(texPosition, st).xyz;
// if (-viewPosition.z >= CAMERA_FAR_CLIP)
// return st;
vec2 ssr_uv = st;
float thickness = 0.05;
float opacity = op;
op = 0.0;
dist = 0.0;
vec3 viewNormal = SAMPLER_FNC(texNormal, st).xyz;
vec3 viewIncidentDir = normalize(viewPosition);
vec3 viewReflectDir = reflect(viewIncidentDir, viewNormal);
float maxReflectRayLen = SSR_MAX_DISTANCE / dot(-viewIncidentDir, viewNormal);
vec3 d1viewPosition = viewPosition + viewReflectDir * maxReflectRayLen;
if (d1viewPosition.z > -CAMERA_NEAR_CLIP) {
//https://tutorial.math.lamar.edu/Classes/CalcIII/EqnsOfLines.aspx
float t = (-CAMERA_NEAR_CLIP - viewPosition.z)/viewReflectDir.z;
d1viewPosition = viewPosition + viewReflectDir * t;
}
vec2 resolution = 1.0 / pixel;
vec2 d0 = st * resolution;
vec2 d1 = view2screenPosition(d1viewPosition) * resolution;
float totalLen = length(d1-d0);
float xLen = d1.x-d0.x;
float yLen = d1.y-d0.y;
float totalStep = max(abs(xLen),abs(yLen));
float xSpan = (xLen/totalStep);// * pixel.x;
float ySpan = (yLen/totalStep);// * pixel.y;
for (float i = 0.0; i < float(SSR_MAX_STEP); i++) {
if ( i >= totalStep)
break;
vec2 xy = vec2(d0.x + i*xSpan, d0.y + i*ySpan);
vec2 uv = xy * pixel;
vec3 vP = SAMPLER_FNC(texPosition, uv).xyz;
// if (-vP.z >= CAMERA_FAR_CLIP)
// continue;
// https://comp.nus.edu.sg/~lowkl/publications/lowk_persp_interp_techrep.pdf
float recipVPZ = 1.0/viewPosition.z;
float s = length(xy-d0)/totalLen;
float viewReflectRayZ = 1.0 / (recipVPZ + s * (1.0/d1viewPosition.z - recipVPZ) );
if (viewReflectRayZ <= vP.z) {
if (lineSDF(vP, viewPosition, d1viewPosition) <= max(0.0, thickness)) {
vec3 vN = SAMPLER_FNC(texNormal, uv).xyz;
if (dot(viewReflectDir, vN) >= 0.0)
continue;
dist = planeSDF(vP, viewPosition, viewNormal);
if (dist > SSR_MAX_DISTANCE)
break;
dist = s;
op = opacity;
#ifdef SSR_FRESNEL
float fresnelCoe = (dot(viewIncidentDir, viewReflectDir) + 1.0) * 0.5;
op *= fresnelCoe;
#endif
ssr_uv = uv;
break;
}
}
}
return ssr_uv;
}
vec2 ssr(SAMPLER_TYPE texPosition, SAMPLER_TYPE texNormal, vec2 st, vec2 pixel, inout float op) {
float dist = 0.0;
return ssr(texPosition, texNormal, st, pixel, op, dist);
}
vec3 ssr(SAMPLER_TYPE tex, SAMPLER_TYPE texPosition, SAMPLER_TYPE texNormal, vec2 st, vec2 pixel, float opacity) {
vec3 color = SAMPLER_FNC(tex, st).rgb;
float dist = 0.0;
vec2 uv = ssr(texPosition, texNormal, st, pixel, opacity, dist);
return mix(color, SAMPLER_FNC(tex, uv).rgb, opacity);
}
vec3 ssr(SAMPLER_TYPE tex, SAMPLER_TYPE texPosition, SAMPLER_TYPE texNormal, vec2 st, vec2 pixel) {
return ssr(tex, texPosition, texNormal, st, pixel, 0.5);
}
#endif
LYGIA is dual-licensed under the Prosperity License and the Patron License for sponsors and contributors.
Sponsors and contributors are automatically added to the Patron License and they can ignore the any non-commercial rule of the Prosperity Licensed software (please take a look to the exception).
It's also possible to get a permanent comercial license hook to a single and specific version of LYGIA.
Sign up for the news letter bellow, joing the LYGIA's channel on Discord or follow the Github repository