Last active
December 17, 2015 12:08
-
-
Save Samsy/14707f05c99c89cf6ddb to your computer and use it in GitHub Desktop.
SAO Shading WEBGL
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
varying float depth; | |
uniform float mNear; | |
uniform float mFar; | |
vec4 pack_depth(float value) { | |
return vec4(floor(value * 256.0)/255.0, floor(fract(value * 256.0) * 256.0)/255.0 , floor(fract(value * 65536.0) * 256.0)/255.0, 1.0); | |
} | |
void main() { | |
gl_FragColor = pack_depth( depth ); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
varying float depth; | |
uniform float mNear; | |
uniform float mFar; | |
void main() { | |
vec4 viewPos = vec4( modelViewMatrix * vec4( position, 1.0 ) ); // this will transform the vertex into eyespace | |
depth = 1. - ( mNear + viewPos.z ) / ( mNear - mFar ); | |
vec3 vPosition = vec4( modelViewMatrix * vec4( position, 1.0 ) ).xyz; | |
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); | |
// depth = -viewPos.z; | |
depth = (-viewPos.z-mNear)/(mFar-mNear); // will map near..far to 0..1 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#extension GL_OES_standard_derivatives : enable | |
#define NUM_SAMPLES 13 | |
#define NUM_SPIRAL_TURNS 11 | |
#define VARIATION 1 | |
#define DOUBLEPI 6.283185307179586 | |
varying vec2 vUv; | |
uniform sampler2D tDepth; | |
uniform float intensity; | |
uniform float sampleRadiusWS; | |
uniform float bias; | |
uniform float zNear; | |
uniform float zFar; | |
uniform float debugDepth; | |
uniform vec4 projInfo; | |
uniform vec2 viewportResolution; | |
uniform float projScale; // how many pixels a 1m size 1m away from the camera is. Or 500 is a good value. | |
float rand(vec2 co){ | |
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); | |
} | |
// RGBA depth | |
float unpackDepth( vec4 pos ) { | |
return pos.r * 255.0 / 256.0 + pos.g * 255.0 / 65536.0 + pos.b * 255.0 / 16777216.0; | |
} | |
/* | |
Clipping plane constants for use by reconstructZ | |
clipInfo = (z_f == -inf()) ? Vector3(z_n, -1.0f, 1.0f) : Vector3(z_n * z_f, z_n - z_f, z_f); | |
*/ | |
float reconstructCSZ(float depth) { | |
// return clipInfo[0] / (clipInfo[1] * d + zFar[2]); | |
return ( zNear * zFar ) / ( (zNear - zFar) * depth + zFar ); | |
} | |
/** vec4(-2.0f / (width*P[0][0]), | |
-2.0f / (height*P[1][1]), | |
( 1.0f - P[0][2]) / P[0][0], | |
( 1.0f + P[1][2]) / P[1][1]) | |
where P is the projection matrix that maps camera space points | |
to [-1, 1] x [-1, 1]. That is, GCamera::getProjectUnit(). */ | |
/** Reconstruct camera-space P.xyz from screen-space S = (x, y) in | |
pixels and camera-space z < 0. Assumes that the upper-left pixel center | |
is at (0.5, 0.5) [but that need not be the location at which the sample tap | |
was placed!] | |
Costs 3 MADD. Error is on the order of 10^3 at the far plane, partly due to z precision. | |
*/ | |
vec3 reconstructCSPosition(vec2 S, float z) { | |
return vec3((S.xy * projInfo.xy + projInfo.zw) * z, z); | |
} | |
/** Reconstructs screen-space unit normal from screen-space position */ | |
vec3 reconstructCSFaceNormal(vec3 C) { | |
return -normalize(cross(dFdy(C), dFdx(C))); | |
} | |
vec3 reconstructNonUnitCSFaceNormal(vec3 C) { | |
return -cross(dFdy(C), dFdx(C)); | |
} | |
//// reconstructs view-space unit normal from view-space position, | |
// vec3 reconstructNormalVS(vec3 positionVS) { | |
// return normalize(cross(dFdx(positionVS), dFdy(positionVS))); | |
// } | |
// returns a unit vector and a screen-space radius for the tap on a unit disk | |
// (the caller should scale by the actual disk radius) | |
vec2 tapLocation(int sampleNumber, float spinAngle, out float radiusSS) { | |
// radius relative to radiusSS, | |
float alpha = (float(sampleNumber) + 0.5) * (1.0 / float(NUM_SAMPLES)); | |
float angle = alpha * (float(NUM_SPIRAL_TURNS) * 6.28) + spinAngle; | |
radiusSS = alpha; | |
return vec2(cos(angle), sin(angle)); | |
} | |
vec3 getOffsetPositionVS(vec2 uv, vec2 unitOffset, float radiusSS) { | |
uv = uv + radiusSS * unitOffset * (1.0 / viewportResolution.x); | |
float zDepth = unpackDepth( texture2D( tDepth, uv ) ); | |
float depthCS = reconstructCSZ( zDepth ); | |
return reconstructCSPosition( uv, depthCS ); | |
} | |
float sampleAO(vec2 uv, vec3 positionVS, vec3 normalVS, float sampleRadiusSS, int tapIndex, float rotationAngle) | |
{ | |
const float epsilon = 0.01; | |
float radius2 = sampleRadiusWS * sampleRadiusWS; | |
// offset on the unit disk, spun for this pixel, | |
float radiusSS; | |
vec2 unitOffset = tapLocation(tapIndex, rotationAngle, radiusSS); | |
radiusSS *= sampleRadiusSS; | |
vec3 Q = getOffsetPositionVS(uv, unitOffset, radiusSS); | |
vec3 v = Q - positionVS; | |
float vv = dot(v, v); | |
float vn = dot(v, normalVS) - bias; | |
#if VARIATION == 0 | |
// (from the HPG12 paper) | |
// Note large epsilon to avoid overdarkening within cracks | |
return float(vv < radius2) * max(vn / (epsilon + vv), 0.0); | |
#elif VARIATION == 1 // default / recommended, | |
// Smoother transition to zero (lowers contrast, smoothing out corners). [Recommended] | |
float f = max(radius2 - vv, 0.0) / radius2; | |
return f * f * f * max(vn / (epsilon + vv), 0.0); | |
#elif VARIATION == 2 | |
// Medium contrast (which looks better at high radii), no division. Note that the | |
// contribution still falls off with radius^2, but we've adjusted the rate in a way that is | |
// more computationally efficient and happens to be aesthetically pleasing. | |
float invRadius2 = 1.0 / radius2; | |
return 4.0 * max(1.0 - vv * invRadius2, 0.0) * max(vn, 0.0); | |
#else | |
// Low contrast, no division operation | |
return 2.0 * float(vv < radius2) * max(vn, 0.0); | |
#endif | |
} | |
void main() { | |
if(debugDepth == 0.0) { | |
float depthCS = reconstructCSZ( unpackDepth( texture2D( tDepth, vUv ) ) ); | |
vec3 originVS = reconstructCSPosition( vUv, depthCS ); | |
vec3 normalVS = normalize( reconstructNonUnitCSFaceNormal(originVS) ); | |
float randomPatternRotationAngle = DOUBLEPI * rand(vUv); | |
// radius of influence in screen space | |
// float radiusWS = sampleRadiusWS; | |
// radius of influence in world space | |
float radiusSS = projScale * sampleRadiusWS / originVS.y; | |
float occlusion = 0.0; | |
for (int i = 0; i < NUM_SAMPLES; ++i) { | |
occlusion += sampleAO(vUv, originVS, normalVS, radiusSS, i, randomPatternRotationAngle); | |
} | |
occlusion = 1.0 - occlusion / float( NUM_SAMPLES ); | |
occlusion = clamp(pow(occlusion, 1.0 + intensity), 0.0, 1.0); | |
gl_FragColor = vec4( vec3(occlusion), 1.0 ); | |
} | |
else { | |
float depth = unpackDepth( texture2D( tDepth, vUv )); | |
gl_FragColor = vec4(vec3(depth), 1.0); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment