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

ExtendedRenderPass.ts 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  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. Texture,
  11. UnsignedByteType,
  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. const msaa = this.renderManager.msaa
  33. this._transparentTarget = this.renderManager.getTempTarget({
  34. sizeMultiplier: 1,
  35. samples: msaa ? typeof msaa !== 'number' ? ViewerRenderManager.DEFAULT_MSAA_SAMPLES : msaa : 0,
  36. colorSpace: NoColorSpace,
  37. type: this.renderManager.renderer.extensions.has('EXT_color_buffer_half_float') ? HalfFloatType : UnsignedByteType,
  38. format: RGBAFormat,
  39. minFilter: LinearFilter,
  40. magFilter: LinearFilter,
  41. depthBuffer: false,
  42. })
  43. }
  44. return this._transparentTarget
  45. }
  46. private _releaseTransparentTarget() {
  47. if (this._transparentTarget)
  48. this.renderManager.releaseTempTarget(this._transparentTarget)
  49. this._transparentTarget = undefined
  50. }
  51. readonly preserveOpaqueTarget = false
  52. private _opaqueTarget?: WebGLRenderTarget
  53. get opaqueTarget(): WebGLRenderTarget {
  54. if (!this._opaqueTarget) {
  55. const composerTarget = this.renderManager.composerTarget as WebGLRenderTarget
  56. const msaa = this.renderManager.msaa
  57. this._opaqueTarget = this.renderManager.getTempTarget({
  58. sizeMultiplier: 1,
  59. samples: msaa ? typeof msaa !== 'number' ? ViewerRenderManager.DEFAULT_MSAA_SAMPLES : msaa : 0,
  60. colorSpace: composerTarget.texture.colorSpace,
  61. type: this.renderManager.rgbm ? UnsignedByteType : HalfFloatType,
  62. format: composerTarget.texture.format,
  63. minFilter: composerTarget.texture.minFilter,
  64. magFilter: composerTarget.texture.magFilter,
  65. depthBuffer: composerTarget.depthBuffer,
  66. generateMipmaps: composerTarget.texture.generateMipmaps,
  67. })
  68. // console.log(this._opaqueTarget.samples)
  69. }
  70. return this._opaqueTarget
  71. }
  72. private _releaseOpaqueTarget() {
  73. if (this._opaqueTarget)
  74. this.renderManager.releaseTempTarget(this._opaqueTarget)
  75. this._opaqueTarget = undefined
  76. }
  77. constructor(renderManager: ViewerRenderManager, overrideMaterial?: Material, clearColor = new Color(0, 0, 0), clearAlpha = 0) {
  78. super(undefined, undefined, overrideMaterial, clearColor, clearAlpha)
  79. this.renderManager = renderManager
  80. this._blendPass = new GenericBlendTexturePass({}, 'c = vec4(a.rgb * (1. - b.a) + b.rgb * b.a, 1.);', '', undefined, renderManager.maxHDRIntensity)
  81. this.setDirty = this.setDirty.bind(this)
  82. }
  83. // names are incorrect. We read from `writeBuffer` and write to `readBuffer`. same in super class
  84. render(renderer: IWebGLRenderer, writeBuffer?: WebGLRenderTarget<Texture|Texture[]>|null, readBuffer?: WebGLRenderTarget<Texture|Texture[]>, deltaTime?: number, maskActive?: boolean) {
  85. if (!this.enabled) return
  86. let needsSwap = false
  87. renderer.userData.mainRenderPass = true
  88. if (!this._doTransmissionFix && !this.renderManager.rgbm) {
  89. super.render(renderer, writeBuffer || null, readBuffer, deltaTime, maskActive)
  90. this.needsSwap = needsSwap
  91. renderer.userData.mainRenderPass = undefined
  92. return
  93. }
  94. const ud = renderer.userData
  95. if (!ud) console.error('threejs is not patched. Use the @repalash/three.js-modded to this functionality.')
  96. const useGBufferDepth = (this.renderManager.zPrepass || !this.renderManager.depthBuffer) && this.renderManager.gbufferTarget
  97. let depthRenderBuffer: WebGLRenderbuffer | undefined = undefined
  98. if (useGBufferDepth) {
  99. const gbuffer = this.renderManager.gbufferTarget
  100. if (gbuffer) {
  101. const renderBufferProps = renderer.properties.get(gbuffer)
  102. depthRenderBuffer = renderBufferProps.__webglDepthRenderbuffer || renderBufferProps.__webglDepthbuffer
  103. }
  104. if (!depthRenderBuffer) {
  105. console.warn('No depth/gbuffer present for zPrepass.')
  106. }
  107. }
  108. const lastReadBuffer = readBuffer
  109. // if msaa we need to create a new multi sampled target to render to
  110. // todo
  111. readBuffer = this.renderManager.msaa ? this.opaqueTarget : readBuffer
  112. // readBuffer = this.opaqueTarget
  113. // if(readBuffer?.samples !== gbuffer?.samples)
  114. // console.error('ExtendedRenderPass - readBuffer and gbuffer samples are not same', readBuffer?.samples, gbuffer?.samples)
  115. let renderFn = ()=> {
  116. super.render(renderer, null, readBuffer, deltaTime, maskActive, depthRenderBuffer) // read is write in super.render (RenderPass)
  117. }
  118. if (!this.renderManager.rgbm) {
  119. // Opaque + Transparent
  120. {
  121. const curClear = this.clear
  122. const curClearDepth = renderer.autoClearDepth
  123. renderer.autoClearDepth = !useGBufferDepth
  124. this.clear = true
  125. renderer.renderWithModes({
  126. shadowMapRender: true,
  127. backgroundRender: true,
  128. opaqueRender: true,
  129. transparentRender: true,
  130. transmissionRender: false,
  131. }, renderFn)
  132. this.clear = curClear
  133. renderer.autoClearDepth = curClearDepth
  134. }
  135. // Transmissive
  136. {
  137. const source = !readBuffer ? undefined : Array.isArray(readBuffer.texture) ? readBuffer.texture[0] : readBuffer.texture
  138. // todo: first check if any transmissive object is there to use this buffer
  139. this.renderManager.blit(writeBuffer, {clear: true, source})
  140. // viewer.renderer.blit(writeBuffer.texture as any, readBuffer as any, {})
  141. // super.render(renderer, undefined as any, writeBuffer, deltaTime, maskActive); // copy read to write buffer
  142. const curClear = this.clear
  143. this.clear = false
  144. // don't need this clear is already false
  145. // const curClearDepth = renderer.autoClearDepth
  146. // renderer.autoClearDepth = false
  147. ud.transmissionRenderTarget = writeBuffer
  148. ud.blurTransmissionTarget = this.blurTransmissionTarget && ud.transmissionRenderTarget.samples === 0 // todo: not working with msaa target. its fine now because writeBuffer is never multi sampled
  149. renderer.renderWithModes({
  150. shadowMapRender: false,
  151. backgroundRender: false,
  152. opaqueRender: false,
  153. transparentRender: false,
  154. transmissionRender: true,
  155. }, renderFn)
  156. ud.blurTransmissionTarget = undefined
  157. ud.transmissionRenderTarget = undefined
  158. // renderer.autoClearDepth = curClearDepth
  159. this.clear = curClear
  160. }
  161. needsSwap = false
  162. } else if (this.renderManager.rgbm) {
  163. needsSwap = false
  164. const renderToScreen = this.renderToScreen
  165. if (renderToScreen && !writeBuffer) {
  166. console.error('ExtendedRenderPass: renderToScreen is true but writeBuffer is not set, which is required for rgbm')
  167. }
  168. this.renderToScreen = false // for super RenderPass.render
  169. if (renderer.info && !renderer.info.autoReset)
  170. 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.
  171. // Opaque
  172. {
  173. const curClearDepth = renderer.autoClearDepth
  174. renderer.autoClearDepth = !useGBufferDepth
  175. renderer.renderWithModes({
  176. shadowMapRender: true,
  177. backgroundRender: true,
  178. opaqueRender: true,
  179. transparentRender: false,
  180. transmissionRender: false,
  181. }, renderFn) // render to readBuffer
  182. renderer.autoClearDepth = curClearDepth
  183. }
  184. if (!useGBufferDepth && readBuffer) {
  185. const renderBufferProps2 = renderer.properties.get(readBuffer)
  186. depthRenderBuffer = renderBufferProps2.__webglDepthRenderbuffer || renderBufferProps2.__webglDepthbuffer
  187. }
  188. // readBuffer has data
  189. renderFn = ()=> {
  190. super.render(renderer, null, this.transparentTarget as any, deltaTime, maskActive, depthRenderBuffer)
  191. }
  192. // Transparent
  193. {
  194. const curClear = this.clear
  195. const curClearDepth = renderer.autoClearDepth
  196. renderer.autoClearDepth = false
  197. this.clear = true
  198. renderer.renderWithModes({
  199. shadowMapRender: false,
  200. backgroundRender: false,
  201. opaqueRender: false,
  202. transparentRender: true,
  203. transmissionRender: false,
  204. }, renderFn) // render to transparentTarget
  205. this.clear = curClear
  206. renderer.autoClearDepth = curClearDepth
  207. }
  208. if (!renderer.info || renderer.info.render.calls > 0) {
  209. // writeBuffer = transparentTarget + readBuffer
  210. this._blendPass.uniforms.tDiffuse2.value = this.transparentTarget.texture
  211. this._blendPass.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive)
  212. needsSwap = true // writeBuffer has the data now.
  213. }
  214. // if needsSwap, writeBuffer has data, else readBuffer
  215. // Transmission
  216. {
  217. const curClear = this.clear
  218. this.clear = false // it is cleared in transparent pass above even if no object is rendered
  219. // const curClearDepth = renderer.autoClearDepth
  220. // renderer.autoClearDepth = false
  221. // if needsSwap, writeBuffer has current data, else readBuffer
  222. ud.transmissionRenderTarget = needsSwap ? writeBuffer : readBuffer
  223. ud.blurTransmissionTarget = this.blurTransmissionTarget && ud.transmissionRenderTarget.samples === 0 // todo: not working with msaa
  224. renderer.renderWithModes({
  225. shadowMapRender: false,
  226. backgroundRender: false,
  227. opaqueRender: false,
  228. transparentRender: false,
  229. transmissionRender: true,
  230. }, renderFn) // render to transparentTarget
  231. ud.blurTransmissionTarget = undefined
  232. ud.transmissionRenderTarget = undefined
  233. // renderer.autoClearDepth = curClearDepth
  234. this.clear = curClear
  235. }
  236. // console.log(renderer.info.render.calls)
  237. if (!renderer.info || renderer.info.render.calls > 0) {
  238. // console.log('missive blit', renderer.info.render.frame)
  239. // writeBuffer = transparentTarget + readBuffer. opaque will overwrite opaque pixels again
  240. this._blendPass.uniforms.tDiffuse2.value = this.transparentTarget.texture
  241. this._blendPass.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive)
  242. needsSwap = true // writeBuffer has the data now.
  243. }
  244. // if needsSwap, writeBuffer has data, else readBuffer
  245. if (renderToScreen) {
  246. this.renderToScreen = true
  247. const tex = needsSwap ? writeBuffer?.texture : readBuffer?.texture
  248. const source = Array.isArray(tex) ? tex[0] : tex
  249. source && this.renderManager.blit(undefined, {
  250. source, respectColorSpace: true,
  251. })
  252. // needsSwap = false
  253. }
  254. }
  255. // todo no need to do this if renderToScreen is true
  256. // resolve msaa
  257. if (!needsSwap && lastReadBuffer !== readBuffer && readBuffer) {
  258. // copy from readBuffer to lastReadBuffer
  259. const source = Array.isArray(readBuffer.texture) ? readBuffer.texture[0] : readBuffer.texture
  260. source && this.renderManager.blit(lastReadBuffer, {
  261. source: source, clear: true,
  262. })
  263. readBuffer = lastReadBuffer
  264. }
  265. if (!this.preserveTransparentTarget)
  266. this._releaseTransparentTarget()
  267. if (!this.preserveOpaqueTarget)
  268. this._releaseOpaqueTarget()
  269. this.needsSwap = needsSwap
  270. renderer.userData.mainRenderPass = undefined
  271. }
  272. public onDirty: (()=>void)[] = []
  273. dispose() {
  274. this._releaseTransparentTarget()
  275. this.onDirty = []
  276. this.scene = undefined
  277. this.camera = undefined
  278. super.dispose?.()
  279. }
  280. setDirty() {
  281. this.onDirty.forEach(v=>v())
  282. }
  283. beforeRender(scene: IScene, camera: ICamera, _: IRenderManager): void {
  284. this.scene = scene
  285. this.camera = camera
  286. }
  287. uiConfig: UiObjectConfig = {
  288. label: 'Render Pass',
  289. type: 'folder',
  290. children: generateUiConfig(this),
  291. }
  292. // legacy
  293. /**
  294. * @deprecated renamed to {@link isExtendedRenderPass}
  295. */
  296. get isRenderPass2() {
  297. console.error('isRenderPass2 is deprecated, use isExtendedRenderPass instead')
  298. return true
  299. }
  300. }
  301. /**
  302. * @deprecated renamed to {@link ExtendedRenderPass}
  303. */
  304. export class RenderPass2 extends ExtendedRenderPass {
  305. constructor(...args: ConstructorParameters<typeof ExtendedRenderPass>) {
  306. console.error('RenderPass2 is deprecated, use ExtendedRenderPass instead')
  307. super(...args)
  308. }
  309. }