おもちゃラボ

Unityで遊びを作ってます

【Blender】ASCIIのFBXファイルが読み込めない場合の対処法

Blenderでfbxファイルをインポートしようとすると、次のようなエラーが出ることがあります。

ASCII FBX files are not supported

f:id:nn_hokuson:20170328193629p:plain:w250

これはBlenderがASCII形式のFBXファイルをサポートしていないために出るエラーです。
Blenderで読み込めるようにするためには、FBXファイルをBinary形式に変換する必要があります

FBXファイルをBinary形式にするには次のAutodesk FBX Converterを使います。
FBX ConverterはWindows用とMac用が用意されているので、次のサイトからお使いの環境のものをインストールしてください。

usa.autodesk.com

使い方は非常に簡単で、FBX Converterが起動したら、画面左上の「Add FBX Converter」をクリックしてください。

f:id:nn_hokuson:20170328193639p:plain:w400

次のようなウインドウが表示されるので、左側の欄に変換したいFBXファイルをドラッグ&ドロップします。

画面右側に出力フォーマットの選択画面が出るので、出力フォルダと出力形式(FBX 2013かOBJあたり・・・)を選択し、「FBX Save Mode」が「Binary」になっているのを確認してから、「Convert」ボタンを押してください。

変換する3Dモデルがメッシュだけならobj形式でも問題ありませんが、ボーンが入っているモデルの場合はfbx形式を選択して下さい。

f:id:nn_hokuson:20170328193652p:plain:w600

指定したパスにBinary形式のFBXファイルが生成されるので、これをBlenderで読み込めば、正しく3Dモデルが表示されます。

【Unityシェーダ入門】トゥーンシェーダを自作してみる

Unityではフォトリアルな映像を作るのは簡単ですが、風ノ旅ビトやGravity dazeなどの独自にデフォルメされた映像を作るためにはシェーダを使う必要があります。

f:id:nn_hokuson:20170327192144j:plain

その中でアニメ風の映像を作るためには、トゥーンシェーダ(Toon Shader)を使います。UnityにはEffectパッケージにToonシェーダが用意されているのですが、ここでは勉強のためにトゥーンシェーダを自作してみましょう。

Unityに標準で付属するToonシェーダについては、↓の記事で非常にわかりやすく説明されていますよ〜!
tips.hecomi.com

Toonシェーダのアルゴリズム

今回作るトゥーンシェーダではRampテクスチャと呼ばれる、色をサンプリングするための専用テクスチャを使います。

f:id:nn_hokuson:20170327201313p:plain:w300

オブジェクト表面の暗い所はこの色、明るい所はこの色、といった感じでRampテクスチャから色を取得していきます。

f:id:nn_hokuson:20170327193602p:plain:w400

では、どのようにして暗い・明るいを計算するのでしょうか。
オブジェクト表面の明るさを計算するためには、ライトの方向とオブジェクトの法線の内積を取ります。これにより、ライトと法線方向が一致する場合は値が大きくなり、ライトと法線が垂直に近づくにつれて値が小さくなります。

f:id:nn_hokuson:20170327193238p:plain:w500

ライトの方向とオブジェクトの法線の内積で明るさが計算できたら、それを0〜1にスケーリングしてRampテクスチャのu座標とします。
Rampテクスチャはu座標に沿って段階的に明るくなるため、アニメのような明るさの変化がしっかり出る表現になります。

Toonシェーダファイルを作る

まずはトゥーンシェーダ用のシェーダファイルと、シェーダをアタッチしたマテリアルを作ります。

プロジェクトビューで「Create」→「Shader」→「Standard Surface Shader」を選択し、「Toon」というファイル名で保存します。
このファイルを選択した状態で、「Create」→「Material」を選択し、Toonマテリアルを作成してください。作成したマテリアルにToonシェーダが使われていればOKです。

f:id:nn_hokuson:20170327192212p:plain:w400

Toonシェーダのファイルに次のシェーダプログラムを入力してください。

Shader "Custom/Toon" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _RampTex ("Ramp", 2D) = "white"{}
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200
        
        CGPROGRAM
        #pragma surface surf ToonRamp
        #pragma target 3.0

        sampler2D _MainTex;
        sampler2D _RampTex;

        struct Input {
            float2 uv_MainTex;
        };

        fixed4 _Color;

        fixed4 LightingToonRamp (SurfaceOutput s, fixed3 lightDir, fixed atten)
        {
            half d = dot(s.Normal, lightDir)*0.5 + 0.5;
            fixed3 ramp = tex2D(_RampTex, fixed2(d, 0.5)).rgb;
            fixed4 c;
            c.rgb = s.Albedo * _LightColor0.rgb * ramp;
            c.a = 0;
            return c;
        }

        void surf (Input IN, inout SurfaceOutput o) {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

今回作成するToonシェーダの主要な部分はLightingToonRampメソッドです。このメソッドでは上のアルゴリズムどおり、ライトの方向とオブジェクトの法線の内積を取り、その値に応じてRampテクスチャから値を取得しています。

このLightingToonRampメソッドはカスタムライティングを行うためのメソッドです。どのようにして、カスタムライティングのためのメソッドを作るのかを下で紹介します。

サーフェイスシェーダでカスタムライティングを使う

トゥーンシェーダのプログラムは、これまで出てきたsurfシェーダだけでは作れません。サーフェイスシェーダに追加するかたちで、ライティングの工程をフックする必要があります。

前の記事でも書いたとおり、Unityのサーフェイスシェーダは頂点シェーダ(Vertex)とマテリアルの表面の色を定義するサーフェイスシェーダ(Surf)、ライティングシェーダ(Lighting)の3つに分かれていましたね。

nn-hokuson.hatenablog.com

これまでは、VertexとLightingシェーダはUnityにお任せしていました。

f:id:nn_hokuson:20170401082008p:plain

今回は自分でライティングを行いたいのでライティングの工程をフックします。

f:id:nn_hokuson:20170401082156p:plain

ライティングの工程をフックするためには次の3つの手順が必要になります。

  1. ライティング用のメソッド(Lighting◯◯◯)を作る
  2. メソッド名をUnityに伝える
  3. StandardSurfaceOutputを使わないようにする

まずはライティング用のメソッドを作ります。ライティング用のメソッド名はLightingから始める必要があります。ここではLightingToonRampという名前にしました。

fixed4 LightingToonRamp (SurfaceOutput s, fixed3 lightDir, fixed atten)

また、ライティングのメソッドをフックしたことをUnityに伝えるため、#pragmaの行にライティングのメソッド名の◯◯◯の部分を追加します。

#pragma surface surf ToonRamp

surfシェーダでライティングの工程をフックした場合には、surfの出力にはSurfaceOutputStandard型を使うことができません。ここではSurfaceOutput型に書き換えています。
SurfaceOutput型にはEmmisionやSmoothnessは定義されていないので、surfメソッドからこれらの行も削除します。

Rampテクスチャをマテリアルにセットする

最後にToonシェーダのマテリアルにRampテクスチャをセットします。プロジェクトビューでToonマテリアルを選択し、インスペクタからRampの欄にRampテクスチャをドラッグ&ドロップして下さい。

f:id:nn_hokuson:20170327201900p:plain:w300

実行すると次のようなシェーダの表現になりました。Rampテクスチャを差し替えるだけで、さまざまな絵作りが出来るので、試してみて下さい!
f:id:nn_hokuson:20170327192812p:plain:w500

【Arduino】マウスホイール(ロータリーエンコーダ)の回転量を取得する

マウスホイール(ロータリーエンコーダ)がどれだけ回転したかをArduinoで調べる方法を紹介します。

f:id:nn_hokuson:20170326102120j:plain

まずはマウスを分解して、ホイール部分だけ取り出しました。

f:id:nn_hokuson:20170326082121p:plain:w350

取り出したホイールとロータリーエンコーダはこんな感じです。ロータリーエンコーダの足にはワイヤをつけています。

f:id:nn_hokuson:20170326083121j:plain:w350

ロータリーエンコーダの仕組み

ロータリーエンコーダには3本の足があり、1本はGNDで、残り2本からパルス(A相とB相)が出力されます。出力される2相の信号は位相が90度がずれるように作られています。
2相の信号の出力を見ることで、正回転と逆回転を判別することができます。

正回転の場合は下図のように、A相がB相に対して位相が90度進みます。
そこで前回と今回のA相B相の値が(1110、0100、0010、1011)になっている場合は正回転しているとみなすことができます。

f:id:nn_hokuson:20170326091440p:plain:w300

一方、逆回転の場合はA相がB相に対して90度遅れるので下図のようになります。そこで、前回と今回のA相B相の値が(1110、1000、0001、0111)になっている場合には逆回転とみなします。

f:id:nn_hokuson:20170326091446p:plain:w300

マウスホイールとArduinoをつなぐ回路図

マウスホイールのロータリーエンコーダとArduinoをつなぐ回路図は次のようになります。

f:id:nn_hokuson:20170326093205j:plain:w300

ここではロータリーエンコーダのA相とB相をArduinoの2番ポートと3番ポートに接続しています。ロータリーエンコーダから出ているもう一本はGNDに接続します。

Aruduinoのプログラム

ロータリーエンコーダの値を検出するArduinoのプログラムは次のとおりです。

volatile int value = 0;
volatile uint8_t prev = 0;

void setup() 
{
  pinMode(2, INPUT); 
  pinMode(3, INPUT);
  
  attachInterrupt(0, updateEncoder, CHANGE);
  attachInterrupt(1, updateEncoder, CHANGE);
  digitalWrite(2, HIGH);
  digitalWrite(3, HIGH);
  
  Serial.begin(9600);
}

void updateEncoder()
{
  uint8_t a = digitalRead(2);
  uint8_t b = digitalRead(3);
 
  uint8_t ab = (a << 1) | b;
  uint8_t encoded  = (prev << 2) | ab;

  if(encoded == 0b1101 || encoded == 0b0100 || encoded == 0b0010 || encoded == 0b1011){
    value ++;
  } else if(encoded == 0b1110 || encoded == 0b0111 || encoded == 0b0001 || encoded == 0b1000) {
    value --;
  }

  prev = ab;
}

void loop()
{
   Serial.println(value);
}

今回は波形が変化したタイミングでロータリーエンコーダからの値を読み出したいので、割り込みの仕組みを使っています。割り込みを使うことで、ピンに入力されている値が変化したタイミングで、指定した関数を実行することができます。

Arduinoで割り込みを使うにはattachInterrupt関数を使います。第一引数には使用するポートのインデックス(2番ポートならインデックスは0、3番ポートならインデックスは1になります・・・ややこしい!)を指定します。

第二引数に呼び出す割り込み用の関数名、第三引数には値がどのように変化(LOW, RISING, FALLING, CHANGE)したときに割り込みの関数を呼び出すかを指定します。

  • LOW ピンがLOWのとき発生
  • CHANGE ピンの状態が変化したときに発生
  • RISING ピンの状態がLOWからHIGHに変わったときに発生
  • FALLING ピンの状態がHIGHからLOWに変わったときに発生

f:id:nn_hokuson:20170326094506p:plain:w200

ここでは2番ポートと3番ポートを割り込みに使用して、波形が変化(CHANGE)した場合にupdateEncoder関数を呼びだしています。

updateEncoder関数の中では2番ポートと3番ポートから、ロータリーエンコーダのA相とB相の値を読み出し、前回のA相B相の値と繋げて4bitの値にして、正回転か逆回転かを判定しています。

実行結果

マウスのホイールを回した結果がこちらです。分かりやすいように、エンコードした値が偶数のときにはLEDを点灯、奇数の場合には消灯しています。

www.youtube.com

今回はこちらの記事を参考にさせていただきました!
マウスホイールでトイレットペーパの使用量を調べるって・・・・斬新w
面白いなぁ〜^^/
eleclog.quitsq.com

おまけ

使うマイコンによっては割り込みを使えない場合もあります。その場合は次のようにloop関数の中で割り込みをエミュレートしましょう。

void loop()
{
    int a = (PIND & _BV(2))>>2;
    int b = (PIND & _BV(3))>>3;

    if( a != prevA || b != prevB ){
      updateEncoder(a, b);
    }

    prevA = a;
    prevB = b;
}

ポート2とポート3の入力を調べるときにdigitalRead関数を使うかわりに、PIND & _BV(N)を使って高速化しています。

また、これで得られる値は0x00010や0x00100なので、1/0に変換するためにビットシフトしています。詳しくは次のサイトが参考になりました。

ehbtj.com