Pixel-Composer/shaders/BBMOD_ShSSAO/bbmod_shssao.fsh
2023-04-23 16:47:33 +02:00

188 lines
4.9 KiB
GLSL

// Reference: https://de45xmedrsdbp.cloudfront.net/__resources/files/The_Technology_Behind_the_Elemental_Demo_16x9-1248544805.pdf
// Reference: http://frederikaalund.com/wp-content/uploads/2013/05/A-Comparative-Study-of-Screen-Space-Ambient-Occlusion-Methods.pdf
// The size of the SSAO kernel.
#define BBMOD_SSAO_KERNEL_SIZE 8
varying vec2 v_vTexCoord;
// Texture of random rotations.
uniform sampler2D u_texNoise;
// (1 / screenWidth, 1 / screenHeight)
uniform vec2 u_vTexel;
// (dtan(fov / 2) * (screenWidth / screenHeight), -dtan(fov / 2))
uniform vec2 u_vTanAspect;
// Distance to the far clipping plane.
uniform float u_fClipFar;
// Kernel of random vectors.
uniform vec2 u_vSampleKernel[BBMOD_SSAO_KERNEL_SIZE];
// (screenWidth, screenHeight) / noiseTextureSize
uniform vec2 u_vNoiseScale;
// Strength of the occlusion effect.
uniform float u_fPower;
// Screen-space radius of the occlusion effect.
uniform float u_fRadius;
// Angle bias of the occlusion effect (in radians).
uniform float u_fAngleBias;
// Maximum depth difference of samples taken into account.
uniform float u_fDepthRange;
//#pragma include("DepthEncoding.xsh", "glsl")
/// @param d Linearized depth to encode.
/// @return Encoded depth.
/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/
vec3 xEncodeDepth(float d)
{
const float inv255 = 1.0 / 255.0;
vec3 enc;
enc.x = d;
enc.y = d * 255.0;
enc.z = enc.y * 255.0;
enc = fract(enc);
float temp = enc.z * inv255;
enc.x -= enc.y * inv255;
enc.y -= temp;
enc.z -= temp;
return enc;
}
/// @param c Encoded depth.
/// @return Docoded linear depth.
/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/
float xDecodeDepth(vec3 c)
{
const float inv255 = 1.0 / 255.0;
return c.x + (c.y * inv255) + (c.z * inv255 * inv255);
}
// include("DepthEncoding.xsh")
//#pragma include("Projecting.xsh", "glsl")
/// @param tanAspect (tanFovY*(screenWidth/screenHeight),-tanFovY), where
/// tanFovY = dtan(fov*0.5)
/// @param texCoord Sceen-space UV.
/// @param depth Scene depth at texCoord.
/// @return Point projected to view-space.
vec3 xProject(vec2 tanAspect, vec2 texCoord, float depth)
{
return vec3(tanAspect * (texCoord * 2.0 - 1.0) * depth, depth);
}
/// @param p A point in clip space (transformed by projection matrix, but not
/// normalized).
/// @return P's UV coordinates on the screen.
vec2 xUnproject(vec4 p)
{
vec2 uv = p.xy / p.w;
uv = uv * 0.5 + 0.5;
uv.y = 1.0 - uv.y;
return uv;
}
// include("Projecting.xsh")
//#pragma include("Math.xsh", "glsl")
#define X_PI 3.14159265359
#define X_2_PI 6.28318530718
/// @return x^2
#define xPow2(x) ((x) * (x))
/// @return x^3
#define xPow3(x) ((x) * (x) * (x))
/// @return x^4
#define xPow4(x) ((x) * (x) * (x) * (x))
/// @return x^5
#define xPow5(x) ((x) * (x) * (x) * (x) * (x))
/// @return arctan2(x,y)
#define xAtan2(x, y) atan(y, x)
/// @return Direction from point `from` to point `to` in degrees (0-360 range).
float xPointDirection(vec2 from, vec2 to)
{
float x = xAtan2(from.x - to.x, from.y - to.y);
return ((x > 0.0) ? x : (2.0 * X_PI + x)) * 180.0 / X_PI;
}
// include("Math.xsh")
// Source: http://stackoverflow.com/a/3380723/554283
float AcosApprox(float x)
{
return (-0.69813170079773212 * x * x - 0.87266462599716477) * x + 1.5707963267948966;
}
void main()
{
// Origin
float depth = xDecodeDepth(texture2D(gm_BaseTexture, v_vTexCoord).rgb) * u_fClipFar;
if (depth == 0.0 || depth == u_fClipFar)
{
gl_FragColor = vec4(1.0);
return;
}
vec3 origin = xProject(u_vTanAspect, v_vTexCoord, depth);
vec2 noise = texture2D(u_texNoise, v_vTexCoord * u_vNoiseScale).xy * 2.0 - 1.0;
mat2 rot = mat2(
noise.x, -noise.y,
noise.y, noise.x
);
// Occlusion
float occlusion = 0.0;
for (int i = 0; i < BBMOD_SSAO_KERNEL_SIZE; ++i)
{
vec2 dir = (rot * u_vSampleKernel[i].xy) * u_fRadius;
vec2 uv1 = v_vTexCoord + dir * u_vTexel;
vec2 uv2 = v_vTexCoord - dir * u_vTexel;
float angle = 1.0;
if (uv1.x > 0.0 && uv1.x < 1.0
&& uv1.y > 0.0 && uv1.y < 1.0
&& uv2.x > 0.0 && uv2.x < 1.0
&& uv2.y > 0.0 && uv2.y < 1.0)
{
float depth1 = xDecodeDepth(texture2D(gm_BaseTexture, uv1).rgb) * u_fClipFar;
vec3 pos1 = xProject(u_vTanAspect, uv1, depth1);
vec3 diff1 = pos1 - origin;
float depth2 = xDecodeDepth(texture2D(gm_BaseTexture, uv2).rgb) * u_fClipFar;
vec3 pos2 = xProject(u_vTanAspect, uv2, depth2);
vec3 diff2 = pos2 - origin;
float cosAngle = dot(diff1, diff2) / (length(diff1) * length(diff2));
angle = max(AcosApprox(cosAngle - u_fAngleBias), 0.0) / X_PI;
if (-diff1.z - diff2.z < 0.01)
{
angle = 1.0;
}
float att = (abs(diff1.z) + abs(diff2.z)) / (u_fDepthRange * 2.0);
att = clamp(att * att, 0.0, 1.0);
angle = mix(angle, 1.0, att);
}
occlusion += angle;
}
occlusion /= float(BBMOD_SSAO_KERNEL_SIZE);
occlusion = pow(occlusion, u_fPower);
// Output
gl_FragColor.rgb = vec3(occlusion);
gl_FragColor.a = 1.0;
}