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

ProgressivePlugin.ts 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import {IUniform, Texture, TextureDataType, UnsignedByteType, WebGLRenderTarget} from 'three'
  2. import {IPassID, IPipelinePass} from '../../postprocessing'
  3. import {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 {serialize, ValOrFunc} from 'ts-browser-helpers'
  9. export type ProgressivePluginEventTypes = ''
  10. export type ProgressivePluginTarget = WebGLRenderTarget
  11. /**
  12. * Progressive Plugin
  13. *
  14. * Adds a post-render pass to blend the last frame with the current frame.
  15. * 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.
  16. * @category Plugins
  17. */
  18. @uiFolderContainer('Progressive Plugin')
  19. export class ProgressivePlugin
  20. extends PipelinePassPlugin<ProgressiveBlendPass, 'progressive', ProgressivePluginEventTypes> {
  21. readonly passId = 'progressive'
  22. public static readonly PluginType = 'ProgressivePlugin'
  23. target?: ProgressivePluginTarget
  24. @serialize() @uiInput('Frame count') maxFrameCount: number
  25. // todo: deserialize jitter
  26. @uiImage('Last Texture' /* {readOnly: true}*/) texture?: Texture
  27. // @onChange2(ProgressivePlugin.prototype._createTarget)
  28. // @uiDropdown('Buffer Type', threeConstMappings.TextureDataType.uiConfig)
  29. readonly bufferType: TextureDataType // cannot be changed after creation (for now)
  30. constructor(
  31. maxFrameCount = 32,
  32. bufferType: TextureDataType = UnsignedByteType,
  33. enabled = true,
  34. ) {
  35. super()
  36. this.maxFrameCount = maxFrameCount
  37. this.enabled = enabled
  38. this.bufferType = bufferType
  39. }
  40. protected _createTarget(recreate = true) {
  41. if (!this._viewer) return
  42. if (recreate) this._disposeTarget()
  43. if (!this.target) this.target = this._viewer.renderManager.composerTarget.clone(true) as WebGLRenderTarget
  44. this.texture = this.target.texture
  45. this.texture.name = 'progressiveLastBuffer'
  46. if (this._pass) this._pass.target = this.target
  47. }
  48. protected _disposeTarget() {
  49. if (!this._viewer) return
  50. if (this.target) {
  51. this._viewer.renderManager.disposeTarget(this.target)
  52. this.target = undefined
  53. }
  54. this.texture = undefined
  55. }
  56. protected _createPass() {
  57. this._createTarget(true)
  58. if (!this.target) throw new Error('ProgressivePlugin: target not created')
  59. const pass = new ProgressiveBlendPass(this.passId, this.target)
  60. pass.dirty = () => (this._viewer?.renderManager.frameCount || 0) < this.maxFrameCount // todo use isConverged function
  61. return pass
  62. }
  63. onRemove(viewer: ThreeViewer): void {
  64. this._disposeTarget()
  65. return super.onRemove(viewer)
  66. }
  67. /**
  68. *
  69. * @param postRender - if called after rendering frame.
  70. */
  71. public isConverged(postRender = false): boolean {
  72. return (this._viewer?.renderer.frameCount || 0) >= this.maxFrameCount - 1 + (postRender ? 1 : 0)
  73. }
  74. updateShaderProperties(material: {defines: Record<string, string | number | undefined>; uniforms: {[p: string]: IUniform}}): this {
  75. if (material.uniforms.tLastFrame) material.uniforms.tLastFrame.value = this.target?.texture ?? undefined
  76. return this
  77. }
  78. /**
  79. * Get recording delta post render, For use with animations to sync with converge mode in canvas recorder. See PopmotionPlugin for usage.
  80. * @returns {number} - delta time in milliseconds, or 0 when converging, or -1 in case of not recording in converge mode
  81. */
  82. postFrameConvergedRecordingDelta(_ = 'CanvasRecorder'): number {
  83. // const recorder = this._viewer!.getPluginByType<IConvergedCanvasRecorder&IViewerPlugin>(recorderPlugin)
  84. // if (recorder && recorder.isRecording() && recorder.convergeMode)
  85. // return this.isConverged(true) ? 1. / recorder.videoFrameRate : 0
  86. return -1
  87. }
  88. }
  89. class ProgressiveBlendPass extends AddBlendTexturePass implements IPipelinePass {
  90. before = ['screen']
  91. after = ['render']
  92. required = ['render']
  93. dirty: ValOrFunc<boolean> = () => false
  94. constructor(public readonly passId: IPassID, public target: WebGLRenderTarget) {
  95. super()
  96. }
  97. render(renderer: IWebGLRenderer, writeBuffer: WebGLRenderTarget, readBuffer: WebGLRenderTarget, deltaTime: number, maskActive: boolean) {
  98. if (renderer.renderManager.frameCount < 1) {
  99. this.needsSwap = false
  100. if (readBuffer?.texture)
  101. renderer.renderManager.blit(this.target, {
  102. source: readBuffer.texture,
  103. respectColorSpace: false,
  104. })
  105. return
  106. }
  107. this.needsSwap = true
  108. super.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive)
  109. renderer.renderManager.blit(this.target, {
  110. source: writeBuffer.texture,
  111. respectColorSpace: false,
  112. })
  113. }
  114. beforeRender(_: IScene, _1: ICamera, renderManager: IRenderManager) {
  115. if (!this.enabled) return
  116. if (!this.target) {
  117. console.error('ProgressiveBlendPass: render target undefined')
  118. return
  119. }
  120. let f = 1. / (Math.max(renderManager.frameCount, 0) + 1)
  121. this.uniforms.weight.value.set(f, f, f, f)
  122. f = 1. - f
  123. this.uniforms.weight2.value.set(f, f, f, f)
  124. this.uniforms.tDiffuse2.value = this.target.texture
  125. this.material.uniformsNeedUpdate = true
  126. }
  127. }