Class BlueNoise

java.lang.Object
com.github.yellowstonegames.grid.BlueNoise

public final class BlueNoise extends Object
Provides access to precalculated tiling planes of 2D blue noise, that is, noise without high-frequency components, as well as seeded mixes of different blue noise planes that make it have even fewer patterns. This doesn't produce continuous noise such as Perlin or Simplex noise, instead producing noise in the signal-to-noise sense. The blue-noise distribution has the practical effect that a value only appears next to a similar value very rarely. For a quick introduction, see Alan Wolfe's blog, which has many more useful posts and links that often relate to blue noise and its relatives, like low-discrepancy sequences (also called quasi-random sequences, QRNGs, or sub-random sequences).
This uses 64x64 blue noise textures generated by a "test" in SquidSquad, using code adapted from Bart Wronski's Colab notebook in Python. The big contribution here is that all the textures generated can seamlessly tile with each other, as if they were meant to tile with the other tile, for any of the 64 tiles. I call this "omni-tiling" because unlike a Wang tiling, which only permits half or fewer of tiles to match a particular edge, all the tiles can appear to be seamless when laid out in a grid, even when randomly selected. Normally, just placing blue noise textures next to each other produces clear seams or "breaks" in the blue noise, so this is a nice thing to have.
Big thanks to Bart Wronski for making Ulichney's Void and Cluster algorithm accessible to me, and Alan Wolfe for helping me understand how useful blue noise is in the first place!
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final byte[][]
    Stores the same values as the first element in TILE_NOISE if considered [x][y] indexed; available for convenient usage by code that uses 2D byte arrays.
    static final byte[][]
    Raw data this class uses for omni-tiling blue noise, as 32 1D byte arrays.
    static final byte[][]
    Raw data this class uses for omni-tiling triangular-mapped blue noise, as 32 1D byte arrays.
  • Method Summary

    Modifier and Type
    Method
    Description
    static int
    fullHash(int x, int y)
    Gets a unique int hash code given x and y that are valid short numbers, or a likely-unique hash if they are larger than short.
    static byte
    get(int x, int y)
    Gets a byte from the raw data this stores, treating it as an infinite 2D plane of tiling 64x64 regions.
    static byte
    get(int x, int y, byte[] noiseData)
    Gets a byte from some raw data at least 16384 bytes in length, treating it as an infinite 2D plane of tiling 128x128 regions.
    static byte
    get(int x, int y, int z)
    Gets a byte from the raw data this stores, treating it as an infinite 3D space of tiling 128x128x32 regions.
    static byte
    getSeeded(int x, int y, int seed)
    Gets a point in a potentially-infinite aperiodically-tiling plane of blue noise, where each 128x128 square of blue noise is drawn from TILE_NOISE.
    static byte
    getSeededTriangular(int x, int y, int seed)
    Gets a point in a potentially-infinite aperiodically-tiling plane of blue noise, where each 128x128 square of blue noise is drawn from TILE_TRI_NOISE (which is blue noise with a triangular mapping).

    Methods inherited from class Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • TILE_NOISE

      public static final byte[][] TILE_NOISE
      Raw data this class uses for omni-tiling blue noise, as 32 1D byte arrays. The omni-tiling property here means that you can place any of the 1D arrays here in a grid with any other 1D array from here next to it, and no seam will be visible. This is used by getSeeded(int, int, int). This group of byte arrays is static, which is probably OK even on Android as long as no one writes to it, and uses StandardCharsets to decode itself from a String, which only works on GWT 2.8.2 and newer (current version used by libGDX is 2.11.0). Each byte array can be considered [x][y] indexed or [y][x] indexed; the original images were [y][x] indexed.
    • TILE_TRI_NOISE

      public static final byte[][] TILE_TRI_NOISE
      Raw data this class uses for omni-tiling triangular-mapped blue noise, as 32 1D byte arrays. The omni-tiling property here means that you can place any of the 1D arrays here in a grid with any other 1D array from here next to it, and no seam will be visible. This is used by getSeededTriangular(int, int, int). This group of byte arrays is static, which is probably OK even on Android as long as no one writes to it, and uses StandardCharsets to decode itself from a String, which only works on GWT 2.8.2 and newer. Each byte array can be considered [x][y] indexed or [y][x] indexed; the original images were [y][x] indexed.
    • RAW_2D

      public static final byte[][] RAW_2D
      Stores the same values as the first element in TILE_NOISE if considered [x][y] indexed; available for convenient usage by code that uses 2D byte arrays.
  • Method Details

    • get

      public static byte get(int x, int y)
      Gets a byte from the raw data this stores, treating it as an infinite 2D plane of tiling 64x64 regions. You can interpret the blue noise in many ways, but it is meant to be used with a threshold to select mostly widely-spaced points when the threshold requires values greater than 100 or less than -100, or irregularly-shaped patterns of points when the threshold is in the middle, like for values less than 0. This tiles extremely well, or at least it appears so for a human; computers can detect the repetitive nature of this blue noise quite easily. Blue noise at left, magnitude histogram at right.
      Parameters:
      x - x position, can be any int; results will repeat every 64 cells
      y - y position, can be any int; results will repeat every 64 cells
      Returns:
      a byte that obeys a blue-noise distribution, so a value is only next to a similar value very rarely
    • get

      public static byte get(int x, int y, int z)
      Gets a byte from the raw data this stores, treating it as an infinite 3D space of tiling 128x128x32 regions. You can interpret the blue noise in many ways, but it is meant to be used with a threshold to select mostly widely-spaced points when the threshold requires values greater than 100 or less than -100, or irregularly-shaped patterns of points when the threshold is in the middle, like for values less than 0. This tiles extremely well, or at least it appears so for a human; computers can detect the repetitive nature of this blue noise quite easily. The blue noise property is only really preserved for slices of noise with the same z; changing z can be used to select a 2D noise plane as well.
      This uses any and all elements of TILE_NOISE, so it has a uniform blue noise distribution.
      Parameters:
      x - x position, can be any int; results will repeat every 128 cells
      y - y position, can be any int; results will repeat every 128 cells
      z - z position, can be any int; results will repeat every 32 cells, but this is different from x and y
      Returns:
      a byte that obeys a blue-noise distribution, so a value is only next to a similar value very rarely
    • get

      public static byte get(int x, int y, byte[] noiseData)
      Gets a byte from some raw data at least 16384 bytes in length, treating it as an infinite 2D plane of tiling 128x128 regions. Most usage gets the noiseData from an element of TILE_NOISE or TILE_TRI_NOISE.
      Parameters:
      x - x position, can be any int; results will repeat every 128 cells
      y - y position, can be any int; results will repeat every 128 cells
      noiseData - a byte array with length 16384; probably from TILE_NOISE or TILE_TRI_NOISE
      Returns:
      a byte drawn from noiseData by treating it as a 128x128 grid, with x in the least significant 7 bits
    • getSeeded

      public static byte getSeeded(int x, int y, int seed)
      Gets a point in a potentially-infinite aperiodically-tiling plane of blue noise, where each 128x128 square of blue noise is drawn from TILE_NOISE. Because each square of TILE_NOISE can tile with any other from that group, this can choose those squares randomly, and it does so using a simplistic (and fast) hash based on the Rosenberg-Strong pairing function. Unlike earlier attempts here to make aperiodic tilings of blue noise, this makes high-quality blue noise that satisfies the progressive quality. This also tolerates both positive and negative inputs for x and y.
      Blue noise at left, magnitude histogram at right.
      Parameters:
      x - x coordinate, in grid cells, pixels, or some other fine-grained measurement
      y - y coordinate, in grid cells, pixels, or some other fine-grained measurement
      seed - only the low 25 bits will be used, so this should probably be between 0 and 33554431
      Returns:
      a byte obeying a blue noise distribution
    • getSeededTriangular

      public static byte getSeededTriangular(int x, int y, int seed)
      Gets a point in a potentially-infinite aperiodically-tiling plane of blue noise, where each 128x128 square of blue noise is drawn from TILE_TRI_NOISE (which is blue noise with a triangular mapping). The triangular mapping may improve the appearance of the blue noise when used for dithering or other smoothly-fading gradients. Because each square of TILE_TRI_NOISE can tile with any other from that group, this can choose those squares randomly, and it does so using a simplistic (and fast) hash based on the Rosenberg-Strong pairing function. Unlike earlier attempts here to make aperiodic tilings of blue noise, this makes high-quality blue noise that satisfies the progressive quality. This also tolerates both positive and negative inputs for x and y.
      Blue noise at left, magnitude histogram at right.
      Parameters:
      x - x coordinate, in grid cells, pixels, or some other fine-grained measurement
      y - y coordinate, in grid cells, pixels, or some other fine-grained measurement
      seed - only the low 25 bits will be used, so this should probably be between 0 and 33554431
      Returns:
      a byte obeying a triangular-mapped blue noise distribution
    • fullHash

      public static int fullHash(int x, int y)
      Gets a unique int hash code given x and y that are valid short numbers, or a likely-unique hash if they are larger than short. Uses the Rosenberg-Strong pairing function, with inputs masked so it produces different values for (x,y), (-x,y), (x,-y), and (-x,-y).
      Parameters:
      x - ideally in the range -32768 to 32767
      y - ideally in the range -32768 to 32767
      Returns:
      an int that combines the arguments to make a unique result for x and y in the ideal range