simplestarの技術ブログ

目的を書いて、思想と試行、結果と考察、そして具体的な手段を記録します。

Unity:頂点カラーマテリアルの作り方

先週の予告通り、Unity で頂点カラーマテリアルを作る方法を習得しましたので、私のメンタルモデルを記録・共有します。

f:id:simplestar_tech:20180922204437j:plain
Unity のマテリアルは元となるシェーダーのインスタンスと捉えることができ、特殊なマテリアルが欲しい場合はシェーダーをカスタマイズすることになります。

作り方は現在2通り提供されており
1.直感的な方法で Shader Graph を使ってシェーダーを作成する方法
2.シェーダーコードの書式を理解して書き換える方法
があります。

順番に具体的な手順を追ってみましょう。

■Shader Graph

Shader Graph の導入と使い方について、こちらの記事を参考に実践してみてください。
nn-hokuson.hatenablog.com

そのあとは覚えた手順に従って
Create > Shader > Unlit Graph
を選択し、作成された Shader ファイルから
Shader Graph のエディタを開いて、ノード作成より
Input > Geometry > Vertex Color
を選び out を Unlit Master ノードの color に接続します。
Save Asset して、その Shader から Create > Material を行えば頂点カラーマテリアルが完成します。

f:id:simplestar_tech:20180922205139j:plain

ちょ… Shader Graph やばくない?
何がやばいって、頂点カラーシェーダー作るのが、簡単すぎ
直感的に何でもできそうですよ、ぜひ皆さんも触ってみてください。

■シェーダーコードの書き換え

Unity のシェーダーコードは Surface 形式とピクセルシェーダー形式の二通りの記述形式があります。
Surface 形式は Unity 独自のピクセル値のライティングお任せ書式です。
ピクセルシェーダー形式は最後のピクセル値の決定まで操作できる書式です。

頂点カラーは頂点情報のみに頼るので Surface 形式を使うのが楽に書けます。
ということで Surface 形式でテンプレートを作り、そこに手を入れていきます。

Craete > Shader > Standard Surface Shader
を選択して Shader ファイルを作成します。
これを開くと Visual Studioテキストエディターになります。
その時の何もしていないテンプレコードは次の通り

Shader "Custom/VertexColor"
{
    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
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows

        // 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;

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

ここから頂点カラーシェーダに書き換えますが、参考にする記事はこちら
nn-hokuson.hatenablog.com

上記のシェーダーコードを頂点カラー用に書き換えた結果がこちら

Shader "Custom/VertexColor"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

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

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

        struct Input
        {
            float4 vertColor;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;

        // 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);
            o.vertColor = v.color;
        }

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            o.Albedo = IN.vertColor.rgb;
            o.Alpha = IN.vertColor.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

注目するのは Input の構造体に color を入れたのと、 vert 関数を作り vert に vert を利用すると pragma で宣言しているところ
この時 surf 内で頂点カラーを出力するように書き換えています。
これで頂点カラーを利用するようになるはずです。

結果がこちら
f:id:simplestar_tech:20180922212309j:plain

Shader Graph で作った Unlit と異なり、Surface によりライティングが施されていることが確認できます。

■おまけ Scriptable Render Pipeline 対応

ライティングが Standard だと Light Weight Scriptable Render Pipeline で正常に描画されません。
ライティングを SRP 対応に書き換えるとうまくいきます。

ここを

        #pragma surface surf Standard fullforwardshadows vertex:vert

        #pragma surface surf Lambert vertex:vert

こうすることで LWRP で正常に描画されるようになりました。

■参考

ちょっと頂点カラーより難しいですが、皆さんは次の水シェーダーのコードを読めますでしょうか?
こういう動きのあるシェーダーも書けるようになりたいですね。
baba-s.hatenablog.com

Unity:頂点生成三角柱のインスタンスの統合による高速化

前回 15 x 15 x 15 の計3375個の三角柱を生成したとき、その時のインスタンス作成処理に 0.3秒ほど要していました。
ここから先、画面を埋め尽くすほどのブロックの数を表示しようというのに、もう既に諦めなければならないほど低速です。
この処理を高速化するため、インスタンスの数を減らして、メッシュを統合する方法をとってみます。

これも昔行った手法です。
simplestar-tech.hatenablog.com

メッシュ作成の結果、見た目は同じでも
f:id:simplestar_tech:20180922175632j:plain

インスタンス生成時の負荷は 11.84 ms (頂点バッファ確保と計算が主な仕事)
f:id:simplestar_tech:20180922175703j:plain

メッシュ作成時の負荷は 4.72 ms (一気に 5000 頂点分の設定のため多少時間を要する)
f:id:simplestar_tech:20180922175918j:plain

でした。
もともとの三角柱ごとにインスタンスを作る方法は、インスタンス生成時に 267.92 ms 要していました。
f:id:simplestar_tech:20180922180132j:plain

もともとの三角柱ごとにインスタンスを作る方法は、メッシュ作成時に 58.73 ms 要していました。
f:id:simplestar_tech:20180922180330j:plain

もともとの三角柱ごとにインスタンスを作る方法は、このチャンク(16 x 16 x 16 の三角柱の塊り)一つ作る場合でもフレームレートが維持できませんでしたが
メッシュを結合してインスタンスを統合することで、メッシュ作成処理の負荷が抑えられ、フレームレートをなんとか維持できる処理に変わりました。

以降の記事では、この 16 x 16 x 16 のチャンクをしばらく利用します。

具体的なコードは以下、よろしければ参考にしてください。(主に自分用)

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

[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(MeshFilter))]
public class Prism : MonoBehaviour
{
    [SerializeField]
    private Material _mat;

    const float root3 = 1.732051f;

    private Mesh _mesh;
    private MeshFilter _filter;

    const int SIDE = 16;
    private Vector3[] _vertices;
    private Vector3[] _normals;
    private Vector2[] _uvs;
    private int[] _triangles;

    // Use this for initialization
    void Start()
    {
        _vertices = new Vector3[SIDE * SIDE * (SIDE / 2) * vertIndices.Length];
        _normals = new Vector3[_vertices.Length];
        _uvs = new Vector2[_vertices.Length];
        _triangles = new int[_vertices.Length];
        for (int x = 0; x < SIDE; x++)
        {
            for (int y = 0; y < SIDE; y++)
            {
                for (int z = 0; z < SIDE; z += 2)
                {
                    for (int i = 0; i < vertIndices.Length; i++)
                    {
                        int vertIndex = (x * SIDE * (SIDE / 2) + y * (SIDE / 2) + z / 2) * vertIndices.Length + i;
                        ref Vector3 vert = ref _vertices[vertIndex];
                        ref Vector3 normal = ref _normals[vertIndex];
                        ref Vector2 uv = ref _uvs[vertIndex];
                        vert = positions[vertIndices[i]];
                        vert.x += x * 2;
                        vert.y += y;
                        vert.z += z * root3;

                        normal = normals[i];
                        uv = uvs[i];
                        _triangles[vertIndex] = vertIndex;
                    }
                }
            }
        }
        _mesh = new Mesh();
        
        _filter = GetComponent<MeshFilter>();

        var renderer = GetComponent<MeshRenderer>();
        renderer.material = _mat;
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            CreateMesh();
        }
    }
    private void CreateMesh()
    {
        _mesh.vertices = _vertices;
        _mesh.triangles = _triangles;
        _mesh.uv = _uvs;
        _mesh.normals = _normals;
        _filter.sharedMesh = _mesh;
    }

    static Vector3[] positions = new Vector3[] {
        // top
        new Vector3 (0f, 1f, 0f),
        new Vector3 (1f, 1f, -root3),
        new Vector3 (-1f, 1f, -root3),
        // bottom
        new Vector3 (0f, 0f, 0f),
        new Vector3 (-1f, 0f, -root3),
        new Vector3 (1f, 0f, -root3),
    };
    static int[] vertIndices = new int[]
    {
        0, 1, 2,
        3, 4, 5,
        0, 2, 4,
        4, 3, 0,
        2, 1, 5,
        5, 4, 2,
        1, 0, 3,
        3, 5, 1
    };
    static Vector3[] vertices = new Vector3[vertIndices.Length];
    static Vector2[] uvs = new Vector2[]
    {
        // top
        new Vector2 (.25f, 0.5f),
        new Vector2 (0.5f, 0.0f),
        new Vector2 (0.0f, 0.0f),

        // bottom
        new Vector2 (.75f, 0.0f),
        new Vector2 (0.5f, 0.5f),
        new Vector2 (1.0f, 0.5f),

        // side R
        new Vector2 (0.0f, 1.0f),
        new Vector2 (.33f, 1.0f),
        new Vector2 (.33f, 0.5f),

        new Vector2 (.33f, 0.5f),
        new Vector2 (0.0f, 0.5f),
        new Vector2 (0.0f, 1.0f),

        // side G
        new Vector2 (.33f, 1.0f),
        new Vector2 (.66f, 1.0f),
        new Vector2 (.66f, 0.5f),

        new Vector2 (.66f, 0.5f),
        new Vector2 (.33f, 0.5f),
        new Vector2 (.33f, 1.0f),
        
        // side B
        new Vector2 (.66f, 1.0f),
        new Vector2 (1.0f, 1.0f),
        new Vector2 (1.0f, 0.5f),

        new Vector2 (1.0f, 0.5f),
        new Vector2 (.66f, 0.5f),
        new Vector2 (.66f, 1.0f)
    };
    static Vector3[] normals = new Vector3[]
    {
        // top
        new Vector3 (0f, 1f, 0f),
        new Vector3 (0f, 1f, 0f),
        new Vector3 (0f, 1f, 0f),

        // bottom
        new Vector3 (0f,-1f, 0f),
        new Vector3 (0f,-1f, 0f),
        new Vector3 (0f,-1f, 0f),

        // side R
        new Vector3 (-0.8660254f, 0f, 0.5f),
        new Vector3 (-0.8660254f, 0f, 0.5f),
        new Vector3 (-0.8660254f, 0f, 0.5f),

        new Vector3 (-0.8660254f, 0f, 0.5f),
        new Vector3 (-0.8660254f, 0f, 0.5f),
        new Vector3 (-0.8660254f, 0f, 0.5f),

        // side G
        new Vector3 (0f, 0f,-1f),
        new Vector3 (0f, 0f,-1f),
        new Vector3 (0f, 0f,-1f),

        new Vector3 (0f, 0f,-1f),
        new Vector3 (0f, 0f,-1f),
        new Vector3 (0f, 0f,-1f),
        
        // side B
        new Vector3 (0.8660254f, 0f, 0.5f),
        new Vector3 (0.8660254f, 0f, 0.5f),
        new Vector3 (0.8660254f, 0f, 0.5f),

        new Vector3 (0.8660254f, 0f, 0.5f),
        new Vector3 (0.8660254f, 0f, 0.5f),
        new Vector3 (0.8660254f, 0f, 0.5f),
    };
}

Unity:頂点法線の計算と事前設定の速度の違い

2週間前に、Unityのパフォーマンス測定の差先端について調べた記事を書きました。
simplestar-tech.hatenablog.com

ここで調べた手法で、頂点生成系のプログラムのいくつかの負荷試験を行ってみます。
タイトルの通り、頂点法線の計算をランタイムで行うのと、事前に設定するのとでどれくらいパフォーマンスが変わるのか見ていきたいとおもいます。

負荷が計測できる程度に前回の頂点生成で作る三角柱を並べます。(15 x 15 x 15 個、計3375)
f:id:simplestar_tech:20180922081047j:plain

処理はインスタンス生成とメッシュ作成に分けましたが、それぞれで高負荷が発生しました。
まずはインスタンス生成時のタイムラインを確認してみました。
f:id:simplestar_tech:20180922081503j:plain
CreateInstance という Unity 側に予約されている部分が大部分を占め、続いて mesh オブジェクト作成と GCAlloc にも犯罪的な処理時間を要していました。

メッシュ生成のタイムラインを確認すると次の通り
f:id:simplestar_tech:20180922081616j:plain
mesh に頂点を設定、インデックスを設定、UVを設定、メッシュを割り当てといったことに、ちりも積もれば山となるという感じで、短い計算が重なって全体的な処理時間が大きくなるという結果でした。
頂点法線の計算はほとんど時間を要していないことがわかります。

こういうことがわかるのは本当、プロファイラの良いところだと思います。

タイトルの通り、RecalculateNormals を使うか、そのまま設定するかでどのように変化するかを示して終わりたいと思います。

まずは RecalculateNormals を使う場合 PlayerLoop は total 72.97 ms 要していました。
ここで頂点法線を事前に決めて与えるように修正しました。

set normals に 9ms ほど要していました。PlayerLoop は total 72.60 ms 要しています。
ほとんど変化していないですね。

次の記事で、インスタンス化する数を減らす工夫をしてみます。

Unity:頂点生成による三角柱

頂点カラーシェーダーのテスト用にメッシュオブジェクトを用意します。

ちょうど1年前に六角柱を作りましたが、やはり最も頂点数の少ない三角柱から作ろうと思います。
simplestar-tech.hatenablog.com

もう既に書式は習っているので、知っていることを利用してコードに起こしてみます。
詳しい解説が欲しい方はこちらが参考になりますよ。
qiita.com


カタカタ…

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

[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(MeshFilter))]
public class Prism : MonoBehaviour
{

    [SerializeField]
    private Material _mat;

    const float root3 = 1.732051f;

    // Use this for initialization
    void Start()
    {
        var mesh = new Mesh();

        Vector3[] positions = new Vector3[] {
            // top
            new Vector3 (0f, 1f, 0f),
            new Vector3 (1f, 1f, -root3),
            new Vector3 (-1f, 1f, -root3),
            // bottom
            new Vector3 (0f, 0f, 0f),
            new Vector3 (-1f, 0f, -root3),
            new Vector3 (1f, 0f, -root3),
        };
        int[] vertIndices = new int[]
        {
            0, 1, 2,
            3, 4, 5,
            0, 2, 4,
            4, 3, 0,
            2, 1, 5,
            5, 4, 2,
            1, 0, 3,
            3, 5, 1
        };
        Vector3[] vertices = new Vector3[vertIndices.Length];
        for (int i = 0; i < vertIndices.Length; i++)
        {
            vertices[i] = positions[vertIndices[i]];
        }
        mesh.vertices = vertices;

        int[] triangles = new int[mesh.vertices.Length];
        for (int i = 0; i < mesh.vertices.Length; i++)
        {
            triangles[i] = i;
        }
        mesh.triangles = triangles;

        Vector2[] uvSources = new Vector2[]
        {
            // 天
            new Vector2 (.25f, 0.5f),
            new Vector2 (0.5f, 0.0f),
            new Vector2 (0.0f, 0.0f),

            // 底
            new Vector2 (.75f, 0.0f),
            new Vector2 (0.5f, 0.5f),
            new Vector2 (1.0f, 0.5f),

            // side R
            new Vector2 (0.0f, 1.0f),
            new Vector2 (.33f, 1.0f),
            new Vector2 (.33f, 0.5f),

            new Vector2 (.33f, 0.5f),
            new Vector2 (0.0f, 0.5f),
            new Vector2 (0.0f, 1.0f),

            // side G
            new Vector2 (.33f, 1.0f),
            new Vector2 (.66f, 1.0f),
            new Vector2 (.66f, 0.5f),

            new Vector2 (.66f, 0.5f),
            new Vector2 (.33f, 0.5f),
            new Vector2 (.33f, 1.0f),
            
            // side B
            new Vector2 (.66f, 1.0f),
            new Vector2 (1.0f, 1.0f),
            new Vector2 (1.0f, 0.5f),

            new Vector2 (1.0f, 0.5f),
            new Vector2 (.66f, 0.5f),
            new Vector2 (.66f, 1.0f)
        };
        Vector2[] uvs = new Vector2[uvSources.Length];
        for (int i = 0; i < uvSources.Length; i++)
        {
            uvs[i] = uvSources[i];
        }
        mesh.uv = uvs;

        mesh.RecalculateNormals();

        var filter = GetComponent<MeshFilter>();
        filter.sharedMesh = mesh;

        var renderer = GetComponent<MeshRenderer>();
        renderer.material = _mat;

    }

    // Update is called once per frame
    void Update()
    {

    }
}

ジャン!
f:id:simplestar_tech:20180920090443j:plain

思いついていることがいくつかあるので、次の記事で試していきます。

Unity:レールに沿ったカメラを表現するCinemachine

前回、初めて Cinemachine を触ってみましたが、サクッと三人称視点のフリーカメラの動きを実現できました。
simplestar-tech.hatenablog.com

Cinemachine の見どころは多く、まだまだその機能を紹介しきれていません。
カメラワークのイメージと、それを Cinemachine で実装する手順の間のメンタルモデルの定着を目指して
今回はレールに沿って動くカメラの動きを実現してみたいと思います。

■Cinemachine の導入
Unity 2018.3 beta を使っていますが、それでもまだ Cinemachine はプレビュー版のパッケージの様子
まずは Windos > Package Manager とメニューを選んで、最初は All packages でも Cinemachine は見つけられませんが
Show preview packages を Advanced メニューから選んでインストールします。

インストールした Cinemachine のバージョンは 2.2.7 でした。

■Main Camera に Brain を追加してカメラの主導権を Cinemachine に譲渡
ゲームの絵を作り出すのは、変わらず Main Camera オブジェクトです。
その Main Camera オブジェクトを制御するわけですから、そこに Cinemachine Brain コンポーネントをアタッチします。
これで Main Camera を手動で編集できなくなりました。(強制的に Cinemachine が制御します)

■カメラの動きを決めるレールを敷く
Cinemachine Smooth Path というスクリプトコンポーネントが使えるようになっていますので、空のオブジェクトに設定します。
f:id:simplestar_tech:20180918084124j:plain

Waypoints の + ボタンをポチポチ押せば、こうしたレールが表示されるので適当な軌跡を描きます。

Cinemachine Smooth Path は他にも使い道ありそうだなぁって思ったのが、次のインタフェースを提供してくれる点です。
具体的には 0~1の値を渡して軌跡上の点の位置を返してくれるというもの

    [SerializeField]
    private Cinemachine.CinemachineSmoothPath path;

    void Update()
    {
        transform.position = path.EvaluatePositionAtUnit(0.5f, Cinemachine.CinemachinePathBase.PositionUnits.Normalized);
    }

手付けのアニメーションには必須だと思う機能なのですよ!
このオブジェクトはここでこういう風に動いて…など
レールだけにちょっと脱線しました。

■カメラの動きを強制する Virtual Camera に Path を指定する
今回は Path を指定してその Path に沿って Track & Dolly というレールに沿った動きを実現するので
インスペクターにある Body と Aim をいじります。
Body のドロップダウンメニューから Track & Dolly を選択し Path に先ほど作成した Cinemachine Smooth Path をアタッチしたオブジェクトを設定します。
Auto Dolly の Enabled にチェックを入れれば、レールに沿ってカメラが対象を追従します。(LookAt、Follow に対象となるオブジェクトを設定する必要あり)
さらに Aim にて Composer を選び、適当に Dead Zone などを設定して、映画監督のように Shot を指示します。

シーン内にこの Virtual Camera があれば、カメラは勝手にレールの上を Track & Dolly しながら撮影してくれるはずです。
次のように

f:id:simplestar_tech:20180918085640g:plain

■まとめ
レールに沿ったカメラを表現するCinemachine の機能を実践しました。
ゲームのデモムービーや、ゲーム内でのカットシーン(ゲームオブジェクトを使ったリアルタイムレンダリングムービー)の作成で活躍する技法です。
Unity のデザイナー向け機能ですが、プログラマーも覚えて損はないと思いますよ!

■参考資料
ここで紹介する 4分の動画を見れば、この記事で書いた内容がすべて映像で学習できます。最高の資料でした、ありがとうございました。
unity3d.com


■次回予告
次は頂点色を設定するメッシュ作成と頂点カラーを表示するシェーダーについて書いていきたいです。

Unity:CinemachineでTPSカメラ制御してみる

Chinemachine (シネマシーン)とは、映画のカメラワークを強力にサポートするツールセットの名称です。
映画監督ばりに、絵コンテ通りにカメラを動かしたい、そんなときに役立つ公式アセットです。(あるときから公式になった人気ツール?なのかな)

とりあえず映像に関するツールセットなので、映像でどういった機能を私たちに提供してくれるのか確認してみましょう。
unity3d.com

動画から得られた知見:
1.Package Manager を開いて Preview 版の Chinemachine をインストールする
2.知的な Main Camera にするため Main Camera オブジェクトに Chinemachine Brain コンポーネントを追加
3.Timeline Editor に2.のコンポーネント追加済みのオブジェクトをドラッグ&ドロップして、トラックの種類に Cinemachine を選択して Cinemachine Track を追加
4.Timeline の編集とは、オブジェクトに紐づくトラックを並列に追加し、そのうちの一つのトラックの中にクリップを順番に配置することだった
5.トラックの中に Cinemachine Shot Clip を作成、その Shot に関するカメラ位置姿勢と対象の追従設定などを記入する、このShotに関する設定を「 Virtual Camera 」と呼ぶことにしている
6.カメラ画像上の領域にて、対象を領域内にとらえている間はカメラは死んでいるかのように動かないとするカメラワークがある、この時のカメラの画像上の領域を「Dead Zone」と呼ぶ
7.Dead Zone の周りの領域を「Soft Zone」その外側は特に呼ぶ名前はない
8.トラックにはいくつでも Shot Clip を追加できるし、Clip 間でカメラワークを補間したり、ワープしたりをGUI操作で簡単に操作できます。

わずか 5 分で Timeline の編集の概念と、Cinemachine の基本機能と Timeline でのカメラワークの操作まで完璧に示していました。
とても良い動画だと思いました。

ここまでの基本的な知見を持ったうえで次のページを読んでみました。
gametukurikata.com

得られた知見:
1.Main Camera オブジェクトに Chinemachine Brain コンポーネントを追加、シーン内に「Virtual Camera」を追加すると、そのVirtual Camera の設定に従って Main Camera の動きが強制される
2.Virtual Camera の設定に Follow と Look At があるので、ここに移動キャラクターを設定すれば TPS (三人称視点)のカメラの動きに近づく(キャラクターを追いかけるカメラの動き)
3.Free Look タイプの Virtual Camera も用意されている、次の図に示すような三つの円で設定される楕円体の表面に沿って対象を中心に移動するフリーカメラの操作を提供してくれる
f:id:simplestar_tech:20180917164001j:plain
まさに TPS (三人称視点)のカメラの動きじゃないですか!これ

サクッとキャラクターを用意
f:id:simplestar_tech:20180917172029j:plain

Free Look タイプの Virutal Camera をシーンに配置してゲームを再生してみました。

f:id:simplestar_tech:20180917171841g:plain

これはTPSのフリーカメラですね!
歩かせてみます。

f:id:simplestar_tech:20180917172517g:plain

■まとめ
Unity 2018.3.0 beta で動かしました。
Cinemachine のプレビュー版をさわったのですが、素晴らしいカメラ操作を提供してくれるツールセットですね。
これを活用して三人称視点のゲーム作ってみたいと思いました。

ほかにもあらかじめ設定したレールの上を動くカメラなども学んだので、次に紹介したいと思います。

■追記
Free Look タイプのインスペクタービューの具体的な内容を記録します。
f:id:simplestar_tech:20181011221927j:plain

次の記事の予定

Unity に頂点カラーのマテリアルは用意されていません。
そこで、シェーダを多少はいじれるようにしておきたいと考えました。
そのほかUnityのシェーダーを編集できる UI の確認
タイムラインもそろそろ触れるようになっておきたいし
Cinemachine ってのも勉強したいです。

つまりは Unity 2017 で勉強してこなかったことを勉強していこうと思いました。

勉強の資料
nn-hokuson.hatenablog.com
こちらは読了、実践も済んでます
Unityのシェーダーを編集できる UI の Shader Graph には少しだけ慣れました。

こっちはシェーダーの基本を学ぶ資料です。ボリューム満点なので、ゆっくり読み進めます
7日間でマスターするUnityシェーダ入門 - おもちゃラボ

後で別記事でまとめますが、簡単にメモを残します。

なるほど Surface シェーダーという Unity 独特のシェーダーを使う場合に出てくる予約語はここから確認すればいいのか
docs.unity3d.com

Unity のシェーダーの設計思想が見えてきた
ライティングのシェーダーを書くのが難しく、また基本的に最先端の技術を取り入れて同じコードになるため
そこはラッピングして
ピクセルシェーダーを技術者に書かせるかわりに、ライティングの一歩手前のサーフェースシェーダーを用意し
頂点とサーフェースシェーダーだけを技術者は書けば良いように設計している。

Unity やさしい

シェーダーのプロパティ追加書式

docs.unity3d.com

ところで _BaseColor を宣言なく利用できていたけど、どこかで予約されていたりするのかな?
公式ドキュメントに一覧が書いてあるなら知りたいです。

そろそろ別記事書きます。