172 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			172 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | import { | ||
|  | 	Vector2 | ||
|  | } from 'three'; | ||
|  | 
 | ||
|  | /** | ||
|  |  * TODO | ||
|  |  */ | ||
|  | 
 | ||
|  | const DepthLimitedBlurShader = { | ||
|  | 
 | ||
|  | 	name: 'DepthLimitedBlurShader', | ||
|  | 
 | ||
|  | 	defines: { | ||
|  | 		'KERNEL_RADIUS': 4, | ||
|  | 		'DEPTH_PACKING': 1, | ||
|  | 		'PERSPECTIVE_CAMERA': 1 | ||
|  | 	}, | ||
|  | 
 | ||
|  | 	uniforms: { | ||
|  | 		'tDiffuse': { value: null }, | ||
|  | 		'size': { value: new Vector2( 512, 512 ) }, | ||
|  | 		'sampleUvOffsets': { value: [ new Vector2( 0, 0 ) ] }, | ||
|  | 		'sampleWeights': { value: [ 1.0 ] }, | ||
|  | 		'tDepth': { value: null }, | ||
|  | 		'cameraNear': { value: 10 }, | ||
|  | 		'cameraFar': { value: 1000 }, | ||
|  | 		'depthCutoff': { value: 10 }, | ||
|  | 	}, | ||
|  | 
 | ||
|  | 	vertexShader: /* glsl */`
 | ||
|  | 
 | ||
|  | 		#include <common> | ||
|  | 
 | ||
|  | 		uniform vec2 size; | ||
|  | 
 | ||
|  | 		varying vec2 vUv; | ||
|  | 		varying vec2 vInvSize; | ||
|  | 
 | ||
|  | 		void main() { | ||
|  | 			vUv = uv; | ||
|  | 			vInvSize = 1.0 / size; | ||
|  | 
 | ||
|  | 			gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); | ||
|  | 		}`,
 | ||
|  | 
 | ||
|  | 	fragmentShader: /* glsl */`
 | ||
|  | 
 | ||
|  | 		#include <common> | ||
|  | 		#include <packing> | ||
|  | 
 | ||
|  | 		uniform sampler2D tDiffuse; | ||
|  | 		uniform sampler2D tDepth; | ||
|  | 
 | ||
|  | 		uniform float cameraNear; | ||
|  | 		uniform float cameraFar; | ||
|  | 		uniform float depthCutoff; | ||
|  | 
 | ||
|  | 		uniform vec2 sampleUvOffsets[ KERNEL_RADIUS + 1 ]; | ||
|  | 		uniform float sampleWeights[ KERNEL_RADIUS + 1 ]; | ||
|  | 
 | ||
|  | 		varying vec2 vUv; | ||
|  | 		varying vec2 vInvSize; | ||
|  | 
 | ||
|  | 		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 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		void main() { | ||
|  | 			float depth = getDepth( vUv ); | ||
|  | 			if( depth >= ( 1.0 - EPSILON ) ) { | ||
|  | 				discard; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			float centerViewZ = -getViewZ( depth ); | ||
|  | 			bool rBreak = false, lBreak = false; | ||
|  | 
 | ||
|  | 			float weightSum = sampleWeights[0]; | ||
|  | 			vec4 diffuseSum = texture2D( tDiffuse, vUv ) * weightSum; | ||
|  | 
 | ||
|  | 			for( int i = 1; i <= KERNEL_RADIUS; i ++ ) { | ||
|  | 
 | ||
|  | 				float sampleWeight = sampleWeights[i]; | ||
|  | 				vec2 sampleUvOffset = sampleUvOffsets[i] * vInvSize; | ||
|  | 
 | ||
|  | 				vec2 sampleUv = vUv + sampleUvOffset; | ||
|  | 				float viewZ = -getViewZ( getDepth( sampleUv ) ); | ||
|  | 
 | ||
|  | 				if( abs( viewZ - centerViewZ ) > depthCutoff ) rBreak = true; | ||
|  | 
 | ||
|  | 				if( ! rBreak ) { | ||
|  | 					diffuseSum += texture2D( tDiffuse, sampleUv ) * sampleWeight; | ||
|  | 					weightSum += sampleWeight; | ||
|  | 				} | ||
|  | 
 | ||
|  | 				sampleUv = vUv - sampleUvOffset; | ||
|  | 				viewZ = -getViewZ( getDepth( sampleUv ) ); | ||
|  | 
 | ||
|  | 				if( abs( viewZ - centerViewZ ) > depthCutoff ) lBreak = true; | ||
|  | 
 | ||
|  | 				if( ! lBreak ) { | ||
|  | 					diffuseSum += texture2D( tDiffuse, sampleUv ) * sampleWeight; | ||
|  | 					weightSum += sampleWeight; | ||
|  | 				} | ||
|  | 
 | ||
|  | 			} | ||
|  | 
 | ||
|  | 			gl_FragColor = diffuseSum / weightSum; | ||
|  | 		}`
 | ||
|  | 
 | ||
|  | }; | ||
|  | 
 | ||
|  | const BlurShaderUtils = { | ||
|  | 
 | ||
|  | 	createSampleWeights: function ( kernelRadius, stdDev ) { | ||
|  | 
 | ||
|  | 		const weights = []; | ||
|  | 
 | ||
|  | 		for ( let i = 0; i <= kernelRadius; i ++ ) { | ||
|  | 
 | ||
|  | 			weights.push( gaussian( i, stdDev ) ); | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return weights; | ||
|  | 
 | ||
|  | 	}, | ||
|  | 
 | ||
|  | 	createSampleOffsets: function ( kernelRadius, uvIncrement ) { | ||
|  | 
 | ||
|  | 		const offsets = []; | ||
|  | 
 | ||
|  | 		for ( let i = 0; i <= kernelRadius; i ++ ) { | ||
|  | 
 | ||
|  | 			offsets.push( uvIncrement.clone().multiplyScalar( i ) ); | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return offsets; | ||
|  | 
 | ||
|  | 	}, | ||
|  | 
 | ||
|  | 	configure: function ( material, kernelRadius, stdDev, uvIncrement ) { | ||
|  | 
 | ||
|  | 		material.defines[ 'KERNEL_RADIUS' ] = kernelRadius; | ||
|  | 		material.uniforms[ 'sampleUvOffsets' ].value = BlurShaderUtils.createSampleOffsets( kernelRadius, uvIncrement ); | ||
|  | 		material.uniforms[ 'sampleWeights' ].value = BlurShaderUtils.createSampleWeights( kernelRadius, stdDev ); | ||
|  | 		material.needsUpdate = true; | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | }; | ||
|  | 
 | ||
|  | function gaussian( x, stdDev ) { | ||
|  | 
 | ||
|  | 	return Math.exp( - ( x * x ) / ( 2.0 * ( stdDev * stdDev ) ) ) / ( Math.sqrt( 2.0 * Math.PI ) * stdDev ); | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | export { DepthLimitedBlurShader, BlurShaderUtils }; |