どーも、ぐるたか@guru_takaです。
先日、セルラーノイズとボロノイ図を学び、UnityのShaderでお絵かきしてみました。成果物はこちら!


セルラーノイズとは?
セルラーノイズとは簡単にまとめると、あるピクセルにおいて複数の点(ランダム)から最も近い距離を出力するノイズです。距離は色で表現しています。

画像はセルラーノイズです。白い点が複数のランダムの点を表しています。自身のピクセルに対し、最も近い白点から近ければ近いほどゼロに寄るので、白点付近は黒っぽく映し出されます。
そして、ある2つの白点との中点は最も距離が離れるため、白で区切られたような絵になります。

セルラーノイズのshader
Shaderのソースはこちらです。コメントも記載しています!
Shader "Custom/CellNoise"
{
Properties
{
_SquareNum ("SquareNum", int) = 10
_Brightness ("Brightness", Range(0.0, 1.0)) = 0.5
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
int _SquareNum;
float _Brightness;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
float2 random2(float2 st)
{
st = float2(dot(st, float2(127.1, 311.7)),
dot(st, float2(269.5, 183.3)));
return -1.0 + 2.0 * frac(sin(st) * 43758.5453123);
}
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float2 st = i.uv;
st *= _SquareNum; //格子状の1辺のマス目数
float2 ist = floor(st);//整数
float2 fst = frac(st);//小数点以下
float distance = 5;
//自身含む周囲のマスを探索
for (int y = -1; y <= 1; y++)
for (int x = -1; x <= 1; x++)
{
//マスの起点(0,0)
float2 neighbor = float2(x, y);
//マスの起点を基準にした白点のxy座標
float2 p = 0.5 + 0.5 * sin(_Time.y + 6.2831 * random2(ist + neighbor));
//白点と処理対象のピクセルとの距離ベクトル
float2 diff = neighbor + p - fst;
//白点との距離が短くなれば更新
distance = min(distance, length(diff));
}
//白点から最も短い距離を色に反映
return distance * _Brightness;
}
ENDCG
}
}
}
以下のサイトを参考にしました!
参考
セルラーノイズthe book of shaders
参考
Unity-ShaderSketches | setchiGitHub
簡単な解説
処理の流れはこんな感じ!
格子状のマス目について
格子状のマス目の原理は、引用したスライドページの通りです。興味ある方はぜひチェックしてみて下さい!
float2 st = i.uv; st *= _SquareNum; //格子状の1辺のマス目数 float2 ist = floor(st);//整数 float2 fst = frac(st);//小数点以下
マス目のイメージはこんな感じ!

周囲のマス目のforループについて
周囲のマス目にある白点との距離をforループで算出します。

//自身含む周囲のマスを探索
//自身のマスは(0,0)
for (int y = -1; y <= 1; y++)
for (int x = -1; x <= 1; x++)
{
//マスの起点
float2 neighbor = float2(x, y);
//マスの起点を基準にした白点のxy座標
float2 p = 0.5 + 0.5 * sin(_Time.y + 6.2831 * random2(ist + neighbor));
//白点と処理対象のピクセルとの距離ベクトル
float2 diff = neighbor + p - fst;
//白点との距離が短くなれば更新
distance = min(distance, length(diff));
}
distanceを算出するイメージは以下の通りです。

白点と自身のピクセルの距離ベクトルdiffを図にならってわかりやすく書くと、diff = (neighbor + p) - (0 + fst)になります。その最終結果が、diff = neighbor + p - fstとなるわけです!
各マス目内の白点の座標pはist,neighborで表しているので、共有できています。

あとは距離を色として出力すれば完成!
ボロノイ図のshader
ボロノイ図とは、あるピクセルにおいて複数の点(ランダム)から最も近い点にならって領域をわけた図です。セルラーノイズのshaderを少し改変すれば簡単にできます。
ソースはこちら!
Shader "Custom/CellNoiseSketch"
{
Properties
{
_SquareNum ("SquareNum", int) = 10
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
float2 random2(float2 st)
{
st = float2(dot(st, float2(127.1, 311.7)),
dot(st, float2(269.5, 183.3)));
return -1.0 + 2.0 * frac(sin(st) * 43758.5453123);
}
int _SquareNum;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float2 st = i.uv;
st *= _SquareNum;
float2 ist = floor(st);//整数
float2 fst = frac(st);//少数の右側
float distance = 5;
float2 p_min;
for (int y = -1; y <= 1; y++)
for (int x = -1; x <= 1; x++)
{
float2 neighbor = float2(x, y);
float2 p = 0.5 + 0.5 * sin(_Time.y + 6.2831 * random2(ist + neighbor));
float2 diff = neighbor + p - fst;
if(distance > length(diff)){
distance = length(diff);
//最も近い白点も更新
p_min = p;
}
}
//最も近い白点の座標を基に色を出力
//色を変化させるための式
p_min.x += sin(_Time.y);
p_min.y += cos(_Time.y);
return fixed4(p_min.x ,p_min.y, p_min.x + p_min.y,1);
}
ENDCG
}
}
}
セルラーノイズとの違いはこちらです。
if(distance > length(diff)){
distance = length(diff);
//最も近い白点も更新
p_min = p;
}

最も近い白点の座標に基づいて色を出力すればボロノイ図が完成です!
参考
シェーダでノイズ3(セルノイズ)空の缶詰
最後に:他にも色んな表現ができる!
セルラーノイズの原理を使うと、色んなお絵かきができます。例えばメタボールのような表現も可能です!

他にもテクスチャをボロノイ図で表現できたりして、遊んでみると面白いです。こちらはGitHubでソースを公開しています。

ぜひ色々試してみてください!!
参考
セルラーノイズ「ボロノイを改善する」the book of shaders



[…] shader でセルラーノイズ・ボロノイ図を描いてみる | ぐるたかログ […]