threepipe
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DepthBufferPlugin.ts 8.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. import {
  2. BasicDepthPacking,
  3. BufferGeometry,
  4. Camera,
  5. Color,
  6. DepthPackingStrategies,
  7. DoubleSide,
  8. FrontSide,
  9. MeshDepthMaterial,
  10. MeshDepthMaterialParameters,
  11. NoBlending,
  12. Object3D,
  13. Scene,
  14. Texture,
  15. TextureDataType,
  16. UnsignedByteType,
  17. WebGLRenderer,
  18. WebGLRenderTarget,
  19. } from 'three'
  20. import {GBufferRenderPass} from '../../postprocessing'
  21. import {ThreeViewer} from '../../viewer'
  22. import {MaterialExtension} from '../../materials'
  23. import {PipelinePassPlugin} from '../base/PipelinePassPlugin'
  24. import {uiDropdown, uiFolderContainer, uiImage} from 'uiconfig.js'
  25. import {shaderReplaceString} from '../../utils'
  26. import {onChange} from 'ts-browser-helpers'
  27. import DepthBufferUnpack from './shaders/DepthBufferPlugin.unpack.glsl'
  28. import {threeConstMappings} from '../../three'
  29. import {IMaterial, PhysicalMaterial} from '../../core'
  30. export type DepthBufferPluginEventTypes = ''
  31. // type DepthBufferPluginTarget = WebGLMultipleRenderTargets | WebGLRenderTarget
  32. export type DepthBufferPluginTarget = WebGLRenderTarget
  33. export type DepthBufferPluginPass = GBufferRenderPass<'depth', DepthBufferPluginTarget>
  34. /**
  35. * Depth Buffer Plugin
  36. *
  37. * Adds a pre-render pass to render the depth buffer to a render target that can be used as gbuffer or for postprocessing.
  38. * @category Plugins
  39. */
  40. @uiFolderContainer('Depth Buffer Plugin')
  41. export class DepthBufferPlugin
  42. extends PipelinePassPlugin<DepthBufferPluginPass, 'depth', DepthBufferPluginEventTypes> {
  43. readonly passId = 'depth'
  44. public static readonly PluginType = 'DepthBufferPlugin'
  45. target?: DepthBufferPluginTarget
  46. @uiImage('Depth Buffer' /* {readOnly: true}*/) texture?: Texture
  47. // @uiConfig() // not supported in this material yet
  48. readonly material: MeshDepthMaterial = new MeshDepthMaterialOverride({
  49. depthPacking: BasicDepthPacking,
  50. blending: NoBlending,
  51. transparent: true,
  52. })
  53. @onChange(DepthBufferPlugin.prototype._depthPackingChanged)
  54. @uiDropdown('Depth Packing', threeConstMappings.DepthPackingStrategies.uiConfig) depthPacking: DepthPackingStrategies
  55. // @onChange2(DepthBufferPlugin.prototype._createTarget)
  56. // @uiDropdown('Buffer Type', threeConstMappings.TextureDataType.uiConfig)
  57. readonly bufferType: TextureDataType // cannot be changed after creation (for now) todo line 139: unregisterMaterialExtensions, maybe because the priority is not set so its added at the end?
  58. // @uiToggle()
  59. // @onChange2(DepthBufferPlugin.prototype._createTarget)
  60. readonly isPrimaryGBuffer: boolean // cannot be changed after creation (for now)
  61. protected _depthPackingChanged() {
  62. this.material.depthPacking = this.depthPacking
  63. this.material.needsUpdate = true
  64. if (this.unpackExtension && this.unpackExtension.extraDefines) {
  65. this.unpackExtension.extraDefines.DEPTH_PACKING = this.depthPacking
  66. this.unpackExtension.setDirty?.()
  67. }
  68. this.setDirty()
  69. }
  70. unpackExtension: MaterialExtension = {
  71. shaderExtender: (shader)=>{
  72. const includes = ['depth_buffer_unpack', 'gbuffer_unpack', 'packing'] as const
  73. const include = includes.find(i=>shader.fragmentShader.includes(`#include <${i}>`))
  74. shader.fragmentShader = shaderReplaceString(shader.fragmentShader,
  75. `#include <${include}>`,
  76. '\n' + DepthBufferUnpack + '\n', {append: include === 'packing'})
  77. },
  78. extraUniforms: {
  79. tDepthBuffer: ()=>({value: this.target?.texture}),
  80. },
  81. extraDefines: {
  82. ['DEPTH_PACKING']: BasicDepthPacking,
  83. ['HAS_DEPTH_BUFFER']: ()=>this.target?.texture ? 1 : undefined,
  84. ['HAS_GBUFFER']: ()=>this.isPrimaryGBuffer && this.target?.texture ? 1 : undefined,
  85. },
  86. priority: 100,
  87. isCompatible: () => true,
  88. }
  89. private _isPrimaryGBufferSet = false
  90. protected _createTarget(recreate = true) {
  91. if (!this._viewer) return
  92. if (recreate) this._disposeTarget()
  93. if (!this.target)
  94. this.target = this._viewer.renderManager.createTarget<DepthBufferPluginTarget>(
  95. {
  96. depthBuffer: true,
  97. samples: this._viewer.renderManager.zPrepass && this.isPrimaryGBuffer ? // requirement for zPrepass
  98. this._viewer.renderManager.composerTarget.samples || 0 : 0,
  99. type: this.bufferType,
  100. // magFilter: NearestFilter,
  101. // minFilter: NearestFilter,
  102. // generateMipmaps: false,
  103. // encoding: LinearEncoding,
  104. })
  105. this.texture = this.target.texture
  106. this.texture.name = 'depthBuffer'
  107. if (this._pass) this._pass.target = this.target
  108. if (this.isPrimaryGBuffer) {
  109. this._viewer.renderManager.gbufferTarget = this.target
  110. this._viewer.renderManager.gbufferUnpackExtension = this.unpackExtension
  111. this._viewer.renderManager.screenPass.material.registerMaterialExtensions([this.unpackExtension])
  112. this._isPrimaryGBufferSet = true
  113. }
  114. }
  115. protected _disposeTarget() {
  116. if (!this._viewer) return
  117. if (this.target) {
  118. this._viewer.renderManager.disposeTarget(this.target)
  119. this.target = undefined
  120. }
  121. this.texture = undefined
  122. if (this._isPrimaryGBufferSet) { // using a separate flag as when isPrimaryGBuffer is changed, we cannot check it.
  123. this._viewer.renderManager.gbufferTarget = undefined
  124. this._viewer.renderManager.gbufferUnpackExtension = undefined
  125. // this._viewer.renderManager.screenPass.material.unregisterMaterialExtensions([this.unpackExtension]) // todo this has an issue
  126. this._isPrimaryGBufferSet = false
  127. }
  128. }
  129. protected _createPass() {
  130. this._createTarget(true)
  131. if (!this.target) throw new Error('DepthBufferPlugin: target not created')
  132. this.material.userData.isGBufferMaterial = true
  133. const pass = new GBufferRenderPass(this.passId, this.target, this.material, new Color(0, 0, 0), 1)
  134. const preprocessMaterial = pass.preprocessMaterial
  135. pass.preprocessMaterial = (m) => preprocessMaterial(m, m.userData.renderToDepth) // if renderToDepth is undefined then renderToGbuffer is taken internally
  136. pass.before = ['render']
  137. pass.after = []
  138. pass.required = ['render']
  139. return pass
  140. }
  141. constructor(
  142. bufferType: TextureDataType = UnsignedByteType,
  143. isPrimaryGBuffer = false,
  144. enabled = true,
  145. depthPacking: DepthPackingStrategies = BasicDepthPacking,
  146. ) {
  147. super()
  148. this.enabled = enabled
  149. this.depthPacking = depthPacking
  150. this.bufferType = bufferType
  151. this.isPrimaryGBuffer = isPrimaryGBuffer
  152. }
  153. onRemove(viewer: ThreeViewer): void {
  154. this._disposeTarget()
  155. return super.onRemove(viewer)
  156. }
  157. }
  158. class MeshDepthMaterialOverride extends MeshDepthMaterial {
  159. constructor(parameters: MeshDepthMaterialParameters) {
  160. super(parameters)
  161. this.reset()
  162. }
  163. onBeforeRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D) {
  164. super.onBeforeRender(renderer, scene, camera, geometry, object)
  165. let material = (object as any).material as IMaterial & Partial<PhysicalMaterial>
  166. if (Array.isArray(material)) { // todo: add support for multi materials.
  167. material = material[0]
  168. }
  169. if (!material) return
  170. if (material.map !== undefined) this.map = material.map // in case there is alpha in the map.
  171. if (material.side !== undefined) this.side = material.side ?? FrontSide
  172. if (material.alphaMap !== undefined) this.alphaMap = material.alphaMap
  173. if (material.alphaTest !== undefined) this.alphaTest = material.alphaTest < 1e-4 ? 1e-4 : material.alphaTest
  174. if (material.displacementMap !== undefined) this.displacementMap = material.displacementMap
  175. if (material.displacementScale !== undefined) this.displacementScale = material.displacementScale
  176. if (material.displacementBias !== undefined) this.displacementBias = material.displacementBias
  177. if (material.wireframe !== undefined) this.wireframe = material.wireframe
  178. if (material.wireframeLinewidth !== undefined) this.wireframeLinewidth = material.wireframeLinewidth
  179. this.needsUpdate = true
  180. }
  181. onAfterRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D) {
  182. super.onAfterRender(renderer, scene, camera, geometry, object)
  183. this.reset()
  184. }
  185. reset() {
  186. this.map = null
  187. this.side = DoubleSide
  188. this.alphaMap = null
  189. this.alphaTest = 0.001
  190. this.displacementMap = null
  191. this.displacementScale = 1
  192. this.displacementBias = 0
  193. this.wireframe = false
  194. this.wireframeLinewidth = 1
  195. }
  196. }