2017年2月21日火曜日

シェーダーについて

Sprites-Defaultシェーダーを使用して、シェーダーの勉強をしてみた。
何となくわかった気がした。
Sprites-Defaultシェーダーはアーカーブのダウンロードの項目のビルドインシェーダーに入っています。

アーカイブリンク
https://unity3d.com/jp/get-unity/download/archive

以下 よく分からないなりにSprites-Defaultシェーダーにコメントなどをつけてみた。

Shader "Sprites/Default"
{
 // カラーやベクトルなどのプロパティもこの箇所に記述
 // スクリプトなどからアクセスできる変数群,インスペクターにも表示される
    Properties
    {
     // メインテクスチャを白に設定
     //  [属性:テクスチャUI] 変数名 ( インスペクタ場で表示される名前設定, 2Dに設定) = 初期値:白のテクスチャ
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}

        // カラー
        // プロパティ名 ( 適当な名前, カラーに設定) = 初期値:
        _Color ("Color", Color) = (1,1,1,1)

        // この属性がついた値はインスペクタ上でチェックボックス切り替えになる。
        // ピクセルをスナップするか?
        //  [属性:トグルスイッチ] 変数名 ( 名前設定, floatに設定) = 初期値:0
        [MaterialToggle] PixelSnap ("is Pixel snap", Float) = 0
    }
 
    SubShader
    {
        Tags
        {
         // 描画順、アルファブレンディングするものはすべて、Transparentであるべき
         // "Background" = 1000 "Geometry" = 2000、"AlphaTest" = 2450、"Transparent" = 3000 "Overlay" = 4000
         // 値は "Queue" = "Geometry+1" のようにしてインデックスを明示することもできる
            "Queue"="Transparent"

            // Projector の投影を無視するか?
            "IgnoreProjector"="True"

            // 描画タイプ Opaque:不透明 Transparent:部分的な透過 他
            "RenderType"="Transparent"

            // マテリアルのインスペクター上でのプレビューの表示タイプ。
            // Plane:2D sphere:球体 Skybox:空テクスチャ
            "PreviewType"="Plane"

            // シェーダーがスプライトに用いられる場合やアトラスにパッキングされたとき動作しない場合、CanUseSpriteAtlas タグを “False” に設定します。
            "CanUseSpriteAtlas"="True"
        }

        // ポリンゴンのどちら側をカリング(描画しない)する
        // Back:視点と反対側をカリングする (デフォルト)  
        // Front:視点と同じ側のポリゴンをレンダリングしない。オブジェクトを反転するのに使用 (デフォルト) 
        // Off:カリングを無効にして、すべての面を描画します。特殊なエフェクトで使用します。
        Cull Off

        // ライティングの設定
        // On:ライティングを有効 Off:ライティングを無効
        Lighting Off

        // 深度情報を書き込むか制御する
        // On:不透明なオブジェクトを描画する場合(デフォルト)
        // Off:部分的に透過のエフェクトを描く場合
        ZWrite Off

        // ブレンド
        // ここはいろんなややこしい
        Blend One OneMinusSrcAlpha
 
        Pass
        {
        CGPROGRAM
   // #pragma vertex [関数名]
         // vertという関数内で 頂点(vertex)のプログラムを記述できるようになる。
            #pragma vertex vert
           
            // fragという関数内で 断片(fragment)のプログラムを記述できるようになる。
            #pragma fragment frag

            // よくわからない?
            // 色々定義する箇所?
            #pragma multi_compile _ PIXELSNAP_ON

            // インクルードする
            // 様々な関数が使えるようになる
            #include "UnityCG.cginc"

            // appdata_tという構造体
            struct appdata_t
            {
             // 頂点位置
                float4 vertex   : POSITION;

                // カラー
                float4 color    : COLOR;

                // UV座標とはテクスチャの座標
                // TEXCOORD0 は、第 1 の UV 座標で、一般的に、float2, float3, float4 です。
    // TEXCOORD1, TEXCOORD2, TEXCOORD3 は、それぞれ第 2、第 3、第 4 の UV 座標です。
    // この場合1 つ目のテクスチャ座標を使用
                float2 texcoord : TEXCOORD0;
            };

            // v2fという構造体を作成
            struct v2f
            {
                float4 vertex   : SV_POSITION;
                fixed4 color    : COLOR;
                float2 texcoord  : TEXCOORD0;
            };

            // _Colorの定義
            fixed4 _Color;

            // v2f構造体を返す頂点シェーダーのプログラム 
            // 返り値はfrag関数の引数で使われるらしい
            // appdata_t IN は Unity側から送られるテクスチャ?データ
            v2f vert(appdata_t IN)
            { 
             // OUTという名前でv2f構造体を作成
                v2f OUT;

                // mul ベクトルや行列の掛け算をする
                // UNITY_MATRIX_MVP 現在のモデルビュー行列×射影行列 らしい?
                // 計算したデータの取得
                // この行を消すと表示されない
                OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);

                // UV座標の取得
                OUT.texcoord = IN.texcoord;

                // 元の色に Propertiesで入力された色を掛け合わせている
                OUT.color = IN.color * _Color;

                //PIXELSNAP_ON Onの場合
                #ifdef PIXELSNAP_ON
                OUT.vertex = UnityPixelSnap (OUT.vertex);
                #endif
 
                return OUT;
            }

            // 2Dテクスチャの変数定義
            sampler2D _MainTex;

            // 2Dテクスチャの変数定義
            sampler2D _AlphaTex;

            // float型の変数定義
            float _AlphaSplitEnabled;

            // fixed4を返す関数 引数はfloat2 uv
            fixed4 SampleSpriteTexture (float2 uv)
            {

             // 該当するテクスチャ座標の色を参照
             // _MainTexとuvでtex2Dのピクセルごとの色を作成している?
                fixed4 color = tex2D (_MainTex, uv);

                //fixed4 color = fixed4(1, 1, 1, 1);

                // ALPHASPLITが許可されている場合?
#if UNITY_TEXTURE_ALPHASPLIT_ALLOWED
                if (_AlphaSplitEnabled)
                    color.a = tex2D (_AlphaTex, uv).r;
#endif // UNITY_TEXTURE_ALPHASPLIT_ALLOWED

    // 色を返す
                return color;
            }

            // fixed4を返す断片シェーダーのプログラム
            // vertex頂点シェーダー関数の後に呼ばれる
   // 引数には頂点シェーダー関数(vert)の返り値(OUT)が入ってくる。
   // ピクセル シェーダーは、SV_Depth および SV_Target システム値セマンティクスを持つパラメーターにのみ書き込むことができるらしい
            fixed4 frag(v2f IN) : SV_Target
            {
             // 2DテクスチャとUV座標からテクスチャのピクセルを取り出して、そこにパラメータで指定された色を合わせる
                //fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;

                // テストコード1 IN.colorを消してみる
                // 結果:綺麗に表示されない、透過されない部分がでる。
                //fixed4 c = SampleSpriteTexture (IN.texcoord);

                // テストコード2 SampleSpriteTexture (IN.texcoord)を消してみる 
                // 白で表示される
                //fixed4 c = IN.color;

                // テストコード3  IN.colorを消して白色を掛け合わせてみる
                // 結果:綺麗に表示される
                //fixed4 c = SampleSpriteTexture (IN.texcoord) * fixed4(1, 1, 1, 1);

                // テストコード4  IN.colorを消して半透明を掛け合わせてみる
                // 結果:半透明に表示される
                // IN.colorはプロパティで設定された色である
                //fixed4 c = SampleSpriteTexture (IN.texcoord) * fixed4(1, 1, 1, 0.5);

        // テストコード5加算合成
                fixed4 c = SampleSpriteTexture (IN.texcoord);
                c.rgb += IN.color.rgb;
                c.a *= IN.color.a;
                
                // これがない場合,透過されない部分がでる。
                c.rgb *= c.a;

                return c;
            }
        ENDCG
        }
    }
}


参考サイト
[Unity]ShaderLab入門とか
http://komaken.me/blog/2016/03/07/unityshaderlab%E5%85%A5%E9%96%80%E3%81%A8%E3%81%8B/

"UV"のお話
http://rudora7.blog81.fc2.com/blog-entry-308.html

2017年2月11日土曜日

Unityでファイルの読み書きについて3

Androidのファイルの読み書きについてちょっとめんどくさかったので書きます。
Androidの場合ファイルの読み書きは初回起動時はStreamingAssetsフォルダから読み込み、
2回目以降はApplication.persistentDataPathで読み書きするようにします。

しかもAndroid のみ StreamingAssetsフォルダから読み込む場合 WWW クラスを使わないと読み込めないのが面倒くさいところです。

なぜこういう作りにするのか
StreamingAssetsフォルダを作っておけばビルド先のプラットフォームの、特定のフォルダーに何も変換されない状態で保持されます。しかし読み込みしかできず書き込みができないので
Application.persistentDataPathを使います。読み書きができるデータパスなので
初回はStreamingAssetsから読み込みそれ以降は、Application.persistentDataPathでデータのやり取りをします。


プロジェクトをGitHubに上げておきました。
Unityバージョン  5.5.0f3
https://github.com/templa00/FIleTest

一応ソースコードも貼っておきます。

以下ソースコード

// データを初期化するか?
//#define DATA_INIT

using UnityEngine;
using System.IO;
using System.Text;
using System.Collections;
using System;


/// ファイル管理
public class FIleManager
{
    // StreamingAssetsパス
#if UNITY_EDITOR
    // StreamingAssetsのパス
    public static string StreamingAssetsPath = Application.dataPath + "/StreamingAssets";

    // Android
#elif UNITY_ANDROID
    // パス(Android)
    public static string StreamingAssetsPath = "jar:file://" + Application.dataPath + "!/assets";


    // iOS
#elif UNITY_IPHONE
    public static string StreamingAssetsPath = path = Application.dataPath + "/Raw";


#else
    public static string StreamingAssetsPath = Application.dataPath + "/StreamingAssets";
#endif


    /// ファイル読み込み
    public static IEnumerator ReadFileText (Action callback, string _file_path)
    {
        // 結果
        string result = string.Empty;

        // ファイル
        FileInfo file;

        // 初回読み込みか?
        bool is_init_load = false;

        // 保存データパス
        var save_file_path = Application.persistentDataPath + _file_path;

        // 初回起動時ロードパス
        var init_file_path = StreamingAssetsPath + _file_path;

        // 読み込み先パス
        string load_path = string.Empty;

        

        // 保存先にデータがある場合
        if (File.Exists(save_file_path))
        {

            // データを初期化する場合
#if DATA_INIT

            Debug.Log("強制的に初回読み込み");

            // 初回起動時と同じ処理をするようにする
            is_init_load = true;

            // セーブデータ読み込み
            file = new FileInfo(save_file_path);
            
            // ファイル削除
            file.Delete();
            
            // 初回パスデータを取得する
            load_path = init_file_path;
#else
            Debug.Log("2回目以降の読み込み");

            // セーブデータのパスを設定する
            load_path = save_file_path;
#endif
        }
        // 初回起動時
        else
        {
            Debug.Log("初回読み込み");

            is_init_load = true;

            // 初回パスデータを取得する
            load_path = init_file_path;
        }

        Debug.Log("データ読込パス : " + load_path);

        // win or ios
#if UNITY_EDITOR || UNITY_IPHONE

        // JSONファイルを読み込む
        file = new FileInfo(load_path);
        using (StreamReader sr = new StreamReader(file.OpenRead(), Encoding.UTF8))
        {
            result = sr.ReadToEnd();
        }
        yield return new WaitForSeconds(0f);
        // Android
#elif UNITY_ANDROID
        // 初回ロードの場合
        if (init_load_data)
        {
            WWW www = new WWW(path);
            /// wwwの通信が終わるまで待機
            yield return www;

            string txtBuffer = string.Empty;
            TextReader txtReader = new StringReader(www.text);
            string description = string.Empty;
            while ((txtBuffer = txtReader.ReadLine()) != null)
            {
                description = description + txtBuffer + "\r\n";
                Debug.Log("description : " + description);
            }
            result = description;
        }
        // 初回ロードではない場合
        else
        {
            // ファイルを読み込む
            file = new FileInfo(path);
            using (StreamReader sr = new StreamReader(file.OpenRead(), Encoding.UTF8))
            {
                result = sr.ReadToEnd();
            }
            yield return new WaitForSeconds(0f);
        }
#endif
        callback(result);
    }

    /// ファイルを上書きする
    public static void WriteText (string _folda_path, string _file_name, string _contents)
    {
        // 保存フォルダパス
        var save_folda_path = Application.persistentDataPath + _folda_path;

        // 保存データパス
        var save_path = Application.persistentDataPath + _folda_path + _file_name;

        // フォルダがある場合
        if (Directory.Exists(save_folda_path))
        {
            Debug.Log("フォルダがあります");
        }
        else
        {
            Debug.Log("フォルダが無いので作成します");

            // ディレクトリ作成
            Directory.CreateDirectory(save_folda_path);
        }

        // 保存先 persistentDataPath
        // [win] : C:/Users/HomePC/AppData/LocalLow/DefaultCompany/プロジェクト名/save_data.json
        // [Android] : /data/app/xxxx.apk
        // [ios] : /var/mobile/Applications/xxxxxx/myappname.app/Data
        FileInfo file = new FileInfo(save_path);

        Debug.Log("保存先パス : " + save_path);

        // ファイルがある場合
        if (File.Exists(save_path))
        {
            // 削除
            file.Delete();
        }

        // 書き込み用ストリーム
        StreamWriter stream;

        stream = file.AppendText();      // StreamWriter を作成
        stream.WriteLine(_contents);     // 書き込み
        stream.Flush();                  // バッファ書き込み
        stream.Close();                  // 閉じる
    }
}

以下、使用方法

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

public class FileTest : MonoBehaviour {

 // Use this for initialization
 IEnumerator Start ()
    {
        // テストデータの文字列
        string test_data_str = string.Empty;

        // TestData読み込み(ロード)
        yield return FIleManager.ReadFileText(r => test_data_str = r, "/Data/TestData.txt");

        // 読み込んだテストデータの文字列を表示
        Debug.Log(test_data_str);

        // TestData書き込み(セーブ)
        FIleManager.WriteText("/Data", "/TestData.txt", "テストデータに書き込みました!");

        // 書き込んだTestDataを読み込み(ロード)
        yield return FIleManager.ReadFileText(r => test_data_str = r, "/Data/TestData.txt");

        // 読み込んだテストデータの文字列を表示
        Debug.Log(test_data_str);
    }
}

実行したら以下のようなログが出力されればOKです。
windowsでデバッグしました。
Androidでデバッグする場合はMonitorなどを使用するとログが表示されます。

初回起動時








2回目以降の起動時












2017年2月5日日曜日

スマホVRゴーグル買いました

購入したVRゴーグル 4〜6インチ対応(使用スマホ iPhone SE)


ついでに購入したBluetoothゲームパッド 単4電池をまだ購入していないため未使用

スマホVRについて

スマホVRとはスマホでVR体験ができるというものだ、必要なものはスマホ、VRゴーグル、VRアプリである。
購入したVRゴーグルは少し高いが目が悪くても裸眼で見える、眼鏡をつけながら見るのは無理だと思う。スマホである程度の質で楽しみたいならこれらしい。というかAmazonで評価が高かったので買いましたが、欠点としてはスマホを装着するとき傷つきやすい。
2、3千円のVRゴーグルもあるのでどんなものか手軽に体験したいだけの場合そっちを買ったほうがいのかも。ただ質が悪い?
Bluetoothゲームパッドも買ってみたがまだ未使用。
ただ買った日だけ使ってあまり使ってない。

以下体験したVRアプリ iOSリンク



アプリリンク作成サイト
http://banners.itunes.apple.com/jp/catalog

ジェットコースター系はVR入門としてはいいと思う。
VRアプリの現状、自分が動かず風景を見るようなアプリが多い。

ただVRミュージアムは首を動かして移動ができる。600円で有料だがよくできていると思う。
ただ女性にはお勧めできないかも。
難点としては移動するとき首が疲れる。いろいろ疲れる。酔う。
ラブラがいないのはなんでなんだろう。アップデートで対応される?

VRJumpについてこのアプリはなかなかすごかった。
無料アプリでいろんな場所でジャンプするアプリである。
実際VRを付けたままジャンプすると、すごいジャンプするのだ!なかなかすごい!

ただスマホVRなのか他のVRもなのか結構ぼやけててドットが見えるのがどうなんだろうと思う。
もっと解像度が必要?
VRアプリの形態については風景を見るものや、コントローラーで操作するものが主流になるのだろうか?
自分の体がリンクするようになるのだろうか?
ただVRはいろいろ難点があると思う。

その他のVRについて


・google  Daydream 

高品質なスマホVRらしい なかなか面白そう。
参考サイト
http://www.moguravr.com/daydream-google-vr-matome/

・任天堂 Nintendo Switch 

VR対応するかもしれない。ただすごい重そう。
参考サイト
http://japanese.engadget.com/2016/12/17/nintendo-switch-vr-vr/

・マイクロソフト HoloLens 

レンズなので自分の手が見え実際にそこにあるかのように見える、視野角が狭いのが難点あと単純に値段が高い、自分の手が見えるということはHoloLensをつけながらプログラミングがしやすいと思う。現実世界の座標に固定したホログラムがブレない、HoloLensが赤外線を周囲に照射し、センサーが反射光の戻る時間を測定して空間を認識します。じっくり見つめるほど精度はよくなりますが、赤外線を使っているので、光を通すガラスや光を吸収する黒いモノは苦手らしいです。。未来感が一番ある。視野角広くなってある程度安くなったら欲しい、PCの代わりになるなら欲しい。
参考サイト
https://www.microsoft.com/microsoft-hololens/ja-jp

https://www.webprofessional.jp/hololens-mr-device-of-attention-what-developers-are-not-interested-in-games-and-vr/

・Vivi と Oculus 

今エロゲーをするならこれだと思う。
参考サイト
http://vrinside.jp/news/vive-vs-oculus-rift-touch-roomscale/

・Ps VR

品切れらしい、今はそこまで欲しくはない。
参考サイト 
 http://www.jp.playstation.com/psvr/


今後のVR

スマホさえ持っていれば、安価でVRゴーグルが買えるので学生とかも手は出しやすいと思う。
一番期待してるのはHoloLens、現実とリンクしてる感がすごいと思う。
赤外線で距離を測るのは、デジタルカメラのオートフォーカス(ピント調整)に似てるような気がする。
ただデジタルカメラのオートフォーカスはコントラスト(色・トーン・形などの差違)で見ているらしい。ぼやけるとコントラストがにじむのでコントラストが高くなる位置に合わせる方法や、顔認識でピントを合わせるようになっているらしい。いろんな方法が試されるのは面白い。
 
オートフォーカスについて
参考サイト
http://web.canon.jp/technology/kids/mystery/m_03_03.html

話がずれたが、VRが成功するには、重いのと目が疲れるのと酔うのと値段が高いのが手を出しずらい要因となっていると思う。
VRゴーグルは手を出しやすいが性能があまりよろしくない、、、が、これからVRは成長していくと思うので、期待している。
Google Glassには少し期待していた。

VRアプリもUnityで簡単?にある程度楽に作れるので作ってみたいと思っている。

【早い者勝ち!】 あなたのお名前、残ってる?

シャドウバースにPC版が誕生