export const hbao_utils = ` #include uniform sampler2D normalTexture; uniform float cameraNear; uniform float cameraFar; uniform mat4 projectionMatrixInverse; uniform mat4 cameraMatrixWorld; // source: https://github.com/mrdoob/three.js/blob/342946c8392639028da439b6dc0597e58209c696/examples/js/shaders/SAOShader.js#L123 float getViewZ(const float depth) { #ifdef PERSPECTIVE_CAMERA return perspectiveDepthToViewZ(depth, cameraNear, cameraFar); #else return orthographicDepthToViewZ(depth, cameraNear, cameraFar); #endif } // source: https://github.com/N8python/ssao/blob/master/EffectShader.js#L52 vec3 getWorldPos(const float depth, const vec2 coord) { float z = depth * 2.0 - 1.0; vec4 clipSpacePosition = vec4(coord * 2.0 - 1.0, z, 1.0); vec4 viewSpacePosition = projectionMatrixInverse * clipSpacePosition; // Perspective division vec4 worldSpacePosition = cameraMatrixWorld * viewSpacePosition; worldSpacePosition.xyz /= worldSpacePosition.w; return worldSpacePosition.xyz; } vec3 slerp(const vec3 a, const vec3 b, const float t) { float cosAngle = dot(a, b); float angle = acos(cosAngle); if (abs(angle) < 0.001) { return mix(a, b, t); } float sinAngle = sin(angle); float t1 = sin((1.0 - t) * angle) / sinAngle; float t2 = sin(t * angle) / sinAngle; return (a * t1) + (b * t2); } vec3 computeWorldNormal() { vec2 size = vec2(textureSize(depthTexture, 0)); ivec2 p = ivec2(vUv * size); float c0 = texelFetch(depthTexture, p, 0).x; float l2 = texelFetch(depthTexture, p - ivec2(2, 0), 0).x; float l1 = texelFetch(depthTexture, p - ivec2(1, 0), 0).x; float r1 = texelFetch(depthTexture, p + ivec2(1, 0), 0).x; float r2 = texelFetch(depthTexture, p + ivec2(2, 0), 0).x; float b2 = texelFetch(depthTexture, p - ivec2(0, 2), 0).x; float b1 = texelFetch(depthTexture, p - ivec2(0, 1), 0).x; float t1 = texelFetch(depthTexture, p + ivec2(0, 1), 0).x; float t2 = texelFetch(depthTexture, p + ivec2(0, 2), 0).x; float dl = abs((2.0 * l1 - l2) - c0); float dr = abs((2.0 * r1 - r2) - c0); float db = abs((2.0 * b1 - b2) - c0); float dt = abs((2.0 * t1 - t2) - c0); vec3 ce = getWorldPos(c0, vUv).xyz; vec3 dpdx = (dl < dr) ? ce - getWorldPos(l1, (vUv - vec2(1.0 / size.x, 0.0))).xyz : -ce + getWorldPos(r1, (vUv + vec2(1.0 / size.x, 0.0))).xyz; vec3 dpdy = (db < dt) ? ce - getWorldPos(b1, (vUv - vec2(0.0, 1.0 / size.y))).xyz : -ce + getWorldPos(t1, (vUv + vec2(0.0, 1.0 / size.y))).xyz; return normalize(cross(dpdx, dpdy)); } vec3 getWorldNormal(const vec2 uv) { #ifdef useNormalTexture vec3 worldNormal = unpackRGBToNormal(textureLod(normalTexture, uv, 0.).rgb); worldNormal = (vec4(worldNormal, 1.) * viewMatrix).xyz; // view-space to world-space return normalize(worldNormal); #else return computeWorldNormal(); // compute world normal from depth #endif } #define PI 3.14159265358979323846264338327950288 // source: https://www.shadertoy.com/view/cll3R4 vec3 cosineSampleHemisphere(const vec3 n, const vec2 u) { float r = sqrt(u.x); float theta = 2.0 * PI * u.y; vec3 b = normalize(cross(n, vec3(0.0, 1.0, 1.0))); vec3 t = cross(b, n); return normalize(r * sin(theta) * b + sqrt(1.0 - u.x) * n + r * cos(theta) * t); } `