Water2Mesh.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. import {
  2. Color,
  3. Mesh,
  4. Vector2,
  5. Vector3,
  6. NodeMaterial,
  7. NodeUpdateType,
  8. TempNode
  9. } from 'three/webgpu';
  10. import { Fn, vec2, viewportSafeUV, viewportSharedTexture, reflector, pow, float, abs, texture, uniform, vec4, cameraPosition, positionWorld, uv, mix, vec3, normalize, max, dot, screenUV } from 'three/tsl';
  11. /** @module Water2Mesh */
  12. /**
  13. * An advanced water effect that supports reflections, refractions and flow maps.
  14. *
  15. * Note that this class can only be used with {@link WebGPURenderer}.
  16. * When using {@link WebGLRenderer}, use {@link module:Water2}.
  17. *
  18. * References:
  19. *
  20. * - {@link https://alex.vlachos.com/graphics/Vlachos-SIGGRAPH10-WaterFlow.pdf}
  21. * - {@link http://graphicsrunner.blogspot.de/2010/08/water-using-flow-maps.html}
  22. *
  23. * @augments Mesh
  24. * @three_import import { WaterMesh } from 'three/addons/objects/Water2Mesh.js';
  25. */
  26. class WaterMesh extends Mesh {
  27. /**
  28. * Constructs a new water mesh.
  29. *
  30. * @param {BufferGeometry} geometry - The water's geometry.
  31. * @param {module:Water2~Options} [options] - The configuration options.
  32. */
  33. constructor( geometry, options = {} ) {
  34. const material = new NodeMaterial();
  35. material.transparent = true;
  36. super( geometry, material );
  37. /**
  38. * This flag can be used for type testing.
  39. *
  40. * @type {boolean}
  41. * @readonly
  42. * @default true
  43. */
  44. this.isWater = true;
  45. material.colorNode = new WaterNode( options, this );
  46. }
  47. }
  48. class WaterNode extends TempNode {
  49. constructor( options, waterBody ) {
  50. super( 'vec4' );
  51. this.waterBody = waterBody;
  52. this.normalMap0 = texture( options.normalMap0 );
  53. this.normalMap1 = texture( options.normalMap1 );
  54. this.flowMap = texture( options.flowMap !== undefined ? options.flowMap : null );
  55. this.color = uniform( options.color !== undefined ? new Color( options.color ) : new Color( 0xffffff ) );
  56. this.flowDirection = uniform( options.flowDirection !== undefined ? options.flowDirection : new Vector2( 1, 0 ) );
  57. this.flowSpeed = uniform( options.flowSpeed !== undefined ? options.flowSpeed : 0.03 );
  58. this.reflectivity = uniform( options.reflectivity !== undefined ? options.reflectivity : 0.02 );
  59. this.scale = uniform( options.scale !== undefined ? options.scale : 1 );
  60. this.flowConfig = uniform( new Vector3() );
  61. this.updateBeforeType = NodeUpdateType.RENDER;
  62. this._cycle = 0.15; // a cycle of a flow map phase
  63. this._halfCycle = this._cycle * 0.5;
  64. this._USE_FLOW = options.flowMap !== undefined;
  65. }
  66. updateFlow( delta ) {
  67. this.flowConfig.value.x += this.flowSpeed.value * delta; // flowMapOffset0
  68. this.flowConfig.value.y = this.flowConfig.value.x + this._halfCycle; // flowMapOffset1
  69. // Important: The distance between offsets should be always the value of "halfCycle".
  70. // Moreover, both offsets should be in the range of [ 0, cycle ].
  71. // This approach ensures a smooth water flow and avoids "reset" effects.
  72. if ( this.flowConfig.value.x >= this._cycle ) {
  73. this.flowConfig.value.x = 0;
  74. this.flowConfig.value.y = this._halfCycle;
  75. } else if ( this.flowConfig.value.y >= this._cycle ) {
  76. this.flowConfig.value.y = this.flowConfig.value.y - this._cycle;
  77. }
  78. this.flowConfig.value.z = this._halfCycle;
  79. }
  80. updateBefore( frame ) {
  81. this.updateFlow( frame.deltaTime );
  82. }
  83. setup() {
  84. const outputNode = Fn( () => {
  85. const flowMapOffset0 = this.flowConfig.x;
  86. const flowMapOffset1 = this.flowConfig.y;
  87. const halfCycle = this.flowConfig.z;
  88. const toEye = normalize( cameraPosition.sub( positionWorld ) );
  89. let flow;
  90. if ( this._USE_FLOW === true ) {
  91. flow = this.flowMap.rg.mul( 2 ).sub( 1 );
  92. } else {
  93. flow = vec2( this.flowDirection.x, this.flowDirection.y );
  94. }
  95. flow.x.mulAssign( - 1 );
  96. // sample normal maps (distort uvs with flowdata)
  97. const uvs = uv();
  98. const normalUv0 = uvs.mul( this.scale ).add( flow.mul( flowMapOffset0 ) );
  99. const normalUv1 = uvs.mul( this.scale ).add( flow.mul( flowMapOffset1 ) );
  100. const normalColor0 = this.normalMap0.sample( normalUv0 );
  101. const normalColor1 = this.normalMap1.sample( normalUv1 );
  102. // linear interpolate to get the final normal color
  103. const flowLerp = abs( halfCycle.sub( flowMapOffset0 ) ).div( halfCycle );
  104. const normalColor = mix( normalColor0, normalColor1, flowLerp );
  105. // calculate normal vector
  106. const normal = normalize( vec3( normalColor.r.mul( 2 ).sub( 1 ), normalColor.b, normalColor.g.mul( 2 ).sub( 1 ) ) );
  107. // calculate the fresnel term to blend reflection and refraction maps
  108. const theta = max( dot( toEye, normal ), 0 );
  109. const reflectance = pow( float( 1.0 ).sub( theta ), 5.0 ).mul( float( 1.0 ).sub( this.reflectivity ) ).add( this.reflectivity );
  110. // reflector, refractor
  111. const offset = normal.xz.mul( 0.05 ).toVar();
  112. const reflectionSampler = reflector();
  113. this.waterBody.add( reflectionSampler.target );
  114. reflectionSampler.uvNode = reflectionSampler.uvNode.add( offset );
  115. const refractorUV = screenUV.add( offset );
  116. const refractionSampler = viewportSharedTexture( viewportSafeUV( refractorUV ) );
  117. // calculate final uv coords
  118. return vec4( this.color, 1.0 ).mul( mix( refractionSampler, reflectionSampler, reflectance ) );
  119. } )();
  120. return outputNode;
  121. }
  122. }
  123. /**
  124. * Constructor options of `WaterMesh`.
  125. *
  126. * @typedef {Object} module:Water2Mesh~Options
  127. * @property {number|Color|string} [color=0xFFFFFF] - The water color.
  128. * @property {Vector2} [flowDirection=(1,0)] - The water's flow direction.
  129. * @property {number} [flowSpeed=0.03] - The water's flow speed.
  130. * @property {number} [reflectivity=0.02] - The water's reflectivity.
  131. * @property {number} [scale=1] - The water's scale.
  132. * @property {?Texture} [flowMap=null] - The flow map. If no flow map is assigned, the water flow is defined by `flowDirection`.
  133. * @property {Texture} normalMap0 - The first water normal map.
  134. * @property {Texture} normalMap1 - The second water normal map.
  135. **/
  136. export { WaterMesh };