Cell (Worley) noise in Unity3D

Cell NoiseNoise algorithms are a fantastic thing to have in your 3D graphics bag of tricks.  I can’t tell you how many times Perlin noise has saved my life.  Unfortunately, most good noise algorithms don’t lend themselves particularly well to computation on the GPU.  Enter Cell noise.

Cell noise, otherwise known as Worley noise,  is actually a visualization of a Voronoi diagram: given a random scattering of points, color each pixel on the surface relative to the distance from the closest randomly scattered point.  Cell noise appears all over nature, it looks great, and is easy to compute!  A cell noise function can give us a volume of noise, which we can sample in a fragment shader, giving us seamless patterns on any object – no UV mapping required.  Cool!

I’m going to base this implementation off of a very nice one I found here.  To get started, we need a way to generate our randomly scattered points.  Normally, we would accelerate this sort of thing with a look-up table of some kind, which in GPU land means a texture.  Hop into your favorite image editing program and make a texture that looks something like this:

Random

And here’s the shader:

Shader "Miller/CellNoise" {
	Properties {
		_Color ("Main Color", Color) = (1,1,1,1)
		_SecondaryColor ("Secondary Color", Color) = (1,1,1,1)
		_Random ("Random Noise", 2D) = "white" {}
		_Intensity ("Intensity", Range(0.001, 10.0)) = 1.0
		_Size ("Size", Range(0.000, 10.0)) = 1.0
		_Offset ("Time", float) = 0.0
	}
 
	SubShader {
		pass {
			Name "Noise"
 
			/* // Enable this if you want addative blending
			ZWrite Off
			Tags { "Queue" = "Transparent" }
			Blend One One
			*/
 
			CGPROGRAM
			#pragma target 3.0
			#pragma vertex vert
			#pragma fragment frag
			#pragma fragmentoption ARB_fog_exp2
			#include "UnityCG.cginc"
 
			uniform float4 _Color;
			uniform float4 _SecondaryColor;
			uniform float _Intensity;
			uniform sampler2D _Random;
			uniform float _Size;
			uniform float _Offset;
 
			struct v2f {
				V2F_POS_FOG;
				float3 noisepos;
			};
 
			v2f vert(appdata_base v) {
				v2f o;
 
				PositionFog(v.vertex, o.pos, o.fog);
				o.noisepos = v.vertex;
 
				return o;
			}
 
			float4 getCell3D(const in int x, const in int y, const in int z) {
				float u = (x + y * 31) / 20.0;
				float v = (z - x * 3) / 30.0;
				return tex2D(_Random, float2(u, v));
			}
 
			float2 cellNoise3D(float3 xyz) {
				int xi = int(floor(xyz.x));
				int yi = int(floor(xyz.y));
				int zi = int(floor(xyz.z));
 
				float xf = xyz.x - float(xi);
				float yf = xyz.y - float(yi);
				float zf = xyz.z - float(zi);
 
				float dist1 = 9999999.0;
				float dist2 = 9999999.0;
				float3 cell;
 
				for (int z = -1; z <= 1; z++) {
					for (int y = -1; y <= 1; y++) {
						for (int x = -1; x <= 1; x++) {
							cell = getCell3D(xi + x, yi + y, zi + z).xyz;
							cell.x += (float(x) - xf);
							cell.y += (float(y) - yf);
							cell.z += (float(z) - zf);
							float dist = dot(cell, cell);
							if (dist < dist1) {
								dist2 = dist1;
								dist1 = dist;
							}
							else if (dist < dist2) {
								dist2 = dist;
							}
						}
					}
				}
 
				return float2(sqrt(dist1), sqrt(dist2));
			}
 
			float4 frag(v2f i) : COLOR {
				float2 dists = cellNoise3D((i.noisepos + _Offset) * _Size);
				// float4 c = ((_Color * dists.x) + (_SecondaryColor * dists.y)) * _Intensity; // Add the terms for a different look
				float4 c = ((_Color * dists.x) * (_SecondaryColor * dists.y)) * _Intensity;
				return c;
			}
 
			ENDCG
		}
	}
 
	FallBack "Diffuse"
}

You can animate the texture by changing the _Offset parameter over time. It looks great!

5 Replies to “Cell (Worley) noise in Unity3D”

  1. Hi Will,

    This looks really cool. Thanks for sharing it!

    Unfortunately, I haven’t been able to get it to work for me. I copied the shader code above and used it and your sample texture in Unity. I get no errors (after I fixed the &lt. syntax that shows up for me – using Firefox) on shader compile. When I apply the shader to a material it ends up using the fallback shader. I can successfully use other shaders that use ARB_fog_exp2, but maybe there’s something else here my card doesn’t support? I’m running an Nvidia GT8800 with 195.62 drivers.

    Any suggestions?

    Thanks!
    Mal

  2. Hi Mal,

    Sorry this isn’t working for you. I have a similar configuration at work that I could try the shader out on. It will have to be after the holidays though. Try commenting out the #pragma target 3.0 and see if that does anything

  3. I also can’t get it working, using Windows7 with an ATI 5770 and latest drivers. It’s just the fallback.

  4. The problem is that it’s making far to many instructions in D3D. So it won’t work without opengl at all.

    I’m not sure why it’s making so many more for D3D though..

  5. Ah, that makes sense. When the shader is compiled, all the loops in it are unrolled. That’s why there are so many calls.

Comments are closed.