Noise Generation Algorithms : Voronoi

January 19, 2018
20m
Unity 3DShadersAlgorithm

You may have heard of Voronoi noise if you have worked with software like blender or substance painter. It's really good way of making surfaces that look like they have sharp creases enclosing a smooth surface.

Usually used to make hardened lava flows, parched ground as well as more organic looking structures like skin and animal hide, living cells.

In this part we will see how to create a Voronoi pattern in Unity and how to set your own points in the shader itself to create the Voronoi pattern.
We will go over the 'Noise' part of Voronoi noise in the later part.
The basic element that the Voronoi pattern depends is the 'Distance Field' function.
Take the case where there are 'N' points and we have to find the distance field of those points,
We have to calculate the distance between each pixel and the point closest to it.
This is what we will end up having in this tutorial:

Voronoi Pattern Made With Given Points

We will be creating a script that passes a set of Vector2s to a shader which in turn draws it.
Before we get to C# scripting, we will see how to make the shader.
First of all we don't have any properties for this shader.😋. We will have properties in the coming parts.
But we do have a global variable called float2 _points[5] - This can be accessed through C# code.
Let's look the two structs that we have:


float2 _points[5];
struct appdata
{
 float4 vertex : POSITION;
 float2 uv : TEXCOORD0;
};

struct v2f
{
 float2 uv : TEXCOORD0;
 float4 vertex : SV_POSITION;
};
No fancy-pancy stuff.
Now the vertex shader:
v2f vert (appdata v)
{
 v2f o;
 o.vertex = UnityObjectToClipPos(v.vertex);
 o.uv = v.uv;
 return o;
}

Nothing special here either. Now the fragment shader:


fixed4 frag (v2f i) : SV_Target
{
 fixed4 col = fixed4(0,0,0,1);
 float minDist = 1.0;
 float2 coord = i.uv;
 for (int i = 0; i < 5; i++) 
 {
  float dist = distance(coord, _points[i]);  
  minDist = min(minDist, dist);
 }
 col += minDist;
 return col;
}

We will see all the important parts and break it down.


for (int i = 0; i < 5; i++) 
{
 float dist = distance(coord, _points[i]);  
 minDist = min(minDist, dist);
}

Here we are iterating through all the points and keeping the minimum distance value in minDist.
Then after that we are just adding that as the color of the pixel.
So we essentially just drew the distance field of those points.
What we have to do now... is that we have to access those points by C# code.
So the Unity API provides us a way to do so, with the Material.SetVectorArray function.
So here is the C# code:


using UnityEngine;
public class VoronoiNoise : MonoBehaviour
{
    public Material mat;
    public List<Vector2> points;

    void Update ()
    {
        if(points.Count == 5)
            mat.SetVectorArray("_points", ConvertToVec4(points));
    }

    List<Vector4> ConvertToVec4(List<Vector2> vec2)
    {
        List<Vector4> vec4 = new List<Vector4>();
        for (int i = 0; i < vec2.Count; i++)
            vec4.Add(new Vector4(vec2[i].x, vec2[i].y, 0, 0));
        return vec4;
    }
}

For ease of use in Editor I have used Vector2s as member variables and then later convert them to Vector4s before passing to the function. SetVectorArray only takes in List of Vector4s.
Now you can play around with it.. till your heart's content. 

Moving onto where we will do even cooler stuff and actually do Voronoi noise instead of just making a pattern with out inputs.

Now we will go over making a more cooler version of what we previously did.
This is what we will end up with:

Voronoi Noise Shader In Unity

In this part we will go over the actual 'Noise' part of it.
For that we need a pseudo random number generator, We already made one in a previous tutorial on making White Noise with shaders.
But for this case we need to make 2D noise rather than 1D noise like we did before.
So we will go over our random function:


float2 random2(float2 p)
{
 return frac(sin(float2(dot(p,float2(117.12,341.7)),dot(p,float2(269.5,123.3))))*43458.5453);
}

If you have gone the previous pseudo-random number generator that was made.. nothing really complicated is happening here.
We are still using the frac function to give us the fractional part of a a number made with the dot function which contains a float2 and some arbitrary numbers as parameters.
Now the fragment shader where all the magic happens:


fixed4 frag(v2f i) : SV_Target
{
    fixed4 col = fixed4(0,0,0,1);
    float2 uv = i.uv;
    uv *= 6.0; //Scaling amount (larger number more cells can be seen)
    float2 iuv = floor(i.uv); //gets integer values no floating point
    float2 fuv = frac(i.uv); // gets only the fractional part
    float minDist = 1.0;  // minimun distance
    for (int y = -1; y <= 1; y++) 
    {
        for (int x = -1; x <= 1; x++)
        {
            // Position of neighbour on the grid
            float2 neighbour = float2(float(x), float(y));
     // Random position from current + neighbour place in the grid
     float2 pointv = random2(iuv + neighbour);
     // Move the point with time
     pointv = 0.5 + 0.5*sin(_Time.z + 6.2236*pointv);//each point moves in a certain way
            // Vector between the pixel and the point
     float2 diff = neighbour + pointv - fuv;
     // Distance to the point
     float dist = length(diff);
     // Keep the closer distance
     minDist = min(minDist, dist);
  }
     }
     // Draw the min distance (distance field)
     col.r += minDist * minDist; // squared it to to make edges look sharper
     return col;
}

Play around with different values and see what comes up.

This is the entire source code:

Shader Lab

1Shader "BitShiftProductions/VoronoiMagic2"
2{
3	Properties
4	{
5
6	}
7	SubShader
8	{
9		Tags { "RenderType" = "Opaque" }
10
11		Pass
12		{
13			CGPROGRAM
14			#pragma vertex vert
15			#pragma fragment frag
16
17			#include "UnityCG.cginc"
18
19			struct appdata
20			{
21				float4 vertex : POSITION;
22				float2 uv : TEXCOORD0;
23			};
24
25			struct v2f
26			{
27				float2 uv : TEXCOORD0;
28				float4 vertex : SV_POSITION;
29			};
30
31			v2f vert(appdata v)
32			{
33				v2f o;
34				o.vertex = UnityObjectToClipPos(v.vertex);
35				o.uv = v.uv;
36				return o;
37			}
38			float2 random2(float2 p)
39			{
40				return frac(sin(float2(dot(p,float2(117.12,341.7)),dot(p,float2(269.5,123.3))))*43458.5453);
41			}
42			fixed4 frag(v2f i) : SV_Target
43			{
44				fixed4 col = fixed4(0,0,0,1);
45				float2 uv = i.uv;
46				uv *= 6.0; //Scaling amount (larger number more cells can be seen)
47				float2 iuv = floor(uv); //gets integer values no floating point
48				float2 fuv = frac(uv); // gets only the fractional part
49				float minDist = 1.0;  // minimun distance
50				for (int y = -1; y <= 1; y++)
51				{
52					for (int x = -1; x <= 1; x++)
53					{
54						// Position of neighbour on the grid
55						float2 neighbour = float2(float(x), float(y));
56						// Random position from current + neighbour place in the grid
57						float2 pointv = random2(iuv + neighbour);
58						// Move the point with time
59						pointv = 0.5 + 0.5*sin(_Time.z + 6.2236*pointv);//each point moves in a certain way
60																		// Vector between the pixel and the point
61						float2 diff = neighbour + pointv - fuv;
62						// Distance to the point
63						float dist = length(diff);
64						// Keep the closer distance
65						minDist = min(minDist, dist);
66					}
67				}
68				// Draw the min distance (distance field)
69				col.r += minDist * minDist; // squared it to to make edges look sharper
70				return col;
71			}
72		ENDCG
73		}
74	}
75}