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