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.

ScreenPass.ts 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import {ExtendedShaderPass} from './ExtendedShaderPass'
  2. import {
  3. ColorSpace,
  4. FrontSide,
  5. NoBlending,
  6. ShaderMaterialParameters,
  7. SRGBColorSpace,
  8. WebGLMultipleRenderTargets,
  9. WebGLRenderTarget,
  10. } from 'three'
  11. import {ICamera, IRenderManager, IScene, IWebGLRenderer, ShaderMaterial2} from '../core'
  12. import {CopyShader} from 'three/examples/jsm/shaders/CopyShader.js'
  13. import {IPassID, IPipelinePass} from './Pass'
  14. import {uiDropdown, uiFolderContainer, UiObjectConfig, uiToggle} from 'uiconfig.js'
  15. import {ViewerRenderManager} from '../viewer'
  16. import {matDefineBool, threeConstMappings} from '../three'
  17. import ScreenPassShader from './ScreenPass.glsl'
  18. import {shaderReplaceString} from '../utils'
  19. export type TViewerScreenShaderFrag = string | [string, string] | {pars?: string, main: string}
  20. export type TViewerScreenShader = TViewerScreenShaderFrag | ShaderMaterialParameters | ShaderMaterial2
  21. @uiFolderContainer('Screen Pass')
  22. export class ScreenPass extends ExtendedShaderPass implements IPipelinePass<'screen'> {
  23. declare uiConfig: UiObjectConfig
  24. readonly passId = 'screen'
  25. after: IPassID[] = ['render']
  26. required: IPassID[] = ['render']
  27. constructor(shader: TViewerScreenShader = '', ...textureID: string[]) {
  28. super(
  29. (<any>shader)?.fragmentShader || (<ShaderMaterial2>shader)?.isShaderMaterial ? <ShaderMaterialParameters|ShaderMaterial2>shader :
  30. makeScreenShader(shader),
  31. ...textureID.length ? textureID : ['tDiffuse', 'tTransparent'])
  32. this.material.addEventListener('materialUpdate', this.setDirty)
  33. }
  34. /**
  35. * Output Color Space
  36. * Note: this is ignored when renderToScreen is false (it will take the color space of the render target)
  37. */
  38. @uiDropdown('Output Color Space', threeConstMappings.ColorSpace.uiConfig, (t: ScreenPass)=>({onChange: t.setDirty}))
  39. outputColorSpace: ColorSpace = SRGBColorSpace
  40. private _lastReadBuffer?: WebGLMultipleRenderTargets | WebGLRenderTarget
  41. render(renderer: IWebGLRenderer, writeBuffer?: WebGLMultipleRenderTargets | WebGLRenderTarget | null, readBuffer?: WebGLMultipleRenderTargets | WebGLRenderTarget, deltaTime?: number, maskActive?: boolean) {
  42. const colorSpace = renderer.outputColorSpace
  43. if (!writeBuffer || this.renderToScreen) renderer.outputColorSpace = this.outputColorSpace
  44. // else console.warn('ScreenPass: outputColorSpace is ignored when renderToScreen is false')
  45. super.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive)
  46. this._lastReadBuffer = readBuffer
  47. renderer.outputColorSpace = colorSpace
  48. this._needsReRender = false
  49. }
  50. reRender(renderer: IWebGLRenderer, writeBuffer?: WebGLMultipleRenderTargets | WebGLRenderTarget | null, deltaTime?: number, maskActive?: boolean) {
  51. if (this._lastReadBuffer) this.render(renderer, writeBuffer, this._lastReadBuffer, deltaTime, maskActive)
  52. }
  53. private _needsReRender = false
  54. onPostFrame(renderManager: IRenderManager) {
  55. if (!this._needsReRender) return
  56. this._needsReRender = false
  57. this.reRender(renderManager.renderer)
  58. if (this.clipBackground && !(renderManager as ViewerRenderManager).gbufferTarget) {
  59. // todo warn only when rgbm
  60. console.warn('ScreenPass: clipBackground set to true but no gbufferTarget set. Try adding GBufferPlugin.')
  61. }
  62. }
  63. dispose() {
  64. this._lastReadBuffer = undefined
  65. super.dispose()
  66. }
  67. /**
  68. * Force clip background. If this is `true` {@link clipBackground} is overridden.
  69. * This happens when scene.background and scene.backgroundColor are both null.
  70. * This is set in {@link ViewerRenderManager.render}.
  71. */
  72. @matDefineBool('CLIP_BACKGROUND_FORCE', undefined, undefined, ScreenPass.prototype.setDirty, true)
  73. clipBackgroundForce = false
  74. // todo: this is not serialized anymore?
  75. @matDefineBool('CLIP_BACKGROUND', undefined, undefined, ScreenPass.prototype.setDirty)
  76. @uiToggle() clipBackground = false
  77. beforeRender(_: IScene, _1: ICamera, renderManager: ViewerRenderManager) {
  78. this.material.uniforms.tTransparent.value = renderManager.renderPass.preserveTransparentTarget ? renderManager.renderPass.transparentTarget?.texture || null : null
  79. this.material.defines.HAS_TRANSPARENT_TARGET = this.material.uniforms.tTransparent.value ? 1 : undefined
  80. if (!this.material.defines.HAS_TRANSPARENT_TARGET) delete this.material.defines.HAS_TRANSPARENT_TARGET
  81. }
  82. setDirty() {
  83. super.setDirty()
  84. this._needsReRender = true
  85. }
  86. }
  87. function makeScreenShader(shader: string | [string, string] | {pars?: string; main: string} | ShaderMaterialParameters | ShaderMaterial2) {
  88. return {
  89. ...CopyShader,
  90. fragmentShader:
  91. shaderReplaceString(shaderReplaceString(ScreenPassShader,
  92. 'void main()', (Array.isArray(shader) ? shader[0] : (<any>shader)?.pars || '') + '\n', {prepend: true}),
  93. '#glMarker', (Array.isArray(shader) ? shader[1] : typeof shader === 'string' ? shader : (shader as any)?.main || '') + '\n', {prepend: true}),
  94. uniforms: {
  95. tDiffuse: {value: null},
  96. tTransparent: {value: null},
  97. },
  98. transparent: true,
  99. blending: NoBlending,
  100. side: FrontSide,
  101. } as ShaderMaterialParameters
  102. }