236 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			236 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | import { | ||
|  | 	WebGLRenderTarget, | ||
|  | 	MeshNormalMaterial, | ||
|  | 	ShaderMaterial, | ||
|  | 	Vector2, | ||
|  | 	Vector4, | ||
|  | 	DepthTexture, | ||
|  | 	NearestFilter, | ||
|  | 	HalfFloatType | ||
|  | } from 'three'; | ||
|  | import { Pass, FullScreenQuad } from './Pass.js'; | ||
|  | 
 | ||
|  | class RenderPixelatedPass extends Pass { | ||
|  | 
 | ||
|  | 	constructor( pixelSize, scene, camera, options = {} ) { | ||
|  | 
 | ||
|  | 		super(); | ||
|  | 
 | ||
|  | 		this.pixelSize = pixelSize; | ||
|  | 		this.resolution = new Vector2(); | ||
|  | 		this.renderResolution = new Vector2(); | ||
|  | 
 | ||
|  | 		this.pixelatedMaterial = this.createPixelatedMaterial(); | ||
|  | 		this.normalMaterial = new MeshNormalMaterial(); | ||
|  | 
 | ||
|  | 		this.fsQuad = new FullScreenQuad( this.pixelatedMaterial ); | ||
|  | 		this.scene = scene; | ||
|  | 		this.camera = camera; | ||
|  | 
 | ||
|  | 		this.normalEdgeStrength = options.normalEdgeStrength || 0.3; | ||
|  | 		this.depthEdgeStrength = options.depthEdgeStrength || 0.4; | ||
|  | 
 | ||
|  | 		this.beautyRenderTarget = new WebGLRenderTarget(); | ||
|  | 		this.beautyRenderTarget.texture.minFilter = NearestFilter; | ||
|  | 		this.beautyRenderTarget.texture.magFilter = NearestFilter; | ||
|  | 		this.beautyRenderTarget.texture.type = HalfFloatType; | ||
|  | 		this.beautyRenderTarget.depthTexture = new DepthTexture(); | ||
|  | 
 | ||
|  | 		this.normalRenderTarget = new WebGLRenderTarget(); | ||
|  | 		this.normalRenderTarget.texture.minFilter = NearestFilter; | ||
|  | 		this.normalRenderTarget.texture.magFilter = NearestFilter; | ||
|  | 		this.normalRenderTarget.texture.type = HalfFloatType; | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	dispose() { | ||
|  | 
 | ||
|  | 		this.beautyRenderTarget.dispose(); | ||
|  | 		this.normalRenderTarget.dispose(); | ||
|  | 
 | ||
|  | 		this.pixelatedMaterial.dispose(); | ||
|  | 		this.normalMaterial.dispose(); | ||
|  | 
 | ||
|  | 		this.fsQuad.dispose(); | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	setSize( width, height ) { | ||
|  | 
 | ||
|  | 		this.resolution.set( width, height ); | ||
|  | 		this.renderResolution.set( ( width / this.pixelSize ) | 0, ( height / this.pixelSize ) | 0 ); | ||
|  | 		const { x, y } = this.renderResolution; | ||
|  | 		this.beautyRenderTarget.setSize( x, y ); | ||
|  | 		this.normalRenderTarget.setSize( x, y ); | ||
|  | 		this.fsQuad.material.uniforms.resolution.value.set( x, y, 1 / x, 1 / y ); | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	setPixelSize( pixelSize ) { | ||
|  | 
 | ||
|  | 		this.pixelSize = pixelSize; | ||
|  | 		this.setSize( this.resolution.x, this.resolution.y ); | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	render( renderer, writeBuffer ) { | ||
|  | 
 | ||
|  | 		const uniforms = this.fsQuad.material.uniforms; | ||
|  | 		uniforms.normalEdgeStrength.value = this.normalEdgeStrength; | ||
|  | 		uniforms.depthEdgeStrength.value = this.depthEdgeStrength; | ||
|  | 
 | ||
|  | 		renderer.setRenderTarget( this.beautyRenderTarget ); | ||
|  | 		renderer.render( this.scene, this.camera ); | ||
|  | 
 | ||
|  | 		const overrideMaterial_old = this.scene.overrideMaterial; | ||
|  | 		renderer.setRenderTarget( this.normalRenderTarget ); | ||
|  | 		this.scene.overrideMaterial = this.normalMaterial; | ||
|  | 		renderer.render( this.scene, this.camera ); | ||
|  | 		this.scene.overrideMaterial = overrideMaterial_old; | ||
|  | 
 | ||
|  | 		uniforms.tDiffuse.value = this.beautyRenderTarget.texture; | ||
|  | 		uniforms.tDepth.value = this.beautyRenderTarget.depthTexture; | ||
|  | 		uniforms.tNormal.value = this.normalRenderTarget.texture; | ||
|  | 
 | ||
|  | 		if ( this.renderToScreen ) { | ||
|  | 
 | ||
|  | 			renderer.setRenderTarget( null ); | ||
|  | 
 | ||
|  | 		} else { | ||
|  | 
 | ||
|  | 			renderer.setRenderTarget( writeBuffer ); | ||
|  | 
 | ||
|  | 			if ( this.clear ) renderer.clear(); | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		this.fsQuad.render( renderer ); | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	createPixelatedMaterial() { | ||
|  | 
 | ||
|  | 		return new ShaderMaterial( { | ||
|  | 			uniforms: { | ||
|  | 				tDiffuse: { value: null }, | ||
|  | 				tDepth: { value: null }, | ||
|  | 				tNormal: { value: null }, | ||
|  | 				resolution: { | ||
|  | 					value: new Vector4( | ||
|  | 						this.renderResolution.x, | ||
|  | 						this.renderResolution.y, | ||
|  | 						1 / this.renderResolution.x, | ||
|  | 						1 / this.renderResolution.y, | ||
|  | 					) | ||
|  | 				}, | ||
|  | 				normalEdgeStrength: { value: 0 }, | ||
|  | 				depthEdgeStrength: { value: 0 } | ||
|  | 			}, | ||
|  | 			vertexShader: /* glsl */`
 | ||
|  | 				varying vec2 vUv; | ||
|  | 
 | ||
|  | 				void main() { | ||
|  | 
 | ||
|  | 					vUv = uv; | ||
|  | 					gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); | ||
|  | 
 | ||
|  | 				} | ||
|  | 			`,
 | ||
|  | 			fragmentShader: /* glsl */`
 | ||
|  | 				uniform sampler2D tDiffuse; | ||
|  | 				uniform sampler2D tDepth; | ||
|  | 				uniform sampler2D tNormal; | ||
|  | 				uniform vec4 resolution; | ||
|  | 				uniform float normalEdgeStrength; | ||
|  | 				uniform float depthEdgeStrength; | ||
|  | 				varying vec2 vUv; | ||
|  | 
 | ||
|  | 				float getDepth(int x, int y) { | ||
|  | 
 | ||
|  | 					return texture2D( tDepth, vUv + vec2(x, y) * resolution.zw ).r; | ||
|  | 
 | ||
|  | 				} | ||
|  | 
 | ||
|  | 				vec3 getNormal(int x, int y) { | ||
|  | 
 | ||
|  | 					return texture2D( tNormal, vUv + vec2(x, y) * resolution.zw ).rgb * 2.0 - 1.0; | ||
|  | 
 | ||
|  | 				} | ||
|  | 
 | ||
|  | 				float depthEdgeIndicator(float depth, vec3 normal) { | ||
|  | 
 | ||
|  | 					float diff = 0.0; | ||
|  | 					diff += clamp(getDepth(1, 0) - depth, 0.0, 1.0); | ||
|  | 					diff += clamp(getDepth(-1, 0) - depth, 0.0, 1.0); | ||
|  | 					diff += clamp(getDepth(0, 1) - depth, 0.0, 1.0); | ||
|  | 					diff += clamp(getDepth(0, -1) - depth, 0.0, 1.0); | ||
|  | 					return floor(smoothstep(0.01, 0.02, diff) * 2.) / 2.; | ||
|  | 
 | ||
|  | 				} | ||
|  | 
 | ||
|  | 				float neighborNormalEdgeIndicator(int x, int y, float depth, vec3 normal) { | ||
|  | 
 | ||
|  | 					float depthDiff = getDepth(x, y) - depth; | ||
|  | 					vec3 neighborNormal = getNormal(x, y); | ||
|  | 
 | ||
|  | 					// Edge pixels should yield to faces who's normals are closer to the bias normal.
 | ||
|  | 					vec3 normalEdgeBias = vec3(1., 1., 1.); // This should probably be a parameter.
 | ||
|  | 					float normalDiff = dot(normal - neighborNormal, normalEdgeBias); | ||
|  | 					float normalIndicator = clamp(smoothstep(-.01, .01, normalDiff), 0.0, 1.0); | ||
|  | 
 | ||
|  | 					// Only the shallower pixel should detect the normal edge.
 | ||
|  | 					float depthIndicator = clamp(sign(depthDiff * .25 + .0025), 0.0, 1.0); | ||
|  | 
 | ||
|  | 					return (1.0 - dot(normal, neighborNormal)) * depthIndicator * normalIndicator; | ||
|  | 
 | ||
|  | 				} | ||
|  | 
 | ||
|  | 				float normalEdgeIndicator(float depth, vec3 normal) { | ||
|  | 
 | ||
|  | 					float indicator = 0.0; | ||
|  | 
 | ||
|  | 					indicator += neighborNormalEdgeIndicator(0, -1, depth, normal); | ||
|  | 					indicator += neighborNormalEdgeIndicator(0, 1, depth, normal); | ||
|  | 					indicator += neighborNormalEdgeIndicator(-1, 0, depth, normal); | ||
|  | 					indicator += neighborNormalEdgeIndicator(1, 0, depth, normal); | ||
|  | 
 | ||
|  | 					return step(0.1, indicator); | ||
|  | 
 | ||
|  | 				} | ||
|  | 
 | ||
|  | 				void main() { | ||
|  | 
 | ||
|  | 					vec4 texel = texture2D( tDiffuse, vUv ); | ||
|  | 
 | ||
|  | 					float depth = 0.0; | ||
|  | 					vec3 normal = vec3(0.0); | ||
|  | 
 | ||
|  | 					if (depthEdgeStrength > 0.0 || normalEdgeStrength > 0.0) { | ||
|  | 
 | ||
|  | 						depth = getDepth(0, 0); | ||
|  | 						normal = getNormal(0, 0); | ||
|  | 
 | ||
|  | 					} | ||
|  | 
 | ||
|  | 					float dei = 0.0; | ||
|  | 					if (depthEdgeStrength > 0.0) | ||
|  | 						dei = depthEdgeIndicator(depth, normal); | ||
|  | 
 | ||
|  | 					float nei = 0.0; | ||
|  | 					if (normalEdgeStrength > 0.0) | ||
|  | 						nei = normalEdgeIndicator(depth, normal); | ||
|  | 
 | ||
|  | 					float Strength = dei > 0.0 ? (1.0 - depthEdgeStrength * dei) : (1.0 + normalEdgeStrength * nei); | ||
|  | 
 | ||
|  | 					gl_FragColor = texel * Strength; | ||
|  | 
 | ||
|  | 				} | ||
|  | 			`
 | ||
|  | 		} ); | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | export { RenderPixelatedPass }; |