ChromaticAberrationNode.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. import { Vector2, TempNode } from 'three/webgpu';
  2. import {
  3. nodeObject,
  4. Fn,
  5. uniform,
  6. convertToTexture,
  7. float,
  8. vec4,
  9. uv,
  10. NodeUpdateType,
  11. } from 'three/tsl';
  12. /**
  13. * Post processing node for applying chromatic aberration effect.
  14. * This effect simulates the color fringing that occurs in real camera lenses
  15. * by separating and offsetting the red, green, and blue channels.
  16. *
  17. * @augments TempNode
  18. * @three_import import { chromaticAberration } from 'three/addons/tsl/display/ChromaticAberrationNode.js';
  19. */
  20. class ChromaticAberrationNode extends TempNode {
  21. static get type() {
  22. return 'ChromaticAberrationNode';
  23. }
  24. /**
  25. * Constructs a new chromatic aberration node.
  26. *
  27. * @param {TextureNode} textureNode - The texture node that represents the input of the effect.
  28. * @param {Node} strengthNode - The strength of the chromatic aberration effect as a node.
  29. * @param {Node} centerNode - The center point of the effect as a node.
  30. * @param {Node} scaleNode - The scale factor for stepped scaling from center as a node.
  31. */
  32. constructor( textureNode, strengthNode, centerNode, scaleNode ) {
  33. super( 'vec4' );
  34. /**
  35. * The texture node that represents the input of the effect.
  36. *
  37. * @type {texture}
  38. */
  39. this.textureNode = textureNode;
  40. /**
  41. * The `updateBeforeType` is set to `NodeUpdateType.FRAME` since the node updates
  42. * its internal uniforms once per frame in `updateBefore()`.
  43. *
  44. * @type {string}
  45. * @default 'frame'
  46. */
  47. this.updateBeforeType = NodeUpdateType.FRAME;
  48. /**
  49. * A node holding the strength of the effect.
  50. *
  51. * @type {Node}
  52. */
  53. this.strengthNode = strengthNode;
  54. /**
  55. * A node holding the center point of the effect.
  56. *
  57. * @type {Node}
  58. */
  59. this.centerNode = centerNode;
  60. /**
  61. * A node holding the scale factor for stepped scaling.
  62. *
  63. * @type {Node}
  64. */
  65. this.scaleNode = scaleNode;
  66. /**
  67. * A uniform node holding the inverse resolution value.
  68. *
  69. * @private
  70. * @type {UniformNode<vec2>}
  71. */
  72. this._invSize = uniform( new Vector2() );
  73. }
  74. /**
  75. * This method is used to update the effect's uniforms once per frame.
  76. *
  77. * @param {NodeFrame} frame - The current node frame.
  78. */
  79. updateBefore( /* frame */ ) {
  80. const map = this.textureNode.value;
  81. this._invSize.value.set( 1 / map.image.width, 1 / map.image.height );
  82. }
  83. /**
  84. * This method is used to setup the effect's TSL code.
  85. *
  86. * @param {NodeBuilder} builder - The current node builder.
  87. * @return {ShaderCallNodeInternal}
  88. */
  89. setup( /* builder */ ) {
  90. const textureNode = this.textureNode;
  91. const uvNode = textureNode.uvNode || uv();
  92. const ApplyChromaticAberration = Fn( ( [ uv, strength, center, scale ] ) => {
  93. // Calculate distance from center
  94. const offset = uv.sub( center );
  95. const distance = offset.length();
  96. // Create stepped scaling zones based on distance
  97. // Each channel gets different scaling steps
  98. const redScale = float( 1.0 ).add( scale.mul( 0.02 ).mul( strength ) ); // Red channel scaled outward
  99. const greenScale = float( 1.0 ); // Green stays at original scale
  100. const blueScale = float( 1.0 ).sub( scale.mul( 0.02 ).mul( strength ) ); // Blue channel scaled inward
  101. // Create radial distortion based on distance from center
  102. const aberrationStrength = strength.mul( distance );
  103. // Calculate scaled UV coordinates for each channel
  104. const redUV = center.add( offset.mul( redScale ) );
  105. const greenUV = center.add( offset.mul( greenScale ) );
  106. const blueUV = center.add( offset.mul( blueScale ) );
  107. // Apply additional chromatic offset based on aberration strength
  108. const rOffset = offset.mul( aberrationStrength ).mul( float( 0.01 ) );
  109. const gOffset = offset.mul( aberrationStrength ).mul( float( 0.0 ) );
  110. const bOffset = offset.mul( aberrationStrength ).mul( float( - 0.01 ) );
  111. // Final UV coordinates combining scale and chromatic aberration
  112. const finalRedUV = redUV.add( rOffset );
  113. const finalGreenUV = greenUV.add( gOffset );
  114. const finalBlueUV = blueUV.add( bOffset );
  115. // Sample texture for each channel
  116. const r = textureNode.sample( finalRedUV ).r;
  117. const g = textureNode.sample( finalGreenUV ).g;
  118. const b = textureNode.sample( finalBlueUV ).b;
  119. // Get original alpha
  120. const a = textureNode.sample( uv ).a;
  121. return vec4( r, g, b, a );
  122. } ).setLayout( {
  123. name: 'ChromaticAberrationShader',
  124. type: 'vec4',
  125. inputs: [
  126. { name: 'uv', type: 'vec2' },
  127. { name: 'strength', type: 'float' },
  128. { name: 'center', type: 'vec2' },
  129. { name: 'scale', type: 'float' },
  130. { name: 'invSize', type: 'vec2' }
  131. ]
  132. } );
  133. const chromaticAberrationFn = Fn( () => {
  134. return ApplyChromaticAberration(
  135. uvNode,
  136. this.strengthNode,
  137. this.centerNode,
  138. this.scaleNode,
  139. this._invSize
  140. );
  141. } );
  142. const outputNode = chromaticAberrationFn();
  143. return outputNode;
  144. }
  145. }
  146. export default ChromaticAberrationNode;
  147. /**
  148. * TSL function for creating a chromatic aberration node for post processing.
  149. *
  150. * @tsl
  151. * @function
  152. * @param {Node<vec4>} node - The node that represents the input of the effect.
  153. * @param {Node|number} [strength=1.0] - The strength of the chromatic aberration effect as a node or value.
  154. * @param {?(Node|Vector2)} [center=null] - The center point of the effect as a node or value. If null, uses screen center (0.5, 0.5).
  155. * @param {Node|number} [scale=1.1] - The scale factor for stepped scaling from center as a node or value.
  156. * @returns {ChromaticAberrationNode}
  157. */
  158. export const chromaticAberration = ( node, strength = 1.0, center = null, scale = 1.1 ) => {
  159. return nodeObject(
  160. new ChromaticAberrationNode(
  161. convertToTexture( node ),
  162. nodeObject( strength ),
  163. nodeObject( center ),
  164. nodeObject( scale )
  165. )
  166. );
  167. };
粤ICP备19079148号