Unity Days ときどき blender

Unity や VR/AR に関すること。たまにblender。

【Unity】TrailRendererで線を描く

TrailRendererを使えば、オブジェクトが動いた時の軌跡を描くことができます。 今回は関数で描いた線を表示するコードです。 (スクリプトリファレンスに載っているサンプルを少し変えたものです。)

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

public class TrailSample1: MonoBehaviour {

    bool isMenu = true;
    public float time = 1.0f;
    public float width = 0.1f;
    public float r = 0;
    public float g = 0;
    public float b = 0;

    Color color = Color.black;
    TrailRenderer tr;

    // Use this for initialization
    void Start () {
        tr = GetComponent<TrailRenderer>();
        tr.material = new Material(Shader.Find("Sprites/Default"));
    }

    // Update is called once per frame
    void Update () {
        tr.time = time;
        tr.startWidth = width;
        tr.endWidth = width;
        color = new Color(r, g, b);
        tr.startColor = color;
        tr.endColor = color;
        tr.transform.position = new Vector3(Mathf.Sin(Time.time * 1.51f) * 7.0f, Mathf.Cos(Time.time * 1.27f) * 4.0f, 0f);
    }

    void OnGUI()
    {
        if(GUI.Button(new Rect(25, 5, 50, 15), "Menu"))
        {
            isMenu = !(isMenu);
        }
        if(isMenu == true)
        {
            GUI.Label(new Rect(25, 20, 200, 30), "Time");
            time = GUI.HorizontalSlider(new Rect(125, 25, 200, 30), time, 0.1f, 100.0f);
            GUI.Label(new Rect(25, 50, 200, 60), "Width");
            width = GUI.HorizontalSlider(new Rect(125, 55, 200, 60), width, 0.01f, 1.0f);
            GUI.Label(new Rect(25, 80, 200, 90), "Color");
            r = GUI.HorizontalSlider(new Rect(125, 90, 200, 60), r, 0f, 1.0f);
            g = GUI.HorizontalSlider(new Rect(125, 120, 200, 60), g, 0f, 1.0f);
            b = GUI.HorizontalSlider(new Rect(125, 150, 200, 60), b, 0f, 1.0f);
        }
    }
}
参考

Unity - スクリプトリファレンス: TrailRenderer

Unityでカメラワークの実装

4つのカメラワークの実装

アニメのカメラワークや撮影処理をまとめた記事を読んで、カメラの動作をUnityで再現してみようと思い立ちました。今回、実装したカメラワークは「パン」「フォロー」「じわパン」「ポン寄り」の4つです。用語を深く理解していないので動作の解釈が間違っている可能性があります。利用するときはお気を付けください。あとフォローの挙動が変です。

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

public class CameraWork : MonoBehaviour {

    public GameObject target;
    public enum CameraType
    {
        fix,
        pan,
        follow,
        pan_slow,
        pon
    }
    [SerializeField] CameraType camType = CameraType.pan;
    Vector3 distance;
    float rotSpeed = 0.1f;
    float timePass;

    // Use this for initialization
    void Start () {
        if (camType.ToString() == "follow") distance = this.transform.position - target.transform.position;
    }
    
    // Update is called once per frame
    void Update () {
        switch (camType.ToString())
        {
            case "fix":
                break;
            case "pan":
                pan(target);
                break;
            case "follow":
                follow(target);
                break;
            case "pan_slow":
                pan_slow(target);
                break;
            case "pon":
                pon(target);
                break;
        }
    }

    void pan(GameObject target)
    {
        this.transform.LookAt(target.transform);
    }

    void pan_slow(GameObject target)
    {
        float step = rotSpeed * Time.deltaTime;
        Vector3 vec = target.transform.position - this.transform.position;
        Quaternion rot = Quaternion.LookRotation(vec);
        this.transform.rotation = Quaternion.Slerp(transform.rotation, rot, step);
    }

    void follow(GameObject target)
    {
        this.transform.LookAt(target.transform);
        this.transform.position = target.transform.position + distance;
    }

    void pon(GameObject target)
    {
        timePass += Time.deltaTime;
        if(timePass >= 1)
        {
            Camera cam;
            cam = this.gameObject.transform.GetComponent<Camera>();
            float view = cam.fieldOfView - 15;
            cam.fieldOfView = Mathf.Clamp(value: view, min: 0.1f, max: 90f);
            if (view < 0) { cam.fieldOfView = 90; }
            timePass = 0;
        }
        this.transform.LookAt(target.transform);
    }
}
参考記事

usapen3.hatenablog.com

tsubakit1.hateblo.jp

Oculus Riftでのカメラ位置の初期化

目的

UnityのVirtual Reality Supportedを利用すると、Main Cameraが自動的にセンサーで取得した位置に移動します。 このままでは扱いにくいので、指定の位置にカメラを配置するスクリプトを作ります。 Unityの標準で使えるInputTrackingを使って実装することで、特別なアセットを使うことなく、他のHMDでも機能するように実装したつもりです(Riftしか持っていないのでViveとかで動作確認できてません。)

手順
  1. Player Settings → Virtual Reality Supported にチェックを入れる
  2. Empty GameObject を作り、VRCameraと名づける
  3. Main CameraをVRCameraの子オブジェクトにして、Main CameraのPositionを(0,0,0)にする
  4. vrcameraスクリプト(以下参照)を作ったオブジェクトに追加する

これでMain Cameraがvrcameraオブジェクトの位置で初期化できる。

f:id:Dmiyamo3:20170709112834p:plain

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

public class vrcamera : MonoBehaviour {
    void Start () {
        Vector3 pos = InputTracking.GetLocalPosition(VRNode.CenterEye);
        this.transform.position -= pos;
    }
}

Shader 勉強メモ 「サンプルを理解する2」

前々回に予告していたサンプルをやっていきます。

Unity - マニュアル: Vertex and fragment shader examples

法線情報を使ったシェーダーは前にやったので、スカイボックスを反射させるシェーダーから。 解説は追記する予定。

f:id:Dmiyamo3:20170311142914p:plain

Shader "Unlit/SkyReflection"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            // vertで値を入力し、fragで利用する値を定義
            struct v2f {
                half3 worldRefl : TEXCOORD0;
                float4 pos : SV_POSITION;
            };

            v2f vert (float4 vertex : POSITION, float3 normal : NORMAL)
            {
                v2f o;
                // オブジェクト空間の頂点位置をクリップ空間に変換
                o.pos = UnityObjectToClipPos(vertex);
                // 定義済値、オブジェクト空間→ワールド空間へ変換する行列と
                // オブジェクト空間の頂点位置の行列積
                float3 worldPos = mul(_Object2World, vertex).xyz;
                // compute world space view direction
                float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
                // world space normal
                float3 worldNormal = UnityObjectToWorldNormal(normal);
                // world space reflection vector
                o.worldRefl = reflect(-worldViewDir, worldNormal);
                return o;
            }
        
            fixed4 frag (v2f i) : SV_Target
            {
                // sample the default reflection cubemap, using the reflection vector
                half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, i.worldRefl);
                // decode cubemap data into actual color
                half3 skyColor = DecodeHDR (skyData, unity_SpecCube0_HDR);
                // output it!
                fixed4 c = 0;
                c.rgb = skyColor;
                return c;
            }
            ENDCG
        }
    }
}

おまけ

ビルドインシェーダーの関数はドキュメントに書かれてないものがあるので、ソースコードを調べてみました。 Unity - Download Archive:ビルドインシェーダーのソースコードがダウンロードできます。 例えば、UnityObjectToWorldNormal()のコード。こんな感じになってます。

// Transforms normal from object to world space
inline float3 UnityObjectToWorldNormal( in float3 norm )
{
#ifdef UNITY_ASSUME_UNIFORM_SCALING
    return UnityObjectToWorldDir(norm);
#else
    // mul(IT_M, norm) => mul(norm, I_M) => {dot(norm, I_M.col0), dot(norm, I_M.col1), dot(norm, I_M.col2)}
    return normalize(mul(norm, (float3x3)unity_WorldToObject));
#endif
}

Shader関連の気になる記事と参考記事

Shader Debugger [ Unity ] と[ Visual Studio ] で [ DirectX 11 ] [Microsoft HLSL Shader Debugger ] [シェーダーのデバッガー] を使ってみる。00200 | Whaison JUGEM! StudyNoteBook .

空間とプラットフォームの狭間で - Unityの座標変換にまつわるお話 -

内蔵(ビルドイン)シェーダのソースコードの配布ページ - 強火で進め

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

Shader 勉強メモ 「定義済値と組み込み関数」

前回まででシェーダーの扱いがなんとなくわかってきたので、次のサンプルをやる前に少し遊びます。 UnityのシェーダーはHLSLの組み込み関数を利用でき、Unity独自の定義済みの値も使えます。

組み込み関数 (DirectX HLSL)

[Unity] シェーダで使える定義済値 - Qiita

これらを使って、グニャーと変形させるシェーダーを作りました。前回のものに二行加えただけです。

Shader "Tutorial/DisplayNormalTransform"
{
    SubShader
    {
        Pass{

        CGPROGRAM

        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"

        struct v2f {
            float4 pos : SV_POSITION;
            fixed3 color : COLOR0;
        };

        v2f vert(appdata_base v) {
            v2f o;
            v.vertex.x *= 1 + pow(sin(_CosTime*v.vertex.y), 2);    // 追加
            v.vertex.z *= 1 + pow(sin(_CosTime*v.vertex.y), 2);    // 追加
            o.pos = UnityObjectToClipPos(v.vertex);
            o.color = v.normal * 0.5 + 0.5;
            return o;
        }

        fixed4 frag(v2f i) : SV_Target{
            return fixed4(i.color,1);
        }
        ENDCG
        }
    }
}

Shader 勉強メモ 「サンプルを理解する」

今回はUnityドキュメントのサンプルを理解していきます。

Unity - マニュアル: シェーダー: 頂点とフラグメントプログラム

最初のサンプルはメッシュの法線情報を色情報に変換するシェーダーです。

f:id:Dmiyamo3:20170308142124p:plain

Shader "Tutorial/DisplayNormal"{
    SubShader{
    Pass{

        CGPROGRAM

        // バーテックスシェーダーとフラグメントシェーダーの関数宣言
        #pragma vertex vert
        #pragma fragment frag

        // 内臓のシェーダーincludeファイル
        // UnityCG.cginc はビルトインシェーダーヘルパー機能
        // UnityCG.cginc は例えば以下のデータ構造を持つ
        // struct appdata_base:位置/法線/テクスチャ座標/頂点シェーダー入力
        // また、UnityObjectToClipPos()などが使えるようになる
        #include "UnityCG.cginc"

        // v2f:vertex to fragment
        // 頂点プログラムからフラグメントプログラムに渡される情報を定義します
        struct v2f {
            float4 pos : SV_POSITION;
            fixed3 color : COLOR0;
        };

        v2f vert(appdata_base v) {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            o.color = v.normal * 0.5 + 0.5;
            return o;
        }

        fixed4 frag(v2f i) : SV_Target{
            return fixed4(i.color,1);
        }
        ENDCG
    }
    }
}

次のサンプルですが、一行だけ変えてMain ColorとTextureの色を足し合わせるようにしました。

f:id:Dmiyamo3:20170308150058p:plain f:id:Dmiyamo3:20170308150108p:plain

Shader "Tutorial/Textured Colored" {
    Properties{
        _Color("Main Color", Color) = (1,1,1,0.5)
        _MainTex("Texture", 2D) = "white" { }
    }
    SubShader{
    Pass{

    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag

    #include "UnityCG.cginc"

    fixed4 _Color;
    sampler2D _MainTex;

    struct v2f {
        float4 pos : SV_POSITION;
        float2 uv : TEXCOORD0;
    };

    float4 _MainTex_ST;

    v2f vert(appdata_base v)
    {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);
        // UnityCG.cgincからTRANSFORM_TEXマクロを使用して
        // テクスチャスケールとオフセットが正しく適用されていることを確認
        o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
        return o;
    }

    fixed4 frag(v2f i) : SV_Target
    {
        fixed4 texcol = tex2D(_MainTex, i.uv);
        return texcol + _Color;   //変更点
    }
    ENDCG

    }
    }
}

テクスチャにチェック模様を入れれば黒い部分だけ色がつくようにできたりします。

f:id:Dmiyamo3:20170308162129p:plain f:id:Dmiyamo3:20170308162136p:plain f:id:Dmiyamo3:20170308162154p:plain

次回は、 Unity - マニュアル: Vertex and fragment shader examples のサンプルを触っていこうと思います。

参考サイト:

dragon & teapot model : http://graphics.cs.williams.edu/data/meshes.xml#7

Unity - マニュアル: シェーダーリファレンス

Unity Shader のコミュニティ

Shader 勉強メモ 「シェーダーとは?」

Unityでのシェーダーの位置づけ「シェーダーで何ができる?」

グラフィックスパイプライン:一連の描画工程

CPU上でのドローコール

 

バーテックスシェーダー

頂点数の増減・プリミティブの変更を含まない頂点属性の変更

プリミティブアセンブリ

 

テッセレーションシェーダー

プリミティブの分割粒度を変更できる

ジオメトリーシェーダー

頂点数の増減・プリミティブの変更を含む頂点属性の変更

ラスタライゼーション

 

フラグメントシェーダー

テクスチャやオブジェクトの表面情報を用いた複雑な陰影処理

ROP処理

 

フレームバッファの出力

 

※バーテックス=頂点情報、フラグメント=色情報・深度情報、プリミティブ=点と直線と三角形からなる基本図形

グラフィックスパイプライン外でのシェーダーの利用

コンピュートシェーダー

GPUでの汎用計算(GPGPU)、物理シミュレーション、画像処理/画像認識/ポストエフェクト、機械学習などの用途

Unity のコンピュートシェーダーは DirectX 11 の DirectCompute テクノロジに非常に近いものです。コンピュートシェーダーを実行できるプラットホームは以下のとおりです。

DirectX 11 グラフィックス API と Shader Model 5.0 GPU を伴う WindowsWindows ストア

・現段階で一般的に使用されている OpenGL プラットフォーム (LinuxWindows 上の OpenGL 4.3、Android 上の OpenGL ES 3.1)。Mac OS XOpenGL 4.3 に対応していないため、コンピュートシェーダーはまだ、利用することはできません。

・現段階で一般的に使用されているコンソール (Sony PS4Microsoft XboxOne)。

ー『Unity - マニュアル: コンピュートシェーダー』より引用

 Unityでのシェーダーの扱い「シェーダーをどうやって使う?」

Shader Labと呼ばれるUnity独自の形式。各種シェーディング言語で書かれたシェーダーをShader Labに埋め込むことでGPUに計算させることができる。以下のフォーマットで書かれる。

Shader “Tutorial/Test”{

              Properties{

 [UnityEditer上から編集できる変数やテクスチャの指定などの設定]

              }

              SubShader{

                            Pass{

                [各種シェーディング言語で書かれたシェーダー]

                            }

              }

              FallBack “Diffuse”

}

シェーディング言語の種類

GLSL (OpenGL Shading Language)

C言語ライクなシェーディング言語

Cg (C for Graphics)

NVIDIAが開発。2012年に開発終了。

HLSL (High Level Shading Language)

Microsoftが開発。コンピュートシェーダーも書ける。

MSL (Metal Shading Language)

Appleが開発。macOS/iOSに対応。

シェーダー実行時の処理

Shader Labの形式にCg/HLSLで記述→実行時プラットフォーム毎にGLSLに変換される。

 

シェーダー関連の気になる記事

Unity - マニュアル: グラフィックスコマンドバッファ

Unity - マニュアル: コンピュートシェーダー

Unity - マニュアル: DirectX 11 と OpenGL Core

Unity - マニュアル: 非同期テクスチャアップロード

Unity 上に自分で物理エンジンを実装したい - yuki-koyama's blog

Unityのシェーダーについて自分なりにまとめてみた - SSSSLIDE

Unityのシェーダを自分の言葉で説明してみる - 渋谷ほととぎす通信

 「塗り」を表現する技術 - SSSSLIDE

Unity備忘録: Unityのshaderまとめ(Shader Lab)