MeshPostProcessingMaterial.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import { MeshPhysicalMaterial } from 'three';
  2. /**
  3. * The aim of this mesh material is to use information from a post processing pass in the diffuse color pass.
  4. * This material is based on the MeshPhysicalMaterial.
  5. *
  6. * In the current state, only the information of a screen space AO pass can be used in the material.
  7. * Actually, the output of any screen space AO (SSAO, GTAO) can be used,
  8. * as it is only necessary to provide the AO in one color channel of a texture,
  9. * however the AO pass must be rendered prior to the color pass,
  10. * which makes the post-processing pass somewhat of a pre-processing pass.
  11. * Fot this purpose a new map (`aoPassMap`) is added to the material.
  12. * The value of the map is used the same way as the `aoMap` value.
  13. *
  14. * Motivation to use the outputs AO pass directly in the material:
  15. * The incident light of a fragment is composed of ambient light, direct light and indirect light
  16. * Ambient Occlusion only occludes ambient light and environment light, but not direct light.
  17. * Direct light is only occluded by geometry that casts shadows.
  18. * And of course the emitted light should not be darkened by ambient occlusion either.
  19. * This cannot be achieved if the AO post processing pass is simply blended with the diffuse render pass.
  20. *
  21. * Further extension work might be to use the output of an SSR pass or an HBIL pass from a previous frame.
  22. * This would then create the possibility of SSR and IR depending on material properties such as `roughness`, `metalness` and `reflectivity`.
  23. *
  24. * @augments MeshPhysicalMaterial
  25. * @three_import import { MeshPostProcessingMaterial } from 'three/addons/materials/MeshPostProcessingMaterial.js';
  26. */
  27. class MeshPostProcessingMaterial extends MeshPhysicalMaterial {
  28. /**
  29. * Constructs a new conditional line material.
  30. *
  31. * @param {Object} [parameters] - An object with one or more properties
  32. * defining the material's appearance. Any property of the material
  33. * (including any property from inherited materials) can be passed
  34. * in here. Color values can be passed any type of value accepted
  35. * by {@link Color#set}.
  36. */
  37. constructor( parameters ) {
  38. const aoPassMap = parameters.aoPassMap;
  39. const aoPassMapScale = parameters.aoPassMapScale || 1.0;
  40. delete parameters.aoPassMap;
  41. delete parameters.aoPassMapScale;
  42. super( parameters );
  43. this.onBeforeCompile = this._onBeforeCompile;
  44. this.customProgramCacheKey = this._customProgramCacheKey;
  45. this._aoPassMap = aoPassMap;
  46. /**
  47. * The scale of the AO pass.
  48. *
  49. * @type {number}
  50. * @default 1
  51. */
  52. this.aoPassMapScale = aoPassMapScale;
  53. this._shader = null;
  54. }
  55. /**
  56. * A texture representing the AO pass.
  57. *
  58. * @type {Texture}
  59. */
  60. get aoPassMap() {
  61. return this._aoPassMap;
  62. }
  63. set aoPassMap( aoPassMap ) {
  64. this._aoPassMap = aoPassMap;
  65. this.needsUpdate = true;
  66. this._setUniforms();
  67. }
  68. _customProgramCacheKey() {
  69. return this._aoPassMap !== undefined && this._aoPassMap !== null ? 'aoPassMap' : '';
  70. }
  71. _onBeforeCompile( shader ) {
  72. this._shader = shader;
  73. if ( this._aoPassMap !== undefined && this._aoPassMap !== null ) {
  74. shader.fragmentShader = shader.fragmentShader.replace(
  75. '#include <aomap_pars_fragment>',
  76. aomap_pars_fragment_replacement
  77. );
  78. shader.fragmentShader = shader.fragmentShader.replace(
  79. '#include <aomap_fragment>',
  80. aomap_fragment_replacement
  81. );
  82. }
  83. this._setUniforms();
  84. }
  85. _setUniforms() {
  86. if ( this._shader ) {
  87. this._shader.uniforms.tAoPassMap = { value: this._aoPassMap };
  88. this._shader.uniforms.aoPassMapScale = { value: this.aoPassMapScale };
  89. }
  90. }
  91. }
  92. const aomap_pars_fragment_replacement = /* glsl */`
  93. #ifdef USE_AOMAP
  94. uniform sampler2D aoMap;
  95. uniform float aoMapIntensity;
  96. #endif
  97. uniform sampler2D tAoPassMap;
  98. uniform float aoPassMapScale;
  99. `;
  100. const aomap_fragment_replacement = /* glsl */`
  101. #ifndef AOPASSMAP_SWIZZLE
  102. #define AOPASSMAP_SWIZZLE r
  103. #endif
  104. float ambientOcclusion = texelFetch( tAoPassMap, ivec2( gl_FragCoord.xy * aoPassMapScale ), 0 ).AOPASSMAP_SWIZZLE;
  105. #ifdef USE_AOMAP
  106. // reads channel R, compatible with a combined OcclusionRoughnessMetallic (RGB) texture
  107. ambientOcclusion = min( ambientOcclusion, texture2D( aoMap, vAoMapUv ).r );
  108. ambientOcclusion *= ( ambientOcclusion - 1.0 ) * aoMapIntensity + 1.0;
  109. #endif
  110. reflectedLight.indirectDiffuse *= ambientOcclusion;
  111. #if defined( USE_CLEARCOAT )
  112. clearcoatSpecularIndirect *= ambientOcclusion;
  113. #endif
  114. #if defined( USE_SHEEN )
  115. sheenSpecularIndirect *= ambientOcclusion;
  116. #endif
  117. #if defined( USE_ENVMAP ) && defined( STANDARD )
  118. float dotNV = saturate( dot( geometryNormal, geometryViewDir ) );
  119. reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness );
  120. #endif
  121. `;
  122. export { MeshPostProcessingMaterial };