LoginSignup
22
22

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-05-19

ここ最近、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

最後に

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

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

22
22
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
22