threepipe
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

ProgressivePlugin.ts 8.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. import {IUniform, Texture, TextureDataType, UnsignedByteType, WebGLRenderTarget} from 'three'
  2. import {IPassID, IPipelinePass} from '../../postprocessing'
  3. import {ISerializedConfig, ThreeViewer} from '../../viewer'
  4. import {PipelinePassPlugin} from '../base/PipelinePassPlugin'
  5. import {uiFolderContainer, uiImage, uiInput} from 'uiconfig.js'
  6. import {ICamera, IRenderManager, IScene, IWebGLRenderer} from '../../core'
  7. import {AddBlendTexturePass} from '../../postprocessing/AddBlendTexturePass'
  8. import {getOrCall, serialize, ValOrFunc} from 'ts-browser-helpers'
  9. import {IShaderPropertiesUpdater} from '../../materials'
  10. import {SerializationMetaType} from '../../utils'
  11. import {SSAAPlugin} from './SSAAPlugin'
  12. export type ProgressivePluginEventTypes = ''
  13. export type ProgressivePluginTarget = WebGLRenderTarget
  14. /**
  15. * Progressive Plugin
  16. *
  17. * Adds a post-render pass to blend the last frame with the current frame.
  18. * This can be used to create a progressive rendering effect which is useful for progressive shadows, gi, denoising, baking, anti-aliasing, and many other effects.
  19. * @category Plugins
  20. */
  21. @uiFolderContainer('Progressive Plugin')
  22. export class ProgressivePlugin
  23. extends PipelinePassPlugin<ProgressiveBlendPass, 'progressive', ProgressivePluginEventTypes> implements IShaderPropertiesUpdater {
  24. readonly passId = 'progressive'
  25. public static readonly PluginType = 'ProgressivePlugin'
  26. public static readonly OldPluginType = 'Progressive'
  27. /**
  28. * Different targets for different render cameras.
  29. * Need to save them all here since we need them in the next frame.
  30. * @protected
  31. */
  32. protected _targets = new Map<string, ProgressivePluginTarget>()
  33. @serialize() @uiInput('Frame count') maxFrameCount: number
  34. // @uiImage('Last Texture' /* {readOnly: true}*/) texture?: Texture
  35. get texture(): Texture | undefined {
  36. return this.target?.texture
  37. }
  38. get target(): ProgressivePluginTarget | undefined {
  39. return this._viewer ? this._targets.get(this._viewer.scene.renderCamera.uuid) : undefined
  40. }
  41. getTarget(camera?: ICamera) {
  42. return this._viewer ? this._targets.get((camera ? camera : this._viewer.scene.renderCamera).uuid) : undefined
  43. }
  44. get textures() {
  45. return this._viewer ? Array.from(this._targets.values()).map(t => t.texture) : []
  46. }
  47. @uiImage('Last Texture' /* {readOnly: true}*/)
  48. get mainTexture() {
  49. return this._viewer ? this.getTarget(this._viewer.scene.mainCamera)?.texture : undefined
  50. }
  51. /**
  52. * Note - this is not used right now
  53. */
  54. // @onChange2(ProgressivePlugin.prototype._createTarget)
  55. // @uiDropdown('Buffer Type', threeConstMappings.TextureDataType.uiConfig)
  56. readonly bufferType: TextureDataType // cannot be changed after creation (for now)
  57. constructor(
  58. maxFrameCount = 32,
  59. bufferType: TextureDataType = UnsignedByteType, // this is not used. todo use halffloat when rgbm = false
  60. enabled = true,
  61. ) {
  62. super()
  63. this.maxFrameCount = maxFrameCount
  64. this.enabled = enabled
  65. this.bufferType = bufferType
  66. }
  67. protected _createTarget(camera?: ICamera, recreate = false) {
  68. if (!this._viewer) return
  69. camera = camera ?? this._viewer.scene.renderCamera
  70. if (recreate) this._disposeTarget(camera)
  71. if (this._targets.has(camera.uuid)) return this._targets.get(camera.uuid)
  72. const target = this._viewer.renderManager.composerTarget.clone(true) as WebGLRenderTarget
  73. target.texture.name = 'progressiveLastBuffer_' + camera.uuid
  74. // target.texture.type = this.bufferType
  75. this._targets.set(camera.uuid, target)
  76. // if (this._pass) this._pass.target = this.target
  77. return target
  78. }
  79. protected _disposeTarget(camera?: ICamera) {
  80. if (!this._viewer) return
  81. if (!camera) {
  82. this._targets.forEach((t) => this._viewer!.renderManager.disposeTarget(t))
  83. this._targets.clear()
  84. } else {
  85. const t = this._targets.get(camera.uuid)
  86. if (t) {
  87. this._viewer!.renderManager.disposeTarget(t)
  88. this._targets.delete(camera.uuid)
  89. }
  90. }
  91. }
  92. protected _createPass() {
  93. // this._createTarget(true)
  94. const pass = new ProgressiveBlendPass(this.passId, ()=>this.target ?? this._createTarget()) // todo: disposeTarget somewhere
  95. pass.dirty = () => (this._viewer?.renderManager.frameCount || 0) < this.maxFrameCount // todo use isConverged function
  96. return pass
  97. }
  98. onAdded(viewer: ThreeViewer) {
  99. super.onAdded(viewer)
  100. }
  101. onRemove(viewer: ThreeViewer): void {
  102. this._disposeTarget()
  103. return super.onRemove(viewer)
  104. }
  105. /**
  106. *
  107. * @param postRender - if called after rendering frame.
  108. */
  109. public isConverged(postRender = false): boolean {
  110. return (this._viewer?.renderManager.frameCount || 0) >= this.maxFrameCount - 1 + (postRender ? 1 : 0)
  111. }
  112. updateShaderProperties(material: {defines: Record<string, string | number | undefined>; uniforms: {[p: string]: IUniform}}): this {
  113. if (material.uniforms.tLastFrame) material.uniforms.tLastFrame.value = this.target?.texture ?? undefined
  114. return this
  115. }
  116. /**
  117. * Get recording delta post render, For use with animations to sync with converge mode in canvas recorder. See PopmotionPlugin for usage.
  118. * @returns {number} - delta time in milliseconds, or 0 when converging, or -1 in case of not recording in converge mode
  119. */
  120. postFrameConvergedRecordingDelta(_ = 'CanvasRecorder'): number {
  121. // const recorder = this._viewer!.getPluginByType<IConvergedCanvasRecorder&IViewerPlugin>(recorderPlugin)
  122. // if (recorder && recorder.isRecording() && recorder.convergeMode)
  123. // return this.isConverged(true) ? 1. / recorder.videoFrameRate : 0
  124. return -1
  125. }
  126. get convergedPromise() {
  127. return new Promise<void>(resolve=>{
  128. if (this.isConverged()) {
  129. this._viewer?.doOnce('postFrame', ()=>resolve())
  130. } else {
  131. const l = ()=>{
  132. if (!this.isConverged(true)) return
  133. this._viewer?.removeEventListener('postRender', l)
  134. this._viewer?.doOnce('postFrame', ()=>resolve())
  135. }
  136. this._viewer?.addEventListener('postRender', l)
  137. }
  138. })
  139. }
  140. fromJSON(data: ISerializedConfig&{pass?: any}, meta?: SerializationMetaType): this|null|Promise<this|null> {
  141. if (data.jitter !== undefined) {
  142. const ssaa = this._viewer?.getPlugin(SSAAPlugin)
  143. if (!ssaa) {
  144. console.warn('Loading old webgi v0 file, add SSAAPlugin to get anti-aliasing')
  145. } else {
  146. data = {...data}
  147. ssaa.enabled = data.jitter
  148. delete data.jitter
  149. }
  150. }
  151. return super.fromJSON(data, meta)
  152. }
  153. }
  154. export class ProgressiveBlendPass extends AddBlendTexturePass implements IPipelinePass {
  155. before = ['screen']
  156. after = ['render']
  157. required = ['render']
  158. dirty: ValOrFunc<boolean> = () => false
  159. constructor(public readonly passId: IPassID, public target?: ValOrFunc<WebGLRenderTarget|undefined>) {
  160. super()
  161. }
  162. render(renderer: IWebGLRenderer, writeBuffer: WebGLRenderTarget, readBuffer: WebGLRenderTarget, deltaTime: number, maskActive: boolean) {
  163. if (!this.enabled) return
  164. const target = getOrCall(this.target)
  165. if (!target) {
  166. console.warn('ProgressiveBlendPass: target not defined')
  167. return
  168. }
  169. if (renderer.renderManager.frameCount < 1) {
  170. this.needsSwap = false
  171. if (readBuffer?.texture)
  172. renderer.renderManager.blit(target, {
  173. source: readBuffer.texture,
  174. respectColorSpace: false,
  175. })
  176. return
  177. }
  178. this.needsSwap = true
  179. super.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive)
  180. renderer.renderManager.blit(target, {
  181. source: writeBuffer.texture,
  182. respectColorSpace: false,
  183. })
  184. }
  185. beforeRender(_: IScene, _1: ICamera, renderManager: IRenderManager) {
  186. if (!this.enabled) return
  187. if (!this.target) {
  188. console.error('ProgressiveBlendPass: render target undefined')
  189. return
  190. }
  191. let f = 1. / (Math.max(renderManager.frameCount, 0) + 1)
  192. this.uniforms.weight.value.set(f, f, f, f)
  193. f = 1. - f
  194. this.uniforms.weight2.value.set(f, f, f, f)
  195. this.uniforms.tDiffuse2.value = getOrCall(this.target)?.texture
  196. this.material.uniformsNeedUpdate = true
  197. }
  198. }