いんでぃーづ

ゲームいろいろ、いろいろ自由

Unityシェーダー:3Dモデルをペッタンコにするシェーダを組む

ディ◯ニーアニメとかでコミカルさを演出するときに、キャラクターをぺったんこにする演出とかありますね。
ゲームだとクローッシー・ロードとかでやってます。

Unityでやるなら transform のScaleをいじればいけるのですが、今回はシェーダを組んでやってみます。

もちろんスクリプトからツブレ加減の操作可能です。

f:id:sugar_affordance:20180530162331g:plain

1. カスタムシェーダーアセットを作成する

プロジェクトビューを右クリックして Create > Shader を選択していくと、カスタム用のシェーダーを作れます。
今回は Standard Shader を使ってみます。シェーダの名前は Press としました。

f:id:sugar_affordance:20180530162349p:plain

2. シェーダーのソースをいじる

いきなりですがソースをいじりはじめちゃいましょう。

プロパティにツブレ具合を指定する値を加える

Press という名前のプロパティを Properties 領域に追加します。

    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0

        // ここ
        _Press ("Press", Range(0,1)) = 0.5
    }

これによってマテリアルを通してツブレを指定することが可能になります。

プロパティをシェーダプログラムから参照できるようにする

変数のリストに __Press を追加します。

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;

        half _Press;    // ここ

頂点シェーダ からツブレ割合を取得できます。

頂点シェーダを定義する

頂点シェーダは主に、3Dモデルの 各頂点の描画位置 をごにょごにょできるシェーダ関数です。

Standard Shader にはデフォルトでは定義されていないので、新しく追加します。

    void vert(inout appdata_full v, out Input o )
    {
        UNITY_INITIALIZE_OUTPUT(Input, o);
        v.vertex.xyz = float3(v.vertex.x, v.vertex.y * _Press, v.vertex.z);            
    }

頂点の y 位置に 0.0 ~ 1.0 をかけることで、y方向に対してツブしています。

頂点シェーダを登録する

定義した頂点シェーダは登録しなければ動きません。

ソースの上のほうに #pragma の定義があるので、そこに新規作成した vert 関数を登録します。

// pragmaの一番最後に追加
#pragma surface surf Standard addshadow vertex:vert

また、fullforwardshadows を addshadow で置換しています。これにより、他のオブジェクトからの影を正常にモデルに描画することができるようになります。

これでシェーダは完成です!

ソース全体は以下。

Shader "Custom/Press" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0

        _Press ("Press", Range(0,1)) = 0.5
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard addshadow vertex:vert

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;
        half _Press;

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)
        // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)

        void vert(inout appdata_full v, out Input o )
        {
            UNITY_INITIALIZE_OUTPUT(Input, o);
            v.vertex.xyz = float3(v.vertex.x, v.vertex.y * _Press, v.vertex.z);            
        }

        void surf (Input IN, inout SurfaceOutputStandard o) {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

3. マテリアルを作成する

プロジェクトビューで Create > Material と選択してマテリアルを作成します。

マテリアルをインスペクタで見て、シェーダに今回作成したシェーダを設定しましょう。
するとプロパティに Press が追加されているはずです。

f:id:sugar_affordance:20180530162403p:plain

あとは対象3Dモデルのテクスチャを設定し、モデルに適用すれば完成です。

4. スクリプトからつぶれを設定する

MeshRenderer の material を参照して値を設定します。SetFloat で作成したプロパティの名前を引数に指定。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ShaderTestScript : MonoBehaviour {

    [SerializeField]
    MeshRenderer TargetMesh;

    void Start () {
        TargetMesh.material.SetFloat("_Press", 1);
    }
    void Update () {
        TargetMesh.material.SetFloat("_Press", Time.realtimeSinceStartup % 1);      
    }
}

参考

頂点シェーダーとフラグメントシェーダーの例 - Unity マニュアル

ビルトインシェーダーヘルパー機能 - Unity マニュアル

サーフェスシェーダーの記述 - Unity マニュアル

Unity - Manual: Predefined Shader preprocessor macros

サーフェスシェーダーの記述 - Unity マニュアル

ShaderLab: Properties - Unity マニュアル

Cg/HLSL でシェーダープロパティーを参照する - Unity マニュアル

ビルトインのシェーダー変数 - Unity マニュアル


“Unity” and Unity logos are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere, and are used under license.


免責事項

当サイトの広告バナー、リンクによって提供される情報、サービス内容について、当サイトは一切の責任を負いません。

また、当サイトの情報を元にユーザ様が不利益を被った場合にも、当サイトは一切の責任を負いません。

すべて自己責任でお願いします。