145 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			145 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | import { MeshPhysicalMaterial } from 'three'; | ||
|  | 
 | ||
|  | /** | ||
|  |  * The aim of this mesh material is to use information from a post processing pass in the diffuse color pass. | ||
|  |  * This material is based on the MeshPhysicalMaterial. | ||
|  |  * | ||
|  |  * In the current state, only the information of a screen space AO pass can be used in the material. | ||
|  |  * Actually, the output of any screen space AO (SSAO, GTAO) can be used, | ||
|  |  * as it is only necessary to provide the AO in one color channel of a texture, | ||
|  |  * however the AO pass must be rendered prior to the color pass, | ||
|  |  * which makes the post-processing pass somewhat of a pre-processing pass. | ||
|  |  * Fot this purpose a new map (`aoPassMap`) is added to the material. | ||
|  |  * The value of the map is used the same way as the `aoMap` value. | ||
|  |  * | ||
|  |  * Motivation to use the outputs AO pass directly in the material: | ||
|  |  * The incident light of a fragment is composed of ambient light, direct light and indirect light | ||
|  |  * Ambient Occlusion only occludes ambient light and environment light, but not direct light. | ||
|  |  * Direct light is only occluded by geometry that casts shadows. | ||
|  |  * And of course the emitted light should not be darkened by ambient occlusion either. | ||
|  |  * This cannot be achieved if the AO post processing pass is simply blended with the diffuse render pass. | ||
|  |  * | ||
|  |  * Further extension work might be to use the output of an SSR pass or an HBIL pass from a previous frame. | ||
|  |  * This would then create the possibility of SSR and IR depending on material properties such as `roughness`, `metalness` and `reflectivity`. | ||
|  | **/ | ||
|  | 
 | ||
|  | class MeshPostProcessingMaterial extends MeshPhysicalMaterial { | ||
|  | 
 | ||
|  | 	constructor( parameters ) { | ||
|  | 
 | ||
|  | 		const aoPassMap = parameters.aoPassMap; | ||
|  | 		const aoPassMapScale = parameters.aoPassMapScale || 1.0; | ||
|  | 		delete parameters.aoPassMap; | ||
|  | 		delete parameters.aoPassMapScale; | ||
|  | 
 | ||
|  | 		super( parameters ); | ||
|  | 
 | ||
|  | 		this.onBeforeCompile = this._onBeforeCompile; | ||
|  | 		this.customProgramCacheKey = this._customProgramCacheKey; | ||
|  | 		this._aoPassMap = aoPassMap; | ||
|  | 		this.aoPassMapScale = aoPassMapScale; | ||
|  | 		this._shader = null; | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	get aoPassMap() { | ||
|  | 
 | ||
|  | 		return this._aoPassMap; | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	set aoPassMap( aoPassMap ) { | ||
|  | 
 | ||
|  | 		this._aoPassMap = aoPassMap; | ||
|  | 		this.needsUpdate = true; | ||
|  | 		this._setUniforms(); | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	_customProgramCacheKey() { | ||
|  | 
 | ||
|  | 		return this._aoPassMap !== undefined && this._aoPassMap !== null ? 'aoPassMap' : ''; | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	_onBeforeCompile( shader ) { | ||
|  | 
 | ||
|  | 		this._shader = shader; | ||
|  | 
 | ||
|  | 		if ( this._aoPassMap !== undefined && this._aoPassMap !== null ) { | ||
|  | 
 | ||
|  | 			shader.fragmentShader = shader.fragmentShader.replace( | ||
|  | 				'#include <aomap_pars_fragment>', | ||
|  | 				aomap_pars_fragment_replacement | ||
|  | 			); | ||
|  | 			shader.fragmentShader = shader.fragmentShader.replace( | ||
|  | 				'#include <aomap_fragment>', | ||
|  | 				aomap_fragment_replacement | ||
|  | 			); | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		this._setUniforms(); | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	_setUniforms() { | ||
|  | 
 | ||
|  | 		if ( this._shader ) { | ||
|  | 
 | ||
|  | 			this._shader.uniforms.tAoPassMap = { value: this._aoPassMap }; | ||
|  | 			this._shader.uniforms.aoPassMapScale = { value: this.aoPassMapScale }; | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | const aomap_pars_fragment_replacement = /* glsl */`
 | ||
|  | #ifdef USE_AOMAP | ||
|  | 
 | ||
|  | 	uniform sampler2D aoMap; | ||
|  | 	uniform float aoMapIntensity; | ||
|  | 
 | ||
|  | #endif | ||
|  | 
 | ||
|  | 	uniform sampler2D tAoPassMap; | ||
|  | 	uniform float aoPassMapScale; | ||
|  | `;
 | ||
|  | 
 | ||
|  | const aomap_fragment_replacement = /* glsl */`
 | ||
|  | #ifndef AOPASSMAP_SWIZZLE | ||
|  | 	#define AOPASSMAP_SWIZZLE r | ||
|  | #endif | ||
|  | 	float ambientOcclusion = texelFetch( tAoPassMap, ivec2( gl_FragCoord.xy * aoPassMapScale ), 0 ).AOPASSMAP_SWIZZLE; | ||
|  | 
 | ||
|  | #ifdef USE_AOMAP | ||
|  | 
 | ||
|  | 	// reads channel R, compatible with a combined OcclusionRoughnessMetallic (RGB) texture
 | ||
|  | 	ambientOcclusion = min( ambientOcclusion, texture2D( aoMap, vAoMapUv ).r ); | ||
|  | 	ambientOcclusion *= ( ambientOcclusion - 1.0 ) * aoMapIntensity + 1.0; | ||
|  | 
 | ||
|  | #endif | ||
|  | 
 | ||
|  | 	reflectedLight.indirectDiffuse *= ambientOcclusion; | ||
|  | 
 | ||
|  | 	#if defined( USE_CLEARCOAT )  | ||
|  | 		clearcoatSpecularIndirect *= ambientOcclusion; | ||
|  | 	#endif | ||
|  | 
 | ||
|  | 	#if defined( USE_SHEEN )  | ||
|  | 		sheenSpecularIndirect *= ambientOcclusion; | ||
|  | 	#endif | ||
|  | 
 | ||
|  | 	#if defined( USE_ENVMAP ) && defined( STANDARD ) | ||
|  | 
 | ||
|  | 		float dotNV = saturate( dot( geometryNormal, geometryViewDir ) ); | ||
|  | 
 | ||
|  | 		reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness ); | ||
|  | 
 | ||
|  | 	#endif | ||
|  | `;
 | ||
|  | 
 | ||
|  | export { MeshPostProcessingMaterial }; |