PassNode.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. import TempNode from '../core/TempNode.js';
  2. import { default as TextureNode/*, texture*/ } from '../accessors/TextureNode.js';
  3. import { NodeUpdateType } from '../core/constants.js';
  4. import { nodeObject } from '../tsl/TSLBase.js';
  5. import { uniform } from '../core/UniformNode.js';
  6. import { viewZToOrthographicDepth, perspectiveDepthToViewZ } from './ViewportDepthNode.js';
  7. import { HalfFloatType/*, FloatType*/ } from '../../constants.js';
  8. import { Vector2 } from '../../math/Vector2.js';
  9. import { DepthTexture } from '../../textures/DepthTexture.js';
  10. import { RenderTarget } from '../../core/RenderTarget.js';
  11. const _size = /*@__PURE__*/ new Vector2();
  12. /**
  13. * Represents the texture of a pass node.
  14. *
  15. * @augments TextureNode
  16. */
  17. class PassTextureNode extends TextureNode {
  18. static get type() {
  19. return 'PassTextureNode';
  20. }
  21. /**
  22. * Constructs a new pass texture node.
  23. *
  24. * @param {PassNode} passNode - The pass node.
  25. * @param {Texture} texture - The output texture.
  26. */
  27. constructor( passNode, texture ) {
  28. super( texture );
  29. /**
  30. * A reference to the pass node.
  31. *
  32. * @type {PassNode}
  33. */
  34. this.passNode = passNode;
  35. this.setUpdateMatrix( false );
  36. }
  37. setup( builder ) {
  38. if ( builder.object.isQuadMesh ) this.passNode.build( builder );
  39. return super.setup( builder );
  40. }
  41. clone() {
  42. return new this.constructor( this.passNode, this.value );
  43. }
  44. }
  45. /**
  46. * An extension of `PassTextureNode` which allows to manage more than one
  47. * internal texture. Relevant for the `getPreviousTexture()` related API.
  48. *
  49. * @augments PassTextureNode
  50. */
  51. class PassMultipleTextureNode extends PassTextureNode {
  52. static get type() {
  53. return 'PassMultipleTextureNode';
  54. }
  55. /**
  56. * Constructs a new pass texture node.
  57. *
  58. * @param {PassNode} passNode - The pass node.
  59. * @param {string} textureName - The output texture name.
  60. * @param {boolean} [previousTexture=false] - Whether previous frame data should be used or not.
  61. */
  62. constructor( passNode, textureName, previousTexture = false ) {
  63. // null is passed to the super call since this class does not
  64. // use an external texture for rendering pass data into. Instead
  65. // the texture is managed by the pass node itself
  66. super( passNode, null );
  67. /**
  68. * The output texture name.
  69. *
  70. * @type {string}
  71. */
  72. this.textureName = textureName;
  73. /**
  74. * Whether previous frame data should be used or not.
  75. *
  76. * @type {boolean}
  77. */
  78. this.previousTexture = previousTexture;
  79. }
  80. /**
  81. * Updates the texture reference of this node.
  82. */
  83. updateTexture() {
  84. this.value = this.previousTexture ? this.passNode.getPreviousTexture( this.textureName ) : this.passNode.getTexture( this.textureName );
  85. }
  86. setup( builder ) {
  87. this.updateTexture();
  88. return super.setup( builder );
  89. }
  90. clone() {
  91. return new this.constructor( this.passNode, this.textureName, this.previousTexture );
  92. }
  93. }
  94. /**
  95. * Represents a render pass (sometimes called beauty pass) in context of post processing.
  96. * This pass produces a render for the given scene and camera and can provide multiple outputs
  97. * via MRT for further processing.
  98. *
  99. * ```js
  100. * const postProcessing = new PostProcessing( renderer );
  101. *
  102. * const scenePass = pass( scene, camera );
  103. *
  104. * postProcessing.outputNode = scenePass;
  105. * ```
  106. *
  107. * @augments TempNode
  108. */
  109. class PassNode extends TempNode {
  110. static get type() {
  111. return 'PassNode';
  112. }
  113. /**
  114. * Constructs a new pass node.
  115. *
  116. * @param {('color'|'depth')} scope - The scope of the pass. The scope determines whether the node outputs color or depth.
  117. * @param {Scene} scene - A reference to the scene.
  118. * @param {Camera} camera - A reference to the camera.
  119. * @param {Object} options - Options for the internal render target.
  120. */
  121. constructor( scope, scene, camera, options = {} ) {
  122. super( 'vec4' );
  123. /**
  124. * The scope of the pass. The scope determines whether the node outputs color or depth.
  125. *
  126. * @type {('color'|'depth')}
  127. */
  128. this.scope = scope;
  129. /**
  130. * A reference to the scene.
  131. *
  132. * @type {Scene}
  133. */
  134. this.scene = scene;
  135. /**
  136. * A reference to the camera.
  137. *
  138. * @type {Camera}
  139. */
  140. this.camera = camera;
  141. /**
  142. * Options for the internal render target.
  143. *
  144. * @type {Object}
  145. */
  146. this.options = options;
  147. /**
  148. * The pass's pixel ratio. Will be kept automatically kept in sync with the renderer's pixel ratio.
  149. *
  150. * @private
  151. * @type {number}
  152. * @default 1
  153. */
  154. this._pixelRatio = 1;
  155. /**
  156. * The pass's pixel width. Will be kept automatically kept in sync with the renderer's width.
  157. * @private
  158. * @type {number}
  159. * @default 1
  160. */
  161. this._width = 1;
  162. /**
  163. * The pass's pixel height. Will be kept automatically kept in sync with the renderer's height.
  164. * @private
  165. * @type {number}
  166. * @default 1
  167. */
  168. this._height = 1;
  169. const depthTexture = new DepthTexture();
  170. depthTexture.isRenderTargetTexture = true;
  171. //depthTexture.type = FloatType;
  172. depthTexture.name = 'depth';
  173. const renderTarget = new RenderTarget( this._width * this._pixelRatio, this._height * this._pixelRatio, { type: HalfFloatType, ...options, } );
  174. renderTarget.texture.name = 'output';
  175. renderTarget.depthTexture = depthTexture;
  176. /**
  177. * The pass's render target.
  178. *
  179. * @type {RenderTarget}
  180. */
  181. this.renderTarget = renderTarget;
  182. /**
  183. * A dictionary holding the internal result textures.
  184. *
  185. * @private
  186. * @type {Object<string, Texture>}
  187. */
  188. this._textures = {
  189. output: renderTarget.texture,
  190. depth: depthTexture
  191. };
  192. /**
  193. * A dictionary holding the internal texture nodes.
  194. *
  195. * @private
  196. * @type {Object<string, TextureNode>}
  197. */
  198. this._textureNodes = {};
  199. /**
  200. * A dictionary holding the internal depth nodes.
  201. *
  202. * @private
  203. * @type {Object}
  204. */
  205. this._linearDepthNodes = {};
  206. /**
  207. * A dictionary holding the internal viewZ nodes.
  208. *
  209. * @private
  210. * @type {Object}
  211. */
  212. this._viewZNodes = {};
  213. /**
  214. * A dictionary holding the texture data of the previous frame.
  215. * Used for computing velocity/motion vectors.
  216. *
  217. * @private
  218. * @type {Object<string, Texture>}
  219. */
  220. this._previousTextures = {};
  221. /**
  222. * A dictionary holding the texture nodes of the previous frame.
  223. * Used for computing velocity/motion vectors.
  224. *
  225. * @private
  226. * @type {Object<string, TextureNode>}
  227. */
  228. this._previousTextureNodes = {};
  229. /**
  230. * The `near` property of the camera as a uniform.
  231. *
  232. * @private
  233. * @type {UniformNode}
  234. */
  235. this._cameraNear = uniform( 0 );
  236. /**
  237. * The `far` property of the camera as a uniform.
  238. *
  239. * @private
  240. * @type {UniformNode}
  241. */
  242. this._cameraFar = uniform( 0 );
  243. /**
  244. * A MRT node configuring the MRT settings.
  245. *
  246. * @private
  247. * @type {?MRTNode}
  248. * @default null
  249. */
  250. this._mrt = null;
  251. /**
  252. * This flag can be used for type testing.
  253. *
  254. * @type {boolean}
  255. * @readonly
  256. * @default true
  257. */
  258. this.isPassNode = true;
  259. /**
  260. * The `updateBeforeType` is set to `NodeUpdateType.FRAME` since the node renders the
  261. * scene once per frame in its {@link PassNode#updateBefore} method.
  262. *
  263. * @type {string}
  264. * @default 'frame'
  265. */
  266. this.updateBeforeType = NodeUpdateType.FRAME;
  267. }
  268. /**
  269. * Sets the given MRT node to setup MRT for this pass.
  270. *
  271. * @param {MRTNode} mrt - The MRT object.
  272. * @return {PassNode} A reference to this pass.
  273. */
  274. setMRT( mrt ) {
  275. this._mrt = mrt;
  276. return this;
  277. }
  278. /**
  279. * Returns the current MRT node.
  280. *
  281. * @return {MRTNode} The current MRT node.
  282. */
  283. getMRT() {
  284. return this._mrt;
  285. }
  286. /**
  287. * The method is overwritten so it always returns `true`.
  288. *
  289. * @return {boolean} Whether this node is global or not.
  290. */
  291. isGlobal() {
  292. return true;
  293. }
  294. /**
  295. * Returns the texture for the given output name.
  296. *
  297. * @param {string} name - The output name to get the texture for.
  298. * @return {Texture} The texture.
  299. */
  300. getTexture( name ) {
  301. let texture = this._textures[ name ];
  302. if ( texture === undefined ) {
  303. const refTexture = this.renderTarget.texture;
  304. texture = refTexture.clone();
  305. texture.name = name;
  306. this._textures[ name ] = texture;
  307. this.renderTarget.textures.push( texture );
  308. }
  309. return texture;
  310. }
  311. /**
  312. * Returns the texture holding the data of the previous frame for the given output name.
  313. *
  314. * @param {string} name - The output name to get the texture for.
  315. * @return {Texture} The texture holding the data of the previous frame.
  316. */
  317. getPreviousTexture( name ) {
  318. let texture = this._previousTextures[ name ];
  319. if ( texture === undefined ) {
  320. texture = this.getTexture( name ).clone();
  321. this._previousTextures[ name ] = texture;
  322. }
  323. return texture;
  324. }
  325. /**
  326. * Switches current and previous textures for the given output name.
  327. *
  328. * @param {string} name - The output name.
  329. */
  330. toggleTexture( name ) {
  331. const prevTexture = this._previousTextures[ name ];
  332. if ( prevTexture !== undefined ) {
  333. const texture = this._textures[ name ];
  334. const index = this.renderTarget.textures.indexOf( texture );
  335. this.renderTarget.textures[ index ] = prevTexture;
  336. this._textures[ name ] = prevTexture;
  337. this._previousTextures[ name ] = texture;
  338. this._textureNodes[ name ].updateTexture();
  339. this._previousTextureNodes[ name ].updateTexture();
  340. }
  341. }
  342. /**
  343. * Returns the texture node for the given output name.
  344. *
  345. * @param {string} [name='output'] - The output name to get the texture node for.
  346. * @return {TextureNode} The texture node.
  347. */
  348. getTextureNode( name = 'output' ) {
  349. let textureNode = this._textureNodes[ name ];
  350. if ( textureNode === undefined ) {
  351. textureNode = nodeObject( new PassMultipleTextureNode( this, name ) );
  352. textureNode.updateTexture();
  353. this._textureNodes[ name ] = textureNode;
  354. }
  355. return textureNode;
  356. }
  357. /**
  358. * Returns the previous texture node for the given output name.
  359. *
  360. * @param {string} [name='output'] - The output name to get the previous texture node for.
  361. * @return {TextureNode} The previous texture node.
  362. */
  363. getPreviousTextureNode( name = 'output' ) {
  364. let textureNode = this._previousTextureNodes[ name ];
  365. if ( textureNode === undefined ) {
  366. if ( this._textureNodes[ name ] === undefined ) this.getTextureNode( name );
  367. textureNode = nodeObject( new PassMultipleTextureNode( this, name, true ) );
  368. textureNode.updateTexture();
  369. this._previousTextureNodes[ name ] = textureNode;
  370. }
  371. return textureNode;
  372. }
  373. /**
  374. * Returns a viewZ node of this pass.
  375. *
  376. * @param {string} [name='depth'] - The output name to get the viewZ node for. In most cases the default `'depth'` can be used however the parameter exists for custom depth outputs.
  377. * @return {Node} The viewZ node.
  378. */
  379. getViewZNode( name = 'depth' ) {
  380. let viewZNode = this._viewZNodes[ name ];
  381. if ( viewZNode === undefined ) {
  382. const cameraNear = this._cameraNear;
  383. const cameraFar = this._cameraFar;
  384. this._viewZNodes[ name ] = viewZNode = perspectiveDepthToViewZ( this.getTextureNode( name ), cameraNear, cameraFar );
  385. }
  386. return viewZNode;
  387. }
  388. /**
  389. * Returns a linear depth node of this pass.
  390. *
  391. * @param {string} [name='depth'] - The output name to get the linear depth node for. In most cases the default `'depth'` can be used however the parameter exists for custom depth outputs.
  392. * @return {Node} The linear depth node.
  393. */
  394. getLinearDepthNode( name = 'depth' ) {
  395. let linearDepthNode = this._linearDepthNodes[ name ];
  396. if ( linearDepthNode === undefined ) {
  397. const cameraNear = this._cameraNear;
  398. const cameraFar = this._cameraFar;
  399. const viewZNode = this.getViewZNode( name );
  400. // TODO: just if ( builder.camera.isPerspectiveCamera )
  401. this._linearDepthNodes[ name ] = linearDepthNode = viewZToOrthographicDepth( viewZNode, cameraNear, cameraFar );
  402. }
  403. return linearDepthNode;
  404. }
  405. setup( { renderer } ) {
  406. this.renderTarget.samples = this.options.samples === undefined ? renderer.samples : this.options.samples;
  407. // TODO: Disable MSAA for WebGL backend for now
  408. if ( renderer.backend.isWebGLBackend === true ) {
  409. this.renderTarget.samples = 0;
  410. }
  411. this.renderTarget.texture.type = renderer.getColorBufferType();
  412. return this.scope === PassNode.COLOR ? this.getTextureNode() : this.getLinearDepthNode();
  413. }
  414. updateBefore( frame ) {
  415. const { renderer } = frame;
  416. const { scene } = this;
  417. let camera;
  418. let pixelRatio;
  419. const outputRenderTarget = renderer.getOutputRenderTarget();
  420. if ( outputRenderTarget && outputRenderTarget.isXRRenderTarget === true ) {
  421. pixelRatio = 1;
  422. camera = renderer.xr.getCamera();
  423. renderer.xr.updateCamera( camera );
  424. _size.set( outputRenderTarget.width, outputRenderTarget.height );
  425. } else {
  426. camera = this.camera;
  427. pixelRatio = renderer.getPixelRatio();
  428. renderer.getSize( _size );
  429. }
  430. this._pixelRatio = pixelRatio;
  431. this.setSize( _size.width, _size.height );
  432. const currentRenderTarget = renderer.getRenderTarget();
  433. const currentMRT = renderer.getMRT();
  434. this._cameraNear.value = camera.near;
  435. this._cameraFar.value = camera.far;
  436. for ( const name in this._previousTextures ) {
  437. this.toggleTexture( name );
  438. }
  439. renderer.setRenderTarget( this.renderTarget );
  440. renderer.setMRT( this._mrt );
  441. renderer.render( scene, camera );
  442. renderer.setRenderTarget( currentRenderTarget );
  443. renderer.setMRT( currentMRT );
  444. }
  445. /**
  446. * Sets the size of the pass's render target. Honors the pixel ratio.
  447. *
  448. * @param {number} width - The width to set.
  449. * @param {number} height - The height to set.
  450. */
  451. setSize( width, height ) {
  452. this._width = width;
  453. this._height = height;
  454. const effectiveWidth = this._width * this._pixelRatio;
  455. const effectiveHeight = this._height * this._pixelRatio;
  456. this.renderTarget.setSize( effectiveWidth, effectiveHeight );
  457. }
  458. /**
  459. * Sets the pixel ratio the pass's render target and updates the size.
  460. *
  461. * @param {number} pixelRatio - The pixel ratio to set.
  462. */
  463. setPixelRatio( pixelRatio ) {
  464. this._pixelRatio = pixelRatio;
  465. this.setSize( this._width, this._height );
  466. }
  467. /**
  468. * Frees internal resources. Should be called when the node is no longer in use.
  469. */
  470. dispose() {
  471. this.renderTarget.dispose();
  472. }
  473. }
  474. PassNode.COLOR = 'color';
  475. PassNode.DEPTH = 'depth';
  476. export default PassNode;
  477. /**
  478. * TSL function for creating a pass node.
  479. *
  480. * @tsl
  481. * @function
  482. * @param {Scene} scene - A reference to the scene.
  483. * @param {Camera} camera - A reference to the camera.
  484. * @param {Object} options - Options for the internal render target.
  485. * @returns {PassNode}
  486. */
  487. export const pass = ( scene, camera, options ) => nodeObject( new PassNode( PassNode.COLOR, scene, camera, options ) );
  488. /**
  489. * TSL function for creating a pass texture node.
  490. *
  491. * @tsl
  492. * @function
  493. * @param {PassNode} pass - The pass node.
  494. * @param {Texture} texture - The output texture.
  495. * @returns {PassTextureNode}
  496. */
  497. export const passTexture = ( pass, texture ) => nodeObject( new PassTextureNode( pass, texture ) );
  498. /**
  499. * TSL function for creating a depth pass node.
  500. *
  501. * @tsl
  502. * @function
  503. * @param {Scene} scene - A reference to the scene.
  504. * @param {Camera} camera - A reference to the camera.
  505. * @param {Object} options - Options for the internal render target.
  506. * @returns {PassNode}
  507. */
  508. export const depthPass = ( scene, camera, options ) => nodeObject( new PassNode( PassNode.DEPTH, scene, camera, options ) );
粤ICP备19079148号