Animating Functions of Improved 3D Perlin Noise

Oct 27, 2011 – a sleepless night

Screenshot of perlin noise
Multi-function scene

Technical Overview

In this experiment, I made a quick port of Ken Perlin’s classical noise in 3 dimensions. Due to its continuity properties, we can take a 2D cross-section and step through the 3rd dimension in time. The end result is a continuous animation.

After reading some of Perlin’s work, namely some of his early SIGGRAPH slides, I became intrigued with the potential this has for both 3D texturing and the animation of 2D textures. Many of these 2D textures, when animated, can produce some very compelling effects.

I employ fragment shaders to calculate per-pixel Perlin noise. Geometry is minimal, consisting of only 2 triangles that are un-transformed. Texture co-ordinates are generated and used in some animations, but for others only Fragment.xy is used. In all cases, time is used as an index to the Z plane of the 3D noise.

Benchmarks & Comparisons

All I can say is: Wow. WebGL (or more accurately, GLSL) fragment shaders are a huge step up in terms of performance when we compare to my old CPU implementation.

In the old implementation, on a 1GHz machine:

  • Firefox 7 would fail to achieve >30FPS on a 128x128 square.
  • Chrome 15 seemed to achieve >30FPS, or if not, the visual stutter wasn’t too noticeable.

In the WebGL shader implementation, on a 1GHz machine:

  • Firefox 7 easily seems to achieve >30FPS on a 1680x945 rectangle.
  • As it did for the old implementation, Chrome 15 seems to outshine Firefox again.
  • Furthermore, both browsers can render arbitrary transforms (the texture mapped cube) of the noise, which is something that I would have considered impossible for real-time in the old implementation.

How do we account for the speed improvements? 99% of it is due to the change from CPU to GPU shaders; programming a highly parallelizable task on the GPU will naturally outperform its corresponding CPU-only implementation. Moreover, the computation of Perlin noise is relatively simple, which makes it a good fit for approximation on the GPU.