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 };
 |