| THREE.SAOShader = { |
| defines: { |
| 'NUM_SAMPLES': 7, |
| 'NUM_RINGS': 4, |
| 'NORMAL_TEXTURE': 0, |
| 'DIFFUSE_TEXTURE': 0, |
| 'DEPTH_PACKING': 1, |
| 'PERSPECTIVE_CAMERA': 1 |
| }, |
| uniforms: { |
|
|
| 'tDepth': { type: 't', value: null }, |
| 'tDiffuse': { type: 't', value: null }, |
| 'tNormal': { type: 't', value: null }, |
| 'size': { type: 'v2', value: new THREE.Vector2( 512, 512 ) }, |
|
|
| 'cameraNear': { type: 'f', value: 1 }, |
| 'cameraFar': { type: 'f', value: 100 }, |
| 'cameraProjectionMatrix': { type: 'm4', value: new THREE.Matrix4() }, |
| 'cameraInverseProjectionMatrix': { type: 'm4', value: new THREE.Matrix4() }, |
|
|
| 'scale': { type: 'f', value: 1.0 }, |
| 'intensity': { type: 'f', value: 0.1 }, |
| 'bias': { type: 'f', value: 0.5 }, |
|
|
| 'minResolution': { type: 'f', value: 0.0 }, |
| 'kernelRadius': { type: 'f', value: 100.0 }, |
| 'randomSeed': { type: 'f', value: 0.0 } |
| }, |
| vertexShader: [ |
| "varying vec2 vUv;", |
|
|
| "void main() {", |
| " vUv = uv;", |
| " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", |
| "}" |
|
|
| ].join( "\n" ), |
| fragmentShader: [ |
| "#include <common>", |
|
|
| "varying vec2 vUv;", |
|
|
| "#if DIFFUSE_TEXTURE == 1", |
| "uniform sampler2D tDiffuse;", |
| "#endif", |
|
|
| "uniform sampler2D tDepth;", |
|
|
| "#if NORMAL_TEXTURE == 1", |
| "uniform sampler2D tNormal;", |
| "#endif", |
|
|
| "uniform float cameraNear;", |
| "uniform float cameraFar;", |
| "uniform mat4 cameraProjectionMatrix;", |
| "uniform mat4 cameraInverseProjectionMatrix;", |
|
|
| "uniform float scale;", |
| "uniform float intensity;", |
| "uniform float bias;", |
| "uniform float kernelRadius;", |
| "uniform float minResolution;", |
| "uniform vec2 size;", |
| "uniform float randomSeed;", |
|
|
| "// RGBA depth", |
|
|
| "#include <packing>", |
|
|
| "vec4 getDefaultColor( const in vec2 screenPosition ) {", |
| " #if DIFFUSE_TEXTURE == 1", |
| " return texture2D( tDiffuse, vUv );", |
| " #else", |
| " return vec4( 1.0 );", |
| " #endif", |
| "}", |
|
|
| "float getDepth( const in vec2 screenPosition ) {", |
| " #if DEPTH_PACKING == 1", |
| " return unpackRGBAToDepth( texture2D( tDepth, screenPosition ) );", |
| " #else", |
| " return texture2D( tDepth, screenPosition ).x;", |
| " #endif", |
| "}", |
|
|
| "float getViewZ( const in float depth ) {", |
| " #if PERSPECTIVE_CAMERA == 1", |
| " return perspectiveDepthToViewZ( depth, cameraNear, cameraFar );", |
| " #else", |
| " return orthographicDepthToViewZ( depth, cameraNear, cameraFar );", |
| " #endif", |
| "}", |
|
|
| "vec3 getViewPosition( const in vec2 screenPosition, const in float depth, const in float viewZ ) {", |
| " float clipW = cameraProjectionMatrix[2][3] * viewZ + cameraProjectionMatrix[3][3];", |
| " vec4 clipPosition = vec4( ( vec3( screenPosition, depth ) - 0.5 ) * 2.0, 1.0 );", |
| " clipPosition *= clipW; // unprojection.", |
|
|
| " return ( cameraInverseProjectionMatrix * clipPosition ).xyz;", |
| "}", |
|
|
| "vec3 getViewNormal( const in vec3 viewPosition, const in vec2 screenPosition ) {", |
| " #if NORMAL_TEXTURE == 1", |
| " return unpackRGBToNormal( texture2D( tNormal, screenPosition ).xyz );", |
| " #else", |
| " return normalize( cross( dFdx( viewPosition ), dFdy( viewPosition ) ) );", |
| " #endif", |
| "}", |
|
|
| "float scaleDividedByCameraFar;", |
| "float minResolutionMultipliedByCameraFar;", |
|
|
| "float getOcclusion( const in vec3 centerViewPosition, const in vec3 centerViewNormal, const in vec3 sampleViewPosition ) {", |
| " vec3 viewDelta = sampleViewPosition - centerViewPosition;", |
| " float viewDistance = length( viewDelta );", |
| " float scaledScreenDistance = scaleDividedByCameraFar * viewDistance;", |
|
|
| " return max(0.0, (dot(centerViewNormal, viewDelta) - minResolutionMultipliedByCameraFar) / scaledScreenDistance - bias) / (1.0 + pow2( scaledScreenDistance ) );", |
| "}", |
|
|
| "// moving costly divides into consts", |
| "const float ANGLE_STEP = PI2 * float( NUM_RINGS ) / float( NUM_SAMPLES );", |
| "const float INV_NUM_SAMPLES = 1.0 / float( NUM_SAMPLES );", |
|
|
| "float getAmbientOcclusion( const in vec3 centerViewPosition ) {", |
| " // precompute some variables require in getOcclusion.", |
| " scaleDividedByCameraFar = scale / cameraFar;", |
| " minResolutionMultipliedByCameraFar = minResolution * cameraFar;", |
| " vec3 centerViewNormal = getViewNormal( centerViewPosition, vUv );", |
|
|
| " // jsfiddle that shows sample pattern: https://jsfiddle.net/a16ff1p7/", |
| " float angle = rand( vUv + randomSeed ) * PI2;", |
| " vec2 radius = vec2( kernelRadius * INV_NUM_SAMPLES ) / size;", |
| " vec2 radiusStep = radius;", |
|
|
| " float occlusionSum = 0.0;", |
| " float weightSum = 0.0;", |
|
|
| " for( int i = 0; i < NUM_SAMPLES; i ++ ) {", |
| " vec2 sampleUv = vUv + vec2( cos( angle ), sin( angle ) ) * radius;", |
| " radius += radiusStep;", |
| " angle += ANGLE_STEP;", |
|
|
| " float sampleDepth = getDepth( sampleUv );", |
| " if( sampleDepth >= ( 1.0 - EPSILON ) ) {", |
| " continue;", |
| " }", |
|
|
| " float sampleViewZ = getViewZ( sampleDepth );", |
| " vec3 sampleViewPosition = getViewPosition( sampleUv, sampleDepth, sampleViewZ );", |
| " occlusionSum += getOcclusion( centerViewPosition, centerViewNormal, sampleViewPosition );", |
| " weightSum += 1.0;", |
| " }", |
|
|
| " if( weightSum == 0.0 ) discard;", |
|
|
| " return occlusionSum * ( intensity / weightSum );", |
| "}", |
|
|
|
|
| "void main() {", |
| " float centerDepth = getDepth( vUv );", |
| " if( centerDepth >= ( 1.0 - EPSILON ) ) {", |
| " discard;", |
| " }", |
|
|
| " float centerViewZ = getViewZ( centerDepth );", |
| " vec3 viewPosition = getViewPosition( vUv, centerDepth, centerViewZ );", |
|
|
| " float ambientOcclusion = getAmbientOcclusion( viewPosition );", |
|
|
| " gl_FragColor = getDefaultColor( vUv );", |
| " gl_FragColor.xyz *= 1.0 - ambientOcclusion;", |
| "}" |
| ].join( "\n" ) |
| }; |
|
|