waterFoam.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import * as THREE from 'three'
  2. const vertexShader = `
  3. varying vec2 vUv;
  4. varying vec4 vParticleColor;
  5. void main() {
  6. vUv = uv;
  7. vParticleColor = vec4(1.0);
  8. gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  9. }
  10. `
  11. const fragmentShader = `
  12. uniform float uTime;
  13. uniform vec3 uColour;
  14. uniform float uErosion;
  15. uniform float uFlowSpeed;
  16. uniform sampler2D uFoamTex;
  17. uniform vec2 uCircleCenter;
  18. uniform float uCircleRadius;
  19. uniform float uCircleSoftness;
  20. uniform float uCircleEnabled;
  21. varying vec2 vUv;
  22. varying vec4 vParticleColor;
  23. float radialGradientExponential(vec2 uv, vec2 center, float radius, float density) {
  24. float dist = length(uv - center);
  25. dist = clamp(dist / radius, 0.0, 1.0);
  26. return pow(1.0 - dist, density);
  27. }
  28. vec2 panner(vec2 uv, float speedX, float speedY, float time) {
  29. return uv + vec2(speedX, speedY) * time;
  30. }
  31. float circleMask(vec2 uv, vec2 center, float radius, float softness) {
  32. float dist = length(uv - center);
  33. return 1.0 - smoothstep(radius - softness, radius + softness, dist);
  34. }
  35. void main() {
  36. vec2 animatedUV = panner(vUv, uFlowSpeed, 0.0, uTime);
  37. vec4 foamTex = texture2D(uFoamTex, animatedUV);
  38. float gradient = radialGradientExponential(vUv, vec2(0.5), 1.0, 1.0);
  39. float addResult = vParticleColor.r * foamTex.r + vParticleColor.g * foamTex.g;
  40. float subtractResult = addResult - uErosion;
  41. float multiplyResult = subtractResult * gradient;
  42. float opacityShape = mix(multiplyResult, addResult, gradient);
  43. opacityShape = clamp(opacityShape, 0.0, 1.0);
  44. opacityShape = pow(opacityShape, 2.0);
  45. float circle = circleMask(vUv, uCircleCenter, uCircleRadius, uCircleSoftness);
  46. opacityShape *= mix(1.0, circle, uCircleEnabled);
  47. vec3 emissive = uColour * vParticleColor.rgb;
  48. gl_FragColor = vec4(emissive, opacityShape);
  49. }
  50. `
  51. interface WaterFoamMaterialOptions {
  52. colour?: THREE.Color
  53. erosion?: number
  54. flowSpeed?: number
  55. foamTexture?: THREE.Texture
  56. circleCenter?: THREE.Vector2
  57. circleRadius?: number
  58. circleSoftness?: number
  59. circleEnabled?: boolean
  60. }
  61. function createWaterFoamMaterial(options: WaterFoamMaterialOptions = {}) {
  62. const {
  63. colour = new THREE.Color(0xffffff),
  64. erosion = 1.0,
  65. flowSpeed = 0.2,
  66. foamTexture,
  67. circleCenter = new THREE.Vector2(0.5, 0.5),
  68. circleRadius = 0.34,
  69. circleSoftness = 0.195,
  70. circleEnabled = true,
  71. } = options
  72. return new THREE.ShaderMaterial({
  73. vertexShader,
  74. fragmentShader,
  75. transparent: true,
  76. depthWrite: false,
  77. uniforms: {
  78. uTime: { value: 0 },
  79. uColour: { value: colour },
  80. uErosion: { value: erosion },
  81. uFlowSpeed: { value: flowSpeed },
  82. uFoamTex: { value: foamTexture || new THREE.Texture() },
  83. uCircleCenter: { value: circleCenter },
  84. uCircleRadius: { value: circleRadius },
  85. uCircleSoftness: { value: circleSoftness },
  86. uCircleEnabled: { value: circleEnabled ? 1.0 : 0.0 },
  87. },
  88. })
  89. }
  90. export { createWaterFoamMaterial }
  91. export type { WaterFoamMaterialOptions }