しゅみぷろ

プログラミングとか

法線に影響される液垂れの表現

はじめに

液垂れ表現についてです。 最近Switchばっかやってて全然更新してなかったので久しぶりにネタ消化。

液垂れの基本的な部分は過去記事で。

esprog.hatenablog.com

esprog.hatenablog.com

今回は法線マップに影響させる部分について軽くメモしていきます。

f:id:es_program:20170401115307g:plain:w600

f:id:es_program:20170401135622g:plain:w600

法線に応じて流れやすさを変える

Shaderに法線マップを入力として取らせ、接空間上で法線と液体の付着量から、力の働いている方向にどの程度流れるかをそれっぽく計算してやります。

float3 normal = normalize(UnpackNormal(tex2D(_NormalMap, i.uv)).xyz);

で法線マップの法線を取ってきて

float VITIATE_Z = pow(normal.b, 2) - normal.y * 0.2;
float VITIATE_X = 0.1 * rand(float3(i.uv.xy, i.uv.x + i.uv.y)) * (1 + normal.b * 30);

どのくらい縦、横の液体を考慮するかの係数(VATATE_ZとVITATE_X)を計算します。 式自体に特に深い意味はなく、それっぽく流れてくれるよう、法線が上向きなら流れにくく・・・とかやってるだけです。

この係数を使って液体マップの周囲(液体が垂れてくると予想される場所)をサンプリングし、付着量を出します。

float2 shiftZ = float2(_FlowDirection.x * _MainTex_TexelSize.x, _FlowDirection.y * _MainTex_TexelSize.y) * _ScaleFactor * _Viscosity * VITIATE_Z;
float2 shiftX = float2(_MainTex_TexelSize.x * _FlowDirection.y, _MainTex_TexelSize.y * _FlowDirection.x) * _ScaleFactor * _Viscosity * VITIATE_X;
float2 shiftx = -shiftX;

float4 texZ = tex2D(_MainTex, clamp(i.uv.xy + shiftZ, 0, 1));
float4 texx = tex2D(_MainTex, clamp(i.uv.xy + shiftx + shiftZ, 0, 1));
float4 texX = tex2D(_MainTex, clamp(i.uv.xy + shiftX + shiftZ, 0, 1));

float amountUp = (texZ.a + texx.a + texX.a) * 0.3333;

この付着量が粘性係数を上回る場合は液垂れ計算が行われ、下回る場合は変化なしということになります。

最後に

Texture basedなので、勿論uv依存です。 使用用途として考えているのは壁等のシンプルなオブジェクトへの液体の付着表現(血の表現、Splatoonのようなインク表現)なので、uvが複雑になるモデルには不向きです。

今はまだ試作段階なので、係数のパラメーター化や色の減法混色なども実装していこうと思います。

シンプルなMatcapシェーダーを書いてみる

はじめに

UnityでMatcapシェーダー書いたのでメモ。

Matcapシェーダーは考え方とか実装がめちゃくちゃ簡単なので、シェーダーの勉強に良さそうです。

Matcapの特徴はとにかく軽量で良い感じに写実的な表現が行える上、実装が簡単なことだと思います。

Matcap

レンダリングされた球の画像を使うことでシェーディングを行う手法のことだと思います(多分)。

考え方はとてもシンプルで、レンダリングするオブジェクトの法線を球の画像に射影するだけです。

球の画像はこんな感じで与えます。

f:id:es_program:20170128223022p:plain:w500

適当に作ったMatcap用の画像ですが、これ使ってレンダリングするとこんな感じに。

f:id:es_program:20170128223400p:plain:w500

モデルにはこちらのアセットを使わせて頂きました。

Asset Storeでシェーダーと画像を入手できるので自分で書く必要はないのですが、自分で書けるとそれなりにカスタマイズできて面白いです。

シンプルなMatcapシェーダー

とりあえずコードはこんな感じです。

Shader "Custom/SimpleMatcap"
{
    Properties
    {
        _Matcap ("Matcap Texture", 2D) = "white" {}
        _Range ("Range", Range(0, 1.5)) = 0
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
           #pragma vertex vert
           #pragma fragment frag

           #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float4 normal : TEXCOORD1;//Phong-Shading
            };

            sampler2D _Matcap;
            float _Range;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                o.uv = v.uv;
                o.normal = float4(v.normal, 1);//LocalSpace Normal
                //o.normal = float4(mul((float3x3)_Object2World, v.normal), 1);//WorldSpace Normal
                //o.normal = mul(UNITY_MATRIX_IT_MV, float4(v.normal, 1));//ProjectionSpace Normal
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float2 normalProj = normalize(i.normal.xyz) * 0.5 + 0.5;
                fixed4 col = tex2D(_Matcap, normalProj * _Range);
                return col;
            }
            ENDCG
        }
    }
}

Phong-Shadingを行って、ラスタライザに法線を補間して滑らかにしてもらってます。

o.normal = float4(v.normal, 1);//LocalSpace Normal
//o.normal = float4(mul((float3x3)_Object2World, v.normal), 1);//WorldSpace Normal
//o.normal = mul(UNITY_MATRIX_IT_MV, float4(v.normal, 1));//ProjectionSpace Normal

上の部分では、法線をお好みの座標空間に変換しています。

テカテカした感じの画像を使用する場合、ProjectionSpaceだとちょっといい感じに見えます。

メインのテクスチャを考慮するMatcapシェーダー

左がメインテクスチャのみ、右がMatcap適用後です。

f:id:es_program:20170128230831p:plain:w500

下のような感じで、ブレンディングを調整できます。

f:id:es_program:20170128230845g:plain:w500

テカテカしててなかなかカッコイイです。洞窟にいそう。

コードはこんな感じ。

Shader "Unlit/BlendMatcap"
{
    Properties
    {
        _MainTex("MainTex", 2D) = "white" {}
        _Matcap ("Matcap Texture", 2D) = "white" {}
        _Range ("Range", Range(0, 1.5)) = 1
        _Blend ("Blend", Range(0, 1)) = 0.5
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
           #pragma vertex vert
           #pragma fragment frag

           #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float4 normal : TEXCOORD1;//Phong-Shading
            };

            sampler2D _MainTex;
            sampler2D _Matcap;
            float _Range;
            float _Blend;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                o.uv = v.uv;
                //o.normal = float4(v.normal, 1);//LocalSpace Normal
                //o.normal = float4(mul((float3x3)_Object2World, v.normal), 1);//WorldSpace Normal
                o.normal = mul(UNITY_MATRIX_IT_MV, float4(v.normal, 1));//ProjectionSpace Normal
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float2 normalProj = normalize(i.normal.xyz) * 0.5 + 0.5;
                float4 matcap = tex2D(_Matcap, normalProj * _Range);
                float4 main = tex2D(_MainTex, i.uv);
                float4 col = lerp(main, matcap, _Blend);
                return col;
            }
            ENDCG
        }
    }
}

さいごに

まだまだ色々カスタマイズしてみたいと思います。