Edited at

【Unity】2Dメタボールの作り方を一から紹介してみる

ここ最近、Unityの開発にて、2DメタボールっぽいAssetを使ってます。ただ、仕組みはスルーしていました。

使うにつれて2Dメタボールの仕組みが気になったので、色々ググり、実際に作ってみました!(当然ながら、偉大なる先人が残した情報のお陰様でできました)

RenderTextureの使い方を学ぶ良いキッカケにもなり、個人的に学習満足度が高かったです!

ここでは、自分の備忘録もかねて、Unityの2Dメタボールの作り方を紹介します。

mg6.gif

追記:2019/6/21

GithubにサンプルコードをUpしました。参考になれば幸いです!

2DMetaball_Shader(サンプル| Github


参考にした記事

2Dメタボールの作り方は色々あるのですが、個人的に下記の記事が最もわかりやすかったです。

神記事:【Unity】 簡単に水に近い表現を実現したい (Metaball)

他の2DメタボールのチュートリアルやAssetなどのソースも見たのですが、上記の記事が直感的でシンプル!最高でした。

参考:METABALL TUTORIAL

今回は上記の記事を参考に、実装までの流れと作り方を紹介していきます。


2Dメタボールの式

2Dメタボールの式はこちら。wikiから引用しています。x0,y0はメタボールの中心です。

\sum_{i=0}^{m} \frac{1}{(x-x_0)^2 + (y-y_0)^2} \leq threshold\\

wiki:Metaballs

参考にした記事に、2つのメタボールが存在するときの等高線グラフがありました。こちらの図をみますと、何となくイメージできると思います。

スクリーンショット 2019-05-19 20.43.33.png


2Dメタボールの実装の流れ


  1. Shaderで2Dメタボールの式を表現(メタボール1個の式)

  2. RenderTextureに描画

  3. ShaderでRenderTextureを完成形にもっていく(各メタボールの和)

  4. Quadで描画!

  5. CullingやLayerの設定して完成!


STEP1:Shaderで2Dメタボールの式を表現

2Dメタボール1個分の式をShaderで表現します。参考にした記事と大きく変わりません。というか殆ど同じです(爆)


MetaballParticle.shader

Shader "Metaball/MetaballParticle" {

Properties
{
// _Color ("Color", Color) = (1,1,1,1)
_Scale ("Scale", Range(0,0.05)) = 0.01
_Cutoff ("Cutoff", Range(0,05)) = 0.01
}

SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
}

Cull Off
Lighting Off
ZWrite Off
Blend One OneMinusSrcAlpha

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog

#include "UnityCG.cginc"

struct appdata_t
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};

struct v2f
{
float4 vertex : SV_POSITION;
float2 texcoord : TEXCOORD0;
};

fixed _Scale;
fixed _Cutoff;

v2f vert(appdata_t IN)
{
v2f OUT;
OUT.vertex = UnityObjectToClipPos(IN.vertex);
OUT.texcoord = IN.texcoord;
// OUT.color = IN.color * _Color;
return OUT;
}

fixed4 frag (v2f i) : SV_Target {
fixed2 uv = i.texcoord - 0.5;
fixed a = 1 / (uv.x * uv.x + uv.y * uv.y);
a *= _Scale;
fixed4 color = a;
clip(color.a - _Cutoff);
return color;
}
ENDCG
}
}
}


3行でまとめると


  • メタボールの左辺の式を色colorで出力

  • メタボールの中央は、uv座標の中心(0,5, 0,5)


  • clip(a,b)baが下回ると、0になる

スクリーンショット 2019-05-19 20.59.34.png

そして、CircleのSpriteを作成。

スクリーンショット_2019-05-19_21_02_39.png

マテリアルをアタッチするとこんな感じになります!

スクリーンショット 2019-05-19 21.01.25.png


STEP2: RenderTextureに描画

続いて、RenderTextureに描画させます。理由は各々のメタボールの式の和(ここでは色値の和)を計算する際に、必要だからです。

やり方を図で紹介します。

まずProjectのフォルダ内にRenderTextureを作成。

スクリーンショット_2019-05-19_21_04_55.png

ヒエラルキー内にMainCameraとは別のカメラを用意して、TargetTextureにアタッチします。これで準備OK!

Unity_2018_3_8f1_Personal_-_MetaBall_unity_-_Metaball_shader_-_PC__Mac___Linux_Standalone__Personal___Metal_.png


STEP3:3. ShaderでRenderTextureを完成形にもっていく(各メタボールの和)

続いて、RenderTexture内の色情報の和を計算し、閾値を設けます。参考記事のソースを掲載させて頂きます。


MetaballRenderer.shader

Shader "Metaball/MetaballRenderer" {

Properties
{
_MainTex ("MainTex", 2D) = "white" {}
_Color ("Color", Color) = (1,1,1,1)
_Cutoff ("Cutoff", Range(0,1)) = 0.5
_Stroke ("Storke", Range(0,1)) = 0.1
_StrokeColor ("StrokeColor", Color) = (1,1,1,1)
}

SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
// "PreviewType"="Plane"
}

Cull Off//裏も描画する
// Lighting Off
ZWrite Off //transparentで使用する部分
Blend One OneMinusSrcAlpha

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog

#include "UnityCG.cginc"

struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
};

struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
};

sampler2D _MainTex;
half4 _Color;
fixed _Cutoff;
fixed _Stroke;
half4 _StrokeColor;

v2f vert(appdata_t IN)
{
v2f OUT;
OUT.vertex = UnityObjectToClipPos(IN.vertex);
OUT.texcoord = IN.texcoord;
OUT.color = IN.color * _Color;
return OUT;
}

fixed4 frag (v2f i) : SV_Target
{
fixed4 color = tex2D(_MainTex, i.texcoord);
clip(color.a - _Cutoff);
color = color.a < _Stroke ? _StrokeColor : _Color;
return color;
}
ENDCG
}
}
}


clipで最初にマスクし、_Strokeの閾値で、メタボールの枠線が出たり出なかったりします。

これで大体終わりです。あともう少し!


STEP4:Quadで描画!

STEP2のRenderTextureとSTEP3のshaderをQuadにぶちこみます。

スクリーンショット_2019-05-19_21_19_08.png

すると、こんな感じになるかと思います。

スクリーンショット 2019-05-19 21.20.37.png


STEP5:CullingやLayerの設定して完成!

カメラのCullingMaskを設定やLayerを設定して、メインカメラにはメタボールの最終系のみ写します。

やり方はこちらになります。

まず、メタボールたちのLayerをDefaultから変更(ここではEffectLayer)します。

スクリーンショット_2019-05-19_21_24_54.png

その後、メタボールカメラのCullingMaskを上記で設定したLayerに変更。するとRenderTextureでは完成形前のメタボールのみ描画されるので、所望の描画ができるわけです!

スクリーンショット_2019-05-19_21_25_00.png

スクリーンショット 2019-05-19 21.29.16.png


最後に

いかがでしょうか。思ったより簡単にできる!と感じた方も多いかも知れません。

色々な表現に使えるので、興味ある方はぜひお試し頂ければと思います!