Unity Days

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

Octane for Unity のインストール

はじめに

Octane for Unity のプレビュー版がリリースされましたので、その紹介と使い方を説明します。

Octaneとは、物理ベースで高度なグラフィックを作るためのGPUレンダリングツールです。 簡単に言えば、Octane を使うことで映画のような絵を出すことができます。

今回は、とりあえずインストールして、使うところまでやってみます。

Asset Store のサンプルを利用する方法
  1. Unity 2017.1.0.f3 をインストール
  2. The Courtyard | OctaneRender for Unity Sample Sceneをダウンロード&インポート
  3. Introduction to OctaneRender for Unity - YouTubeを見ながら色々試してみる。 (NVIDIA GeForce GTX 970 で試してみたら、レンダリングの失敗が頻繁に起こりました。GPUのメモリが足りない?)

結果

f:id:Dmiyamo3:20170806013922p:plain:w300

Asset Store のサンプルを利用しない方法

サンプルのほうが頻繁に落ちたので、こちらの方法で小さなプロジェクトを試してみました。

Render a Simple Scene Using Octane for Unity - OctaneRender for Unityを見たほうがわかりやすいと思いますが、念のため、以下に手順を書いておきます。

  1. Unity 2017.1.0.f3 をインストール
  2. Octane for Unity (プレビュー版)をインストール f:id:Dmiyamo3:20170806020310p:plain
  3. Unityを起動
  4. Unityエディタ上のメニューに"Octane"があることを確認
  5. Octane -> Install
  6. Octane -> Settings -> Load Octane
  7. Octane -> Create PBR Render Target
  8. Hierarch 内に作成された PBR Render Target->PBR Render Target Component -> Renderボタンを押すと"PBR Viewport"が出てくる

結果(1枚目がデフォルト、2枚目がOctane)

f:id:Dmiyamo3:20170806011047p:plain:w300 f:id:Dmiyamo3:20170806011054p:plain:w300

参考資料

【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 のコミュニティ