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

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

1 はじめに

こんにちは、カヤックのソーシャルゲーム事業部のmadaです。この記事はカヤックUnityアドベントカレンダー2018の22日目の記事です。

「Lion Studios」が作成した「Happy Glass」というゲームを遊んでみて、液体の表現に興味を持ちました。この記事では「簡単に水に近い表現を実現したい」話をします。



IMAGE ALT TEXT HERE


Happy Glass - Lion Studios- Youtube

2 Metaball?

調べてみると水の表現に、Metaballという手法が使えそうでした。Metaballの説明をしようとすると数式が出てくるので、先に実装した結果を紹介します。2つの水滴が接近したときに水滴がひっつくような表現が、なんとなくできているのではないでしょうか。

f:id:kayac-mada:20181221151922g:plain

2DのMetaballを数式で説明すると、以下の式になります。画面上の(x,y)ピクセルを塗るときに、式の条件を満たしている場合塗る、そうでない場合塗らない、と制御します。例として2つのメタボールが存在するときの2次元の等高線グラフも作成してみました。



f:id:kayac-mada:20181221151929p:plain




f:id:kayac-mada:20181221151932p:plain


3 Metaballの実装概要

早速Metaballを実装していきましょう。実装するのは先程紹介した数式です。数式を分解し、3ステップに分けて紹介します。

3.1 ShaderでΣ内の計算

Σ内の計算を実装します。数式は以下の通りです。Shaderで実装するので、出力を画像のRGBAにする必要があり、_Scale, _Clip という調整用パラメーターを用意しています。frag関数の中で、uvの中心(0.5, 0.5)からの距離を色に変換しています。今回は後から調整しやすいようにShaderで実装しましたが、調整用パラメーターが定まれば画像データにしてしまっても良いところです。



f:id:kayac-mada:20181221151936p:plain:h80


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; 
            float4 color    : COLOR; 
            float2 texcoord : TEXCOORD0; 
        }; 
 
        struct v2f 
        { 
            float4 vertex   : SV_POSITION; 
            fixed4 color    : COLOR; 
            float2 texcoord : TEXCOORD0; 
        }; 
 
        sampler2D _MainTex; 
        fixed4 _Color; 
        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 = i.color * a; 
            clip(color.a - _Cutoff); 
 
            return color; 
        } 
     ENDCG 
     } 
} 
}


f:id:kayac-mada:20181221151941p:plain:w300


3.2 カメラとRenderTextureでΣの実装

つぎにΣの実装を行います。3.1でΣ内の計算出力を画像にしたので、Σの実装はn個のMetaballを描写するだけです。次の処理で必要なのでCameraの出力はRenderTextureに設定しておきます。



f:id:kayac-mada:20181221151944p:plain:h80




f:id:kayac-mada:20181221151947p:plain:w300




f:id:kayac-mada:20181221151950p:plain:w300


3.3 ShaderでThresholdの実装

次にthresholdの実装を行います。実装は3.2で出力したRenderTextureを描写するShaderのfrag関数の中で、clip(color.a - _Cutoff);を呼ぶだけです。内側と境界に異なる色を設定できるように、clip関数の後に条件分岐を設定しています。



f:id:kayac-mada:20181221151953p:plain:h80


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 
    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 
    } 
} 
}


f:id:kayac-mada:20181221151957p:plain:w300


成果物

以上で2DのMetaballのRenderingができるようになりました。ボールにColliderを付けて上から落としてみた画像を貼ります。まぁまぁ水に近い表現ができていると思います。比較的に簡単にできるのでタイトル通り、「簡単に水に近い表現が実現できた!」と言い切っておきます。



f:id:kayac-mada:20181221152001g:plain


簡単に上のシーンの構成を紹介します。

1. 「MetaBallLayer」のみを描写する「MetaBallCamera」を作成します。 
2. 単体Metaball(水玉)を描写するために空のPrefabを作成します。 
3. Prefabに(3.1)で紹介したShaderで描写するQuadを作成します。 
4. Prefabのレイヤーを「MetaBallLayer」に設定します。 
5. (3.2)で紹介したように、「MetaBallCamera」のRenderTargetにRenderTextureを設定します。 
6. RenderTextureを「MainCamera」に描写するQuadを作成します。 
7. ステージ(黒い棒)にColliderを付け、MainCameraで描写します。 
8. 単体MetaballのPrefabを大量にInstantiateし、物理挙動に任せます。 
 
※ Quadは UnityMenu > GameObject > 3D Object > Quad から生成できます

明日は?

明日は目からビームが出ている漫画名刺の「アファトさん」による「Compute ShaderでランタイムにSDFテクスチャーを生成するエクスペリメント」です。明日の記事もお楽しみに!

オリジナルのエンクロージャ:
20181221151957.png

コメント

このブログの人気の投稿

投稿時間:2021-06-17 22:08:45 RSSフィード2021-06-17 22:00 分まとめ(2089件)

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2021-06-17 05:05:34 RSSフィード2021-06-17 05:00 分まとめ(1274件)