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.

ExtendedRenderPass.ts 12KB

пре 3 година
пре 3 година
пре 3 година
пре 3 година
пре 3 година
пре 3 година
пре 3 година
пре 3 година
пре 3 година
пре 3 година
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. import {IPipelinePass} from './Pass'
  2. import {RenderPass} from 'three/examples/jsm/postprocessing/RenderPass.js'
  3. import {
  4. Color,
  5. HalfFloatType,
  6. LinearFilter,
  7. Material,
  8. NoColorSpace,
  9. RGBAFormat,
  10. UnsignedByteType,
  11. WebGLMultipleRenderTargets,
  12. WebGLRenderTarget,
  13. } from 'three'
  14. import {generateUiConfig, UiObjectConfig, uiToggle} from 'uiconfig.js'
  15. import {serialize} from 'ts-browser-helpers'
  16. import {GenericBlendTexturePass} from './GenericBlendTexturePass'
  17. import {IRenderTarget} from '../rendering'
  18. import {ICamera, IRenderManager, IScene, IWebGLRenderer} from '../core'
  19. import {ViewerRenderManager} from '../viewer'
  20. export class ExtendedRenderPass extends RenderPass implements IPipelinePass<'render'> {
  21. readonly isExtendedRenderPass = true
  22. @uiToggle('Enabled') @serialize() enabled = true
  23. readonly passId = 'render'
  24. private _blendPass: GenericBlendTexturePass
  25. readonly renderManager: ViewerRenderManager
  26. private _doTransmissionFix = true
  27. blurTransmissionTarget = true
  28. preserveTransparentTarget = false
  29. private _transparentTarget?: IRenderTarget
  30. get transparentTarget(): IRenderTarget {
  31. if (!this._transparentTarget) {
  32. this._transparentTarget = this.renderManager.getTempTarget({
  33. sizeMultiplier: 1,
  34. samples: this.renderManager.composerTarget.samples || 0,
  35. colorSpace: NoColorSpace,
  36. type: this.renderManager.renderer.extensions.has('EXT_color_buffer_half_float') ? HalfFloatType : UnsignedByteType,
  37. format: RGBAFormat,
  38. minFilter: LinearFilter,
  39. magFilter: LinearFilter,
  40. depthBuffer: false,
  41. })
  42. }
  43. return this._transparentTarget
  44. }
  45. private _releaseTransparentTarget() {
  46. if (this._transparentTarget)
  47. this.renderManager.releaseTempTarget(this._transparentTarget)
  48. this._transparentTarget = undefined
  49. }
  50. constructor(renderManager: ViewerRenderManager, overrideMaterial?: Material, clearColor = new Color(0, 0, 0), clearAlpha = 0) {
  51. super(undefined, undefined, overrideMaterial, clearColor, clearAlpha)
  52. this.renderManager = renderManager
  53. this._blendPass = new GenericBlendTexturePass({}, 'c = vec4(a.rgb * (1. - b.a) + b.rgb * b.a, 1.);')
  54. this.setDirty = this.setDirty.bind(this)
  55. }
  56. render(renderer: IWebGLRenderer, writeBuffer?: WebGLMultipleRenderTargets|WebGLRenderTarget|null, readBuffer?: WebGLMultipleRenderTargets|WebGLRenderTarget, deltaTime?: number, maskActive?: boolean) {
  57. if (!this.enabled) return
  58. let needsSwap = false
  59. renderer.userData.mainRenderPass = true
  60. if (!this._doTransmissionFix && !this.renderManager.rgbm) {
  61. super.render(renderer, writeBuffer || null, readBuffer, deltaTime, maskActive)
  62. this.needsSwap = needsSwap
  63. renderer.userData.mainRenderPass = undefined
  64. return
  65. }
  66. const ud = renderer.userData
  67. if (!ud) console.error('threejs is not patched?')
  68. const useGBufferDepth = (this.renderManager.zPrepass || !this.renderManager.depthBuffer) && this.renderManager.gbufferTarget
  69. let depthRenderBuffer: WebGLRenderbuffer | undefined = undefined
  70. if (useGBufferDepth) {
  71. const gbuffer = this.renderManager.gbufferTarget
  72. if (gbuffer) {
  73. const renderBufferProps = renderer.properties.get(gbuffer)
  74. depthRenderBuffer = renderBufferProps.__webglDepthRenderbuffer || renderBufferProps.__webglDepthbuffer
  75. }
  76. if (!depthRenderBuffer) {
  77. console.warn('No depth/gbuffer present for zPrepass.')
  78. }
  79. }
  80. let renderFn = ()=> {
  81. super.render(renderer, null, readBuffer, deltaTime, maskActive, depthRenderBuffer) // read is write in super.render (RenderPass)
  82. }
  83. if (!this.renderManager.rgbm) {
  84. // Opaque + Transparent
  85. {
  86. const curClear = this.clear
  87. const curClearDepth = renderer.autoClearDepth
  88. renderer.autoClearDepth = !useGBufferDepth
  89. this.clear = true
  90. renderer.renderWithModes({
  91. shadowMapRender: true,
  92. backgroundRender: true,
  93. opaqueRender: true,
  94. transparentRender: true,
  95. transmissionRender: false,
  96. }, renderFn)
  97. this.clear = curClear
  98. renderer.autoClearDepth = curClearDepth
  99. }
  100. // Transmissive
  101. {
  102. const source = !readBuffer ? undefined : Array.isArray(readBuffer.texture) ? readBuffer.texture[0] : readBuffer.texture
  103. // todo: first check if any transmissive object is there to use this buffer
  104. this.renderManager.blit(writeBuffer, {clear: true, source})
  105. // viewer.renderer.blit(writeBuffer.texture as any, readBuffer as any, {})
  106. // super.render(renderer, undefined as any, writeBuffer, deltaTime, maskActive); // copy read to write buffer
  107. const curClear = this.clear
  108. this.clear = false
  109. // don't need this clear is already false
  110. // const curClearDepth = renderer.autoClearDepth
  111. // renderer.autoClearDepth = false
  112. ud.transmissionRenderTarget = writeBuffer
  113. ud.blurTransmissionTarget = this.blurTransmissionTarget && ud.transmissionRenderTarget.samples === 0 // todo: not working with msaa
  114. renderer.renderWithModes({
  115. shadowMapRender: false,
  116. backgroundRender: false,
  117. opaqueRender: false,
  118. transparentRender: false,
  119. transmissionRender: true,
  120. }, renderFn)
  121. ud.blurTransmissionTarget = undefined
  122. ud.transmissionRenderTarget = undefined
  123. // renderer.autoClearDepth = curClearDepth
  124. this.clear = curClear
  125. }
  126. needsSwap = false
  127. } else if (this.renderManager.rgbm) {
  128. needsSwap = false
  129. const renderToScreen = this.renderToScreen
  130. if (renderToScreen && !writeBuffer) {
  131. console.error('ExtendedRenderPass: renderToScreen is true but writeBuffer is not set, which is required for rgbm')
  132. }
  133. this.renderToScreen = false // for super RenderPass.render
  134. if (renderer.info && !renderer.info.autoReset)
  135. throw 'renderer.info.autoReset must be true' // it is required to check if any object is rendered in the scene, also frame count is maintained separately in the render manager, use that.
  136. // Opaque
  137. {
  138. const curClearDepth = renderer.autoClearDepth
  139. renderer.autoClearDepth = !useGBufferDepth
  140. renderer.renderWithModes({
  141. shadowMapRender: true,
  142. backgroundRender: true,
  143. opaqueRender: true,
  144. transparentRender: false,
  145. transmissionRender: false,
  146. }, renderFn)
  147. renderer.autoClearDepth = curClearDepth
  148. }
  149. if (!useGBufferDepth && readBuffer) {
  150. const renderBufferProps2 = renderer.properties.get(readBuffer)
  151. depthRenderBuffer = renderBufferProps2.__webglDepthRenderbuffer || renderBufferProps2.__webglDepthbuffer
  152. }
  153. renderFn = ()=> {
  154. super.render(renderer, null, this.transparentTarget as any, deltaTime, maskActive, depthRenderBuffer)
  155. }
  156. // Transparent
  157. {
  158. const curClear = this.clear
  159. const curClearDepth = renderer.autoClearDepth
  160. renderer.autoClearDepth = false
  161. this.clear = true
  162. renderer.renderWithModes({
  163. shadowMapRender: false,
  164. backgroundRender: false,
  165. opaqueRender: false,
  166. transparentRender: true,
  167. transmissionRender: false,
  168. }, renderFn)
  169. this.clear = curClear
  170. renderer.autoClearDepth = curClearDepth
  171. }
  172. if (!renderer.info || renderer.info.render.calls > 0) {
  173. this._blendPass.uniforms.tDiffuse2.value = this.transparentTarget.texture
  174. this._blendPass.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive)
  175. needsSwap = true
  176. }
  177. // Transmission
  178. {
  179. const curClear = this.clear
  180. this.clear = false // it is cleared in transparent pass above even if no object is rendered
  181. // const curClearDepth = renderer.autoClearDepth
  182. // renderer.autoClearDepth = false
  183. ud.transmissionRenderTarget = needsSwap ? writeBuffer : readBuffer
  184. ud.blurTransmissionTarget = this.blurTransmissionTarget && ud.transmissionRenderTarget.samples === 0 // todo: not working with msaa
  185. renderer.renderWithModes({
  186. shadowMapRender: false,
  187. backgroundRender: false,
  188. opaqueRender: false,
  189. transparentRender: false,
  190. transmissionRender: true,
  191. }, renderFn)
  192. ud.blurTransmissionTarget = undefined
  193. ud.transmissionRenderTarget = undefined
  194. // renderer.autoClearDepth = curClearDepth
  195. this.clear = curClear
  196. }
  197. // console.log(renderer.info.render.calls)
  198. if (!renderer.info || renderer.info.render.calls > 0) {
  199. // console.log('missive blit', renderer.info.render.frame)
  200. this._blendPass.uniforms.tDiffuse2.value = this.transparentTarget.texture
  201. this._blendPass.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive)
  202. needsSwap = true
  203. }
  204. if (renderToScreen) {
  205. this.renderToScreen = true
  206. const tex = needsSwap ? writeBuffer?.texture : readBuffer?.texture
  207. const source = Array.isArray(tex) ? tex[0] : tex
  208. source && this.renderManager.blit(undefined, {
  209. source, respectColorSpace: true,
  210. })
  211. // needsSwap = false
  212. }
  213. }
  214. if (!this.preserveTransparentTarget)
  215. this._releaseTransparentTarget()
  216. this.needsSwap = needsSwap
  217. renderer.userData.mainRenderPass = undefined
  218. }
  219. public onDirty: (()=>void)[] = []
  220. dispose() {
  221. this._releaseTransparentTarget()
  222. this.onDirty = []
  223. this.scene = undefined
  224. this.camera = undefined
  225. super.dispose?.()
  226. }
  227. setDirty() {
  228. this.onDirty.forEach(v=>v())
  229. }
  230. beforeRender(scene: IScene, camera: ICamera, _: IRenderManager): void {
  231. this.scene = scene
  232. this.camera = camera
  233. }
  234. uiConfig: UiObjectConfig = {
  235. label: 'Render Pass',
  236. type: 'folder',
  237. children: generateUiConfig(this),
  238. }
  239. // legacy
  240. /**
  241. * @deprecated renamed to {@link isExtendedRenderPass}
  242. */
  243. get isRenderPass2() {
  244. console.error('isRenderPass2 is deprecated, use isExtendedRenderPass instead')
  245. return true
  246. }
  247. }
  248. /**
  249. * @deprecated renamed to {@link ExtendedRenderPass}
  250. */
  251. export class RenderPass2 extends ExtendedRenderPass {
  252. constructor(...args: ConstructorParameters<typeof ExtendedRenderPass>) {
  253. console.error('RenderPass2 is deprecated, use ExtendedRenderPass instead')
  254. super(...args)
  255. }
  256. }