RNoise.js 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. import { float, Fn, fract, int, vec2, vec4 } from 'three/tsl';
  2. /**
  3. * Returns a TSL function that samples texture-free analytic R² noise.
  4. * Index 0 uses continuous screen pixels; other indices tile-shift with an R²
  5. * sequence into a 64×64 period. Values are four independent R² dimensions
  6. * hashed from the sample coordinates.
  7. *
  8. * @param {UniformNode<Vector2>} resolution
  9. * @param {number} [seed=0] - Added to the coordinate hash so each pass gets an independent R² phase.
  10. */
  11. export function bindAnalyticNoise( resolution, seed = 0 ) {
  12. const seedOffset = int( seed );
  13. const r4 = ( coords ) => {
  14. const P = 1.32471795724474602596;
  15. const t = coords.x.mul( 1 / P ).add( coords.y.mul( 1 / P ** 2 ) ).add( float( seed ) );
  16. return vec4(
  17. fract( t.mul( P ).mul( 1 / P ) ),
  18. fract( t.mul( P * 2 ).mul( 1 / P ** 2 ) ),
  19. fract( t.mul( P * 3 ).mul( 0.4198754210 ) ), // this is not 1 / P ** 3, however this magic constant gives better noise
  20. fract( t.mul( P * 4 ).mul( 1 / P ** 3 ) )
  21. );
  22. };
  23. return Fn( ( [ uvCoord, sampleIndex ] ) => {
  24. const index = int( sampleIndex ).add( seedOffset );
  25. const noise = vec4().toVar();
  26. const tileSize = float( 32 );
  27. const screenPixel = uvCoord.mul( resolution ).floor();
  28. const offset = fract( vec2(
  29. float( index ).mul( 0.7548776662 ),
  30. float( index ).mul( 0.5698402910 )
  31. ) ).mul( tileSize ).floor();
  32. const coords = screenPixel.add( offset ).mod( tileSize );
  33. noise.assign( r4( coords ) );
  34. return noise;
  35. } );
  36. }
粤ICP备19079148号