WaterMesh.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import {
  2. Color,
  3. Mesh,
  4. Vector3,
  5. MeshLambertNodeMaterial
  6. } from 'three/webgpu';
  7. import { Fn, add, cameraPosition, div, normalize, positionWorld, sub, time, texture, vec2, vec3, max, dot, reflect, pow, length, float, uniform, reflector, mul, mix, diffuseColor } from 'three/tsl';
  8. /**
  9. * A basic flat, reflective water effect.
  10. *
  11. * Note that this class can only be used with {@link WebGPURenderer}.
  12. * When using {@link WebGLRenderer}, use {@link Water}.
  13. *
  14. * References:
  15. *
  16. * - [Flat mirror for three.js]{@link https://github.com/Slayvin}
  17. * - [An implementation of water shader based on the flat mirror]{@link https://home.adelphi.edu/~stemkoski/}
  18. * - [Water shader explanations in WebGL]{@link http://29a.ch/slides/2012/webglwater/ }
  19. *
  20. * @augments Mesh
  21. * @three_import import { WaterMesh } from 'three/addons/objects/WaterMesh.js';
  22. */
  23. class WaterMesh extends Mesh {
  24. /**
  25. * Constructs a new water mesh.
  26. *
  27. * @param {BufferGeometry} geometry - The water mesh's geometry.
  28. * @param {WaterMesh~Options} [options] - The configuration options.
  29. */
  30. constructor( geometry, options ) {
  31. const material = new MeshLambertNodeMaterial();
  32. super( geometry, material );
  33. /**
  34. * This flag can be used for type testing.
  35. *
  36. * @type {boolean}
  37. * @readonly
  38. * @default true
  39. */
  40. this.isWaterMesh = true;
  41. /**
  42. * The effect's resolution scale.
  43. *
  44. * @type {number}
  45. * @default 0.5
  46. */
  47. this.resolution = options.resolution !== undefined ? options.resolution : 0.5;
  48. // Uniforms
  49. /**
  50. * The water's normal map.
  51. *
  52. * @type {TextureNode}
  53. */
  54. this.waterNormals = texture( options.waterNormals );
  55. /**
  56. * The alpha value.
  57. *
  58. * @type {UniformNode<float>}
  59. * @default 1
  60. */
  61. this.alpha = uniform( options.alpha !== undefined ? options.alpha : 1.0 );
  62. /**
  63. * The size value.
  64. *
  65. * @type {UniformNode<float>}
  66. * @default 1
  67. */
  68. this.size = uniform( options.size !== undefined ? options.size : 1.0 );
  69. /**
  70. * The sun color.
  71. *
  72. * @type {UniformNode<color>}
  73. * @default 0xffffff
  74. */
  75. this.sunColor = uniform( new Color( options.sunColor !== undefined ? options.sunColor : 0xffffff ) );
  76. /**
  77. * The sun direction.
  78. *
  79. * @type {UniformNode<vec3>}
  80. * @default (0.70707,0.70707,0.0)
  81. */
  82. this.sunDirection = uniform( options.sunDirection !== undefined ? options.sunDirection : new Vector3( 0.70707, 0.70707, 0.0 ) );
  83. /**
  84. * The water color.
  85. *
  86. * @type {UniformNode<color>}
  87. * @default 0x7f7f7f
  88. */
  89. this.waterColor = uniform( new Color( options.waterColor !== undefined ? options.waterColor : 0x7f7f7f ) );
  90. /**
  91. * The distortion scale.
  92. *
  93. * @type {UniformNode<float>}
  94. * @default 20
  95. */
  96. this.distortionScale = uniform( options.distortionScale !== undefined ? options.distortionScale : 20.0 );
  97. // TSL
  98. const getNoise = Fn( ( [ uv ] ) => {
  99. const offset = time;
  100. const uv0 = add( div( uv, 103 ), vec2( div( offset, 17 ), div( offset, 29 ) ) ).toVar();
  101. const uv1 = div( uv, 107 ).sub( vec2( div( offset, - 19 ), div( offset, 31 ) ) ).toVar();
  102. const uv2 = add( div( uv, vec2( 8907.0, 9803.0 ) ), vec2( div( offset, 101 ), div( offset, 97 ) ) ).toVar();
  103. const uv3 = sub( div( uv, vec2( 1091.0, 1027.0 ) ), vec2( div( offset, 109 ), div( offset, - 113 ) ) ).toVar();
  104. const sample0 = this.waterNormals.sample( uv0 );
  105. const sample1 = this.waterNormals.sample( uv1 );
  106. const sample2 = this.waterNormals.sample( uv2 );
  107. const sample3 = this.waterNormals.sample( uv3 );
  108. const noise = sample0.add( sample1 ).add( sample2 ).add( sample3 );
  109. return noise.mul( 0.5 ).sub( 1 );
  110. } );
  111. const noise = getNoise( positionWorld.xz.mul( this.size ) );
  112. const surfaceNormal = normalize( noise.xzy.mul( 1.5, 1.0, 1.5 ) );
  113. const worldToEye = cameraPosition.sub( positionWorld );
  114. const eyeDirection = normalize( worldToEye );
  115. const reflection = normalize( reflect( this.sunDirection.negate(), surfaceNormal ) );
  116. const direction = max( 0.0, dot( eyeDirection, reflection ) );
  117. const specularLight = pow( direction, 100 ).mul( this.sunColor ).mul( 2.0 );
  118. const diffuseLight = max( dot( this.sunDirection, surfaceNormal ), 0.0 ).mul( this.sunColor ).mul( 0.5 );
  119. const distance = length( worldToEye );
  120. const distortion = surfaceNormal.xz.mul( float( 0.001 ).add( float( 1.0 ).div( distance ) ) ).mul( this.distortionScale );
  121. // Material
  122. material.transparent = true;
  123. material.opacityNode = this.alpha;
  124. material.receivedShadowPositionNode = positionWorld.add( distortion );
  125. material.setupOutgoingLight = () => diffuseColor.rgb; // backwards compatibility
  126. material.colorNode = Fn( () => {
  127. const mirrorSampler = reflector();
  128. mirrorSampler.uvNode = mirrorSampler.uvNode.add( distortion );
  129. mirrorSampler.resolution = this.resolution;
  130. this.add( mirrorSampler.target );
  131. const theta = max( dot( eyeDirection, surfaceNormal ), 0.0 );
  132. const rf0 = float( 0.3 );
  133. const reflectance = mul( pow( float( 1.0 ).sub( theta ), 5.0 ), float( 1.0 ).sub( rf0 ) ).add( rf0 );
  134. const scatter = max( 0.0, dot( surfaceNormal, eyeDirection ) ).mul( this.waterColor );
  135. const albedo = mix( this.sunColor.mul( diffuseLight ).mul( 0.3 ).add( scatter ), mirrorSampler.rgb.mul( specularLight ).add( mirrorSampler.rgb.mul( 0.9 ) ).add( vec3( 0.1 ) ), reflectance );
  136. return albedo;
  137. } )();
  138. }
  139. }
  140. /**
  141. * Constructor options of `WaterMesh`.
  142. *
  143. * @typedef {Object} WaterMesh~Options
  144. * @property {number} [resolution=0.5] - The resolution scale.
  145. * @property {?Texture} [waterNormals=null] - The water's normal map.
  146. * @property {number} [alpha=1] - The alpha value.
  147. * @property {number} [size=1] - The size value.
  148. * @property {number|Color|string} [sunColor=0xffffff] - The sun color.
  149. * @property {Vector3} [sunDirection=(0.70707,0.70707,0.0)] - The sun direction.
  150. * @property {number|Color|string} [waterColor=0x7F7F7F] - The water color.
  151. * @property {number} [distortionScale=20] - The distortion scale.
  152. **/
  153. export { WaterMesh };