180 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			180 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | import { | ||
|  | 	Matrix4, | ||
|  | 	Vector2 | ||
|  | } from 'three'; | ||
|  | 
 | ||
|  | /** | ||
|  |  * TODO | ||
|  |  */ | ||
|  | 
 | ||
|  | const SAOShader = { | ||
|  | 
 | ||
|  | 	name: 'SAOShader', | ||
|  | 
 | ||
|  | 	defines: { | ||
|  | 		'NUM_SAMPLES': 7, | ||
|  | 		'NUM_RINGS': 4, | ||
|  | 		'DIFFUSE_TEXTURE': 0, | ||
|  | 		'PERSPECTIVE_CAMERA': 1 | ||
|  | 	}, | ||
|  | 
 | ||
|  | 	uniforms: { | ||
|  | 
 | ||
|  | 		'tDepth': { value: null }, | ||
|  | 		'tDiffuse': { value: null }, | ||
|  | 		'tNormal': { value: null }, | ||
|  | 		'size': { value: new Vector2( 512, 512 ) }, | ||
|  | 
 | ||
|  | 		'cameraNear': { value: 1 }, | ||
|  | 		'cameraFar': { value: 100 }, | ||
|  | 		'cameraProjectionMatrix': { value: new Matrix4() }, | ||
|  | 		'cameraInverseProjectionMatrix': { value: new Matrix4() }, | ||
|  | 
 | ||
|  | 		'scale': { value: 1.0 }, | ||
|  | 		'intensity': { value: 0.1 }, | ||
|  | 		'bias': { value: 0.5 }, | ||
|  | 
 | ||
|  | 		'minResolution': { value: 0.0 }, | ||
|  | 		'kernelRadius': { value: 100.0 }, | ||
|  | 		'randomSeed': { value: 0.0 } | ||
|  | 	}, | ||
|  | 
 | ||
|  | 	vertexShader: /* glsl */`
 | ||
|  | 
 | ||
|  | 		varying vec2 vUv; | ||
|  | 
 | ||
|  | 		void main() { | ||
|  | 			vUv = uv; | ||
|  | 			gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); | ||
|  | 		}`,
 | ||
|  | 
 | ||
|  | 	fragmentShader: /* glsl */`
 | ||
|  | 		#include <common> | ||
|  | 
 | ||
|  | 		varying vec2 vUv; | ||
|  | 
 | ||
|  | 		#if DIFFUSE_TEXTURE == 1 | ||
|  | 		uniform sampler2D tDiffuse; | ||
|  | 		#endif | ||
|  | 
 | ||
|  | 		uniform highp sampler2D tDepth; | ||
|  | 		uniform highp sampler2D tNormal; | ||
|  | 
 | ||
|  | 		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 ) { | ||
|  | 			return texture2D( tDepth, screenPosition ).x; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		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 ) { | ||
|  | 			return unpackRGBToNormal( texture2D( tNormal, screenPosition ).xyz ); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		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; | ||
|  | 		}`
 | ||
|  | 
 | ||
|  | }; | ||
|  | 
 | ||
|  | export { SAOShader }; |