20201027

Random noise textures on the GPU in WebGL

Procedural rendering routines often need pseudorandom numbers. For graphics rendering, we usually prefer correct-ish and fast code over properly "correct" code. My approach to random number generation is usually to intermittently seed a weak pseudo-random number generator, like a linear-feedback shift register, from a stronger source like Javascript's Math.random(). 

On the GPU, things are not so simple: we want a pseudorandom algorithm that is fast, local, and parallel. For compatibility, we should also have this RNG use an 8-bit RGBA texture for its internal state. Bitops are a bit tricky in WebGL shaders (though not impossible), so we'll use a linear congruential generator.

20201015

WebGL shader rules for rendering dendriform growth

I've been playing around with cellular automata for procedural generation in WebGL. Here's an algorithm for rendering dendriform growth. I was aiming for rivers, but it came out like neurons.

[view in browser]

It's built atop a diffusion-limited aggregation cellular automata. Algorithm sketch: 

  • Each cell looks at the 4 nearest neighbors on the grid. We also check the 4 corners ("far" neighbors) to avoid creating loops.
  • Random numbers are provided by a separate noise texture
  • State is stored in the channels of an RGBA texture. Each [0,1] channel maps to a [0,255] byte value
  • The texture is seeded with at least one "active" pixel (existing water/lake/cell body). The seed is set to active (green=1.0)
  • Green channel runs a a diffusion limited aggregation: 
    • If exactly one nearby pixel is active, then we have a 5% of becoming active. 
    • Avoid creating loops: don't activate a pixel if more than one "far" neighbor is already active.
    • Because pixels act in parallel and at random, sometimes two pixels turn on in the same iteration, creating a loop. Detect and remove these after-the-fact, if they happen. 
  • Keep track of distance from the "seed" region in blue channel
    • Seeds are initialized with blue channel = 255
    • Newly activated cells get blue-1
    • This counts down to zero, at which point expansion stops
  • The texture is also initialized with various water sources / "synapses" scattered about it
    • This is stored in the alpha channel
    • If an active cell hits a source, it becomes a "river"
    • If a cell is active, and an adjacent cell is a "river", and is also further away from the source (check distance in blue channel), then this cell also becomes a "river"
  • A separate shader checks which cells are rivers, and generates tile ID values which are then passed to a tile shader.

Pixel-art-style Conway's game-of-life using a tile shader in WebGL

Implementation of Conway's game of life cellular automata with a retro-style shader. Added as example on webgpgpu Github.




Sketch of WebGL implementation:

  • Underlying cellular automata runs Conway's game of life
  • States as game tiles: new cells: small blue; live cells: blue; died: skulls
  • Life diffuses out coloring nearby areas
  • A single RGBA texture stores the game state. We render to/from this texture as a framebuffer.
  • Each color channel in [0,1] can be mapped to a [0,255] byte value
  • The green channel stores the game of life state
  • The blue channel stores the diffusing field
  • The red channel outputs a tile ID value that is passed to a tile shader to render the game


Retro-styled forest-fire cellular automata in WebGL


This is a cellular automata analogue of the model that we used for developmental retinal waves in this paper


Key rules

  • Vegetation "grows" occasionally (flip a coin to increment amount of trees)
  • Fires have a small chance of starting spontaneously
  • Intensity of ignited fires decay down to zero, at which point they leave ash
  • Fires spread to adjacent cells with a probability that depends on their intensity
  • Probability of a cell igniting given a fire is related to how much vegetation there is

Sketch of WebGL implementation:

  • A single RGBA texture stores the game state
  • Each color channel in [0,1] can be mapped to a [0,255] byte value
  • A "noise" texture creates pseudorandom numbers
  • The green channel stores the level of vegetation (BURNT,NONE,GRASS,SCRUB,TREE)
  • The blue channel stores the intensity of the fire
  • The red channel outputs a tile ID value that is passed to a tile shader to render the game